From edbd5ab8d4512a7adb089f3e4791e54523748253 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Tue, 4 Oct 2011 18:26:48 +0200 Subject: Add noVNC HTML5 client connect possibility to our http server. Pure JavaScript, no Java plugin required anymore! (But a recent browser...) --- classes/index.vnc | 10 +- classes/novnc/LICENSE.txt | 33 + classes/novnc/README.md | 93 + classes/novnc/favicon.ico | 1 + classes/novnc/images/clipboard.png | Bin 0 -> 501 bytes classes/novnc/images/connect.png | Bin 0 -> 404 bytes classes/novnc/images/ctrlaltdel.png | Bin 0 -> 317 bytes classes/novnc/images/disconnect.png | Bin 0 -> 1378 bytes classes/novnc/images/drag.png | Bin 0 -> 963 bytes classes/novnc/images/favicon.ico | Bin 0 -> 1150 bytes classes/novnc/images/favicon.png | Bin 0 -> 453 bytes classes/novnc/images/keyboard.png | Bin 0 -> 1283 bytes classes/novnc/images/mouse_left.png | Bin 0 -> 511 bytes classes/novnc/images/mouse_middle.png | Bin 0 -> 517 bytes classes/novnc/images/mouse_none.png | Bin 0 -> 497 bytes classes/novnc/images/mouse_right.png | Bin 0 -> 513 bytes classes/novnc/images/screen_320x460.png | Bin 0 -> 12778 bytes classes/novnc/images/screen_57x57.png | Bin 0 -> 1807 bytes classes/novnc/images/screen_700x700.png | Bin 0 -> 17930 bytes classes/novnc/images/settings.png | Bin 0 -> 2495 bytes classes/novnc/include/Orbitron700.ttf | Bin 0 -> 38580 bytes classes/novnc/include/Orbitron700.woff | Bin 0 -> 17472 bytes classes/novnc/include/base.css | 380 ++++ classes/novnc/include/base64.js | 147 ++ classes/novnc/include/black.css | 45 + classes/novnc/include/blue.css | 27 + classes/novnc/include/des.js | 273 +++ classes/novnc/include/display.js | 671 +++++++ classes/novnc/include/input.js | 1884 ++++++++++++++++++++ classes/novnc/include/logo.js | 1 + classes/novnc/include/playback.js | 90 + classes/novnc/include/rfb.js | 1613 +++++++++++++++++ classes/novnc/include/ui.js | 629 +++++++ classes/novnc/include/util.js | 276 +++ classes/novnc/include/vnc.js | 42 + classes/novnc/include/web-socket-js/README.txt | 109 ++ .../novnc/include/web-socket-js/WebSocketMain.swf | Bin 0 -> 175746 bytes classes/novnc/include/web-socket-js/swfobject.js | 4 + classes/novnc/include/web-socket-js/web_socket.js | 341 ++++ classes/novnc/include/websock.js | 347 ++++ classes/novnc/include/webutil.js | 148 ++ classes/novnc/vnc.html | 180 ++ classes/novnc/vnc_auto.html | 116 ++ libvncserver/httpd.c | 10 +- 44 files changed, 7462 insertions(+), 8 deletions(-) create mode 100644 classes/novnc/LICENSE.txt create mode 100644 classes/novnc/README.md create mode 120000 classes/novnc/favicon.ico create mode 100644 classes/novnc/images/clipboard.png create mode 100644 classes/novnc/images/connect.png create mode 100644 classes/novnc/images/ctrlaltdel.png create mode 100644 classes/novnc/images/disconnect.png create mode 100644 classes/novnc/images/drag.png create mode 100644 classes/novnc/images/favicon.ico create mode 100644 classes/novnc/images/favicon.png create mode 100644 classes/novnc/images/keyboard.png create mode 100644 classes/novnc/images/mouse_left.png create mode 100644 classes/novnc/images/mouse_middle.png create mode 100644 classes/novnc/images/mouse_none.png create mode 100644 classes/novnc/images/mouse_right.png create mode 100644 classes/novnc/images/screen_320x460.png create mode 100644 classes/novnc/images/screen_57x57.png create mode 100644 classes/novnc/images/screen_700x700.png create mode 100644 classes/novnc/images/settings.png create mode 100644 classes/novnc/include/Orbitron700.ttf create mode 100644 classes/novnc/include/Orbitron700.woff create mode 100644 classes/novnc/include/base.css create mode 100644 classes/novnc/include/base64.js create mode 100644 classes/novnc/include/black.css create mode 100644 classes/novnc/include/blue.css create mode 100644 classes/novnc/include/des.js create mode 100644 classes/novnc/include/display.js create mode 100644 classes/novnc/include/input.js create mode 100644 classes/novnc/include/logo.js create mode 100644 classes/novnc/include/playback.js create mode 100644 classes/novnc/include/rfb.js create mode 100644 classes/novnc/include/ui.js create mode 100644 classes/novnc/include/util.js create mode 100644 classes/novnc/include/vnc.js create mode 100644 classes/novnc/include/web-socket-js/README.txt create mode 100644 classes/novnc/include/web-socket-js/WebSocketMain.swf create mode 100644 classes/novnc/include/web-socket-js/swfobject.js create mode 100644 classes/novnc/include/web-socket-js/web_socket.js create mode 100644 classes/novnc/include/websock.js create mode 100644 classes/novnc/include/webutil.js create mode 100644 classes/novnc/vnc.html create mode 100644 classes/novnc/vnc_auto.html diff --git a/classes/index.vnc b/classes/index.vnc index 63b2f56..6997693 100644 --- a/classes/index.vnc +++ b/classes/index.vnc @@ -13,6 +13,12 @@ $USER's $DESKTOP desktop ($DISPLAY) -
-www.TightVNC.com +
+
+If the above Java applet does not work, you can also try the new JavaScript-only viewer. You will need a HTML5-capable browser though. +Click here to connect using noVNC. +
+
+
+LibVNCServer/libVNCClient Homepage diff --git a/classes/novnc/LICENSE.txt b/classes/novnc/LICENSE.txt new file mode 100644 index 0000000..755ace3 --- /dev/null +++ b/classes/novnc/LICENSE.txt @@ -0,0 +1,33 @@ +noVNC is Copyright (C) 2011 Joel Martin + +Some portions of noVNC are copyright to their individual authors. +Please refer to the individual source files and/or to the noVNC commit +history: https://github.com/kanaka/noVNC/commits/master + +noVNC is licensed under the LGPL (GNU Lesser General Public License) +version 3 with the following exceptions (all LGPL-3 compatible): + + include/input.js : LGPL-2 or any later version + + include/base64.js : Dual GPL-2 or LGPL-2.1 + + include/des.js : Various BSD style licenses + + include/web-socket-js/ : New BSD license. Source code at + http://github.com/gimite/web-socket-js + + include/Orbitron* : SIL Open Font License 1.1 + (Copyright 2009 Matt McInerney) + + images/ : Creative Commons Attribution-ShareAlike + http://creativecommons.org/licenses/by-sa/3.0/ + +The license texts are included at: + docs/LICENSE.LGPL-3 and + docs/LICENSE.GPL-3 + docs/LICENSE.OFL-1.1 + +Or alternatively the license texts may be found here: + http://www.gnu.org/licenses/lgpl.html and + http://www.gnu.org/licenses/gpl.html + http://scripts.sil.org/OFL diff --git a/classes/novnc/README.md b/classes/novnc/README.md new file mode 100644 index 0000000..4672969 --- /dev/null +++ b/classes/novnc/README.md @@ -0,0 +1,93 @@ +## noVNC: HTML5 VNC Client + + +### Description + +noVNC is a VNC client implemented using HTML5 technologies, +specifically Canvas and WebSockets (supports 'wss://' encryption). +noVNC is licensed under the +[LGPLv3](http://www.gnu.org/licenses/lgpl.html). + +Special thanks to [Sentry Data Systems](http://www.sentryds.com) for +sponsoring ongoing development of this project (and for employing me). + +There are many companies/projects that have integrated noVNC into +their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), and [SlapOS](http://www.slapos.org). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links. + +Notable commits, announcements and news are posted to +@noVNC + + +### Screenshots + +Running in Chrome before and after connecting: + +  + +See more screenshots here. + + +### Browser Requirements + +* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS + Safari, Opera 11+, Internet Explorer 9+, etc. + +* HTML5 WebSockets: For browsers that do not have builtin + WebSockets support, the project includes + web-socket-js, + a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in + WebSocket support. + +* Fast Javascript Engine: noVNC avoids using new Javascript + functionality so it will run on older browsers, but decode and + rendering happen in Javascript, so a slow Javascript engine will + mean noVNC is painfully slow. + +* I maintain a more detailed browser compatibility list here. + + +### Server Requirements + +Unless you are using a VNC server with support for WebSockets +connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver) +currently), you need to use a WebSockets to TCP socket proxy. There is +a python proxy included ('websockify'). One advantage of using the +proxy is that it has builtin support for SSL/TLS encryption (i.e. +"wss://"). + +There a few reasons why a proxy is required: + + 1. WebSockets is not a pure socket protocol. There is an initial HTTP + like handshake to allow easy hand-off by web servers and allow + some origin policy exchange. Also, each WebSockets frame begins + with 0 ('\x00') and ends with 255 ('\xff'). + + 2. Javascript itself does not have the ability to handle pure byte + arrays. The python proxy encodes the data as base64 so that the + Javascript client can decode the data as an integer array. + + +### Quick Start + +* Use the launch script to start a mini-webserver and the WebSockets + proxy (websockify). The `--vnc` option is used to specify the location of + a running VNC server: + + `./utils/launch.sh --vnc localhost:5901` + +* Point your browser to the cut-and-paste URL that is output by the + launch script. Enter a password if the VNC server has one + configured. Hit the Connect button and enjoy! + + +### Other Pages + +* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL + certificate, starting a VNC server, advanced websockify usage, etc. + +* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects. + +* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems. + + diff --git a/classes/novnc/favicon.ico b/classes/novnc/favicon.ico new file mode 120000 index 0000000..45399c8 --- /dev/null +++ b/classes/novnc/favicon.ico @@ -0,0 +1 @@ +images/favicon.ico \ No newline at end of file diff --git a/classes/novnc/images/clipboard.png b/classes/novnc/images/clipboard.png new file mode 100644 index 0000000..24df33c Binary files /dev/null and b/classes/novnc/images/clipboard.png differ diff --git a/classes/novnc/images/connect.png b/classes/novnc/images/connect.png new file mode 100644 index 0000000..79e71ad Binary files /dev/null and b/classes/novnc/images/connect.png differ diff --git a/classes/novnc/images/ctrlaltdel.png b/classes/novnc/images/ctrlaltdel.png new file mode 100644 index 0000000..31922e5 Binary files /dev/null and b/classes/novnc/images/ctrlaltdel.png differ diff --git a/classes/novnc/images/disconnect.png b/classes/novnc/images/disconnect.png new file mode 100644 index 0000000..8832f5e Binary files /dev/null and b/classes/novnc/images/disconnect.png differ diff --git a/classes/novnc/images/drag.png b/classes/novnc/images/drag.png new file mode 100644 index 0000000..433f896 Binary files /dev/null and b/classes/novnc/images/drag.png differ diff --git a/classes/novnc/images/favicon.ico b/classes/novnc/images/favicon.ico new file mode 100644 index 0000000..c999634 Binary files /dev/null and b/classes/novnc/images/favicon.ico differ diff --git a/classes/novnc/images/favicon.png b/classes/novnc/images/favicon.png new file mode 100644 index 0000000..e2bdb19 Binary files /dev/null and b/classes/novnc/images/favicon.png differ diff --git a/classes/novnc/images/keyboard.png b/classes/novnc/images/keyboard.png new file mode 100644 index 0000000..f797952 Binary files /dev/null and b/classes/novnc/images/keyboard.png differ diff --git a/classes/novnc/images/mouse_left.png b/classes/novnc/images/mouse_left.png new file mode 100644 index 0000000..1de7a48 Binary files /dev/null and b/classes/novnc/images/mouse_left.png differ diff --git a/classes/novnc/images/mouse_middle.png b/classes/novnc/images/mouse_middle.png new file mode 100644 index 0000000..81fbd9b Binary files /dev/null and b/classes/novnc/images/mouse_middle.png differ diff --git a/classes/novnc/images/mouse_none.png b/classes/novnc/images/mouse_none.png new file mode 100644 index 0000000..93dbf57 Binary files /dev/null and b/classes/novnc/images/mouse_none.png differ diff --git a/classes/novnc/images/mouse_right.png b/classes/novnc/images/mouse_right.png new file mode 100644 index 0000000..355b25d Binary files /dev/null and b/classes/novnc/images/mouse_right.png differ diff --git a/classes/novnc/images/screen_320x460.png b/classes/novnc/images/screen_320x460.png new file mode 100644 index 0000000..172ec55 Binary files /dev/null and b/classes/novnc/images/screen_320x460.png differ diff --git a/classes/novnc/images/screen_57x57.png b/classes/novnc/images/screen_57x57.png new file mode 100644 index 0000000..e2085f2 Binary files /dev/null and b/classes/novnc/images/screen_57x57.png differ diff --git a/classes/novnc/images/screen_700x700.png b/classes/novnc/images/screen_700x700.png new file mode 100644 index 0000000..ae67768 Binary files /dev/null and b/classes/novnc/images/screen_700x700.png differ diff --git a/classes/novnc/images/settings.png b/classes/novnc/images/settings.png new file mode 100644 index 0000000..a43f5e1 Binary files /dev/null and b/classes/novnc/images/settings.png differ diff --git a/classes/novnc/include/Orbitron700.ttf b/classes/novnc/include/Orbitron700.ttf new file mode 100644 index 0000000..e28729d Binary files /dev/null and b/classes/novnc/include/Orbitron700.ttf differ diff --git a/classes/novnc/include/Orbitron700.woff b/classes/novnc/include/Orbitron700.woff new file mode 100644 index 0000000..61db630 Binary files /dev/null and b/classes/novnc/include/Orbitron700.woff differ diff --git a/classes/novnc/include/base.css b/classes/novnc/include/base.css new file mode 100644 index 0000000..0a62a1b --- /dev/null +++ b/classes/novnc/include/base.css @@ -0,0 +1,380 @@ +body { + margin:0; + padding:0; + font-family: Helvetica; + /*Background image with light grey curve.*/ + background-color:#494949; + background-repeat:no-repeat; + background-position:right bottom; + height:100%; +} + +html { + height:100%; +} + +#noVNC_controls ul { + list-style: none; + margin: 0px; + padding: 0px; +} +#noVNC_controls li { + padding-bottom:8px; +} + +#noVNC_host { + width:150px; +} +#noVNC_port { + width: 80px; +} +#noVNC_password { + width: 150px; +} +#noVNC_encrypt { +} +#noVNC_connectTimeout { + width: 30px; +} +#noVNC_path { + width: 100px; +} +#noVNC_connect_button { + width: 110px; + float:right; +} + + +#noVNC_view_drag_button { + display: none; +} +#sendCtrlAltDelButton { + display: none; +} +#noVNC_mobile_buttons { + display: none; +} + +.noVNC-buttons-left { + float: left; + padding-left:10px; + padding-top:4px; +} + +.noVNC-buttons-right { + float:right; + right: 0px; + padding-right:10px; + padding-top:4px; +} + +#noVNC_status_bar { + margin-top: 0px; + padding: 0px; +} + +#noVNC_status_bar div { + font-size: 12px; + padding-top: 4px; + width:100%; +} + +#noVNC_status { + height:20px; + text-align: center; +} +#noVNC_settings_menu { + margin: 3px; + text-align: left; +} +#noVNC_settings_menu ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +#noVNC_apply { + float:right; +} + +.noVNC_status_normal { + background: #eee; +} +.noVNC_status_error { + background: #f44; +} +.noVNC_status_warn { + background: #ff4; +} + +/* Do not set width/height for VNC_screen or VNC_canvas or incorrect + * scaling will occur. Canvas resizes to remote VNC settings */ +#noVNC_screen_pad { + margin: 0px; + padding: 0px; + height: 44px; +} +#noVNC_screen { + text-align: center; + display: table; + width:100%; + height:100%; + background-color:#313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_container, #noVNC_canvas { + margin: 0px; + padding: 0px; +} + +#noVNC_canvas { + left: 0px; +} + +#VNC_clipboard_clear_button { + float:right; +} +#VNC_clipboard_text { + font-size: 11px; +} + +#noVNC_clipboard_clear_button { + float:right; +} + +/*Bubble contents divs*/ +#noVNC_settings { + display:none; + margin-top:77px; + right:20px; + position:fixed; +} + +#noVNC_controls { + margin-top:77px; + right:12px; + position:fixed; +} +#noVNC_controls.top:after { + right:15px; +} + +#noVNC_clipboard { + display:none; + margin-top:77px; + right:30px; + position:fixed; +} +#noVNC_clipboard.top:after { + right:85px; +} + +#keyboardinput { + width:1px; + height:1px; + background-color:#fff; + color:#fff; + border:0; + position: relative; + left: -40px; + z-index: -1; +} + +.noVNC_status_warn { + background-color:yellow; +} + +/* + * Advanced Styling + */ + +/* Control bar */ +#noVNC-control-bar { + position:fixed; + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ + + display:block; + height:44px; + left:0; + top:0; + width:100%; + z-index:200; +} + +.noVNC_status_button { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #869dbc; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + +.noVNC_status_button_selected { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #4366a9; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + background: #779ced; /* Old browsers */ + background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + + +/*Settings Bubble*/ +.triangle-right { + position:relative; + padding:15px; + margin:1em 0 3em; + color:#fff; + background:#fff; /* default background for browsers without gradient support */ + /* css3 */ + /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); + background:-moz-linear-gradient(#2e88c4, #075698); + background:-o-linear-gradient(#2e88c4, #075698); + background:linear-gradient(#2e88c4, #075698);*/ + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; + color:#000; + border:2px solid #E0E0E0; +} + +.triangle-right.top:after { + border-color: transparent #E0E0E0; + border-width: 20px 20px 0 0; + bottom: auto; + left: auto; + right: 50px; + top: -20px; +} + +.triangle-right:after { + content:""; + position:absolute; + bottom:-20px; /* value = - border-top-width - border-bottom-width */ + left:50px; /* controls horizontal position */ + border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ + border-style:solid; + border-color:#E0E0E0 transparent; + /* reduce the damage in FF3.0 */ + display:block; + width:0; +} + +.triangle-right.top:after { + top:-40px; /* value = - border-top-width - border-bottom-width */ + right:50px; /* controls horizontal position */ + bottom:auto; + left:auto; + border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ + border-color:transparent #E0E0E0; +} + +/*Default noVNC logo.*/ +/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 700; + src: local('?'), url('Orbitron700.woff') format('woff'), + url('Orbitron700.ttf') format('truetype'); +} + +#noVNC_logo { + margin-top: 170px; + margin-left: 10px; + color:yellow; + text-align:left; + font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + line-height:90%; + text-shadow: + 5px 5px 0 #000, + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 1px 1px 0 #000; +} + + +#noVNC_logo span{ + color:green; +} + +/* ---------------------------------------- + * Media sizing + * ---------------------------------------- + */ + + +.noVNC_status_button { + font-size: 12px; +} + +#noVNC_clipboard_text { + width: 500px; +} + +#noVNC_logo { + font-size: 180px; +} + +@media screen and (min-width: 481px) and (max-width: 640px) { + .noVNC_status_button { + font-size: 10px; + } + #noVNC_clipboard_text { + width: 410px; + } + #noVNC_logo { + font-size: 150px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { + .noVNC_status_button { + font-size: 10px; + } + #noVNC_clipboard_text { + width: 250px; + } + #noVNC_logo { + font-size: 110px; + } +} + +@media screen and (max-width: 320px) { + .noVNC_status_button { + font-size: 9px; + } + #noVNC_clipboard_text { + width: 220px; + } + #noVNC_logo { + font-size: 90px; + } +} diff --git a/classes/novnc/include/base64.js b/classes/novnc/include/base64.js new file mode 100644 index 0000000..c68b33a --- /dev/null +++ b/classes/novnc/include/base64.js @@ -0,0 +1,147 @@ +/* + * Modified from: + * http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla XML-RPC Client component. + * + * The Initial Developer of the Original Code is + * Digital Creations 2, Inc. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Martijn Pieters (original author) + * Samuel Sieb + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/*jslint white: false, bitwise: false, plusplus: false */ +/*global console */ + +var Base64 = { + +/* Convert data (an array of integers) to a Base64 string. */ +toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', +base64Pad : '=', + +encode: function (data) { + "use strict"; + var result = '', + chrTable = Base64.toBase64Table.split(''), + pad = Base64.base64Pad, + length = data.length, + i; + // Convert every three bytes to 4 ascii characters. + for (i = 0; i < (length - 2); i += 3) { + result += chrTable[data[i] >> 2]; + result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; + result += chrTable[data[i+2] & 0x3f]; + } + + // Convert the remaining 1 or 2 bytes, pad out to 4 characters. + if (length%3) { + i = length - (length%3); + result += chrTable[data[i] >> 2]; + if ((length%3) === 2) { + result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += chrTable[(data[i+1] & 0x0f) << 2]; + result += pad; + } else { + result += chrTable[(data[i] & 0x03) << 4]; + result += pad + pad; + } + } + + return result; +}, + +/* Convert Base64 data to a string */ +toBinaryTable : [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +], + +decode: function (data, offset) { + "use strict"; + offset = typeof(offset) !== 'undefined' ? offset : 0; + var binTable = Base64.toBinaryTable, + pad = Base64.base64Pad, + result, result_length, idx, i, c, padding, + leftbits = 0, // number of bits decoded, but yet to be appended + leftdata = 0, // bits decoded, but yet to be appended + data_length = data.indexOf('=') - offset; + + if (data_length < 0) { data_length = data.length - offset; } + + /* Every four characters is 3 resulting numbers */ + result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); + result = new Array(result_length); + + // Convert one by one. + for (idx = 0, i = offset; i < data.length; i++) { + c = binTable[data.charCodeAt(i) & 0x7f]; + padding = (data.charAt(i) === pad); + // Skip illegal characters and whitespace + if (c === -1) { + console.error("Illegal character '" + data.charCodeAt(i) + "'"); + continue; + } + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result[idx++] = (leftdata >> leftbits) & 0xff; + } + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw {name: 'Base64-Error', + message: 'Corrupted base64 string'}; + } + + return result; +} + +}; /* End of Base64 namespace */ diff --git a/classes/novnc/include/black.css b/classes/novnc/include/black.css new file mode 100644 index 0000000..8f80f66 --- /dev/null +++ b/classes/novnc/include/black.css @@ -0,0 +1,45 @@ +#keyboardinput { + background-color:#000; +} + +#noVNC-control-bar { + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.triangle-right { + border:2px solid #fff; + background:#000; + color:#fff; +} + +.noVNC_status_button { + font-size: 12px; + vertical-align: middle; + border:1px solid #4c4c4c; + + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.noVNC_status_button_selected { + background: #9dd53a; /* Old browsers */ + background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ +} diff --git a/classes/novnc/include/blue.css b/classes/novnc/include/blue.css new file mode 100644 index 0000000..a8baf70 --- /dev/null +++ b/classes/novnc/include/blue.css @@ -0,0 +1,27 @@ + +#noVNC-control-bar { + background-color:#04073d; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(10,15,79)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(10,15,79) 54%, + rgb(4,7,61) 50% + ); +} + +.triangle-right { + border:2px solid #fff; + background:#04073d; + color:#fff; +} + +#keyboardinput { + background-color:#04073d; +} + diff --git a/classes/novnc/include/des.js b/classes/novnc/include/des.js new file mode 100644 index 0000000..1f95285 --- /dev/null +++ b/classes/novnc/include/des.js @@ -0,0 +1,273 @@ +/* + * Ported from Flashlight VNC ActionScript implementation: + * http://www.wizhelp.com/flashlight-vnc/ + * + * Full attribution follows: + * + * ------------------------------------------------------------------------- + * + * This DES class has been extracted from package Acme.Crypto for use in VNC. + * The unnecessary odd parity code has been removed. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + * + + * DesCipher - the DES encryption method + * + * The meat of this code is by Dave Zimmerman , and is: + * + * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and + * without fee is hereby granted, provided that this copyright notice is kept + * intact. + * + * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY + * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + * + * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE + * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE + * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE + * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP + * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR + * HIGH RISK ACTIVITIES. + * + * + * The rest is: + * + * Copyright (C) 1996 by Jef Poskanzer . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Visit the ACME Labs Java page for up-to-date versions of this and other + * fine Java utilities: http://www.acme.com/java/ + */ + +"use strict"; +/*jslint white: false, bitwise: false, plusplus: false */ + +function DES(passwd) { + +// Tables, permutations, S-boxes, etc. +var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, + 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, + 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], + totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], + z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, + keys = []; + +a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; +SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, + z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, + a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, + c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; +a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; +SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, + a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, + z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, + z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; +a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; +SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, + b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, + c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, + b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; +a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; +SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, + z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, + b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, + c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; +a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; +SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, + a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, + z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, + c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; +a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; +SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, + z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, + b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, + a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; +a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; +SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, + b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, + b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, + z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; +a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; +SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, + c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, + a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, + z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; + +// Set the key. +function setKeys(keyBlock) { + var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], + raw0, raw1, rawi, KnLi; + + for (j = 0, l = 56; j < 56; ++j, l-=8) { + l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1 + m = l & 0x7; + pc1m[j] = ((keyBlock[l >>> 3] & (1<>> 10; + keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; + ++KnLi; + keys[KnLi] = (raw0 & 0x0003f000) << 12; + keys[KnLi] |= (raw0 & 0x0000003f) << 16; + keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; + keys[KnLi] |= (raw1 & 0x0000003f); + ++KnLi; + } +} + +// Encrypt 8 bytes of text +function enc8(text) { + var i = 0, b = text.slice(), fval, keysi = 0, + l, r, x; // left, right, accumulator + + // Squash 8 bytes to 2 ints + l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; + r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; + + x = ((l >>> 4) ^ r) & 0x0f0f0f0f; + r ^= x; + l ^= (x << 4); + x = ((l >>> 16) ^ r) & 0x0000ffff; + r ^= x; + l ^= (x << 16); + x = ((r >>> 2) ^ l) & 0x33333333; + l ^= x; + r ^= (x << 2); + x = ((r >>> 8) ^ l) & 0x00ff00ff; + l ^= x; + r ^= (x << 8); + r = (r << 1) | ((r >>> 31) & 1); + x = (l ^ r) & 0xaaaaaaaa; + l ^= x; + r ^= x; + l = (l << 1) | ((l >>> 31) & 1); + + for (i = 0; i < 8; ++i) { + x = (r << 28) | (r >>> 4); + x ^= keys[keysi++]; + fval = SP7[x & 0x3f]; + fval |= SP5[(x >>> 8) & 0x3f]; + fval |= SP3[(x >>> 16) & 0x3f]; + fval |= SP1[(x >>> 24) & 0x3f]; + x = r ^ keys[keysi++]; + fval |= SP8[x & 0x3f]; + fval |= SP6[(x >>> 8) & 0x3f]; + fval |= SP4[(x >>> 16) & 0x3f]; + fval |= SP2[(x >>> 24) & 0x3f]; + l ^= fval; + x = (l << 28) | (l >>> 4); + x ^= keys[keysi++]; + fval = SP7[x & 0x3f]; + fval |= SP5[(x >>> 8) & 0x3f]; + fval |= SP3[(x >>> 16) & 0x3f]; + fval |= SP1[(x >>> 24) & 0x3f]; + x = l ^ keys[keysi++]; + fval |= SP8[x & 0x0000003f]; + fval |= SP6[(x >>> 8) & 0x3f]; + fval |= SP4[(x >>> 16) & 0x3f]; + fval |= SP2[(x >>> 24) & 0x3f]; + r ^= fval; + } + + r = (r << 31) | (r >>> 1); + x = (l ^ r) & 0xaaaaaaaa; + l ^= x; + r ^= x; + l = (l << 31) | (l >>> 1); + x = ((l >>> 8) ^ r) & 0x00ff00ff; + r ^= x; + l ^= (x << 8); + x = ((l >>> 2) ^ r) & 0x33333333; + r ^= x; + l ^= (x << 2); + x = ((r >>> 16) ^ l) & 0x0000ffff; + l ^= x; + r ^= (x << 16); + x = ((r >>> 4) ^ l) & 0x0f0f0f0f; + l ^= x; + r ^= (x << 4); + + // Spread ints to bytes + x = [r, l]; + for (i = 0; i < 8; i++) { + b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256; + if (b[i] < 0) { b[i] += 256; } // unsigned + } + return b; +} + +// Encrypt 16 bytes of text using passwd as key +function encrypt(t) { + return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16))); +} + +setKeys(passwd); // Setup keys +return {'encrypt': encrypt}; // Public interface + +} // function DES diff --git a/classes/novnc/include/display.js b/classes/novnc/include/display.js new file mode 100644 index 0000000..2cf262d --- /dev/null +++ b/classes/novnc/include/display.js @@ -0,0 +1,671 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint browser: true, white: false, bitwise: false */ +/*global Util, Base64, changeCursor */ + +function Display(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + // Private Display namespace variables + c_ctx = null, + c_forceCanvas = false, + + // Predefine function variables (jslint) + imageDataGet, rgbxImageData, cmapImageData, + setFillColor, rescale, + + // The full frame buffer (logical canvas) size + fb_width = 0, + fb_height = 0, + // The visible "physical canvas" viewport + viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 }, + cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1}, + + c_prevStyle = "", + tile = null, + tile16x16 = null, + tile_x = 0, + tile_y = 0; + + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', null, 'Canvas element for rendering'], + ['context', 'ro', 'raw', null, 'Canvas 2D context for rendering (read-only)'], + ['logo', 'rw', 'raw', null, 'Logo to display when cleared: {"width": width, "height": height, "data": data}'], + ['true_color', 'rw', 'bool', true, 'Use true-color pixel data'], + ['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'], + ['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'], + ['viewport', 'rw', 'bool', false, 'Use a viewport set with viewportChange()'], + ['width', 'rw', 'int', null, 'Display area width'], + ['height', 'rw', 'int', null, 'Display area height'], + + ['render_mode', 'ro', 'str', '', 'Canvas rendering mode (read-only)'], + + ['prefer_js', 'rw', 'str', null, 'Prefer Javascript over canvas methods'], + ['cursor_uri', 'rw', 'raw', null, 'Can we render cursor using data URI'] + ]); + +// Override some specific getters/setters +that.get_context = function () { return c_ctx; }; + +that.set_scale = function(scale) { rescale(scale); }; + +that.set_width = function (val) { that.resize(val, fb_height); }; +that.get_width = function() { return fb_width; }; + +that.set_height = function (val) { that.resize(fb_width, val); }; +that.get_height = function() { return fb_height; }; + + + +// +// Private functions +// + +// Create the public API interface +function constructor() { + Util.Debug(">> Display.constructor"); + + var c, func, i, curDat, curSave, + has_imageData = false, UE = Util.Engine; + + if (! conf.target) { throw("target must be set"); } + + if (typeof conf.target === 'string') { + throw("target must be a DOM element"); + } + + c = conf.target; + + if (! c.getContext) { throw("no getContext method"); } + + if (! c_ctx) { c_ctx = c.getContext('2d'); } + + Util.Debug("User Agent: " + navigator.userAgent); + if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); } + if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); } + if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); } + if (UE.presto) { Util.Debug("Browser: presto " + UE.presto); } + + that.clear(); + + // Check canvas features + if ('createImageData' in c_ctx) { + conf.render_mode = "canvas rendering"; + } else { + throw("Canvas does not support createImageData"); + } + if (conf.prefer_js === null) { + Util.Info("Prefering javascript operations"); + conf.prefer_js = true; + } + + // Initialize cached tile imageData + tile16x16 = c_ctx.createImageData(16, 16); + + /* + * Determine browser support for setting the cursor via data URI + * scheme + */ + curDat = []; + for (i=0; i < 8 * 8 * 4; i += 1) { + curDat.push(255); + } + try { + curSave = c.style.cursor; + changeCursor(conf.target, curDat, curDat, 2, 2, 8, 8); + if (c.style.cursor) { + if (conf.cursor_uri === null) { + conf.cursor_uri = true; + } + Util.Info("Data URI scheme cursor supported"); + } else { + if (conf.cursor_uri === null) { + conf.cursor_uri = false; + } + Util.Warn("Data URI scheme cursor not supported"); + } + c.style.cursor = curSave; + } catch (exc2) { + Util.Error("Data URI scheme cursor test exception: " + exc2); + conf.cursor_uri = false; + } + + Util.Debug("<< Display.constructor"); + return that ; +} + +rescale = function(factor) { + var c, tp, x, y, + properties = ['transform', 'WebkitTransform', 'MozTransform', null]; + c = conf.target; + tp = properties.shift(); + while (tp) { + if (typeof c.style[tp] !== 'undefined') { + break; + } + tp = properties.shift(); + } + + if (tp === null) { + Util.Debug("No scaling support"); + return; + } + + + if (typeof(factor) === "undefined") { + factor = conf.scale; + } else if (factor > 1.0) { + factor = 1.0; + } else if (factor < 0.1) { + factor = 0.1; + } + + if (conf.scale === factor) { + //Util.Debug("Display already scaled to '" + factor + "'"); + return; + } + + conf.scale = factor; + x = c.width - c.width * factor; + y = c.height - c.height * factor; + c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)"; +}; + +setFillColor = function(color) { + var rgb, newStyle; + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + if (newStyle !== c_prevStyle) { + c_ctx.fillStyle = newStyle; + c_prevStyle = newStyle; + } +}; + + +// +// Public API interface functions +// + +// Shift and/or resize the visible viewport +that.viewportChange = function(deltaX, deltaY, width, height) { + var c = conf.target, v = viewport, cr = cleanRect, + saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h; + + if (!conf.viewport) { + Util.Debug("Setting viewport to full display region"); + deltaX = -v.w; // Clamped later if out of bounds + deltaY = -v.h; // Clamped later if out of bounds + width = fb_width; + height = fb_height; + } + + if (typeof(deltaX) === "undefined") { deltaX = 0; } + if (typeof(deltaY) === "undefined") { deltaY = 0; } + if (typeof(width) === "undefined") { width = v.w; } + if (typeof(height) === "undefined") { height = v.h; } + + // Size change + + if (width > fb_width) { width = fb_width; } + if (height > fb_height) { height = fb_height; } + + if ((v.w !== width) || (v.h !== height)) { + // Change width + if ((width < v.w) && (cr.x2 > v.x + width -1)) { + cr.x2 = v.x + width - 1; + } + v.w = width; + + // Change height + if ((height < v.h) && (cr.y2 > v.y + height -1)) { + cr.y2 = v.y + height - 1; + } + v.h = height; + + + if (v.w > 0 && v.h > 0 && c.width > 0 && c.height > 0) { + saveImg = c_ctx.getImageData(0, 0, + (c.width < v.w) ? c.width : v.w, + (c.height < v.h) ? c.height : v.h); + } + + c.width = v.w; + c.height = v.h; + + if (saveImg) { + c_ctx.putImageData(saveImg, 0, 0); + } + } + + vx2 = v.x + v.w - 1; + vy2 = v.y + v.h - 1; + + + // Position change + + if ((deltaX < 0) && ((v.x + deltaX) < 0)) { + deltaX = - v.x; + } + if ((vx2 + deltaX) >= fb_width) { + deltaX -= ((vx2 + deltaX) - fb_width + 1); + } + + if ((v.y + deltaY) < 0) { + deltaY = - v.y; + } + if ((vy2 + deltaY) >= fb_height) { + deltaY -= ((vy2 + deltaY) - fb_height + 1); + } + + if ((deltaX === 0) && (deltaY === 0)) { + //Util.Debug("skipping viewport change"); + return; + } + Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); + + v.x += deltaX; + vx2 += deltaX; + v.y += deltaY; + vy2 += deltaY; + + // Update the clean rectangle + if (v.x > cr.x1) { + cr.x1 = v.x; + } + if (vx2 < cr.x2) { + cr.x2 = vx2; + } + if (v.y > cr.y1) { + cr.y1 = v.y; + } + if (vy2 < cr.y2) { + cr.y2 = vy2; + } + + if (deltaX < 0) { + // Shift viewport left, redraw left section + x1 = 0; + w = - deltaX; + } else { + // Shift viewport right, redraw right section + x1 = v.w - deltaX; + w = deltaX; + } + if (deltaY < 0) { + // Shift viewport up, redraw top section + y1 = 0; + h = - deltaY; + } else { + // Shift viewport down, redraw bottom section + y1 = v.h - deltaY; + h = deltaY; + } + + // Copy the valid part of the viewport to the shifted location + saveStyle = c_ctx.fillStyle; + c_ctx.fillStyle = "rgb(255,255,255)"; + if (deltaX !== 0) { + //that.copyImage(0, 0, -deltaX, 0, v.w, v.h); + //that.fillRect(x1, 0, w, v.h, [255,255,255]); + c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h); + c_ctx.fillRect(x1, 0, w, v.h); + } + if (deltaY !== 0) { + //that.copyImage(0, 0, 0, -deltaY, v.w, v.h); + //that.fillRect(0, y1, v.w, h, [255,255,255]); + c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h); + c_ctx.fillRect(0, y1, v.w, h); + } + c_ctx.fillStyle = saveStyle; +}; + + +// Return a map of clean and dirty areas of the viewport and reset the +// tracking of clean and dirty areas. +// +// Returns: {'cleanBox': {'x': x, 'y': y, 'w': w, 'h': h}, +// 'dirtyBoxes': [{'x': x, 'y': y, 'w': w, 'h': h}, ...]} +that.getCleanDirtyReset = function() { + var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [], + vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1; + + + // Copy the cleanRect + cleanBox = {'x': c.x1, 'y': c.y1, + 'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1}; + + if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) { + // Whole viewport is dirty + dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h}); + } else { + // Redraw dirty regions + if (v.x < c.x1) { + // left side dirty region + dirtyBoxes.push({'x': v.x, 'y': v.y, + 'w': c.x1 - v.x + 1, 'h': v.h}); + } + if (vx2 > c.x2) { + // right side dirty region + dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y, + 'w': vx2 - c.x2, 'h': v.h}); + } + if (v.y < c.y1) { + // top/middle dirty region + dirtyBoxes.push({'x': c.x1, 'y': v.y, + 'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y}); + } + if (vy2 > c.y2) { + // bottom/middle dirty region + dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1, + 'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2}); + } + } + + // Reset the cleanRect to the whole viewport + cleanRect = {'x1': v.x, 'y1': v.y, + 'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1}; + + return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; +}; + +// Translate viewport coordinates to absolute coordinates +that.absX = function(x) { + return x + viewport.x; +} +that.absY = function(y) { + return y + viewport.y; +} + + +that.resize = function(width, height) { + c_prevStyle = ""; + + fb_width = width; + fb_height = height; + + rescale(conf.scale); + that.viewportChange(); +}; + +that.clear = function() { + + if (conf.logo) { + that.resize(conf.logo.width, conf.logo.height); + that.blitStringImage(conf.logo.data, 0, 0); + } else { + that.resize(640, 20); + c_ctx.clearRect(0, 0, viewport.w, viewport.h); + } + + // No benefit over default ("source-over") in Chrome and firefox + //c_ctx.globalCompositeOperation = "copy"; +}; + +that.fillRect = function(x, y, width, height, color) { + setFillColor(color); + c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height); +}; + +that.copyImage = function(old_x, old_y, new_x, new_y, w, h) { + var x1 = old_x - viewport.x, y1 = old_y - viewport.y, + x2 = new_x - viewport.x, y2 = new_y - viewport.y; + c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h); +}; + + +// Start updating a tile +that.startTile = function(x, y, width, height, color) { + var data, rgb, red, green, blue, i; + tile_x = x; + tile_y = y; + if ((width === 16) && (height === 16)) { + tile = tile16x16; + } else { + tile = c_ctx.createImageData(width, height); + } + data = tile.data; + if (conf.prefer_js) { + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + red = rgb[0]; + green = rgb[1]; + blue = rgb[2]; + for (i = 0; i < (width * height * 4); i+=4) { + data[i ] = red; + data[i + 1] = green; + data[i + 2] = blue; + data[i + 3] = 255; + } + } else { + that.fillRect(x, y, width, height, color); + } +}; + +// Update sub-rectangle of the current tile +that.subTile = function(x, y, w, h, color) { + var data, p, rgb, red, green, blue, width, j, i, xend, yend; + if (conf.prefer_js) { + data = tile.data; + width = tile.width; + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + red = rgb[0]; + green = rgb[1]; + blue = rgb[2]; + xend = x + w; + yend = y + h; + for (j = y; j < yend; j += 1) { + for (i = x; i < xend; i += 1) { + p = (i + (j * width) ) * 4; + data[p ] = red; + data[p + 1] = green; + data[p + 2] = blue; + data[p + 3] = 255; + } + } + } else { + that.fillRect(tile_x + x, tile_y + y, w, h, color); + } +}; + +// Draw the current tile to the screen +that.finishTile = function() { + if (conf.prefer_js) { + c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y) + } + // else: No-op, if not prefer_js then already done by setSubTile +}; + +rgbxImageData = function(x, y, width, height, arr, offset) { + var img, i, j, data, v = viewport; + /* + if ((x - v.x >= v.w) || (y - v.y >= v.h) || + (x - v.x + width < 0) || (y - v.y + height < 0)) { + // Skipping because outside of viewport + return; + } + */ + img = c_ctx.createImageData(width, height); + data = img.data; + for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) { + data[i ] = arr[j ]; + data[i + 1] = arr[j + 1]; + data[i + 2] = arr[j + 2]; + data[i + 3] = 255; // Set Alpha + } + c_ctx.putImageData(img, x - v.x, y - v.y); +}; + +cmapImageData = function(x, y, width, height, arr, offset) { + var img, i, j, data, rgb, cmap; + img = c_ctx.createImageData(width, height); + data = img.data; + cmap = conf.colourMap; + for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) { + rgb = cmap[arr[j]]; + data[i ] = rgb[0]; + data[i + 1] = rgb[1]; + data[i + 2] = rgb[2]; + data[i + 3] = 255; // Set Alpha + } + c_ctx.putImageData(img, x - viewport.x, y - viewport.y); +}; + +that.blitImage = function(x, y, width, height, arr, offset) { + if (conf.true_color) { + rgbxImageData(x, y, width, height, arr, offset); + } else { + cmapImageData(x, y, width, height, arr, offset); + } +}; + +that.blitStringImage = function(str, x, y) { + var img = new Image(); + img.onload = function () { + c_ctx.drawImage(img, x - viewport.x, y - viewport.y); + }; + img.src = str; +}; + +that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { + if (conf.cursor_uri === false) { + Util.Warn("changeCursor called but no cursor data URI support"); + return; + } + + if (conf.true_color) { + changeCursor(conf.target, pixels, mask, hotx, hoty, w, h); + } else { + changeCursor(conf.target, pixels, mask, hotx, hoty, w, h, conf.colourMap); + } +}; + +that.defaultCursor = function() { + conf.target.style.cursor = "default"; +}; + +return constructor(); // Return the public API interface + +} // End of Display() + + +/* Set CSS cursor property using data URI encoded cursor file */ +function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) { + "use strict"; + var cur = [], rgb, IHDRsz, RGBsz, ANDsz, XORsz, url, idx, alpha, x, y; + //Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h); + + // Push multi-byte little-endian values + cur.push16le = function (num) { + this.push((num ) & 0xFF, + (num >> 8) & 0xFF ); + }; + cur.push32le = function (num) { + this.push((num ) & 0xFF, + (num >> 8) & 0xFF, + (num >> 16) & 0xFF, + (num >> 24) & 0xFF ); + }; + + IHDRsz = 40; + RGBsz = w * h * 4; + XORsz = Math.ceil( (w * h) / 8.0 ); + ANDsz = Math.ceil( (w * h) / 8.0 ); + + // Main header + cur.push16le(0); // 0: Reserved + cur.push16le(2); // 2: .CUR type + cur.push16le(1); // 4: Number of images, 1 for non-animated ico + + // Cursor #1 header (ICONDIRENTRY) + cur.push(w); // 6: width + cur.push(h); // 7: height + cur.push(0); // 8: colors, 0 -> true-color + cur.push(0); // 9: reserved + cur.push16le(hotx); // 10: hotspot x coordinate + cur.push16le(hoty); // 12: hotspot y coordinate + cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); + // 14: cursor data byte size + cur.push32le(22); // 18: offset of cursor data in the file + + + // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) + cur.push32le(IHDRsz); // 22: Infoheader size + cur.push32le(w); // 26: Cursor width + cur.push32le(h*2); // 30: XOR+AND height + cur.push16le(1); // 34: number of planes + cur.push16le(32); // 36: bits per pixel + cur.push32le(0); // 38: Type of compression + + cur.push32le(XORsz + ANDsz); // 43: Size of Image + // Gimp leaves this as 0 + + cur.push32le(0); // 46: reserved + cur.push32le(0); // 50: reserved + cur.push32le(0); // 54: reserved + cur.push32le(0); // 58: reserved + + // 62: color data (RGBQUAD icColors[]) + for (y = h-1; y >= 0; y -= 1) { + for (x = 0; x < w; x += 1) { + idx = y * Math.ceil(w / 8) + Math.floor(x/8); + alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; + + if (cmap) { + idx = (w * y) + x; + rgb = cmap[pixels[idx]]; + cur.push(rgb[2]); // blue + cur.push(rgb[1]); // green + cur.push(rgb[0]); // red + cur.push(alpha); // alpha + } else { + idx = ((w * y) + x) * 4; + cur.push(pixels[idx + 2]); // blue + cur.push(pixels[idx + 1]); // green + cur.push(pixels[idx ]); // red + cur.push(alpha); // alpha + } + } + } + + // XOR/bitmask data (BYTE icXOR[]) + // (ignored, just needs to be right size) + for (y = 0; y < h; y += 1) { + for (x = 0; x < Math.ceil(w / 8); x += 1) { + cur.push(0x00); + } + } + + // AND/bitmask data (BYTE icAND[]) + // (ignored, just needs to be right size) + for (y = 0; y < h; y += 1) { + for (x = 0; x < Math.ceil(w / 8); x += 1) { + cur.push(0x00); + } + } + + url = "data:image/x-icon;base64," + Base64.encode(cur); + target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default"; + //Util.Debug("<< changeCursor, cur.length: " + cur.length); +} diff --git a/classes/novnc/include/input.js b/classes/novnc/include/input.js new file mode 100644 index 0000000..3124d08 --- /dev/null +++ b/classes/novnc/include/input.js @@ -0,0 +1,1884 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-2 or any later version (see LICENSE.txt) + */ + +/*jslint browser: true, white: false, bitwise: false */ +/*global window, Util */ + + +// +// Keyboard event handler +// + +function Keyboard(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + keyDownList = []; // List of depressed keys + // (even if they are happy) + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', document, 'DOM element that captures keyboard input'], + ['focused', 'rw', 'bool', true, 'Capture and send key events'], + + ['onKeyPress', 'rw', 'func', null, 'Handler for key press/release'] + ]); + + +// +// Private functions +// + +// From the event keyCode return the keysym value for keys that need +// to be suppressed otherwise they may trigger unintended browser +// actions +function getKeysymSpecial(evt) { + var keysym = null; + + switch ( evt.keyCode ) { + // These generate a keyDown and keyPress in Firefox and Opera + case 8 : keysym = 0xFF08; break; // BACKSPACE + case 13 : keysym = 0xFF0D; break; // ENTER + + // This generates a keyDown and keyPress in Opera + case 9 : keysym = 0xFF09; break; // TAB + default : break; + } + + if (evt.type === 'keydown') { + switch ( evt.keyCode ) { + case 27 : keysym = 0xFF1B; break; // ESCAPE + case 46 : keysym = 0xFFFF; break; // DELETE + + case 36 : keysym = 0xFF50; break; // HOME + case 35 : keysym = 0xFF57; break; // END + case 33 : keysym = 0xFF55; break; // PAGE_UP + case 34 : keysym = 0xFF56; break; // PAGE_DOWN + case 45 : keysym = 0xFF63; break; // INSERT + // '-' during keyPress + case 37 : keysym = 0xFF51; break; // LEFT + case 38 : keysym = 0xFF52; break; // UP + case 39 : keysym = 0xFF53; break; // RIGHT + case 40 : keysym = 0xFF54; break; // DOWN + case 16 : keysym = 0xFFE1; break; // SHIFT + case 17 : keysym = 0xFFE3; break; // CONTROL + //case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option) + case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command) + + case 112 : keysym = 0xFFBE; break; // F1 + case 113 : keysym = 0xFFBF; break; // F2 + case 114 : keysym = 0xFFC0; break; // F3 + case 115 : keysym = 0xFFC1; break; // F4 + case 116 : keysym = 0xFFC2; break; // F5 + case 117 : keysym = 0xFFC3; break; // F6 + case 118 : keysym = 0xFFC4; break; // F7 + case 119 : keysym = 0xFFC5; break; // F8 + case 120 : keysym = 0xFFC6; break; // F9 + case 121 : keysym = 0xFFC7; break; // F10 + case 122 : keysym = 0xFFC8; break; // F11 + case 123 : keysym = 0xFFC9; break; // F12 + + default : break; + } + } + + if ((!keysym) && (evt.ctrlKey || evt.altKey)) { + if ((typeof(evt.which) !== "undefined") && (evt.which > 0)) { + keysym = evt.which; + } else { + // IE9 always + // Firefox and Opera when ctrl/alt + special + Util.Warn("which not set, using keyCode"); + keysym = evt.keyCode; + } + + /* Remap symbols */ + switch (keysym) { + case 186 : keysym = 59; break; // ; (IE) + case 187 : keysym = 61; break; // = (IE) + case 188 : keysym = 44; break; // , (Mozilla, IE) + case 109 : // - (Mozilla, Opera) + if (Util.Engine.gecko || Util.Engine.presto) { + keysym = 45; } + break; + case 189 : keysym = 45; break; // - (IE) + case 190 : keysym = 46; break; // . (Mozilla, IE) + case 191 : keysym = 47; break; // / (Mozilla, IE) + case 192 : keysym = 96; break; // ` (Mozilla, IE) + case 219 : keysym = 91; break; // [ (Mozilla, IE) + case 220 : keysym = 92; break; // \ (Mozilla, IE) + case 221 : keysym = 93; break; // ] (Mozilla, IE) + case 222 : keysym = 39; break; // ' (Mozilla, IE) + } + + /* Remap shifted and unshifted keys */ + if (!!evt.shiftKey) { + switch (keysym) { + case 48 : keysym = 41 ; break; // ) (shifted 0) + case 49 : keysym = 33 ; break; // ! (shifted 1) + case 50 : keysym = 64 ; break; // @ (shifted 2) + case 51 : keysym = 35 ; break; // # (shifted 3) + case 52 : keysym = 36 ; break; // $ (shifted 4) + case 53 : keysym = 37 ; break; // % (shifted 5) + case 54 : keysym = 94 ; break; // ^ (shifted 6) + case 55 : keysym = 38 ; break; // & (shifted 7) + case 56 : keysym = 42 ; break; // * (shifted 8) + case 57 : keysym = 40 ; break; // ( (shifted 9) + + case 59 : keysym = 58 ; break; // : (shifted `) + case 61 : keysym = 43 ; break; // + (shifted ;) + case 44 : keysym = 60 ; break; // < (shifted ,) + case 45 : keysym = 95 ; break; // _ (shifted -) + case 46 : keysym = 62 ; break; // > (shifted .) + case 47 : keysym = 63 ; break; // ? (shifted /) + case 96 : keysym = 126; break; // ~ (shifted `) + case 91 : keysym = 123; break; // { (shifted [) + case 92 : keysym = 124; break; // | (shifted \) + case 93 : keysym = 125; break; // } (shifted ]) + case 39 : keysym = 34 ; break; // " (shifted ') + } + } else if ((keysym >= 65) && (keysym <=90)) { + /* Remap unshifted A-Z */ + keysym += 32; + } else if (evt.keyLocation === 3) { + // numpad keys + switch (keysym) { + case 96 : keysym = 48; break; // 0 + case 97 : keysym = 49; break; // 1 + case 98 : keysym = 50; break; // 2 + case 99 : keysym = 51; break; // 3 + case 100: keysym = 52; break; // 4 + case 101: keysym = 53; break; // 5 + case 102: keysym = 54; break; // 6 + case 103: keysym = 55; break; // 7 + case 104: keysym = 56; break; // 8 + case 105: keysym = 57; break; // 9 + case 109: keysym = 45; break; // - + case 110: keysym = 46; break; // . + case 111: keysym = 47; break; // / + } + } + } + + return keysym; +} + +/* Translate DOM keyPress event to keysym value */ +function getKeysym(evt) { + var keysym, msg; + + if (typeof(evt.which) !== "undefined") { + // WebKit, Firefox, Opera + keysym = evt.which; + } else { + // IE9 + Util.Warn("which not set, using keyCode"); + keysym = evt.keyCode; + } + + if ((keysym > 255) && (keysym < 0xFF00)) { + msg = "Mapping character code " + keysym; + // Map Unicode outside Latin 1 to X11 keysyms + keysym = unicodeTable[keysym]; + if (typeof(keysym) === 'undefined') { + keysym = 0; + } + Util.Debug(msg + " to " + keysym); + } + + return keysym; +} + +function show_keyDownList(kind) { + var c; + var msg = "keyDownList (" + kind + "):\n"; + for (c = 0; c < keyDownList.length; c++) { + msg = msg + " " + c + " - keyCode: " + keyDownList[c].keyCode + + " - which: " + keyDownList[c].which + "\n"; + } + Util.Debug(msg); +} + +function copyKeyEvent(evt) { + var members = ['type', 'keyCode', 'charCode', 'which', + 'altKey', 'ctrlKey', 'shiftKey', + 'keyLocation', 'keyIdentifier'], i, obj = {}; + for (i = 0; i < members.length; i++) { + if (typeof(evt[members[i]]) !== "undefined") { + obj[members[i]] = evt[members[i]]; + } + } + return obj; +} + +function pushKeyEvent(fevt) { + keyDownList.push(fevt); +} + +function getKeyEvent(keyCode, pop) { + var i, fevt = null; + for (i = keyDownList.length-1; i >= 0; i--) { + if (keyDownList[i].keyCode === keyCode) { + if ((typeof(pop) !== "undefined") && (pop)) { + fevt = keyDownList.splice(i, 1)[0]; + } else { + fevt = keyDownList[i]; + } + break; + } + } + return fevt; +} + +function ignoreKeyEvent(evt) { + // Blarg. Some keys have a different keyCode on keyDown vs keyUp + if (evt.keyCode === 229) { + // French AZERTY keyboard dead key. + // Lame thing is that the respective keyUp is 219 so we can't + // properly ignore the keyUp event + return true; + } + return false; +} + + +// +// Key Event Handling: +// +// There are several challenges when dealing with key events: +// - The meaning and use of keyCode, charCode and which depends on +// both the browser and the event type (keyDown/Up vs keyPress). +// - We cannot automatically determine the keyboard layout +// - The keyDown and keyUp events have a keyCode value that has not +// been translated by modifier keys. +// - The keyPress event has a translated (for layout and modifiers) +// character code but the attribute containing it differs. keyCode +// contains the translated value in WebKit (Chrome/Safari), Opera +// 11 and IE9. charCode contains the value in WebKit and Firefox. +// The which attribute contains the value on WebKit, Firefox and +// Opera 11. +// - The keyDown/Up keyCode value indicates (sort of) the physical +// key was pressed but only for standard US layout. On a US +// keyboard, the '-' and '_' characters are on the same key and +// generate a keyCode value of 189. But on an AZERTY keyboard even +// though they are different physical keys they both still +// generate a keyCode of 189! +// - To prevent a key event from propagating to the browser and +// causing unwanted default actions (such as closing a tab, +// opening a menu, shifting focus, etc) we must suppress this +// event in both keyDown and keyPress because not all key strokes +// generate on a keyPress event. Also, in WebKit and IE9 +// suppressing the keyDown prevents a keyPress but other browsers +// still generated a keyPress even if keyDown is suppressed. +// +// For safe key events, we wait until the keyPress event before +// reporting a key down event. For unsafe key events, we report a key +// down event when the keyDown event fires and we suppress any further +// actions (including keyPress). +// +// In order to report a key up event that matches what we reported +// for the key down event, we keep a list of keys that are currently +// down. When the keyDown event happens, we add the key event to the +// list. If it is a safe key event, then we update the which attribute +// in the most recent item on the list when we received a keyPress +// event (keyPress should immediately follow keyDown). When we +// received a keyUp event we search for the event on the list with +// a matching keyCode and we report the character code using the value +// in the 'which' attribute that was stored with that key. +// + +function onKeyDown(e) { + if (! conf.focused) { + return true; + } + var fevt = null, evt = (e ? e : window.event), + keysym = null, suppress = false; + //Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + fevt = copyKeyEvent(evt); + + keysym = getKeysymSpecial(evt); + // Save keysym decoding for use in keyUp + fevt.keysym = keysym; + if (keysym) { + // If it is a key or key combination that might trigger + // browser behaviors or it has no corresponding keyPress + // event, then send it immediately + if (conf.onKeyPress && !ignoreKeyEvent(evt)) { + Util.Debug("onKeyPress down, keysym: " + keysym + + " (onKeyDown key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 1, evt); + } + suppress = true; + } + + if (! ignoreKeyEvent(evt)) { + // Add it to the list of depressed keys + pushKeyEvent(fevt); + //show_keyDownList('down'); + } + + if (suppress) { + // Suppress bubbling/default actions + Util.stopEvent(e); + return false; + } else { + // Allow the event to bubble and become a keyPress event which + // will have the character code translated + return true; + } +} + +function onKeyPress(e) { + if (! conf.focused) { + return true; + } + var evt = (e ? e : window.event), + kdlen = keyDownList.length, keysym = null; + //Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + if (((evt.which !== "undefined") && (evt.which === 0)) || + (getKeysymSpecial(evt))) { + // Firefox and Opera generate a keyPress event even if keyDown + // is suppressed. But the keys we want to suppress will have + // either: + // - the which attribute set to 0 + // - getKeysymSpecial() will identify it + Util.Debug("Ignoring special key in keyPress"); + Util.stopEvent(e); + return false; + } + + keysym = getKeysym(evt); + + // Modify the the which attribute in the depressed keys list so + // that the keyUp event will be able to have the character code + // translation available. + if (kdlen > 0) { + keyDownList[kdlen-1].keysym = keysym; + } else { + Util.Warn("keyDownList empty when keyPress triggered"); + } + + //show_keyDownList('press'); + + // Send the translated keysym + if (conf.onKeyPress && (keysym > 0)) { + Util.Debug("onKeyPress down, keysym: " + keysym + + " (onKeyPress key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 1, evt); + } + + // Stop keypress events just in case + Util.stopEvent(e); + return false; +} + +function onKeyUp(e) { + if (! conf.focused) { + return true; + } + var fevt = null, evt = (e ? e : window.event), keysym; + //Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + fevt = getKeyEvent(evt.keyCode, true); + + if (fevt) { + keysym = fevt.keysym; + } else { + Util.Warn("Key event (keyCode = " + evt.keyCode + + ") not found on keyDownList"); + keysym = 0; + } + + //show_keyDownList('up'); + + if (conf.onKeyPress && (keysym > 0)) { + //Util.Debug("keyPress up, keysym: " + keysym + + // " (key: " + evt.keyCode + ", which: " + evt.which + ")"); + Util.Debug("onKeyPress up, keysym: " + keysym + + " (onKeyPress key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 0, evt); + } + Util.stopEvent(e); + return false; +} + +// +// Public API interface functions +// + +that.grab = function() { + //Util.Debug(">> Keyboard.grab"); + var c = conf.target; + + Util.addEvent(c, 'keydown', onKeyDown); + Util.addEvent(c, 'keyup', onKeyUp); + Util.addEvent(c, 'keypress', onKeyPress); + + //Util.Debug("<< Keyboard.grab"); +}; + +that.ungrab = function() { + //Util.Debug(">> Keyboard.ungrab"); + var c = conf.target; + + Util.removeEvent(c, 'keydown', onKeyDown); + Util.removeEvent(c, 'keyup', onKeyUp); + Util.removeEvent(c, 'keypress', onKeyPress); + + //Util.Debug(">> Keyboard.ungrab"); +}; + +return that; // Return the public API interface + +} // End of Keyboard() + + +// +// Mouse event handler +// + +function Mouse(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}; // Configuration attributes + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'ro', 'dom', document, 'DOM element that captures mouse input'], + ['focused', 'rw', 'bool', true, 'Capture and send mouse clicks/movement'], + ['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'], + + ['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'], + ['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'], + ['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)'] + ]); + + +// +// Private functions +// + +function onMouseButton(e, down) { + var evt, pos, bmask; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + if (e.touches || e.changedTouches) { + // Touch device + bmask = conf.touchButton; + // If bmask is set + } else if (evt.which) { + /* everything except IE */ + bmask = 1 << evt.button; + } else { + /* IE including 9 */ + bmask = (evt.button & 0x1) + // Left + (evt.button & 0x2) * 2 + // Right + (evt.button & 0x4) / 2; // Middle + } + //Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down + + // " bmask: " + bmask + "(evt.button: " + evt.button + ")"); + if (bmask > 0 && conf.onMouseButton) { + Util.Debug("onMouseButton " + (down ? "down" : "up") + + ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); + conf.onMouseButton(pos.x, pos.y, down, bmask); + } + Util.stopEvent(e); + return false; +} + +function onMouseDown(e) { + onMouseButton(e, 1); +} + +function onMouseUp(e) { + onMouseButton(e, 0); +} + +function onMouseWheel(e) { + var evt, pos, bmask, wheelData; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40; + if (wheelData > 0) { + bmask = 1 << 3; + } else { + bmask = 1 << 4; + } + //Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y); + if (conf.onMouseButton) { + conf.onMouseButton(pos.x, pos.y, 1, bmask); + conf.onMouseButton(pos.x, pos.y, 0, bmask); + } + Util.stopEvent(e); + return false; +} + +function onMouseMove(e) { + var evt, pos; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + //Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y); + if (conf.onMouseMove) { + conf.onMouseMove(pos.x, pos.y); + } + Util.stopEvent(e); + return false; +} + +function onMouseDisable(e) { + var evt, pos; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + /* Stop propagation if inside canvas area */ + if ((pos.x >= 0) && (pos.y >= 0) && + (pos.x < conf.target.offsetWidth) && + (pos.y < conf.target.offsetHeight)) { + //Util.Debug("mouse event disabled"); + Util.stopEvent(e); + return false; + } + //Util.Debug("mouse event not disabled"); + return true; +} + +// +// Public API interface functions +// + +that.grab = function() { + //Util.Debug(">> Mouse.grab"); + var c = conf.target; + + if ('ontouchstart' in document.documentElement) { + Util.addEvent(c, 'touchstart', onMouseDown); + Util.addEvent(c, 'touchend', onMouseUp); + Util.addEvent(c, 'touchmove', onMouseMove); + } else { + Util.addEvent(c, 'mousedown', onMouseDown); + Util.addEvent(c, 'mouseup', onMouseUp); + Util.addEvent(c, 'mousemove', onMouseMove); + Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', + onMouseWheel); + } + + /* Work around right and middle click browser behaviors */ + Util.addEvent(document, 'click', onMouseDisable); + Util.addEvent(document.body, 'contextmenu', onMouseDisable); + + //Util.Debug("<< Mouse.grab"); +}; + +that.ungrab = function() { + //Util.Debug(">> Mouse.ungrab"); + var c = conf.target; + + if ('ontouchstart' in document.documentElement) { + Util.removeEvent(c, 'touchstart', onMouseDown); + Util.removeEvent(c, 'touchend', onMouseUp); + Util.removeEvent(c, 'touchmove', onMouseMove); + } else { + Util.removeEvent(c, 'mousedown', onMouseDown); + Util.removeEvent(c, 'mouseup', onMouseUp); + Util.removeEvent(c, 'mousemove', onMouseMove); + Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', + onMouseWheel); + } + + /* Work around right and middle click browser behaviors */ + Util.removeEvent(document, 'click', onMouseDisable); + Util.removeEvent(document.body, 'contextmenu', onMouseDisable); + + //Util.Debug(">> Mouse.ungrab"); +}; + +return that; // Return the public API interface + +} // End of Mouse() + + +/* + * Browser keypress to X11 keysym for Unicode characters > U+00FF + */ +unicodeTable = { + 0x0104 : 0x01a1, + 0x02D8 : 0x01a2, + 0x0141 : 0x01a3, + 0x013D : 0x01a5, + 0x015A : 0x01a6, + 0x0160 : 0x01a9, + 0x015E : 0x01aa, + 0x0164 : 0x01ab, + 0x0179 : 0x01ac, + 0x017D : 0x01ae, + 0x017B : 0x01af, + 0x0105 : 0x01b1, + 0x02DB : 0x01b2, + 0x0142 : 0x01b3, + 0x013E : 0x01b5, + 0x015B : 0x01b6, + 0x02C7 : 0x01b7, + 0x0161 : 0x01b9, + 0x015F : 0x01ba, + 0x0165 : 0x01bb, + 0x017A : 0x01bc, + 0x02DD : 0x01bd, + 0x017E : 0x01be, + 0x017C : 0x01bf, + 0x0154 : 0x01c0, + 0x0102 : 0x01c3, + 0x0139 : 0x01c5, + 0x0106 : 0x01c6, + 0x010C : 0x01c8, + 0x0118 : 0x01ca, + 0x011A : 0x01cc, + 0x010E : 0x01cf, + 0x0110 : 0x01d0, + 0x0143 : 0x01d1, + 0x0147 : 0x01d2, + 0x0150 : 0x01d5, + 0x0158 : 0x01d8, + 0x016E : 0x01d9, + 0x0170 : 0x01db, + 0x0162 : 0x01de, + 0x0155 : 0x01e0, + 0x0103 : 0x01e3, + 0x013A : 0x01e5, + 0x0107 : 0x01e6, + 0x010D : 0x01e8, + 0x0119 : 0x01ea, + 0x011B : 0x01ec, + 0x010F : 0x01ef, + 0x0111 : 0x01f0, + 0x0144 : 0x01f1, + 0x0148 : 0x01f2, + 0x0151 : 0x01f5, + 0x0171 : 0x01fb, + 0x0159 : 0x01f8, + 0x016F : 0x01f9, + 0x0163 : 0x01fe, + 0x02D9 : 0x01ff, + 0x0126 : 0x02a1, + 0x0124 : 0x02a6, + 0x0130 : 0x02a9, + 0x011E : 0x02ab, + 0x0134 : 0x02ac, + 0x0127 : 0x02b1, + 0x0125 : 0x02b6, + 0x0131 : 0x02b9, + 0x011F : 0x02bb, + 0x0135 : 0x02bc, + 0x010A : 0x02c5, + 0x0108 : 0x02c6, + 0x0120 : 0x02d5, + 0x011C : 0x02d8, + 0x016C : 0x02dd, + 0x015C : 0x02de, + 0x010B : 0x02e5, + 0x0109 : 0x02e6, + 0x0121 : 0x02f5, + 0x011D : 0x02f8, + 0x016D : 0x02fd, + 0x015D : 0x02fe, + 0x0138 : 0x03a2, + 0x0156 : 0x03a3, + 0x0128 : 0x03a5, + 0x013B : 0x03a6, + 0x0112 : 0x03aa, + 0x0122 : 0x03ab, + 0x0166 : 0x03ac, + 0x0157 : 0x03b3, + 0x0129 : 0x03b5, + 0x013C : 0x03b6, + 0x0113 : 0x03ba, + 0x0123 : 0x03bb, + 0x0167 : 0x03bc, + 0x014A : 0x03bd, + 0x014B : 0x03bf, + 0x0100 : 0x03c0, + 0x012E : 0x03c7, + 0x0116 : 0x03cc, + 0x012A : 0x03cf, + 0x0145 : 0x03d1, + 0x014C : 0x03d2, + 0x0136 : 0x03d3, + 0x0172 : 0x03d9, + 0x0168 : 0x03dd, + 0x016A : 0x03de, + 0x0101 : 0x03e0, + 0x012F : 0x03e7, + 0x0117 : 0x03ec, + 0x012B : 0x03ef, + 0x0146 : 0x03f1, + 0x014D : 0x03f2, + 0x0137 : 0x03f3, + 0x0173 : 0x03f9, + 0x0169 : 0x03fd, + 0x016B : 0x03fe, + 0x1E02 : 0x1001e02, + 0x1E03 : 0x1001e03, + 0x1E0A : 0x1001e0a, + 0x1E80 : 0x1001e80, + 0x1E82 : 0x1001e82, + 0x1E0B : 0x1001e0b, + 0x1EF2 : 0x1001ef2, + 0x1E1E : 0x1001e1e, + 0x1E1F : 0x1001e1f, + 0x1E40 : 0x1001e40, + 0x1E41 : 0x1001e41, + 0x1E56 : 0x1001e56, + 0x1E81 : 0x1001e81, + 0x1E57 : 0x1001e57, + 0x1E83 : 0x1001e83, + 0x1E60 : 0x1001e60, + 0x1EF3 : 0x1001ef3, + 0x1E84 : 0x1001e84, + 0x1E85 : 0x1001e85, + 0x1E61 : 0x1001e61, + 0x0174 : 0x1000174, + 0x1E6A : 0x1001e6a, + 0x0176 : 0x1000176, + 0x0175 : 0x1000175, + 0x1E6B : 0x1001e6b, + 0x0177 : 0x1000177, + 0x0152 : 0x13bc, + 0x0153 : 0x13bd, + 0x0178 : 0x13be, + 0x203E : 0x047e, + 0x3002 : 0x04a1, + 0x300C : 0x04a2, + 0x300D : 0x04a3, + 0x3001 : 0x04a4, + 0x30FB : 0x04a5, + 0x30F2 : 0x04a6, + 0x30A1 : 0x04a7, + 0x30A3 : 0x04a8, + 0x30A5 : 0x04a9, + 0x30A7 : 0x04aa, + 0x30A9 : 0x04ab, + 0x30E3 : 0x04ac, + 0x30E5 : 0x04ad, + 0x30E7 : 0x04ae, + 0x30C3 : 0x04af, + 0x30FC : 0x04b0, + 0x30A2 : 0x04b1, + 0x30A4 : 0x04b2, + 0x30A6 : 0x04b3, + 0x30A8 : 0x04b4, + 0x30AA : 0x04b5, + 0x30AB : 0x04b6, + 0x30AD : 0x04b7, + 0x30AF : 0x04b8, + 0x30B1 : 0x04b9, + 0x30B3 : 0x04ba, + 0x30B5 : 0x04bb, + 0x30B7 : 0x04bc, + 0x30B9 : 0x04bd, + 0x30BB : 0x04be, + 0x30BD : 0x04bf, + 0x30BF : 0x04c0, + 0x30C1 : 0x04c1, + 0x30C4 : 0x04c2, + 0x30C6 : 0x04c3, + 0x30C8 : 0x04c4, + 0x30CA : 0x04c5, + 0x30CB : 0x04c6, + 0x30CC : 0x04c7, + 0x30CD : 0x04c8, + 0x30CE : 0x04c9, + 0x30CF : 0x04ca, + 0x30D2 : 0x04cb, + 0x30D5 : 0x04cc, + 0x30D8 : 0x04cd, + 0x30DB : 0x04ce, + 0x30DE : 0x04cf, + 0x30DF : 0x04d0, + 0x30E0 : 0x04d1, + 0x30E1 : 0x04d2, + 0x30E2 : 0x04d3, + 0x30E4 : 0x04d4, + 0x30E6 : 0x04d5, + 0x30E8 : 0x04d6, + 0x30E9 : 0x04d7, + 0x30EA : 0x04d8, + 0x30EB : 0x04d9, + 0x30EC : 0x04da, + 0x30ED : 0x04db, + 0x30EF : 0x04dc, + 0x30F3 : 0x04dd, + 0x309B : 0x04de, + 0x309C : 0x04df, + 0x06F0 : 0x10006f0, + 0x06F1 : 0x10006f1, + 0x06F2 : 0x10006f2, + 0x06F3 : 0x10006f3, + 0x06F4 : 0x10006f4, + 0x06F5 : 0x10006f5, + 0x06F6 : 0x10006f6, + 0x06F7 : 0x10006f7, + 0x06F8 : 0x10006f8, + 0x06F9 : 0x10006f9, + 0x066A : 0x100066a, + 0x0670 : 0x1000670, + 0x0679 : 0x1000679, + 0x067E : 0x100067e, + 0x0686 : 0x1000686, + 0x0688 : 0x1000688, + 0x0691 : 0x1000691, + 0x060C : 0x05ac, + 0x06D4 : 0x10006d4, + 0x0660 : 0x1000660, + 0x0661 : 0x1000661, + 0x0662 : 0x1000662, + 0x0663 : 0x1000663, + 0x0664 : 0x1000664, + 0x0665 : 0x1000665, + 0x0666 : 0x1000666, + 0x0667 : 0x1000667, + 0x0668 : 0x1000668, + 0x0669 : 0x1000669, + 0x061B : 0x05bb, + 0x061F : 0x05bf, + 0x0621 : 0x05c1, + 0x0622 : 0x05c2, + 0x0623 : 0x05c3, + 0x0624 : 0x05c4, + 0x0625 : 0x05c5, + 0x0626 : 0x05c6, + 0x0627 : 0x05c7, + 0x0628 : 0x05c8, + 0x0629 : 0x05c9, + 0x062A : 0x05ca, + 0x062B : 0x05cb, + 0x062C : 0x05cc, + 0x062D : 0x05cd, + 0x062E : 0x05ce, + 0x062F : 0x05cf, + 0x0630 : 0x05d0, + 0x0631 : 0x05d1, + 0x0632 : 0x05d2, + 0x0633 : 0x05d3, + 0x0634 : 0x05d4, + 0x0635 : 0x05d5, + 0x0636 : 0x05d6, + 0x0637 : 0x05d7, + 0x0638 : 0x05d8, + 0x0639 : 0x05d9, + 0x063A : 0x05da, + 0x0640 : 0x05e0, + 0x0641 : 0x05e1, + 0x0642 : 0x05e2, + 0x0643 : 0x05e3, + 0x0644 : 0x05e4, + 0x0645 : 0x05e5, + 0x0646 : 0x05e6, + 0x0647 : 0x05e7, + 0x0648 : 0x05e8, + 0x0649 : 0x05e9, + 0x064A : 0x05ea, + 0x064B : 0x05eb, + 0x064C : 0x05ec, + 0x064D : 0x05ed, + 0x064E : 0x05ee, + 0x064F : 0x05ef, + 0x0650 : 0x05f0, + 0x0651 : 0x05f1, + 0x0652 : 0x05f2, + 0x0653 : 0x1000653, + 0x0654 : 0x1000654, + 0x0655 : 0x1000655, + 0x0698 : 0x1000698, + 0x06A4 : 0x10006a4, + 0x06A9 : 0x10006a9, + 0x06AF : 0x10006af, + 0x06BA : 0x10006ba, + 0x06BE : 0x10006be, + 0x06CC : 0x10006cc, + 0x06D2 : 0x10006d2, + 0x06C1 : 0x10006c1, + 0x0492 : 0x1000492, + 0x0493 : 0x1000493, + 0x0496 : 0x1000496, + 0x0497 : 0x1000497, + 0x049A : 0x100049a, + 0x049B : 0x100049b, + 0x049C : 0x100049c, + 0x049D : 0x100049d, + 0x04A2 : 0x10004a2, + 0x04A3 : 0x10004a3, + 0x04AE : 0x10004ae, + 0x04AF : 0x10004af, + 0x04B0 : 0x10004b0, + 0x04B1 : 0x10004b1, + 0x04B2 : 0x10004b2, + 0x04B3 : 0x10004b3, + 0x04B6 : 0x10004b6, + 0x04B7 : 0x10004b7, + 0x04B8 : 0x10004b8, + 0x04B9 : 0x10004b9, + 0x04BA : 0x10004ba, + 0x04BB : 0x10004bb, + 0x04D8 : 0x10004d8, + 0x04D9 : 0x10004d9, + 0x04E2 : 0x10004e2, + 0x04E3 : 0x10004e3, + 0x04E8 : 0x10004e8, + 0x04E9 : 0x10004e9, + 0x04EE : 0x10004ee, + 0x04EF : 0x10004ef, + 0x0452 : 0x06a1, + 0x0453 : 0x06a2, + 0x0451 : 0x06a3, + 0x0454 : 0x06a4, + 0x0455 : 0x06a5, + 0x0456 : 0x06a6, + 0x0457 : 0x06a7, + 0x0458 : 0x06a8, + 0x0459 : 0x06a9, + 0x045A : 0x06aa, + 0x045B : 0x06ab, + 0x045C : 0x06ac, + 0x0491 : 0x06ad, + 0x045E : 0x06ae, + 0x045F : 0x06af, + 0x2116 : 0x06b0, + 0x0402 : 0x06b1, + 0x0403 : 0x06b2, + 0x0401 : 0x06b3, + 0x0404 : 0x06b4, + 0x0405 : 0x06b5, + 0x0406 : 0x06b6, + 0x0407 : 0x06b7, + 0x0408 : 0x06b8, + 0x0409 : 0x06b9, + 0x040A : 0x06ba, + 0x040B : 0x06bb, + 0x040C : 0x06bc, + 0x0490 : 0x06bd, + 0x040E : 0x06be, + 0x040F : 0x06bf, + 0x044E : 0x06c0, + 0x0430 : 0x06c1, + 0x0431 : 0x06c2, + 0x0446 : 0x06c3, + 0x0434 : 0x06c4, + 0x0435 : 0x06c5, + 0x0444 : 0x06c6, + 0x0433 : 0x06c7, + 0x0445 : 0x06c8, + 0x0438 : 0x06c9, + 0x0439 : 0x06ca, + 0x043A : 0x06cb, + 0x043B : 0x06cc, + 0x043C : 0x06cd, + 0x043D : 0x06ce, + 0x043E : 0x06cf, + 0x043F : 0x06d0, + 0x044F : 0x06d1, + 0x0440 : 0x06d2, + 0x0441 : 0x06d3, + 0x0442 : 0x06d4, + 0x0443 : 0x06d5, + 0x0436 : 0x06d6, + 0x0432 : 0x06d7, + 0x044C : 0x06d8, + 0x044B : 0x06d9, + 0x0437 : 0x06da, + 0x0448 : 0x06db, + 0x044D : 0x06dc, + 0x0449 : 0x06dd, + 0x0447 : 0x06de, + 0x044A : 0x06df, + 0x042E : 0x06e0, + 0x0410 : 0x06e1, + 0x0411 : 0x06e2, + 0x0426 : 0x06e3, + 0x0414 : 0x06e4, + 0x0415 : 0x06e5, + 0x0424 : 0x06e6, + 0x0413 : 0x06e7, + 0x0425 : 0x06e8, + 0x0418 : 0x06e9, + 0x0419 : 0x06ea, + 0x041A : 0x06eb, + 0x041B : 0x06ec, + 0x041C : 0x06ed, + 0x041D : 0x06ee, + 0x041E : 0x06ef, + 0x041F : 0x06f0, + 0x042F : 0x06f1, + 0x0420 : 0x06f2, + 0x0421 : 0x06f3, + 0x0422 : 0x06f4, + 0x0423 : 0x06f5, + 0x0416 : 0x06f6, + 0x0412 : 0x06f7, + 0x042C : 0x06f8, + 0x042B : 0x06f9, + 0x0417 : 0x06fa, + 0x0428 : 0x06fb, + 0x042D : 0x06fc, + 0x0429 : 0x06fd, + 0x0427 : 0x06fe, + 0x042A : 0x06ff, + 0x0386 : 0x07a1, + 0x0388 : 0x07a2, + 0x0389 : 0x07a3, + 0x038A : 0x07a4, + 0x03AA : 0x07a5, + 0x038C : 0x07a7, + 0x038E : 0x07a8, + 0x03AB : 0x07a9, + 0x038F : 0x07ab, + 0x0385 : 0x07ae, + 0x2015 : 0x07af, + 0x03AC : 0x07b1, + 0x03AD : 0x07b2, + 0x03AE : 0x07b3, + 0x03AF : 0x07b4, + 0x03CA : 0x07b5, + 0x0390 : 0x07b6, + 0x03CC : 0x07b7, + 0x03CD : 0x07b8, + 0x03CB : 0x07b9, + 0x03B0 : 0x07ba, + 0x03CE : 0x07bb, + 0x0391 : 0x07c1, + 0x0392 : 0x07c2, + 0x0393 : 0x07c3, + 0x0394 : 0x07c4, + 0x0395 : 0x07c5, + 0x0396 : 0x07c6, + 0x0397 : 0x07c7, + 0x0398 : 0x07c8, + 0x0399 : 0x07c9, + 0x039A : 0x07ca, + 0x039B : 0x07cb, + 0x039C : 0x07cc, + 0x039D : 0x07cd, + 0x039E : 0x07ce, + 0x039F : 0x07cf, + 0x03A0 : 0x07d0, + 0x03A1 : 0x07d1, + 0x03A3 : 0x07d2, + 0x03A4 : 0x07d4, + 0x03A5 : 0x07d5, + 0x03A6 : 0x07d6, + 0x03A7 : 0x07d7, + 0x03A8 : 0x07d8, + 0x03A9 : 0x07d9, + 0x03B1 : 0x07e1, + 0x03B2 : 0x07e2, + 0x03B3 : 0x07e3, + 0x03B4 : 0x07e4, + 0x03B5 : 0x07e5, + 0x03B6 : 0x07e6, + 0x03B7 : 0x07e7, + 0x03B8 : 0x07e8, + 0x03B9 : 0x07e9, + 0x03BA : 0x07ea, + 0x03BB : 0x07eb, + 0x03BC : 0x07ec, + 0x03BD : 0x07ed, + 0x03BE : 0x07ee, + 0x03BF : 0x07ef, + 0x03C0 : 0x07f0, + 0x03C1 : 0x07f1, + 0x03C3 : 0x07f2, + 0x03C2 : 0x07f3, + 0x03C4 : 0x07f4, + 0x03C5 : 0x07f5, + 0x03C6 : 0x07f6, + 0x03C7 : 0x07f7, + 0x03C8 : 0x07f8, + 0x03C9 : 0x07f9, + 0x23B7 : 0x08a1, + 0x2320 : 0x08a4, + 0x2321 : 0x08a5, + 0x23A1 : 0x08a7, + 0x23A3 : 0x08a8, + 0x23A4 : 0x08a9, + 0x23A6 : 0x08aa, + 0x239B : 0x08ab, + 0x239D : 0x08ac, + 0x239E : 0x08ad, + 0x23A0 : 0x08ae, + 0x23A8 : 0x08af, + 0x23AC : 0x08b0, + 0x2264 : 0x08bc, + 0x2260 : 0x08bd, + 0x2265 : 0x08be, + 0x222B : 0x08bf, + 0x2234 : 0x08c0, + 0x221D : 0x08c1, + 0x221E : 0x08c2, + 0x2207 : 0x08c5, + 0x223C : 0x08c8, + 0x2243 : 0x08c9, + 0x21D4 : 0x08cd, + 0x21D2 : 0x08ce, + 0x2261 : 0x08cf, + 0x221A : 0x08d6, + 0x2282 : 0x08da, + 0x2283 : 0x08db, + 0x2229 : 0x08dc, + 0x222A : 0x08dd, + 0x2227 : 0x08de, + 0x2228 : 0x08df, + 0x2202 : 0x08ef, + 0x0192 : 0x08f6, + 0x2190 : 0x08fb, + 0x2191 : 0x08fc, + 0x2192 : 0x08fd, + 0x2193 : 0x08fe, + 0x25C6 : 0x09e0, + 0x2592 : 0x09e1, + 0x2409 : 0x09e2, + 0x240C : 0x09e3, + 0x240D : 0x09e4, + 0x240A : 0x09e5, + 0x2424 : 0x09e8, + 0x240B : 0x09e9, + 0x2518 : 0x09ea, + 0x2510 : 0x09eb, + 0x250C : 0x09ec, + 0x2514 : 0x09ed, + 0x253C : 0x09ee, + 0x23BA : 0x09ef, + 0x23BB : 0x09f0, + 0x2500 : 0x09f1, + 0x23BC : 0x09f2, + 0x23BD : 0x09f3, + 0x251C : 0x09f4, + 0x2524 : 0x09f5, + 0x2534 : 0x09f6, + 0x252C : 0x09f7, + 0x2502 : 0x09f8, + 0x2003 : 0x0aa1, + 0x2002 : 0x0aa2, + 0x2004 : 0x0aa3, + 0x2005 : 0x0aa4, + 0x2007 : 0x0aa5, + 0x2008 : 0x0aa6, + 0x2009 : 0x0aa7, + 0x200A : 0x0aa8, + 0x2014 : 0x0aa9, + 0x2013 : 0x0aaa, + 0x2026 : 0x0aae, + 0x2025 : 0x0aaf, + 0x2153 : 0x0ab0, + 0x2154 : 0x0ab1, + 0x2155 : 0x0ab2, + 0x2156 : 0x0ab3, + 0x2157 : 0x0ab4, + 0x2158 : 0x0ab5, + 0x2159 : 0x0ab6, + 0x215A : 0x0ab7, + 0x2105 : 0x0ab8, + 0x2012 : 0x0abb, + 0x215B : 0x0ac3, + 0x215C : 0x0ac4, + 0x215D : 0x0ac5, + 0x215E : 0x0ac6, + 0x2122 : 0x0ac9, + 0x2018 : 0x0ad0, + 0x2019 : 0x0ad1, + 0x201C : 0x0ad2, + 0x201D : 0x0ad3, + 0x211E : 0x0ad4, + 0x2032 : 0x0ad6, + 0x2033 : 0x0ad7, + 0x271D : 0x0ad9, + 0x2663 : 0x0aec, + 0x2666 : 0x0aed, + 0x2665 : 0x0aee, + 0x2720 : 0x0af0, + 0x2020 : 0x0af1, + 0x2021 : 0x0af2, + 0x2713 : 0x0af3, + 0x2717 : 0x0af4, + 0x266F : 0x0af5, + 0x266D : 0x0af6, + 0x2642 : 0x0af7, + 0x2640 : 0x0af8, + 0x260E : 0x0af9, + 0x2315 : 0x0afa, + 0x2117 : 0x0afb, + 0x2038 : 0x0afc, + 0x201A : 0x0afd, + 0x201E : 0x0afe, + 0x22A4 : 0x0bc2, + 0x230A : 0x0bc4, + 0x2218 : 0x0bca, + 0x2395 : 0x0bcc, + 0x22A5 : 0x0bce, + 0x25CB : 0x0bcf, + 0x2308 : 0x0bd3, + 0x22A3 : 0x0bdc, + 0x22A2 : 0x0bfc, + 0x2017 : 0x0cdf, + 0x05D0 : 0x0ce0, + 0x05D1 : 0x0ce1, + 0x05D2 : 0x0ce2, + 0x05D3 : 0x0ce3, + 0x05D4 : 0x0ce4, + 0x05D5 : 0x0ce5, + 0x05D6 : 0x0ce6, + 0x05D7 : 0x0ce7, + 0x05D8 : 0x0ce8, + 0x05D9 : 0x0ce9, + 0x05DA : 0x0cea, + 0x05DB : 0x0ceb, + 0x05DC : 0x0cec, + 0x05DD : 0x0ced, + 0x05DE : 0x0cee, + 0x05DF : 0x0cef, + 0x05E0 : 0x0cf0, + 0x05E1 : 0x0cf1, + 0x05E2 : 0x0cf2, + 0x05E3 : 0x0cf3, + 0x05E4 : 0x0cf4, + 0x05E5 : 0x0cf5, + 0x05E6 : 0x0cf6, + 0x05E7 : 0x0cf7, + 0x05E8 : 0x0cf8, + 0x05E9 : 0x0cf9, + 0x05EA : 0x0cfa, + 0x0E01 : 0x0da1, + 0x0E02 : 0x0da2, + 0x0E03 : 0x0da3, + 0x0E04 : 0x0da4, + 0x0E05 : 0x0da5, + 0x0E06 : 0x0da6, + 0x0E07 : 0x0da7, + 0x0E08 : 0x0da8, + 0x0E09 : 0x0da9, + 0x0E0A : 0x0daa, + 0x0E0B : 0x0dab, + 0x0E0C : 0x0dac, + 0x0E0D : 0x0dad, + 0x0E0E : 0x0dae, + 0x0E0F : 0x0daf, + 0x0E10 : 0x0db0, + 0x0E11 : 0x0db1, + 0x0E12 : 0x0db2, + 0x0E13 : 0x0db3, + 0x0E14 : 0x0db4, + 0x0E15 : 0x0db5, + 0x0E16 : 0x0db6, + 0x0E17 : 0x0db7, + 0x0E18 : 0x0db8, + 0x0E19 : 0x0db9, + 0x0E1A : 0x0dba, + 0x0E1B : 0x0dbb, + 0x0E1C : 0x0dbc, + 0x0E1D : 0x0dbd, + 0x0E1E : 0x0dbe, + 0x0E1F : 0x0dbf, + 0x0E20 : 0x0dc0, + 0x0E21 : 0x0dc1, + 0x0E22 : 0x0dc2, + 0x0E23 : 0x0dc3, + 0x0E24 : 0x0dc4, + 0x0E25 : 0x0dc5, + 0x0E26 : 0x0dc6, + 0x0E27 : 0x0dc7, + 0x0E28 : 0x0dc8, + 0x0E29 : 0x0dc9, + 0x0E2A : 0x0dca, + 0x0E2B : 0x0dcb, + 0x0E2C : 0x0dcc, + 0x0E2D : 0x0dcd, + 0x0E2E : 0x0dce, + 0x0E2F : 0x0dcf, + 0x0E30 : 0x0dd0, + 0x0E31 : 0x0dd1, + 0x0E32 : 0x0dd2, + 0x0E33 : 0x0dd3, + 0x0E34 : 0x0dd4, + 0x0E35 : 0x0dd5, + 0x0E36 : 0x0dd6, + 0x0E37 : 0x0dd7, + 0x0E38 : 0x0dd8, + 0x0E39 : 0x0dd9, + 0x0E3A : 0x0dda, + 0x0E3F : 0x0ddf, + 0x0E40 : 0x0de0, + 0x0E41 : 0x0de1, + 0x0E42 : 0x0de2, + 0x0E43 : 0x0de3, + 0x0E44 : 0x0de4, + 0x0E45 : 0x0de5, + 0x0E46 : 0x0de6, + 0x0E47 : 0x0de7, + 0x0E48 : 0x0de8, + 0x0E49 : 0x0de9, + 0x0E4A : 0x0dea, + 0x0E4B : 0x0deb, + 0x0E4C : 0x0dec, + 0x0E4D : 0x0ded, + 0x0E50 : 0x0df0, + 0x0E51 : 0x0df1, + 0x0E52 : 0x0df2, + 0x0E53 : 0x0df3, + 0x0E54 : 0x0df4, + 0x0E55 : 0x0df5, + 0x0E56 : 0x0df6, + 0x0E57 : 0x0df7, + 0x0E58 : 0x0df8, + 0x0E59 : 0x0df9, + 0x0587 : 0x1000587, + 0x0589 : 0x1000589, + 0x055D : 0x100055d, + 0x058A : 0x100058a, + 0x055C : 0x100055c, + 0x055B : 0x100055b, + 0x055E : 0x100055e, + 0x0531 : 0x1000531, + 0x0561 : 0x1000561, + 0x0532 : 0x1000532, + 0x0562 : 0x1000562, + 0x0533 : 0x1000533, + 0x0563 : 0x1000563, + 0x0534 : 0x1000534, + 0x0564 : 0x1000564, + 0x0535 : 0x1000535, + 0x0565 : 0x1000565, + 0x0536 : 0x1000536, + 0x0566 : 0x1000566, + 0x0537 : 0x1000537, + 0x0567 : 0x1000567, + 0x0538 : 0x1000538, + 0x0568 : 0x1000568, + 0x0539 : 0x1000539, + 0x0569 : 0x1000569, + 0x053A : 0x100053a, + 0x056A : 0x100056a, + 0x053B : 0x100053b, + 0x056B : 0x100056b, + 0x053C : 0x100053c, + 0x056C : 0x100056c, + 0x053D : 0x100053d, + 0x056D : 0x100056d, + 0x053E : 0x100053e, + 0x056E : 0x100056e, + 0x053F : 0x100053f, + 0x056F : 0x100056f, + 0x0540 : 0x1000540, + 0x0570 : 0x1000570, + 0x0541 : 0x1000541, + 0x0571 : 0x1000571, + 0x0542 : 0x1000542, + 0x0572 : 0x1000572, + 0x0543 : 0x1000543, + 0x0573 : 0x1000573, + 0x0544 : 0x1000544, + 0x0574 : 0x1000574, + 0x0545 : 0x1000545, + 0x0575 : 0x1000575, + 0x0546 : 0x1000546, + 0x0576 : 0x1000576, + 0x0547 : 0x1000547, + 0x0577 : 0x1000577, + 0x0548 : 0x1000548, + 0x0578 : 0x1000578, + 0x0549 : 0x1000549, + 0x0579 : 0x1000579, + 0x054A : 0x100054a, + 0x057A : 0x100057a, + 0x054B : 0x100054b, + 0x057B : 0x100057b, + 0x054C : 0x100054c, + 0x057C : 0x100057c, + 0x054D : 0x100054d, + 0x057D : 0x100057d, + 0x054E : 0x100054e, + 0x057E : 0x100057e, + 0x054F : 0x100054f, + 0x057F : 0x100057f, + 0x0550 : 0x1000550, + 0x0580 : 0x1000580, + 0x0551 : 0x1000551, + 0x0581 : 0x1000581, + 0x0552 : 0x1000552, + 0x0582 : 0x1000582, + 0x0553 : 0x1000553, + 0x0583 : 0x1000583, + 0x0554 : 0x1000554, + 0x0584 : 0x1000584, + 0x0555 : 0x1000555, + 0x0585 : 0x1000585, + 0x0556 : 0x1000556, + 0x0586 : 0x1000586, + 0x055A : 0x100055a, + 0x10D0 : 0x10010d0, + 0x10D1 : 0x10010d1, + 0x10D2 : 0x10010d2, + 0x10D3 : 0x10010d3, + 0x10D4 : 0x10010d4, + 0x10D5 : 0x10010d5, + 0x10D6 : 0x10010d6, + 0x10D7 : 0x10010d7, + 0x10D8 : 0x10010d8, + 0x10D9 : 0x10010d9, + 0x10DA : 0x10010da, + 0x10DB : 0x10010db, + 0x10DC : 0x10010dc, + 0x10DD : 0x10010dd, + 0x10DE : 0x10010de, + 0x10DF : 0x10010df, + 0x10E0 : 0x10010e0, + 0x10E1 : 0x10010e1, + 0x10E2 : 0x10010e2, + 0x10E3 : 0x10010e3, + 0x10E4 : 0x10010e4, + 0x10E5 : 0x10010e5, + 0x10E6 : 0x10010e6, + 0x10E7 : 0x10010e7, + 0x10E8 : 0x10010e8, + 0x10E9 : 0x10010e9, + 0x10EA : 0x10010ea, + 0x10EB : 0x10010eb, + 0x10EC : 0x10010ec, + 0x10ED : 0x10010ed, + 0x10EE : 0x10010ee, + 0x10EF : 0x10010ef, + 0x10F0 : 0x10010f0, + 0x10F1 : 0x10010f1, + 0x10F2 : 0x10010f2, + 0x10F3 : 0x10010f3, + 0x10F4 : 0x10010f4, + 0x10F5 : 0x10010f5, + 0x10F6 : 0x10010f6, + 0x1E8A : 0x1001e8a, + 0x012C : 0x100012c, + 0x01B5 : 0x10001b5, + 0x01E6 : 0x10001e6, + 0x01D2 : 0x10001d1, + 0x019F : 0x100019f, + 0x1E8B : 0x1001e8b, + 0x012D : 0x100012d, + 0x01B6 : 0x10001b6, + 0x01E7 : 0x10001e7, + 0x01D2 : 0x10001d2, + 0x0275 : 0x1000275, + 0x018F : 0x100018f, + 0x0259 : 0x1000259, + 0x1E36 : 0x1001e36, + 0x1E37 : 0x1001e37, + 0x1EA0 : 0x1001ea0, + 0x1EA1 : 0x1001ea1, + 0x1EA2 : 0x1001ea2, + 0x1EA3 : 0x1001ea3, + 0x1EA4 : 0x1001ea4, + 0x1EA5 : 0x1001ea5, + 0x1EA6 : 0x1001ea6, + 0x1EA7 : 0x1001ea7, + 0x1EA8 : 0x1001ea8, + 0x1EA9 : 0x1001ea9, + 0x1EAA : 0x1001eaa, + 0x1EAB : 0x1001eab, + 0x1EAC : 0x1001eac, + 0x1EAD : 0x1001ead, + 0x1EAE : 0x1001eae, + 0x1EAF : 0x1001eaf, + 0x1EB0 : 0x1001eb0, + 0x1EB1 : 0x1001eb1, + 0x1EB2 : 0x1001eb2, + 0x1EB3 : 0x1001eb3, + 0x1EB4 : 0x1001eb4, + 0x1EB5 : 0x1001eb5, + 0x1EB6 : 0x1001eb6, + 0x1EB7 : 0x1001eb7, + 0x1EB8 : 0x1001eb8, + 0x1EB9 : 0x1001eb9, + 0x1EBA : 0x1001eba, + 0x1EBB : 0x1001ebb, + 0x1EBC : 0x1001ebc, + 0x1EBD : 0x1001ebd, + 0x1EBE : 0x1001ebe, + 0x1EBF : 0x1001ebf, + 0x1EC0 : 0x1001ec0, + 0x1EC1 : 0x1001ec1, + 0x1EC2 : 0x1001ec2, + 0x1EC3 : 0x1001ec3, + 0x1EC4 : 0x1001ec4, + 0x1EC5 : 0x1001ec5, + 0x1EC6 : 0x1001ec6, + 0x1EC7 : 0x1001ec7, + 0x1EC8 : 0x1001ec8, + 0x1EC9 : 0x1001ec9, + 0x1ECA : 0x1001eca, + 0x1ECB : 0x1001ecb, + 0x1ECC : 0x1001ecc, + 0x1ECD : 0x1001ecd, + 0x1ECE : 0x1001ece, + 0x1ECF : 0x1001ecf, + 0x1ED0 : 0x1001ed0, + 0x1ED1 : 0x1001ed1, + 0x1ED2 : 0x1001ed2, + 0x1ED3 : 0x1001ed3, + 0x1ED4 : 0x1001ed4, + 0x1ED5 : 0x1001ed5, + 0x1ED6 : 0x1001ed6, + 0x1ED7 : 0x1001ed7, + 0x1ED8 : 0x1001ed8, + 0x1ED9 : 0x1001ed9, + 0x1EDA : 0x1001eda, + 0x1EDB : 0x1001edb, + 0x1EDC : 0x1001edc, + 0x1EDD : 0x1001edd, + 0x1EDE : 0x1001ede, + 0x1EDF : 0x1001edf, + 0x1EE0 : 0x1001ee0, + 0x1EE1 : 0x1001ee1, + 0x1EE2 : 0x1001ee2, + 0x1EE3 : 0x1001ee3, + 0x1EE4 : 0x1001ee4, + 0x1EE5 : 0x1001ee5, + 0x1EE6 : 0x1001ee6, + 0x1EE7 : 0x1001ee7, + 0x1EE8 : 0x1001ee8, + 0x1EE9 : 0x1001ee9, + 0x1EEA : 0x1001eea, + 0x1EEB : 0x1001eeb, + 0x1EEC : 0x1001eec, + 0x1EED : 0x1001eed, + 0x1EEE : 0x1001eee, + 0x1EEF : 0x1001eef, + 0x1EF0 : 0x1001ef0, + 0x1EF1 : 0x1001ef1, + 0x1EF4 : 0x1001ef4, + 0x1EF5 : 0x1001ef5, + 0x1EF6 : 0x1001ef6, + 0x1EF7 : 0x1001ef7, + 0x1EF8 : 0x1001ef8, + 0x1EF9 : 0x1001ef9, + 0x01A0 : 0x10001a0, + 0x01A1 : 0x10001a1, + 0x01AF : 0x10001af, + 0x01B0 : 0x10001b0, + 0x20A0 : 0x10020a0, + 0x20A1 : 0x10020a1, + 0x20A2 : 0x10020a2, + 0x20A3 : 0x10020a3, + 0x20A4 : 0x10020a4, + 0x20A5 : 0x10020a5, + 0x20A6 : 0x10020a6, + 0x20A7 : 0x10020a7, + 0x20A8 : 0x10020a8, + 0x20A9 : 0x10020a9, + 0x20AA : 0x10020aa, + 0x20AB : 0x10020ab, + 0x20AC : 0x20ac, + 0x2070 : 0x1002070, + 0x2074 : 0x1002074, + 0x2075 : 0x1002075, + 0x2076 : 0x1002076, + 0x2077 : 0x1002077, + 0x2078 : 0x1002078, + 0x2079 : 0x1002079, + 0x2080 : 0x1002080, + 0x2081 : 0x1002081, + 0x2082 : 0x1002082, + 0x2083 : 0x1002083, + 0x2084 : 0x1002084, + 0x2085 : 0x1002085, + 0x2086 : 0x1002086, + 0x2087 : 0x1002087, + 0x2088 : 0x1002088, + 0x2089 : 0x1002089, + 0x2202 : 0x1002202, + 0x2205 : 0x1002205, + 0x2208 : 0x1002208, + 0x2209 : 0x1002209, + 0x220B : 0x100220B, + 0x221A : 0x100221A, + 0x221B : 0x100221B, + 0x221C : 0x100221C, + 0x222C : 0x100222C, + 0x222D : 0x100222D, + 0x2235 : 0x1002235, + 0x2245 : 0x1002248, + 0x2247 : 0x1002247, + 0x2262 : 0x1002262, + 0x2263 : 0x1002263, + 0x2800 : 0x1002800, + 0x2801 : 0x1002801, + 0x2802 : 0x1002802, + 0x2803 : 0x1002803, + 0x2804 : 0x1002804, + 0x2805 : 0x1002805, + 0x2806 : 0x1002806, + 0x2807 : 0x1002807, + 0x2808 : 0x1002808, + 0x2809 : 0x1002809, + 0x280a : 0x100280a, + 0x280b : 0x100280b, + 0x280c : 0x100280c, + 0x280d : 0x100280d, + 0x280e : 0x100280e, + 0x280f : 0x100280f, + 0x2810 : 0x1002810, + 0x2811 : 0x1002811, + 0x2812 : 0x1002812, + 0x2813 : 0x1002813, + 0x2814 : 0x1002814, + 0x2815 : 0x1002815, + 0x2816 : 0x1002816, + 0x2817 : 0x1002817, + 0x2818 : 0x1002818, + 0x2819 : 0x1002819, + 0x281a : 0x100281a, + 0x281b : 0x100281b, + 0x281c : 0x100281c, + 0x281d : 0x100281d, + 0x281e : 0x100281e, + 0x281f : 0x100281f, + 0x2820 : 0x1002820, + 0x2821 : 0x1002821, + 0x2822 : 0x1002822, + 0x2823 : 0x1002823, + 0x2824 : 0x1002824, + 0x2825 : 0x1002825, + 0x2826 : 0x1002826, + 0x2827 : 0x1002827, + 0x2828 : 0x1002828, + 0x2829 : 0x1002829, + 0x282a : 0x100282a, + 0x282b : 0x100282b, + 0x282c : 0x100282c, + 0x282d : 0x100282d, + 0x282e : 0x100282e, + 0x282f : 0x100282f, + 0x2830 : 0x1002830, + 0x2831 : 0x1002831, + 0x2832 : 0x1002832, + 0x2833 : 0x1002833, + 0x2834 : 0x1002834, + 0x2835 : 0x1002835, + 0x2836 : 0x1002836, + 0x2837 : 0x1002837, + 0x2838 : 0x1002838, + 0x2839 : 0x1002839, + 0x283a : 0x100283a, + 0x283b : 0x100283b, + 0x283c : 0x100283c, + 0x283d : 0x100283d, + 0x283e : 0x100283e, + 0x283f : 0x100283f, + 0x2840 : 0x1002840, + 0x2841 : 0x1002841, + 0x2842 : 0x1002842, + 0x2843 : 0x1002843, + 0x2844 : 0x1002844, + 0x2845 : 0x1002845, + 0x2846 : 0x1002846, + 0x2847 : 0x1002847, + 0x2848 : 0x1002848, + 0x2849 : 0x1002849, + 0x284a : 0x100284a, + 0x284b : 0x100284b, + 0x284c : 0x100284c, + 0x284d : 0x100284d, + 0x284e : 0x100284e, + 0x284f : 0x100284f, + 0x2850 : 0x1002850, + 0x2851 : 0x1002851, + 0x2852 : 0x1002852, + 0x2853 : 0x1002853, + 0x2854 : 0x1002854, + 0x2855 : 0x1002855, + 0x2856 : 0x1002856, + 0x2857 : 0x1002857, + 0x2858 : 0x1002858, + 0x2859 : 0x1002859, + 0x285a : 0x100285a, + 0x285b : 0x100285b, + 0x285c : 0x100285c, + 0x285d : 0x100285d, + 0x285e : 0x100285e, + 0x285f : 0x100285f, + 0x2860 : 0x1002860, + 0x2861 : 0x1002861, + 0x2862 : 0x1002862, + 0x2863 : 0x1002863, + 0x2864 : 0x1002864, + 0x2865 : 0x1002865, + 0x2866 : 0x1002866, + 0x2867 : 0x1002867, + 0x2868 : 0x1002868, + 0x2869 : 0x1002869, + 0x286a : 0x100286a, + 0x286b : 0x100286b, + 0x286c : 0x100286c, + 0x286d : 0x100286d, + 0x286e : 0x100286e, + 0x286f : 0x100286f, + 0x2870 : 0x1002870, + 0x2871 : 0x1002871, + 0x2872 : 0x1002872, + 0x2873 : 0x1002873, + 0x2874 : 0x1002874, + 0x2875 : 0x1002875, + 0x2876 : 0x1002876, + 0x2877 : 0x1002877, + 0x2878 : 0x1002878, + 0x2879 : 0x1002879, + 0x287a : 0x100287a, + 0x287b : 0x100287b, + 0x287c : 0x100287c, + 0x287d : 0x100287d, + 0x287e : 0x100287e, + 0x287f : 0x100287f, + 0x2880 : 0x1002880, + 0x2881 : 0x1002881, + 0x2882 : 0x1002882, + 0x2883 : 0x1002883, + 0x2884 : 0x1002884, + 0x2885 : 0x1002885, + 0x2886 : 0x1002886, + 0x2887 : 0x1002887, + 0x2888 : 0x1002888, + 0x2889 : 0x1002889, + 0x288a : 0x100288a, + 0x288b : 0x100288b, + 0x288c : 0x100288c, + 0x288d : 0x100288d, + 0x288e : 0x100288e, + 0x288f : 0x100288f, + 0x2890 : 0x1002890, + 0x2891 : 0x1002891, + 0x2892 : 0x1002892, + 0x2893 : 0x1002893, + 0x2894 : 0x1002894, + 0x2895 : 0x1002895, + 0x2896 : 0x1002896, + 0x2897 : 0x1002897, + 0x2898 : 0x1002898, + 0x2899 : 0x1002899, + 0x289a : 0x100289a, + 0x289b : 0x100289b, + 0x289c : 0x100289c, + 0x289d : 0x100289d, + 0x289e : 0x100289e, + 0x289f : 0x100289f, + 0x28a0 : 0x10028a0, + 0x28a1 : 0x10028a1, + 0x28a2 : 0x10028a2, + 0x28a3 : 0x10028a3, + 0x28a4 : 0x10028a4, + 0x28a5 : 0x10028a5, + 0x28a6 : 0x10028a6, + 0x28a7 : 0x10028a7, + 0x28a8 : 0x10028a8, + 0x28a9 : 0x10028a9, + 0x28aa : 0x10028aa, + 0x28ab : 0x10028ab, + 0x28ac : 0x10028ac, + 0x28ad : 0x10028ad, + 0x28ae : 0x10028ae, + 0x28af : 0x10028af, + 0x28b0 : 0x10028b0, + 0x28b1 : 0x10028b1, + 0x28b2 : 0x10028b2, + 0x28b3 : 0x10028b3, + 0x28b4 : 0x10028b4, + 0x28b5 : 0x10028b5, + 0x28b6 : 0x10028b6, + 0x28b7 : 0x10028b7, + 0x28b8 : 0x10028b8, + 0x28b9 : 0x10028b9, + 0x28ba : 0x10028ba, + 0x28bb : 0x10028bb, + 0x28bc : 0x10028bc, + 0x28bd : 0x10028bd, + 0x28be : 0x10028be, + 0x28bf : 0x10028bf, + 0x28c0 : 0x10028c0, + 0x28c1 : 0x10028c1, + 0x28c2 : 0x10028c2, + 0x28c3 : 0x10028c3, + 0x28c4 : 0x10028c4, + 0x28c5 : 0x10028c5, + 0x28c6 : 0x10028c6, + 0x28c7 : 0x10028c7, + 0x28c8 : 0x10028c8, + 0x28c9 : 0x10028c9, + 0x28ca : 0x10028ca, + 0x28cb : 0x10028cb, + 0x28cc : 0x10028cc, + 0x28cd : 0x10028cd, + 0x28ce : 0x10028ce, + 0x28cf : 0x10028cf, + 0x28d0 : 0x10028d0, + 0x28d1 : 0x10028d1, + 0x28d2 : 0x10028d2, + 0x28d3 : 0x10028d3, + 0x28d4 : 0x10028d4, + 0x28d5 : 0x10028d5, + 0x28d6 : 0x10028d6, + 0x28d7 : 0x10028d7, + 0x28d8 : 0x10028d8, + 0x28d9 : 0x10028d9, + 0x28da : 0x10028da, + 0x28db : 0x10028db, + 0x28dc : 0x10028dc, + 0x28dd : 0x10028dd, + 0x28de : 0x10028de, + 0x28df : 0x10028df, + 0x28e0 : 0x10028e0, + 0x28e1 : 0x10028e1, + 0x28e2 : 0x10028e2, + 0x28e3 : 0x10028e3, + 0x28e4 : 0x10028e4, + 0x28e5 : 0x10028e5, + 0x28e6 : 0x10028e6, + 0x28e7 : 0x10028e7, + 0x28e8 : 0x10028e8, + 0x28e9 : 0x10028e9, + 0x28ea : 0x10028ea, + 0x28eb : 0x10028eb, + 0x28ec : 0x10028ec, + 0x28ed : 0x10028ed, + 0x28ee : 0x10028ee, + 0x28ef : 0x10028ef, + 0x28f0 : 0x10028f0, + 0x28f1 : 0x10028f1, + 0x28f2 : 0x10028f2, + 0x28f3 : 0x10028f3, + 0x28f4 : 0x10028f4, + 0x28f5 : 0x10028f5, + 0x28f6 : 0x10028f6, + 0x28f7 : 0x10028f7, + 0x28f8 : 0x10028f8, + 0x28f9 : 0x10028f9, + 0x28fa : 0x10028fa, + 0x28fb : 0x10028fb, + 0x28fc : 0x10028fc, + 0x28fd : 0x10028fd, + 0x28fe : 0x10028fe, + 0x28ff : 0x10028ff +}; \ No newline at end of file diff --git a/classes/novnc/include/logo.js b/classes/novnc/include/logo.js new file mode 100644 index 0000000..befa598 --- /dev/null +++ b/classes/novnc/include/logo.js @@ -0,0 +1 @@ +noVNC_logo = {"width": 640, "height": 435, "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAGzCAYAAAC/y6a9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAStAAAErQBBHTWggAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N13fBvlwQfw3522ZMm2vPdIGCFkA4GyoYyGsCmjk+7dQksHL2/H2/dtC4W2tLTlfelu2VA2lEILFCgQIHEGJCQkdjzkLdmWZGvfvX8oOkmJEy/pNO73/Xz44DtLzz2RT7qfnnXC8uXLZUxDlqfdnUYQhIP+bjbPn+5xhypzrmUf6rGzOc5cjzVduXN9/nTPyfRrMt/jzOcY05U5n3L2f95s/34LPW4m/p6FbLp/73xe+5nKnWuZs/07ZOOcnusx5nucbJU727LneuxslDmdTBxn/2NmusyEuZS7kHMxG/XP5Gf3TOVmQzY+u/PhPMnkMcSsH5WIiIiI8goDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHG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"}; diff --git a/classes/novnc/include/playback.js b/classes/novnc/include/playback.js new file mode 100644 index 0000000..22a00a3 --- /dev/null +++ b/classes/novnc/include/playback.js @@ -0,0 +1,90 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.LGPL-3) + */ + +"use strict"; +/*jslint browser: true, white: false */ +/*global Util, VNC_frame_data, finish */ + +var rfb, mode, test_state, frame_idx, frame_length, + iteration, iterations, istart_time, + + // Pre-declarations for jslint + send_array, next_iteration, queue_next_packet, do_packet; + +// Override send_array +send_array = function (arr) { + // Stub out send_array +}; + +next_iteration = function () { + if (iteration === 0) { + frame_length = VNC_frame_data.length; + test_state = 'running'; + } else { + rfb.disconnect(); + } + + if (test_state !== 'running') { return; } + + iteration += 1; + if (iteration > iterations) { + finish(); + return; + } + + frame_idx = 0; + istart_time = (new Date()).getTime(); + rfb.connect('test', 0, "bogus"); + + queue_next_packet(); + +}; + +queue_next_packet = function () { + var frame, foffset, toffset, delay; + if (test_state !== 'running') { return; } + + frame = VNC_frame_data[frame_idx]; + while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) { + //Util.Debug("Send frame " + frame_idx); + frame_idx += 1; + frame = VNC_frame_data[frame_idx]; + } + + if (frame === 'EOF') { + Util.Debug("Finished, found EOF"); + next_iteration(); + return; + } + if (frame_idx >= frame_length) { + Util.Debug("Finished, no more frames"); + next_iteration(); + return; + } + + if (mode === 'realtime') { + foffset = frame.slice(1, frame.indexOf('{', 1)); + toffset = (new Date()).getTime() - istart_time; + delay = foffset - toffset; + if (delay < 1) { + delay = 1; + } + + setTimeout(do_packet, delay); + } else { + setTimeout(do_packet, 1); + } +}; + +do_packet = function () { + //Util.Debug("Processing frame: " + frame_idx); + var frame = VNC_frame_data[frame_idx]; + rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)}); + frame_idx += 1; + + queue_next_packet(); +}; + diff --git a/classes/novnc/include/rfb.js b/classes/novnc/include/rfb.js new file mode 100644 index 0000000..b7aa3f6 --- /dev/null +++ b/classes/novnc/include/rfb.js @@ -0,0 +1,1613 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint white: false, browser: true, bitwise: false, plusplus: false */ +/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */ + + +function RFB(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + // Pre-declare private functions used before definitions (jslint) + init_vars, updateState, fail, handle_message, + init_msg, normal_msg, framebufferUpdate, print_stats, + + pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests, + keyEvent, pointerEvent, clientCutText, + + extract_data_uri, scan_tight_imgQ, + keyPress, mouseButton, mouseMove, + + checkEvents, // Overridable for testing + + + // + // Private RFB namespace variables + // + rfb_host = '', + rfb_port = 5900, + rfb_password = '', + rfb_path = '', + + rfb_state = 'disconnected', + rfb_version = 0, + rfb_max_version= 3.8, + rfb_auth_scheme= '', + + + // In preference order + encodings = [ + ['COPYRECT', 0x01 ], + ['TIGHT_PNG', -260 ], + ['HEXTILE', 0x05 ], + ['RRE', 0x02 ], + ['RAW', 0x00 ], + ['DesktopSize', -223 ], + ['Cursor', -239 ], + + // Psuedo-encoding settings + ['JPEG_quality_lo', -32 ], + //['JPEG_quality_hi', -23 ], + ['compress_lo', -255 ] + //['compress_hi', -247 ] + ], + + encHandlers = {}, + encNames = {}, + encStats = {}, // [rectCnt, rectCntTot] + + ws = null, // Websock object + display = null, // Display object + keyboard = null, // Keyboard input handler object + mouse = null, // Mouse input handler object + sendTimer = null, // Send Queue check timer + connTimer = null, // connection timer + disconnTimer = null, // disconnection timer + msgTimer = null, // queued handle_message timer + + // Frame buffer update state + FBU = { + rects : 0, + subrects : 0, // RRE + lines : 0, // RAW + tiles : 0, // HEXTILE + bytes : 0, + x : 0, + y : 0, + width : 0, + height : 0, + encoding : 0, + subencoding : -1, + background : null, + imgQ : [] // TIGHT_PNG image queue + }, + + fb_Bpp = 4, + fb_depth = 3, + fb_width = 0, + fb_height = 0, + fb_name = "", + + scan_imgQ_rate = 40, // 25 times per second or so + last_req_time = 0, + rre_chunk_sz = 100, + + timing = { + last_fbu : 0, + fbu_total : 0, + fbu_total_cnt : 0, + full_fbu_total : 0, + full_fbu_cnt : 0, + + fbu_rt_start : 0, + fbu_rt_total : 0, + fbu_rt_cnt : 0 + }, + + test_mode = false, + + def_con_timeout = Websock_native ? 2 : 5, + + /* Mouse state */ + mouse_buttonMask = 0, + mouse_arr = [], + viewportDragging = false, + viewportDragPos = {}; + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', null, 'VNC display rendering Canvas object'], + ['focusContainer', 'wo', 'dom', document, 'DOM element that captures keyboard input'], + + ['encrypt', 'rw', 'bool', false, 'Use TLS/SSL/wss encryption'], + ['true_color', 'rw', 'bool', true, 'Request true color pixel data'], + ['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'], + ['shared', 'rw', 'bool', true, 'Request shared mode'], + + ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'], + ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'], + + ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'], + + ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'], + ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'], + + // Callback functions + ['onUpdateState', 'rw', 'func', function() { }, + 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '], + ['onPasswordRequired', 'rw', 'func', function() { }, + 'onPasswordRequired(rfb): VNC password is required '], + ['onClipboard', 'rw', 'func', function() { }, + 'onClipboard(rfb, text): RFB clipboard contents received'], + ['onBell', 'rw', 'func', function() { }, + 'onBell(rfb): RFB Bell message received '], + ['onFBUReceive', 'rw', 'func', function() { }, + 'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '], + ['onFBUComplete', 'rw', 'func', function() { }, + 'onFBUComplete(rfb, fbu): RFB FBU received and processed '], + + // These callback names are deprecated + ['updateState', 'rw', 'func', function() { }, + 'obsolete, use onUpdateState'], + ['clipboardReceive', 'rw', 'func', function() { }, + 'obsolete, use onClipboard'] + ]); + + +// Override/add some specific configuration getters/setters +that.set_local_cursor = function(cursor) { + if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) { + conf.local_cursor = false; + } else { + if (display.get_cursor_uri()) { + conf.local_cursor = true; + } else { + Util.Warn("Browser does not support local cursor"); + } + } +}; + +// These are fake configuration getters +that.get_display = function() { return display; }; + +that.get_keyboard = function() { return keyboard; }; + +that.get_mouse = function() { return mouse; }; + + + +// +// Setup routines +// + +// Create the public API interface and initialize values that stay +// constant across connect/disconnect +function constructor() { + var i, rmode; + Util.Debug(">> RFB.constructor"); + + // Create lookup tables based encoding number + for (i=0; i < encodings.length; i+=1) { + encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]]; + encNames[encodings[i][1]] = encodings[i][0]; + encStats[encodings[i][1]] = [0, 0]; + } + // Initialize display, mouse, keyboard, and websock + try { + display = new Display({'target': conf.target}); + } catch (exc) { + Util.Error("Display exception: " + exc); + updateState('fatal', "No working Display"); + } + keyboard = new Keyboard({'target': conf.focusContainer, + 'onKeyPress': keyPress}); + mouse = new Mouse({'target': conf.target, + 'onMouseButton': mouseButton, + 'onMouseMove': mouseMove}); + + rmode = display.get_render_mode(); + + ws = new Websock(); + ws.on('message', handle_message); + ws.on('open', function() { + if (rfb_state === "connect") { + updateState('ProtocolVersion', "Starting VNC handshake"); + } else { + fail("Got unexpected WebSockets connection"); + } + }); + ws.on('close', function() { + if (rfb_state === 'disconnect') { + updateState('disconnected', 'VNC disconnected'); + } else if (rfb_state === 'ProtocolVersion') { + fail('Failed to connect to server'); + } else if (rfb_state in {'failed':1, 'disconnected':1}) { + Util.Error("Received onclose while disconnected"); + } else { + fail('Server disconnected'); + } + }); + ws.on('error', function(e) { + fail("WebSock error: " + e); + }); + + + init_vars(); + + /* Check web-socket-js if no builtin WebSocket support */ + if (Websock_native) { + Util.Info("Using native WebSockets"); + updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode); + } else { + Util.Warn("Using web-socket-js bridge. Flash version: " + + Util.Flash.version); + if ((! Util.Flash) || + (Util.Flash.version < 9)) { + updateState('fatal', "WebSockets or Adobe Flash<\/a> is required"); + } else if (document.location.href.substr(0, 7) === "file://") { + updateState('fatal', + "'file://' URL is incompatible with Adobe Flash"); + } else { + updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode); + } + } + + Util.Debug("<< RFB.constructor"); + return that; // Return the public API interface +} + +function connect() { + Util.Debug(">> RFB.connect"); + + var uri = ""; + if (conf.encrypt) { + uri = "wss://"; + } else { + uri = "ws://"; + } + uri += rfb_host + ":" + rfb_port + "/" + rfb_path; + Util.Info("connecting to " + uri); + ws.open(uri); + + Util.Debug("<< RFB.connect"); +} + +// Initialize variables that are reset before each connection +init_vars = function() { + var i; + + /* Reset state */ + ws.init(); + + FBU.rects = 0; + FBU.subrects = 0; // RRE and HEXTILE + FBU.lines = 0; // RAW + FBU.tiles = 0; // HEXTILE + FBU.imgQ = []; // TIGHT_PNG image queue + mouse_buttonMask = 0; + mouse_arr = []; + + // Clear the per connection encoding stats + for (i=0; i < encodings.length; i+=1) { + encStats[encodings[i][1]][0] = 0; + } +}; + +// Print statistics +print_stats = function() { + var i, s; + Util.Info("Encoding stats for this connection:"); + for (i=0; i < encodings.length; i+=1) { + s = encStats[encodings[i][1]]; + if ((s[0] + s[1]) > 0) { + Util.Info(" " + encodings[i][0] + ": " + + s[0] + " rects"); + } + } + Util.Info("Encoding stats since page load:"); + for (i=0; i < encodings.length; i+=1) { + s = encStats[encodings[i][1]]; + if ((s[0] + s[1]) > 0) { + Util.Info(" " + encodings[i][0] + ": " + + s[1] + " rects"); + } + } +}; + +// +// Utility routines +// + + +/* + * Page states: + * loaded - page load, equivalent to disconnected + * disconnected - idle state + * connect - starting to connect (to ProtocolVersion) + * normal - connected + * disconnect - starting to disconnect + * failed - abnormal disconnect + * fatal - failed to load page, or fatal error + * + * RFB protocol initialization states: + * ProtocolVersion + * Security + * Authentication + * password - waiting for password, not part of RFB + * SecurityResult + * ClientInitialization - not triggered by server message + * ServerInitialization (to normal) + */ +updateState = function(state, statusMsg) { + var func, cmsg, oldstate = rfb_state; + + if (state === oldstate) { + /* Already here, ignore */ + Util.Debug("Already in state '" + state + "', ignoring."); + return; + } + + /* + * These are disconnected states. A previous connect may + * asynchronously cause a connection so make sure we are closed. + */ + if (state in {'disconnected':1, 'loaded':1, 'connect':1, + 'disconnect':1, 'failed':1, 'fatal':1}) { + if (sendTimer) { + clearInterval(sendTimer); + sendTimer = null; + } + + if (msgTimer) { + clearInterval(msgTimer); + msgTimer = null; + } + + if (display && display.get_context()) { + keyboard.ungrab(); + mouse.ungrab(); + display.defaultCursor(); + if ((Util.get_logging() !== 'debug') || + (state === 'loaded')) { + // Show noVNC logo on load and when disconnected if + // debug is off + display.clear(); + } + } + + ws.close(); + } + + if (oldstate === 'fatal') { + Util.Error("Fatal error, cannot continue"); + } + + if ((state === 'failed') || (state === 'fatal')) { + func = Util.Error; + } else { + func = Util.Warn; + } + + if ((oldstate === 'failed') && (state === 'disconnected')) { + // Do disconnect action, but stay in failed state. + rfb_state = 'failed'; + } else { + rfb_state = state; + } + + cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : ""; + func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg); + + if (connTimer && (rfb_state !== 'connect')) { + Util.Debug("Clearing connect timer"); + clearInterval(connTimer); + connTimer = null; + } + + if (disconnTimer && (rfb_state !== 'disconnect')) { + Util.Debug("Clearing disconnect timer"); + clearInterval(disconnTimer); + disconnTimer = null; + } + + switch (state) { + case 'normal': + if ((oldstate === 'disconnected') || (oldstate === 'failed')) { + Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'"); + } + + break; + + + case 'connect': + + connTimer = setTimeout(function () { + fail("Connect timeout"); + }, conf.connectTimeout * 1000); + + init_vars(); + connect(); + + // WebSocket.onopen transitions to 'ProtocolVersion' + break; + + + case 'disconnect': + + if (! test_mode) { + disconnTimer = setTimeout(function () { + fail("Disconnect timeout"); + }, conf.disconnectTimeout * 1000); + } + + print_stats(); + + // WebSocket.onclose transitions to 'disconnected' + break; + + + case 'failed': + if (oldstate === 'disconnected') { + Util.Error("Invalid transition from 'disconnected' to 'failed'"); + } + if (oldstate === 'normal') { + Util.Error("Error while connected."); + } + if (oldstate === 'init') { + Util.Error("Error while initializing."); + } + + // Make sure we transition to disconnected + setTimeout(function() { updateState('disconnected'); }, 50); + + break; + + + default: + // No state change action to take + + } + + if ((oldstate === 'failed') && (state === 'disconnected')) { + // Leave the failed message + conf.updateState(that, state, oldstate); // Obsolete + conf.onUpdateState(that, state, oldstate); + } else { + conf.updateState(that, state, oldstate, statusMsg); // Obsolete + conf.onUpdateState(that, state, oldstate, statusMsg); + } +}; + +fail = function(msg) { + updateState('failed', msg); + return false; +}; + +handle_message = function() { + //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen()); + //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + if (ws.rQlen() === 0) { + Util.Warn("handle_message called on empty receive queue"); + return; + } + switch (rfb_state) { + case 'disconnected': + case 'failed': + Util.Error("Got data while disconnected"); + break; + case 'normal': + if (normal_msg() && ws.rQlen() > 0) { + // true means we can continue processing + // Give other events a chance to run + if (msgTimer === null) { + Util.Debug("More data to process, creating timer"); + msgTimer = setTimeout(function () { + msgTimer = null; + handle_message(); + }, 10); + } else { + Util.Debug("More data to process, existing timer"); + } + } + break; + default: + init_msg(); + break; + } +}; + + +function genDES(password, challenge) { + var i, passwd = []; + for (i=0; i < password.length; i += 1) { + passwd.push(password.charCodeAt(i)); + } + return (new DES(passwd)).encrypt(challenge); +} + +function flushClient() { + if (mouse_arr.length > 0) { + //send(mouse_arr.concat(fbUpdateRequests())); + ws.send(mouse_arr); + setTimeout(function() { + ws.send(fbUpdateRequests()); + }, 50); + + mouse_arr = []; + return true; + } else { + return false; + } +} + +// overridable for testing +checkEvents = function() { + var now; + if (rfb_state === 'normal' && !viewportDragging) { + if (! flushClient()) { + now = new Date().getTime(); + if (now > last_req_time + conf.fbu_req_rate) { + last_req_time = now; + ws.send(fbUpdateRequests()); + } + } + } + setTimeout(checkEvents, conf.check_rate); +}; + +keyPress = function(keysym, down) { + var arr; + arr = keyEvent(keysym, down); + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +mouseButton = function(x, y, down, bmask) { + if (down) { + mouse_buttonMask |= bmask; + } else { + mouse_buttonMask ^= bmask; + } + + if (conf.viewportDrag) { + if (down && !viewportDragging) { + viewportDragging = true; + viewportDragPos = {'x': x, 'y': y}; + + // Skip sending mouse events + return; + } else { + viewportDragging = false; + } + } + + mouse_arr = mouse_arr.concat( + pointerEvent(display.absX(x), display.absY(y)) ); + flushClient(); +}; + +mouseMove = function(x, y) { + //Util.Debug('>> mouseMove ' + x + "," + y); + var deltaX, deltaY; + + if (viewportDragging) { + //deltaX = x - viewportDragPos.x; // drag viewport + deltaX = viewportDragPos.x - x; // drag frame buffer + //deltaY = y - viewportDragPos.y; // drag viewport + deltaY = viewportDragPos.y - y; // drag frame buffer + viewportDragPos = {'x': x, 'y': y}; + + display.viewportChange(deltaX, deltaY); + + // Skip sending mouse events + return; + } + + mouse_arr = mouse_arr.concat( + pointerEvent(display.absX(x), display.absY(y)) ); +}; + + +// +// Server message handlers +// + +// RFB/VNC initialisation message handler +init_msg = function() { + //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']"); + + var strlen, reason, length, sversion, cversion, + i, types, num_types, challenge, response, bpp, depth, + big_endian, red_max, green_max, blue_max, red_shift, + green_shift, blue_shift, true_color, name_length; + + //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0)); + switch (rfb_state) { + + case 'ProtocolVersion' : + if (ws.rQlen() < 12) { + return fail("Incomplete protocol version"); + } + sversion = ws.rQshiftStr(12).substr(4,7); + Util.Info("Server ProtocolVersion: " + sversion); + switch (sversion) { + case "003.003": rfb_version = 3.3; break; + case "003.006": rfb_version = 3.3; break; // UltraVNC + case "003.007": rfb_version = 3.7; break; + case "003.008": rfb_version = 3.8; break; + default: + return fail("Invalid server version " + sversion); + } + if (rfb_version > rfb_max_version) { + rfb_version = rfb_max_version; + } + + if (! test_mode) { + sendTimer = setInterval(function() { + // Send updates either at a rate of one update + // every 50ms, or whatever slower rate the network + // can handle. + ws.flush(); + }, 50); + } + + cversion = "00" + parseInt(rfb_version,10) + + ".00" + ((rfb_version * 10) % 10); + ws.send_string("RFB " + cversion + "\n"); + updateState('Security', "Sent ProtocolVersion: " + cversion); + break; + + case 'Security' : + if (rfb_version >= 3.7) { + // Server sends supported list, client decides + num_types = ws.rQshift8(); + if (ws.rQwait("security type", num_types, 1)) { return false; } + if (num_types === 0) { + strlen = ws.rQshift32(); + reason = ws.rQshiftStr(strlen); + return fail("Security failure: " + reason); + } + rfb_auth_scheme = 0; + types = ws.rQshiftBytes(num_types); + Util.Debug("Server security types: " + types); + for (i=0; i < types.length; i+=1) { + if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) { + rfb_auth_scheme = types[i]; + } + } + if (rfb_auth_scheme === 0) { + return fail("Unsupported security types: " + types); + } + + ws.send([rfb_auth_scheme]); + } else { + // Server decides + if (ws.rQwait("security scheme", 4)) { return false; } + rfb_auth_scheme = ws.rQshift32(); + } + updateState('Authentication', + "Authenticating using scheme: " + rfb_auth_scheme); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + // Triggered by fallthough, not by server message + case 'Authentication' : + //Util.Debug("Security auth scheme: " + rfb_auth_scheme); + switch (rfb_auth_scheme) { + case 0: // connection failed + if (ws.rQwait("auth reason", 4)) { return false; } + strlen = ws.rQshift32(); + reason = ws.rQshiftStr(strlen); + return fail("Auth failure: " + reason); + case 1: // no authentication + if (rfb_version >= 3.8) { + updateState('SecurityResult'); + return; + } + // Fall through to ClientInitialisation + break; + case 2: // VNC authentication + if (rfb_password.length === 0) { + // Notify via both callbacks since it is kind of + // a RFB state change and a UI interface issue. + updateState('password', "Password Required"); + conf.onPasswordRequired(that); + return; + } + if (ws.rQwait("auth challenge", 16)) { return false; } + challenge = ws.rQshiftBytes(16); + //Util.Debug("Password: " + rfb_password); + //Util.Debug("Challenge: " + challenge + + // " (" + challenge.length + ")"); + response = genDES(rfb_password, challenge); + //Util.Debug("Response: " + response + + // " (" + response.length + ")"); + + //Util.Debug("Sending DES encrypted auth response"); + ws.send(response); + updateState('SecurityResult'); + return; + default: + fail("Unsupported auth scheme: " + rfb_auth_scheme); + return; + } + updateState('ClientInitialisation', "No auth required"); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + case 'SecurityResult' : + if (ws.rQwait("VNC auth response ", 4)) { return false; } + switch (ws.rQshift32()) { + case 0: // OK + // Fall through to ClientInitialisation + break; + case 1: // failed + if (rfb_version >= 3.8) { + length = ws.rQshift32(); + if (ws.rQwait("SecurityResult reason", length, 8)) { + return false; + } + reason = ws.rQshiftStr(length); + fail(reason); + } else { + fail("Authentication failed"); + } + return; + case 2: // too-many + return fail("Too many auth attempts"); + } + updateState('ClientInitialisation', "Authentication OK"); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + // Triggered by fallthough, not by server message + case 'ClientInitialisation' : + ws.send([conf.shared ? 1 : 0]); // ClientInitialisation + updateState('ServerInitialisation', "Authentication OK"); + break; + + case 'ServerInitialisation' : + if (ws.rQwait("server initialization", 24)) { return false; } + + /* Screen size */ + fb_width = ws.rQshift16(); + fb_height = ws.rQshift16(); + + /* PIXEL_FORMAT */ + bpp = ws.rQshift8(); + depth = ws.rQshift8(); + big_endian = ws.rQshift8(); + true_color = ws.rQshift8(); + + red_max = ws.rQshift16(); + green_max = ws.rQshift16(); + blue_max = ws.rQshift16(); + red_shift = ws.rQshift8(); + green_shift = ws.rQshift8(); + blue_shift = ws.rQshift8(); + ws.rQshiftStr(3); // padding + + Util.Info("Screen: " + fb_width + "x" + fb_height + + ", bpp: " + bpp + ", depth: " + depth + + ", big_endian: " + big_endian + + ", true_color: " + true_color + + ", red_max: " + red_max + + ", green_max: " + green_max + + ", blue_max: " + blue_max + + ", red_shift: " + red_shift + + ", green_shift: " + green_shift + + ", blue_shift: " + blue_shift); + + /* Connection name/title */ + name_length = ws.rQshift32(); + fb_name = ws.rQshiftStr(name_length); + + display.set_true_color(conf.true_color); + display.resize(fb_width, fb_height); + keyboard.grab(); + mouse.grab(); + + if (conf.true_color) { + fb_Bpp = 4; + fb_depth = 3; + } else { + fb_Bpp = 1; + fb_depth = 1; + } + + response = pixelFormat(); + response = response.concat(clientEncodings()); + response = response.concat(fbUpdateRequests()); + timing.fbu_rt_start = (new Date()).getTime(); + ws.send(response); + + /* Start pushing/polling */ + setTimeout(checkEvents, conf.check_rate); + setTimeout(scan_tight_imgQ, scan_imgQ_rate); + + if (conf.encrypt) { + updateState('normal', "Connected (encrypted) to: " + fb_name); + } else { + updateState('normal', "Connected (unencrypted) to: " + fb_name); + } + break; + } + //Util.Debug("<< init_msg"); +}; + + +/* Normal RFB/VNC server message handler */ +normal_msg = function() { + //Util.Debug(">> normal_msg"); + + var ret = true, msg_type, length, text, + c, first_colour, num_colours, red, green, blue; + + if (FBU.rects > 0) { + msg_type = 0; + } else { + msg_type = ws.rQshift8(); + } + switch (msg_type) { + case 0: // FramebufferUpdate + ret = framebufferUpdate(); // false means need more data + break; + case 1: // SetColourMapEntries + Util.Debug("SetColourMapEntries"); + ws.rQshift8(); // Padding + first_colour = ws.rQshift16(); // First colour + num_colours = ws.rQshift16(); + for (c=0; c < num_colours; c+=1) { + red = ws.rQshift16(); + //Util.Debug("red before: " + red); + red = parseInt(red / 256, 10); + //Util.Debug("red after: " + red); + green = parseInt(ws.rQshift16() / 256, 10); + blue = parseInt(ws.rQshift16() / 256, 10); + display.set_colourMap([red, green, blue], first_colour + c); + } + Util.Debug("colourMap: " + display.get_colourMap()); + Util.Info("Registered " + num_colours + " colourMap entries"); + //Util.Debug("colourMap: " + display.get_colourMap()); + break; + case 2: // Bell + Util.Debug("Bell"); + conf.onBell(that); + break; + case 3: // ServerCutText + Util.Debug("ServerCutText"); + if (ws.rQwait("ServerCutText header", 7, 1)) { return false; } + ws.rQshiftBytes(3); // Padding + length = ws.rQshift32(); + if (ws.rQwait("ServerCutText", length, 8)) { return false; } + + text = ws.rQshiftStr(length); + conf.clipboardReceive(that, text); // Obsolete + conf.onClipboard(that, text); + break; + default: + fail("Disconnected: illegal server message type " + msg_type); + Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); + break; + } + //Util.Debug("<< normal_msg"); + return ret; +}; + +framebufferUpdate = function() { + var now, hdr, fbu_rt_diff, ret = true; + + if (FBU.rects === 0) { + //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20)); + if (ws.rQwait("FBU header", 3)) { + ws.rQunshift8(0); // FBU msg_type + return false; + } + ws.rQshift8(); // padding + FBU.rects = ws.rQshift16(); + //Util.Debug("FramebufferUpdate, rects:" + FBU.rects); + FBU.bytes = 0; + timing.cur_fbu = 0; + if (timing.fbu_rt_start > 0) { + now = (new Date()).getTime(); + Util.Info("First FBU latency: " + (now - timing.fbu_rt_start)); + } + } + + while (FBU.rects > 0) { + if (rfb_state !== "normal") { + return false; + } + if (ws.rQwait("FBU", FBU.bytes)) { return false; } + if (FBU.bytes === 0) { + if (ws.rQwait("rect header", 12)) { return false; } + /* New FramebufferUpdate */ + + hdr = ws.rQshiftBytes(12); + FBU.x = (hdr[0] << 8) + hdr[1]; + FBU.y = (hdr[2] << 8) + hdr[3]; + FBU.width = (hdr[4] << 8) + hdr[5]; + FBU.height = (hdr[6] << 8) + hdr[7]; + FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) + + (hdr[10] << 8) + hdr[11], 10); + + conf.onFBUReceive(that, + {'x': FBU.x, 'y': FBU.y, + 'width': FBU.width, 'height': FBU.height, + 'encoding': FBU.encoding, + 'encodingName': encNames[FBU.encoding]}); + + if (encNames[FBU.encoding]) { + // Debug: + /* + var msg = "FramebufferUpdate rects:" + FBU.rects; + msg += " x: " + FBU.x + " y: " + FBU.y; + msg += " width: " + FBU.width + " height: " + FBU.height; + msg += " encoding:" + FBU.encoding; + msg += "(" + encNames[FBU.encoding] + ")"; + msg += ", ws.rQlen(): " + ws.rQlen(); + Util.Debug(msg); + */ + } else { + fail("Disconnected: unsupported encoding " + + FBU.encoding); + return false; + } + } + + timing.last_fbu = (new Date()).getTime(); + + ret = encHandlers[FBU.encoding](); + + now = (new Date()).getTime(); + timing.cur_fbu += (now - timing.last_fbu); + + if (ret) { + encStats[FBU.encoding][0] += 1; + encStats[FBU.encoding][1] += 1; + } + + if (FBU.rects === 0) { + if (((FBU.width === fb_width) && + (FBU.height === fb_height)) || + (timing.fbu_rt_start > 0)) { + timing.full_fbu_total += timing.cur_fbu; + timing.full_fbu_cnt += 1; + Util.Info("Timing of full FBU, cur: " + + timing.cur_fbu + ", total: " + + timing.full_fbu_total + ", cnt: " + + timing.full_fbu_cnt + ", avg: " + + (timing.full_fbu_total / + timing.full_fbu_cnt)); + } + if (timing.fbu_rt_start > 0) { + fbu_rt_diff = now - timing.fbu_rt_start; + timing.fbu_rt_total += fbu_rt_diff; + timing.fbu_rt_cnt += 1; + Util.Info("full FBU round-trip, cur: " + + fbu_rt_diff + ", total: " + + timing.fbu_rt_total + ", cnt: " + + timing.fbu_rt_cnt + ", avg: " + + (timing.fbu_rt_total / + timing.fbu_rt_cnt)); + timing.fbu_rt_start = 0; + } + } + if (! ret) { + return ret; // false ret means need more data + } + } + + conf.onFBUComplete(that, + {'x': FBU.x, 'y': FBU.y, + 'width': FBU.width, 'height': FBU.height, + 'encoding': FBU.encoding, + 'encodingName': encNames[FBU.encoding]}); + + return true; // We finished this FBU +}; + +// +// FramebufferUpdate encodings +// + +encHandlers.RAW = function display_raw() { + //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)"); + + var cur_y, cur_height; + + if (FBU.lines === 0) { + FBU.lines = FBU.height; + } + FBU.bytes = FBU.width * fb_Bpp; // At least a line + if (ws.rQwait("RAW", FBU.bytes)) { return false; } + cur_y = FBU.y + (FBU.height - FBU.lines); + cur_height = Math.min(FBU.lines, + Math.floor(ws.rQlen()/(FBU.width * fb_Bpp))); + display.blitImage(FBU.x, cur_y, FBU.width, cur_height, + ws.get_rQ(), ws.get_rQi()); + ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp); + FBU.lines -= cur_height; + + if (FBU.lines > 0) { + FBU.bytes = FBU.width * fb_Bpp; // At least another line + } else { + FBU.rects -= 1; + FBU.bytes = 0; + } + //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)"); + return true; +}; + +encHandlers.COPYRECT = function display_copy_rect() { + //Util.Debug(">> display_copy_rect"); + + var old_x, old_y; + + if (ws.rQwait("COPYRECT", 4)) { return false; } + old_x = ws.rQshift16(); + old_y = ws.rQshift16(); + display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height); + FBU.rects -= 1; + FBU.bytes = 0; + return true; +}; + +encHandlers.RRE = function display_rre() { + //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)"); + var color, x, y, width, height, chunk; + + if (FBU.subrects === 0) { + if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; } + FBU.subrects = ws.rQshift32(); + color = ws.rQshiftBytes(fb_Bpp); // Background + display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); + } + while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) { + color = ws.rQshiftBytes(fb_Bpp); + x = ws.rQshift16(); + y = ws.rQshift16(); + width = ws.rQshift16(); + height = ws.rQshift16(); + display.fillRect(FBU.x + x, FBU.y + y, width, height, color); + FBU.subrects -= 1; + } + //Util.Debug(" display_rre: rects: " + FBU.rects + + // ", FBU.subrects: " + FBU.subrects); + + if (FBU.subrects > 0) { + chunk = Math.min(rre_chunk_sz, FBU.subrects); + FBU.bytes = (fb_Bpp + 8) * chunk; + } else { + FBU.rects -= 1; + FBU.bytes = 0; + } + //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes); + return true; +}; + +encHandlers.HEXTILE = function display_hextile() { + //Util.Debug(">> display_hextile"); + var subencoding, subrects, color, cur_tile, + tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh, + rQ = ws.get_rQ(), rQi = ws.get_rQi(); + + if (FBU.tiles === 0) { + FBU.tiles_x = Math.ceil(FBU.width/16); + FBU.tiles_y = Math.ceil(FBU.height/16); + FBU.total_tiles = FBU.tiles_x * FBU.tiles_y; + FBU.tiles = FBU.total_tiles; + } + + /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */ + while (FBU.tiles > 0) { + FBU.bytes = 1; + if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; } + subencoding = rQ[rQi]; // Peek + if (subencoding > 30) { // Raw + fail("Disconnected: illegal hextile subencoding " + subencoding); + //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); + return false; + } + subrects = 0; + cur_tile = FBU.total_tiles - FBU.tiles; + tile_x = cur_tile % FBU.tiles_x; + tile_y = Math.floor(cur_tile / FBU.tiles_x); + x = FBU.x + tile_x * 16; + y = FBU.y + tile_y * 16; + w = Math.min(16, (FBU.x + FBU.width) - x); + h = Math.min(16, (FBU.y + FBU.height) - y); + + /* Figure out how much we are expecting */ + if (subencoding & 0x01) { // Raw + //Util.Debug(" Raw subencoding"); + FBU.bytes += w * h * fb_Bpp; + } else { + if (subencoding & 0x02) { // Background + FBU.bytes += fb_Bpp; + } + if (subencoding & 0x04) { // Foreground + FBU.bytes += fb_Bpp; + } + if (subencoding & 0x08) { // AnySubrects + FBU.bytes += 1; // Since we aren't shifting it off + if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; } + subrects = rQ[rQi + FBU.bytes-1]; // Peek + if (subencoding & 0x10) { // SubrectsColoured + FBU.bytes += subrects * (fb_Bpp + 2); + } else { + FBU.bytes += subrects * 2; + } + } + } + + /* + Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + + " (" + tile_x + "," + tile_y + ")" + + " [" + x + "," + y + "]@" + w + "x" + h + + ", subenc:" + subencoding + + "(last: " + FBU.lastsubencoding + "), subrects:" + + subrects + + ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes + + " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) + + " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10)); + */ + if (ws.rQwait("hextile", FBU.bytes)) { return false; } + + /* We know the encoding and have a whole tile */ + FBU.subencoding = rQ[rQi]; + rQi += 1; + if (FBU.subencoding === 0) { + if (FBU.lastsubencoding & 0x01) { + /* Weird: ignore blanks after RAW */ + Util.Debug(" Ignoring blank after RAW"); + } else { + display.fillRect(x, y, w, h, FBU.background); + } + } else if (FBU.subencoding & 0x01) { // Raw + display.blitImage(x, y, w, h, rQ, rQi); + rQi += FBU.bytes - 1; + } else { + if (FBU.subencoding & 0x02) { // Background + FBU.background = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } + if (FBU.subencoding & 0x04) { // Foreground + FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } + + display.startTile(x, y, w, h, FBU.background); + if (FBU.subencoding & 0x08) { // AnySubrects + subrects = rQ[rQi]; + rQi += 1; + for (s = 0; s < subrects; s += 1) { + if (FBU.subencoding & 0x10) { // SubrectsColoured + color = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } else { + color = FBU.foreground; + } + xy = rQ[rQi]; + rQi += 1; + sx = (xy >> 4); + sy = (xy & 0x0f); + + wh = rQ[rQi]; + rQi += 1; + sw = (wh >> 4) + 1; + sh = (wh & 0x0f) + 1; + + display.subTile(sx, sy, sw, sh, color); + } + } + display.finishTile(); + } + ws.set_rQi(rQi); + FBU.lastsubencoding = FBU.subencoding; + FBU.bytes = 0; + FBU.tiles -= 1; + } + + if (FBU.tiles === 0) { + FBU.rects -= 1; + } + + //Util.Debug("<< display_hextile"); + return true; +}; + + +encHandlers.TIGHT_PNG = function display_tight_png() { + //Util.Debug(">> display_tight_png"); + var ctl, cmode, clength, getCLength, color, img; + //Util.Debug(" FBU.rects: " + FBU.rects); + //Util.Debug(" starting ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + + FBU.bytes = 1; // compression-control byte + if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; } + + // Get 'compact length' header and data size + getCLength = function (arr) { + var header = 1, data = 0; + data += arr[0] & 0x7f; + if (arr[0] & 0x80) { + header += 1; + data += (arr[1] & 0x7f) << 7; + if (arr[1] & 0x80) { + header += 1; + data += arr[2] << 14; + } + } + return [header, data]; + }; + + ctl = ws.rQpeek8(); + switch (ctl >> 4) { + case 0x08: cmode = "fill"; break; + case 0x09: cmode = "jpeg"; break; + case 0x0A: cmode = "png"; break; + default: throw("Illegal basic compression received, ctl: " + ctl); + } + switch (cmode) { + // fill uses fb_depth because TPIXELs drop the padding byte + case "fill": FBU.bytes += fb_depth; break; // TPIXEL + case "jpeg": FBU.bytes += 3; break; // max clength + case "png": FBU.bytes += 3; break; // max clength + } + + if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } + + //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + //Util.Debug(" cmode: " + cmode); + + // Determine FBU.bytes + switch (cmode) { + case "fill": + ws.rQshift8(); // shift off ctl + color = ws.rQshiftBytes(fb_depth); + FBU.imgQ.push({ + 'type': 'fill', + 'img': {'complete': true}, + 'x': FBU.x, + 'y': FBU.y, + 'width': FBU.width, + 'height': FBU.height, + 'color': color}); + break; + case "jpeg": + case "png": + clength = getCLength(ws.rQslice(1, 4)); + FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data + if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } + + // We have everything, render it + //Util.Debug(" png, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]); + ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length + img = new Image(); + //img.onload = scan_tight_imgQ; + FBU.imgQ.push({ + 'type': 'img', + 'img': img, + 'x': FBU.x, + 'y': FBU.y}); + img.src = "data:image/" + cmode + + extract_data_uri(ws.rQshiftBytes(clength[1])); + img = null; + break; + } + FBU.bytes = 0; + FBU.rects -= 1; + //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + //Util.Debug("<< display_tight_png"); + return true; +}; + +extract_data_uri = function(arr) { + //var i, stra = []; + //for (i=0; i< arr.length; i += 1) { + // stra.push(String.fromCharCode(arr[i])); + //} + //return "," + escape(stra.join('')); + return ";base64," + Base64.encode(arr); +}; + +scan_tight_imgQ = function() { + var data, imgQ, ctx; + ctx = display.get_context(); + if (rfb_state === 'normal') { + imgQ = FBU.imgQ; + while ((imgQ.length > 0) && (imgQ[0].img.complete)) { + data = imgQ.shift(); + if (data['type'] === 'fill') { + display.fillRect(data.x, data.y, data.width, data.height, data.color); + } else { + ctx.drawImage(data.img, data.x, data.y); + } + } + setTimeout(scan_tight_imgQ, scan_imgQ_rate); + } +}; + +encHandlers.DesktopSize = function set_desktopsize() { + Util.Debug(">> set_desktopsize"); + fb_width = FBU.width; + fb_height = FBU.height; + display.resize(fb_width, fb_height); + timing.fbu_rt_start = (new Date()).getTime(); + // Send a new non-incremental request + ws.send(fbUpdateRequests()); + + FBU.bytes = 0; + FBU.rects -= 1; + + Util.Debug("<< set_desktopsize"); + return true; +}; + +encHandlers.Cursor = function set_cursor() { + var x, y, w, h, pixelslength, masklength; + //Util.Debug(">> set_cursor"); + x = FBU.x; // hotspot-x + y = FBU.y; // hotspot-y + w = FBU.width; + h = FBU.height; + + pixelslength = w * h * fb_Bpp; + masklength = Math.floor((w + 7) / 8) * h; + + FBU.bytes = pixelslength + masklength; + if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; } + + //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h); + + display.changeCursor(ws.rQshiftBytes(pixelslength), + ws.rQshiftBytes(masklength), + x, y, w, h); + + FBU.bytes = 0; + FBU.rects -= 1; + + //Util.Debug("<< set_cursor"); + return true; +}; + +encHandlers.JPEG_quality_lo = function set_jpeg_quality() { + Util.Error("Server sent jpeg_quality pseudo-encoding"); +}; + +encHandlers.compress_lo = function set_compress_level() { + Util.Error("Server sent compress level pseudo-encoding"); +}; + +/* + * Client message routines + */ + +pixelFormat = function() { + //Util.Debug(">> pixelFormat"); + var arr; + arr = [0]; // msg-type + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + + arr.push8(fb_Bpp * 8); // bits-per-pixel + arr.push8(fb_depth * 8); // depth + arr.push8(0); // little-endian + arr.push8(conf.true_color ? 1 : 0); // true-color + + arr.push16(255); // red-max + arr.push16(255); // green-max + arr.push16(255); // blue-max + arr.push8(0); // red-shift + arr.push8(8); // green-shift + arr.push8(16); // blue-shift + + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + //Util.Debug("<< pixelFormat"); + return arr; +}; + +clientEncodings = function() { + //Util.Debug(">> clientEncodings"); + var arr, i, encList = []; + + for (i=0; i> fbUpdateRequest"); + if (typeof(x) === "undefined") { x = 0; } + if (typeof(y) === "undefined") { y = 0; } + if (typeof(xw) === "undefined") { xw = fb_width; } + if (typeof(yw) === "undefined") { yw = fb_height; } + var arr; + arr = [3]; // msg-type + arr.push8(incremental); + arr.push16(x); + arr.push16(y); + arr.push16(xw); + arr.push16(yw); + //Util.Debug("<< fbUpdateRequest"); + return arr; +}; + +// Based on clean/dirty areas, generate requests to send +fbUpdateRequests = function() { + var cleanDirty = display.getCleanDirtyReset(), + arr = [], i, cb, db; + + cb = cleanDirty.cleanBox; + if (cb.w > 0 && cb.h > 0) { + // Request incremental for clean box + arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h)); + } + for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) { + db = cleanDirty.dirtyBoxes[i]; + // Force all (non-incremental for dirty box + arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h)); + } + return arr; +}; + + + +keyEvent = function(keysym, down) { + //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down); + var arr; + arr = [4]; // msg-type + arr.push8(down); + arr.push16(0); + arr.push32(keysym); + //Util.Debug("<< keyEvent"); + return arr; +}; + +pointerEvent = function(x, y) { + //Util.Debug(">> pointerEvent, x,y: " + x + "," + y + + // " , mask: " + mouse_buttonMask); + var arr; + arr = [5]; // msg-type + arr.push8(mouse_buttonMask); + arr.push16(x); + arr.push16(y); + //Util.Debug("<< pointerEvent"); + return arr; +}; + +clientCutText = function(text) { + //Util.Debug(">> clientCutText"); + var arr, i, n; + arr = [6]; // msg-type + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + arr.push32(text.length); + n = text.length; + for (i=0; i < n; i+=1) { + arr.push(text.charCodeAt(i)); + } + //Util.Debug("<< clientCutText:" + arr); + return arr; +}; + + + +// +// Public API interface functions +// + +that.connect = function(host, port, password, path) { + //Util.Debug(">> connect"); + + rfb_host = host; + rfb_port = port; + rfb_password = (password !== undefined) ? password : ""; + rfb_path = (path !== undefined) ? path : ""; + + if ((!rfb_host) || (!rfb_port)) { + return fail("Must set host and port"); + } + + updateState('connect'); + //Util.Debug("<< connect"); + +}; + +that.disconnect = function() { + //Util.Debug(">> disconnect"); + updateState('disconnect', 'Disconnecting'); + //Util.Debug("<< disconnect"); +}; + +that.sendPassword = function(passwd) { + rfb_password = passwd; + rfb_state = "Authentication"; + setTimeout(init_msg, 1); +}; + +that.sendCtrlAltDel = function() { + if (rfb_state !== "normal") { return false; } + Util.Info("Sending Ctrl-Alt-Del"); + var arr = []; + arr = arr.concat(keyEvent(0xFFE3, 1)); // Control + arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt + arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete + arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete + arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt + arr = arr.concat(keyEvent(0xFFE3, 0)); // Control + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +// Send a key press. If 'down' is not specified then send a down key +// followed by an up key. +that.sendKey = function(code, down) { + if (rfb_state !== "normal") { return false; } + var arr = []; + if (typeof down !== 'undefined') { + Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code); + arr = arr.concat(keyEvent(code, down ? 1 : 0)); + } else { + Util.Info("Sending key code (down + up): " + code); + arr = arr.concat(keyEvent(code, 1)); + arr = arr.concat(keyEvent(code, 0)); + } + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +that.clipboardPasteFrom = function(text) { + if (rfb_state !== "normal") { return; } + //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "..."); + ws.send(clientCutText(text)); + //Util.Debug("<< clipboardPasteFrom"); +}; + +// Override internal functions for testing +that.testMode = function(override_send) { + test_mode = true; + that.recv_message = ws.testMode(override_send); + + checkEvents = function () { /* Stub Out */ }; + that.connect = function(host, port, password) { + rfb_host = host; + rfb_port = port; + rfb_password = password; + updateState('ProtocolVersion', "Starting VNC handshake"); + }; +}; + + +return constructor(); // Return the public API interface + +} // End of RFB() diff --git a/classes/novnc/include/ui.js b/classes/novnc/include/ui.js new file mode 100644 index 0000000..74a0005 --- /dev/null +++ b/classes/novnc/include/ui.js @@ -0,0 +1,629 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +"use strict"; +/*jslint white: false, browser: true */ +/*global window, $D, Util, WebUtil, RFB, Display */ + +var UI = { + +rfb_state : 'loaded', +settingsOpen : false, +connSettingsOpen : true, +clipboardOpen: false, +keyboardVisible: false, + +// Render default UI and initialize settings menu +load: function() { + var html = '', i, sheet, sheets, llevels; + + // Stylesheet selection dropdown + sheet = WebUtil.selectStylesheet(); + sheets = WebUtil.getStylesheets(); + for (i = 0; i < sheets.length; i += 1) { + UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title); + } + + // Logging selection dropdown + llevels = ['error', 'warn', 'info', 'debug']; + for (i = 0; i < llevels.length; i += 1) { + UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]); + } + + // Settings with immediate effects + UI.initSetting('logging', 'warn'); + WebUtil.init_logging(UI.getSetting('logging')); + + UI.initSetting('stylesheet', 'default'); + WebUtil.selectStylesheet(null); + // call twice to get around webkit bug + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + + /* Populate the controls if defaults are provided in the URL */ + UI.initSetting('host', ''); + UI.initSetting('port', ''); + UI.initSetting('password', ''); + UI.initSetting('encrypt', false); + UI.initSetting('true_color', true); + UI.initSetting('cursor', false); + UI.initSetting('shared', true); + UI.initSetting('connectTimeout', 2); + UI.initSetting('path', ''); + + UI.rfb = RFB({'target': $D('noVNC_canvas'), + 'onUpdateState': UI.updateState, + 'onClipboard': UI.clipReceive}); + UI.updateVisualState(); + + // Unfocus clipboard when over the VNC area + //$D('VNC_screen').onmousemove = function () { + // var keyboard = UI.rfb.get_keyboard(); + // if ((! keyboard) || (! keyboard.get_focused())) { + // $D('VNC_clipboard_text').blur(); + // } + // }; + + // Show mouse selector buttons on touch screen devices + if ('ontouchstart' in document.documentElement) { + // Show mobile buttons + $D('noVNC_mobile_buttons').style.display = "inline"; + UI.setMouseButton(); + // Remove the address bar + setTimeout(function() { window.scrollTo(0, 1); }, 100); + UI.forceSetting('clip', true); + $D('noVNC_clip').disabled = true; + } else { + UI.initSetting('clip', false); + } + + //iOS Safari does not support CSS position:fixed. + //This detects iOS devices and enables javascript workaround. + if ((navigator.userAgent.match(/iPhone/i)) || + (navigator.userAgent.match(/iPod/i)) || + (navigator.userAgent.match(/iPad/i))) { + //UI.setOnscroll(); + //UI.setResize(); + } + + $D('noVNC_host').focus(); + + UI.setViewClip(); + Util.addEvent(window, 'resize', UI.setViewClip); + + Util.addEvent(window, 'beforeunload', function () { + if (UI.rfb_state === 'normal') { + return "You are currently connected."; + } + } ); + +}, + +// Read form control compatible setting from cookie +getSetting: function(name) { + var val, ctrl = $D('noVNC_' + name); + val = WebUtil.readCookie(name); + if (ctrl.type === 'checkbox') { + if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) { + val = false; + } else { + val = true; + } + } + return val; +}, + +// Update cookie and form control setting. If value is not set, then +// updates from control to current cookie setting. +updateSetting: function(name, value) { + + var i, ctrl = $D('noVNC_' + name); + // Save the cookie for this session + if (typeof value !== 'undefined') { + WebUtil.createCookie(name, value); + } + + // Update the settings control + value = UI.getSetting(name); + + if (ctrl.type === 'checkbox') { + ctrl.checked = value; + + } else if (typeof ctrl.options !== 'undefined') { + for (i = 0; i < ctrl.options.length; i += 1) { + if (ctrl.options[i].value === value) { + ctrl.selectedIndex = i; + break; + } + } + } else { + /*Weird IE9 error leads to 'null' appearring + in textboxes instead of ''.*/ + if (value === null) { + value = ""; + } + ctrl.value = value; + } +}, + +// Save control setting to cookie +saveSetting: function(name) { + var val, ctrl = $D('noVNC_' + name); + if (ctrl.type === 'checkbox') { + val = ctrl.checked; + } else if (typeof ctrl.options !== 'undefined') { + val = ctrl.options[ctrl.selectedIndex].value; + } else { + val = ctrl.value; + } + WebUtil.createCookie(name, val); + //Util.Debug("Setting saved '" + name + "=" + val + "'"); + return val; +}, + +// Initial page load read/initialization of settings +initSetting: function(name, defVal) { + var val; + + // Check Query string followed by cookie + val = WebUtil.getQueryVar(name); + if (val === null) { + val = WebUtil.readCookie(name, defVal); + } + UI.updateSetting(name, val); + //Util.Debug("Setting '" + name + "' initialized to '" + val + "'"); + return val; +}, + +// Force a setting to be a certain value +forceSetting: function(name, val) { + UI.updateSetting(name, val); + return val; +}, + + +// Show the clipboard panel +toggleClipboardPanel: function() { + //Close settings if open + if (UI.settingsOpen == true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } + //Close connection settings if open + if (UI.connSettingsOpen == true) { + UI.toggleConnectPanel(); + } + //Toggle Clipboard Panel + if (UI.clipboardOpen == true) { + $D('noVNC_clipboard').style.display = "none"; + $D('clipboardButton').className = "noVNC_status_button"; + UI.clipboardOpen = false; + } else { + $D('noVNC_clipboard').style.display = "block"; + $D('clipboardButton').className = "noVNC_status_button_selected"; + UI.clipboardOpen = true; + } +}, + +// Show the connection settings panel/menu +toggleConnectPanel: function() { + //Close connection settings if open + if (UI.settingsOpen == true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + $D('connectButton').className = "noVNC_status_button"; + } + if (UI.clipboardOpen == true) { + UI.toggleClipboardPanel(); + } + + //Toggle Connection Panel + if (UI.connSettingsOpen == true) { + $D('noVNC_controls').style.display = "none"; + $D('connectButton').className = "noVNC_status_button"; + UI.connSettingsOpen = false; + } else { + $D('noVNC_controls').style.display = "block"; + $D('connectButton').className = "noVNC_status_button_selected"; + UI.connSettingsOpen = true; + $D('noVNC_host').focus(); + } +}, + +// Toggle the settings menu: +// On open, settings are refreshed from saved cookies. +// On close, settings are applied +toggleSettingsPanel: function() { + if (UI.settingsOpen) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } else { + UI.updateSetting('encrypt'); + UI.updateSetting('true_color'); + if (UI.rfb.get_display().get_cursor_uri()) { + UI.updateSetting('cursor'); + } else { + UI.updateSetting('cursor', false); + $D('noVNC_cursor').disabled = true; + } + UI.updateSetting('clip'); + UI.updateSetting('shared'); + UI.updateSetting('connectTimeout'); + UI.updateSetting('path'); + UI.updateSetting('stylesheet'); + UI.updateSetting('logging'); + + UI.openSettingsMenu(); + } +}, + +// Open menu +openSettingsMenu: function() { + if (UI.clipboardOpen == true) { + UI.toggleClipboardPanel(); + } + //Close connection settings if open + if (UI.connSettingsOpen == true) { + UI.toggleConnectPanel(); + } + $D('noVNC_settings').style.display = "block"; + $D('settingsButton').className = "noVNC_status_button_selected"; + UI.settingsOpen = true; +}, + +// Close menu (without applying settings) +closeSettingsMenu: function() { + $D('noVNC_settings').style.display = "none"; + $D('settingsButton').className = "noVNC_status_button"; + UI.settingsOpen = false; +}, + +// Save/apply settings when 'Apply' button is pressed +settingsApply: function() { + //Util.Debug(">> settingsApply"); + UI.saveSetting('encrypt'); + UI.saveSetting('true_color'); + if (UI.rfb.get_display().get_cursor_uri()) { + UI.saveSetting('cursor'); + } + UI.saveSetting('clip'); + UI.saveSetting('shared'); + UI.saveSetting('connectTimeout'); + UI.saveSetting('path'); + UI.saveSetting('stylesheet'); + UI.saveSetting('logging'); + + // Settings with immediate (non-connected related) effect + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + WebUtil.init_logging(UI.getSetting('logging')); + UI.setViewClip(); + UI.setViewDrag(UI.rfb.get_viewportDrag()); + //Util.Debug("<< settingsApply"); +}, + + + +setPassword: function() { + UI.rfb.sendPassword($D('noVNC_password').value); + //Reset connect button. + $D('noVNC_connect_button').value = "Connect"; + $D('noVNC_connect_button').onclick = UI.Connect; + //Hide connection panel. + UI.toggleConnectPanel(); + return false; +}, + +sendCtrlAltDel: function() { + UI.rfb.sendCtrlAltDel(); +}, + +setMouseButton: function(num) { + var b, blist = [0, 1,2,4], button; + + if (typeof num === 'undefined') { + // Disable mouse buttons + num = -1; + } + if (UI.rfb) { + UI.rfb.get_mouse().set_touchButton(num); + } + + for (b = 0; b < blist.length; b++) { + button = $D('noVNC_mouse_button' + blist[b]); + if (blist[b] === num) { + button.style.display = ""; + } else { + button.style.display = "none"; + /* + button.style.backgroundColor = "black"; + button.style.color = "lightgray"; + button.style.backgroundColor = ""; + button.style.color = ""; + */ + } + } +}, + +updateState: function(rfb, state, oldstate, msg) { + var s, sb, c, d, cad, vd, klass; + UI.rfb_state = state; + s = $D('noVNC_status'); + sb = $D('noVNC_status_bar'); + switch (state) { + case 'failed': + case 'fatal': + klass = "noVNC_status_error"; + break; + case 'normal': + klass = "noVNC_status_normal"; + break; + case 'disconnected': + $D('noVNC_logo').style.display = "block"; + case 'loaded': + klass = "noVNC_status_normal"; + break; + case 'password': + UI.toggleConnectPanel(); + + $D('noVNC_connect_button').value = "Send Password"; + $D('noVNC_connect_button').onclick = UI.setPassword; + $D('noVNC_password').focus(); + + klass = "noVNC_status_warn"; + break; + default: + klass = "noVNC_status_warn"; + break; + } + + if (typeof(msg) !== 'undefined') { + s.setAttribute("class", klass); + sb.setAttribute("class", klass); + s.innerHTML = msg; + } + + UI.updateVisualState(); +}, + +// Disable/enable controls depending on connection state +updateVisualState: function() { + var connected = UI.rfb_state === 'normal' ? true : false; + + //Util.Debug(">> updateVisualState"); + $D('noVNC_encrypt').disabled = connected; + $D('noVNC_true_color').disabled = connected; + if (UI.rfb && UI.rfb.get_display() && + UI.rfb.get_display().get_cursor_uri()) { + $D('noVNC_cursor').disabled = connected; + } else { + UI.updateSetting('cursor', false); + $D('noVNC_cursor').disabled = true; + } + $D('noVNC_shared').disabled = connected; + $D('noVNC_connectTimeout').disabled = connected; + $D('noVNC_path').disabled = connected; + + if (connected) { + UI.setViewClip(); + UI.setMouseButton(1); + $D('showKeyboard').style.display = "inline"; + $D('sendCtrlAltDelButton').style.display = "inline"; + } else { + UI.setMouseButton(); + $D('showKeyboard').style.display = "none"; + $D('sendCtrlAltDelButton').style.display = "none"; + } + // State change disables viewport dragging. + // It is enabled (toggled) by direct click on the button + UI.setViewDrag(false); + + switch (UI.rfb_state) { + case 'fatal': + case 'failed': + case 'loaded': + case 'disconnected': + $D('connectButton').style.display = ""; + $D('disconnectButton').style.display = "none"; + break; + default: + $D('connectButton').style.display = "none"; + $D('disconnectButton').style.display = ""; + break; + } + + //Util.Debug("<< updateVisualState"); +}, + + +clipReceive: function(rfb, text) { + Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); + $D('noVNC_clipboard_text').value = text; + Util.Debug("<< UI.clipReceive"); +}, + + +connect: function() { + var host, port, password, path; + + UI.closeSettingsMenu(); + UI.toggleConnectPanel(); + + host = $D('noVNC_host').value; + port = $D('noVNC_port').value; + password = $D('noVNC_password').value; + path = $D('noVNC_path').value; + if ((!host) || (!port)) { + throw("Must set host and port"); + } + + UI.rfb.set_encrypt(UI.getSetting('encrypt')); + UI.rfb.set_true_color(UI.getSetting('true_color')); + UI.rfb.set_local_cursor(UI.getSetting('cursor')); + UI.rfb.set_shared(UI.getSetting('shared')); + UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout')); + + UI.rfb.connect(host, port, password, path); + //Close dialog. + setTimeout(UI.setBarPosition, 100); + $D('noVNC_logo').style.display = "none"; +}, + +disconnect: function() { + UI.closeSettingsMenu(); + UI.rfb.disconnect(); + + $D('noVNC_logo').style.display = "block"; + UI.connSettingsOpen = false; + UI.toggleConnectPanel(); +}, + +displayBlur: function() { + UI.rfb.get_keyboard().set_focused(false); + UI.rfb.get_mouse().set_focused(false); +}, + +displayFocus: function() { + UI.rfb.get_keyboard().set_focused(true); + UI.rfb.get_mouse().set_focused(true); +}, + +clipClear: function() { + $D('noVNC_clipboard_text').value = ""; + UI.rfb.clipboardPasteFrom(""); +}, + +clipSend: function() { + var text = $D('noVNC_clipboard_text').value; + Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "..."); + UI.rfb.clipboardPasteFrom(text); + Util.Debug("<< UI.clipSend"); +}, + + +// Enable/disable and configure viewport clipping +setViewClip: function(clip) { + var display, cur_clip, pos, new_w, new_h; + + if (UI.rfb) { + display = UI.rfb.get_display(); + } else { + return; + } + + cur_clip = display.get_viewport(); + + if (typeof(clip) !== 'boolean') { + // Use current setting + clip = UI.getSetting('clip'); + } + + if (clip && !cur_clip) { + // Turn clipping on + UI.updateSetting('clip', true); + } else if (!clip && cur_clip) { + // Turn clipping off + UI.updateSetting('clip', false); + display.set_viewport(false); + $D('noVNC_canvas').style.position = 'static'; + display.viewportChange(); + } + if (UI.getSetting('clip')) { + // If clipping, update clipping settings + $D('noVNC_canvas').style.position = 'absolute'; + pos = Util.getPosition($D('noVNC_canvas')); + new_w = window.innerWidth - pos.x; + new_h = window.innerHeight - pos.y; + display.set_viewport(true); + display.viewportChange(0, 0, new_w, new_h); + } +}, + +// Toggle/set/unset the viewport drag/move button +setViewDrag: function(drag) { + var vmb = $D('noVNC_view_drag_button'); + if (!UI.rfb) { return; } + + if (UI.rfb_state === 'normal' && + UI.rfb.get_display().get_viewport()) { + vmb.style.display = "inline"; + } else { + vmb.style.display = "none"; + } + + if (typeof(drag) === "undefined") { + // If not specified, then toggle + drag = !UI.rfb.get_viewportDrag(); + } + if (drag) { + vmb.className = "noVNC_status_button_selected"; + UI.rfb.set_viewportDrag(true); + } else { + vmb.className = "noVNC_status_button"; + UI.rfb.set_viewportDrag(false); + } +}, + +// On touch devices, show the OS keyboard +showKeyboard: function() { + if(UI.keyboardVisible == false) { + $D('keyboardinput').focus(); + UI.keyboardVisible = true; + $D('showKeyboard').className = "noVNC_status_button_selected"; + } else if(UI.keyboardVisible == true) { + $D('keyboardinput').blur(); + $D('showKeyboard').className = "noVNC_status_button"; + UI.keyboardVisible = false; + } +}, + +keyInputBlur: function() { + $D('showKeyboard').className = "noVNC_status_button"; + //Weird bug in iOS if you change keyboardVisible + //here it does not actually occur so next time + //you click keyboard icon it doesnt work. + setTimeout("UI.setKeyboard()",100) +}, + +setKeyboard: function() { + UI.keyboardVisible = false; +}, + +// iOS < Version 5 does not support position fixed. Javascript workaround: +setOnscroll: function() { + window.onscroll = function() { + UI.setBarPosition(); + }; +}, + +setResize: function () { + window.onResize = function() { + UI.setBarPosition(); + }; +}, + +//Helper to add options to dropdown. +addOption: function(selectbox,text,value ) +{ + var optn = document.createElement("OPTION"); + optn.text = text; + optn.value = value; + selectbox.options.add(optn); +}, + +setBarPosition: function() { + $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px'; + $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; + + var vncwidth = $D('noVNC_screen').style.offsetWidth; + $D('noVNC-control-bar').style.width = vncwidth + 'px'; +} + +}; + + + + diff --git a/classes/novnc/include/util.js b/classes/novnc/include/util.js new file mode 100644 index 0000000..0a9e0e0 --- /dev/null +++ b/classes/novnc/include/util.js @@ -0,0 +1,276 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +"use strict"; +/*jslint bitwise: false, white: false */ +/*global window, console, document, navigator, ActiveXObject */ + +// Globals defined here +var Util = {}; + + +/* + * Make arrays quack + */ + +Array.prototype.push8 = function (num) { + this.push(num & 0xFF); +}; + +Array.prototype.push16 = function (num) { + this.push((num >> 8) & 0xFF, + (num ) & 0xFF ); +}; +Array.prototype.push32 = function (num) { + this.push((num >> 24) & 0xFF, + (num >> 16) & 0xFF, + (num >> 8) & 0xFF, + (num ) & 0xFF ); +}; + +/* + * ------------------------------------------------------ + * Namespaced in Util + * ------------------------------------------------------ + */ + +/* + * Logging/debug routines + */ + +Util._log_level = 'warn'; +Util.init_logging = function (level) { + if (typeof level === 'undefined') { + level = Util._log_level; + } else { + Util._log_level = level; + } + if (typeof window.console === "undefined") { + if (typeof window.opera !== "undefined") { + window.console = { + 'log' : window.opera.postError, + 'warn' : window.opera.postError, + 'error': window.opera.postError }; + } else { + window.console = { + 'log' : function(m) {}, + 'warn' : function(m) {}, + 'error': function(m) {}}; + } + } + + Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; + switch (level) { + case 'debug': Util.Debug = function (msg) { console.log(msg); }; + case 'info': Util.Info = function (msg) { console.log(msg); }; + case 'warn': Util.Warn = function (msg) { console.warn(msg); }; + case 'error': Util.Error = function (msg) { console.error(msg); }; + case 'none': + break; + default: + throw("invalid logging type '" + level + "'"); + } +}; +Util.get_logging = function () { + return Util._log_level; +}; +// Initialize logging level +Util.init_logging(); + + +// Set configuration default for Crockford style function namespaces +Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { + var getter, setter; + + // Default getter function + getter = function (idx) { + if ((type in {'arr':1, 'array':1}) && + (typeof idx !== 'undefined')) { + return cfg[v][idx]; + } else { + return cfg[v]; + } + }; + + // Default setter function + setter = function (val, idx) { + if (type in {'boolean':1, 'bool':1}) { + if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { + val = false; + } else { + val = true; + } + } else if (type in {'integer':1, 'int':1}) { + val = parseInt(val, 10); + } else if (type === 'func') { + if (!val) { + val = function () {}; + } + } + if (typeof idx !== 'undefined') { + cfg[v][idx] = val; + } else { + cfg[v] = val; + } + }; + + // Set the description + api[v + '_description'] = desc; + + // Set the getter function + if (typeof api['get_' + v] === 'undefined') { + api['get_' + v] = getter; + } + + // Set the setter function with extra sanity checks + if (typeof api['set_' + v] === 'undefined') { + api['set_' + v] = function (val, idx) { + if (mode in {'RO':1, 'ro':1}) { + throw(v + " is read-only"); + } else if ((mode in {'WO':1, 'wo':1}) && + (typeof cfg[v] !== 'undefined')) { + throw(v + " can only be set once"); + } + setter(val, idx); + }; + } + + // Set the default value + if (typeof defaults[v] !== 'undefined') { + defval = defaults[v]; + } else if ((type in {'arr':1, 'array':1}) && + (! (defval instanceof Array))) { + defval = []; + } + // Coerce existing setting to the right type + //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]); + setter(defval); +}; + +// Set group of configuration defaults +Util.conf_defaults = function(cfg, api, defaults, arr) { + var i; + for (i = 0; i < arr.length; i++) { + Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], + arr[i][2], arr[i][3], arr[i][4]); + } +} + + +/* + * Cross-browser routines + */ + +// Get DOM element position on page +Util.getPosition = function (obj) { + var x = 0, y = 0; + if (obj.offsetParent) { + do { + x += obj.offsetLeft; + y += obj.offsetTop; + obj = obj.offsetParent; + } while (obj); + } + return {'x': x, 'y': y}; +}; + +// Get mouse event position in DOM element +Util.getEventPosition = function (e, obj, scale) { + var evt, docX, docY, pos; + //if (!e) evt = window.event; + evt = (e ? e : window.event); + evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); + if (evt.pageX || evt.pageY) { + docX = evt.pageX; + docY = evt.pageY; + } else if (evt.clientX || evt.clientY) { + docX = evt.clientX + document.body.scrollLeft + + document.documentElement.scrollLeft; + docY = evt.clientY + document.body.scrollTop + + document.documentElement.scrollTop; + } + pos = Util.getPosition(obj); + if (typeof scale === "undefined") { + scale = 1; + } + return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; +}; + + +// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events +Util.addEvent = function (obj, evType, fn){ + if (obj.attachEvent){ + var r = obj.attachEvent("on"+evType, fn); + return r; + } else if (obj.addEventListener){ + obj.addEventListener(evType, fn, false); + return true; + } else { + throw("Handler could not be attached"); + } +}; + +Util.removeEvent = function(obj, evType, fn){ + if (obj.detachEvent){ + var r = obj.detachEvent("on"+evType, fn); + return r; + } else if (obj.removeEventListener){ + obj.removeEventListener(evType, fn, false); + return true; + } else { + throw("Handler could not be removed"); + } +}; + +Util.stopEvent = function(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } +}; + + +// Set browser engine versions. Based on mootools. +Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; + +Util.Engine = { + 'presto': (function() { + return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), + 'trident': (function() { + return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), + 'webkit': (function() { + try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), + //'webkit': (function() { + // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), + 'gecko': (function() { + return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) +}; +if (Util.Engine.webkit) { + // Extract actual webkit version if available + Util.Engine.webkit = (function(v) { + var re = new RegExp('WebKit/([0-9\.]*) '); + v = (navigator.userAgent.match(re) || ['', v])[1]; + return parseFloat(v, 10); + })(Util.Engine.webkit); +} + +Util.Flash = (function(){ + var v, version; + try { + v = navigator.plugins['Shockwave Flash'].description; + } catch(err1) { + try { + v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + } catch(err2) { + v = '0 r0'; + } + } + version = v.match(/\d+/g); + return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; +}()); diff --git a/classes/novnc/include/vnc.js b/classes/novnc/include/vnc.js new file mode 100644 index 0000000..f938be7 --- /dev/null +++ b/classes/novnc/include/vnc.js @@ -0,0 +1,42 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint evil: true */ +/*global window, document, INCLUDE_URI */ + +/* + * Load supporting scripts + */ +function get_INCLUDE_URI() { + return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; +} + +(function () { + "use strict"; + + var extra = "", start, end; + + start = " + + + + + +
+ +
+ +
+ + + + + + +
+
+ + +
+ + + + + +
+ + +
+ +
+ +
+ + +
+ +
    +
  • Encrypt
  • +
  • True Color
  • +
  • Local Cursor
  • +
  • Clip to window
  • +
  • Shared Mode
  • +
  • Connect Timeout (s)
  • +
  • Path
  • +
    + +
  • +
  • + + +
  • +
  • +
    +
  • +
+
+
+ + +
+
    +
  • +
  • +
  • +
  • +
+
+ +
+ + +
+
+ +
+
Loading
+
+ +

no
VNC

+ + +
+ + Canvas not supported. + +
+ +
+ + + + diff --git a/classes/novnc/vnc_auto.html b/classes/novnc/vnc_auto.html new file mode 100644 index 0000000..a500b79 --- /dev/null +++ b/classes/novnc/vnc_auto.html @@ -0,0 +1,116 @@ + + + + + noVNC + + + + + + + +
+
+ + + +
Loading
+ +
+
+ + Canvas not supported. + +
+ + + + + + diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index ad2a51b..83fc520 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -346,12 +346,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) return; } - if (strchr(fname+1, '/') != NULL) { - rfbErr("httpd: asking for file in other directory\n"); - rfbWriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); - httpCloseSock(rfbScreen); - return; - } getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); rfbLog("httpd: get '%s' for %s\n", fname+1, @@ -447,6 +441,10 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) sprintf(str, "%d", rfbScreen->port); rfbWriteExact(&cl, str, strlen(str)); + } else if (compareAndSkip(&ptr, "$HOST")) { + + rfbWriteExact(&cl, rfbScreen->thisHost, strlen(rfbScreen->thisHost)); + } else if (compareAndSkip(&ptr, "$DESKTOP")) { rfbWriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); -- cgit v1.2.3 From 03d9b519176d6f25ce13e4b86298ae762f32cd4b Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Thu, 6 Oct 2011 12:22:49 +0200 Subject: Fix typo && use proper website. --- classes/index.vnc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/index.vnc b/classes/index.vnc index 6997693..d33865b 100644 --- a/classes/index.vnc +++ b/classes/index.vnc @@ -15,7 +15,7 @@ $USER's $DESKTOP desktop ($DISPLAY)

-If the above Java applet does not work, you can also try the new JavaScript-only
viewer. You will need a HTML5-capable browser though. +If the above Java applet does not work, you can also try the new JavaScript-only noVNC viewer. You will need a HTML5-capable browser though. Click here to connect using noVNC.

-- cgit v1.2.3 From 5756b133f737642e2bb3c9a319b101c0ac23723e Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Thu, 6 Oct 2011 14:29:06 +0200 Subject: httpd: fix sending of binary data such as images. We do this simply by omitting the content-type and let the browser decide upon the mime-type of the sent file. Only exception is 'index.vnc', where we do set the content-type since some browsers fail to detect it's html when it's ending in '.vnc' Also, remove superfluous #defines. We close the connection always. --- libvncserver/httpd.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index 83fc520..3252fc6 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -59,20 +59,6 @@ #include #endif -#define connection_close -#ifndef connection_close - -#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ - "File Not Found\n" \ - "

File Not Found

\n" - -#define INVALID_REQUEST_STR "HTTP/1.0 400 Invalid Request\r\n\r\n" \ - "Invalid Request\n" \ - "

Invalid request

\n" - -#define OK_STR "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n" - -#else #define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\nConnection: close\r\n\r\n" \ "File Not Found\n" \ @@ -82,9 +68,10 @@ "Invalid Request\n" \ "

Invalid request

\n" -#define OK_STR "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n" +#define OK_STR "HTTP/1.0 200 OK\r\nConnection: close\r\n\r\n" +#define OK_STR_HTML "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n" + -#endif static void httpProcessInput(rfbScreenInfoPtr screen); static rfbBool compareAndSkip(char **ptr, const char *str); @@ -386,7 +373,10 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) return; } - rfbWriteExact(&cl, OK_STR, strlen(OK_STR)); + if(performSubstitutions) /* is the 'index.vnc' file */ + rfbWriteExact(&cl, OK_STR_HTML, strlen(OK_STR_HTML)); + else + rfbWriteExact(&cl, OK_STR, strlen(OK_STR)); while (1) { int n = fread(buf, 1, BUF_SIZE-1, fd); -- cgit v1.2.3 From 4d3464236b5a0c9bc28e65ab11dfad8dbea3f4a0 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Wed, 9 Nov 2011 16:39:35 +0100 Subject: Fix tiny typo. --- classes/index.vnc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/index.vnc b/classes/index.vnc index d33865b..1a05d01 100644 --- a/classes/index.vnc +++ b/classes/index.vnc @@ -20,5 +20,5 @@ If the above Java applet does not work, you can also try the new JavaScript-only


-LibVNCServer/libVNCClient Homepage +LibVNCServer/LibVNCClient Homepage -- cgit v1.2.3 From 7cb0e4a9a95886300268e1bff1e72b4c18f1b762 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Wed, 9 Nov 2011 19:20:10 +0100 Subject: novnc client: use the client's notion about the server hostname instead of what the server thinks. --- classes/index.vnc | 15 ++++++++++++++- libvncserver/httpd.c | 4 ---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/classes/index.vnc b/classes/index.vnc index 1a05d01..34ad25d 100644 --- a/classes/index.vnc +++ b/classes/index.vnc @@ -5,9 +5,11 @@ signs ($$) to get a dollar sign in the generated html. --> + $USER's $DESKTOP desktop ($DISPLAY) + @@ -15,8 +17,19 @@ $USER's $DESKTOP desktop ($DISPLAY)

+ If the above Java applet does not work, you can also try the new JavaScript-only noVNC viewer. You will need a HTML5-capable browser though. -Click here to connect using noVNC. + +
+ +
+


diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index 3252fc6..3025aae 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -431,10 +431,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) sprintf(str, "%d", rfbScreen->port); rfbWriteExact(&cl, str, strlen(str)); - } else if (compareAndSkip(&ptr, "$HOST")) { - - rfbWriteExact(&cl, rfbScreen->thisHost, strlen(rfbScreen->thisHost)); - } else if (compareAndSkip(&ptr, "$DESKTOP")) { rfbWriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName)); -- cgit v1.2.3 From faadd484489efddd182d6d0df43d8ecfdbff29ff Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Wed, 9 Nov 2011 20:00:16 +0100 Subject: Rename 'classes' dir to 'webclients'. --- LibVNCServer.spec.in | 2 +- Makefile.am | 4 +- README | 2 +- classes/Makefile.am | 5 - classes/VncViewer.jar | Bin 35462 -> 0 bytes classes/index.vnc | 37 - classes/javaviewer.pseudo_proxy.patch | 141 - classes/novnc/LICENSE.txt | 33 - classes/novnc/README.md | 93 - classes/novnc/favicon.ico | 1 - classes/novnc/images/clipboard.png | Bin 501 -> 0 bytes classes/novnc/images/connect.png | Bin 404 -> 0 bytes classes/novnc/images/ctrlaltdel.png | Bin 317 -> 0 bytes classes/novnc/images/disconnect.png | Bin 1378 -> 0 bytes classes/novnc/images/drag.png | Bin 963 -> 0 bytes classes/novnc/images/favicon.ico | Bin 1150 -> 0 bytes classes/novnc/images/favicon.png | Bin 453 -> 0 bytes classes/novnc/images/keyboard.png | Bin 1283 -> 0 bytes classes/novnc/images/mouse_left.png | Bin 511 -> 0 bytes classes/novnc/images/mouse_middle.png | Bin 517 -> 0 bytes classes/novnc/images/mouse_none.png | Bin 497 -> 0 bytes classes/novnc/images/mouse_right.png | Bin 513 -> 0 bytes classes/novnc/images/screen_320x460.png | Bin 12778 -> 0 bytes classes/novnc/images/screen_57x57.png | Bin 1807 -> 0 bytes classes/novnc/images/screen_700x700.png | Bin 17930 -> 0 bytes classes/novnc/images/settings.png | Bin 2495 -> 0 bytes classes/novnc/include/Orbitron700.ttf | Bin 38580 -> 0 bytes classes/novnc/include/Orbitron700.woff | Bin 17472 -> 0 bytes classes/novnc/include/base.css | 380 -- classes/novnc/include/base64.js | 147 - classes/novnc/include/black.css | 45 - classes/novnc/include/blue.css | 27 - classes/novnc/include/des.js | 273 - classes/novnc/include/display.js | 671 --- classes/novnc/include/input.js | 1884 ------- classes/novnc/include/logo.js | 1 - classes/novnc/include/playback.js | 90 - classes/novnc/include/rfb.js | 1613 ------ classes/novnc/include/ui.js | 629 --- classes/novnc/include/util.js | 276 - classes/novnc/include/vnc.js | 42 - classes/novnc/include/web-socket-js/README.txt | 109 - .../novnc/include/web-socket-js/WebSocketMain.swf | Bin 175746 -> 0 bytes classes/novnc/include/web-socket-js/swfobject.js | 4 - classes/novnc/include/web-socket-js/web_socket.js | 341 -- classes/novnc/include/websock.js | 347 -- classes/novnc/include/webutil.js | 148 - classes/novnc/vnc.html | 180 - classes/novnc/vnc_auto.html | 116 - classes/ssl/Makefile.am | 2 - classes/ssl/README | 338 -- classes/ssl/SignedUltraViewerSSL.jar | Bin 113117 -> 0 bytes classes/ssl/SignedVncViewer.jar | Bin 89208 -> 0 bytes classes/ssl/UltraViewerSSL.jar | Bin 110040 -> 0 bytes classes/ssl/VncViewer.jar | Bin 86228 -> 0 bytes classes/ssl/index.vnc | 26 - classes/ssl/onetimekey | 65 - classes/ssl/proxy.vnc | 73 - classes/ssl/ss_vncviewer | 3676 ------------- ...-vncviewer-cursor-colors+no-tab-traversal.patch | 111 - .../tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch | 2600 --------- classes/ssl/ultra.vnc | 28 - classes/ssl/ultraproxy.vnc | 28 - classes/ssl/ultrasigned.vnc | 28 - classes/ssl/ultravnc-102-JavaViewer-ssl-etc.patch | 5494 -------------------- configure.ac | 4 +- examples/example.c | 2 +- examples/pnmshow.c | 2 +- examples/pnmshow24.c | 2 +- rfb/rfb.h | 2 +- webclients/Makefile.am | 5 + webclients/VncViewer.jar | Bin 0 -> 35462 bytes webclients/index.vnc | 37 + webclients/javaviewer.pseudo_proxy.patch | 141 + webclients/novnc/LICENSE.txt | 33 + webclients/novnc/README.md | 93 + webclients/novnc/favicon.ico | 1 + webclients/novnc/images/clipboard.png | Bin 0 -> 501 bytes webclients/novnc/images/connect.png | Bin 0 -> 404 bytes webclients/novnc/images/ctrlaltdel.png | Bin 0 -> 317 bytes webclients/novnc/images/disconnect.png | Bin 0 -> 1378 bytes webclients/novnc/images/drag.png | Bin 0 -> 963 bytes webclients/novnc/images/favicon.ico | Bin 0 -> 1150 bytes webclients/novnc/images/favicon.png | Bin 0 -> 453 bytes webclients/novnc/images/keyboard.png | Bin 0 -> 1283 bytes webclients/novnc/images/mouse_left.png | Bin 0 -> 511 bytes webclients/novnc/images/mouse_middle.png | Bin 0 -> 517 bytes webclients/novnc/images/mouse_none.png | Bin 0 -> 497 bytes webclients/novnc/images/mouse_right.png | Bin 0 -> 513 bytes webclients/novnc/images/screen_320x460.png | Bin 0 -> 12778 bytes webclients/novnc/images/screen_57x57.png | Bin 0 -> 1807 bytes webclients/novnc/images/screen_700x700.png | Bin 0 -> 17930 bytes webclients/novnc/images/settings.png | Bin 0 -> 2495 bytes webclients/novnc/include/Orbitron700.ttf | Bin 0 -> 38580 bytes webclients/novnc/include/Orbitron700.woff | Bin 0 -> 17472 bytes webclients/novnc/include/base.css | 380 ++ webclients/novnc/include/base64.js | 147 + webclients/novnc/include/black.css | 45 + webclients/novnc/include/blue.css | 27 + webclients/novnc/include/des.js | 273 + webclients/novnc/include/display.js | 671 +++ webclients/novnc/include/input.js | 1884 +++++++ webclients/novnc/include/logo.js | 1 + webclients/novnc/include/playback.js | 90 + webclients/novnc/include/rfb.js | 1613 ++++++ webclients/novnc/include/ui.js | 629 +++ webclients/novnc/include/util.js | 276 + webclients/novnc/include/vnc.js | 42 + webclients/novnc/include/web-socket-js/README.txt | 109 + .../novnc/include/web-socket-js/WebSocketMain.swf | Bin 0 -> 175746 bytes .../novnc/include/web-socket-js/swfobject.js | 4 + .../novnc/include/web-socket-js/web_socket.js | 341 ++ webclients/novnc/include/websock.js | 347 ++ webclients/novnc/include/webutil.js | 148 + webclients/novnc/vnc.html | 180 + webclients/novnc/vnc_auto.html | 116 + webclients/ssl/Makefile.am | 2 + webclients/ssl/README | 338 ++ webclients/ssl/SignedUltraViewerSSL.jar | Bin 0 -> 113117 bytes webclients/ssl/SignedVncViewer.jar | Bin 0 -> 89208 bytes webclients/ssl/UltraViewerSSL.jar | Bin 0 -> 110040 bytes webclients/ssl/VncViewer.jar | Bin 0 -> 86228 bytes webclients/ssl/index.vnc | 26 + webclients/ssl/onetimekey | 65 + webclients/ssl/proxy.vnc | 73 + webclients/ssl/ss_vncviewer | 3676 +++++++++++++ ...-vncviewer-cursor-colors+no-tab-traversal.patch | 111 + .../tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch | 2600 +++++++++ webclients/ssl/ultra.vnc | 28 + webclients/ssl/ultraproxy.vnc | 28 + webclients/ssl/ultrasigned.vnc | 28 + .../ssl/ultravnc-102-JavaViewer-ssl-etc.patch | 5494 ++++++++++++++++++++ 132 files changed, 20112 insertions(+), 20112 deletions(-) delete mode 100644 classes/Makefile.am delete mode 100644 classes/VncViewer.jar delete mode 100644 classes/index.vnc delete mode 100644 classes/javaviewer.pseudo_proxy.patch delete mode 100644 classes/novnc/LICENSE.txt delete mode 100644 classes/novnc/README.md delete mode 120000 classes/novnc/favicon.ico delete mode 100644 classes/novnc/images/clipboard.png delete mode 100644 classes/novnc/images/connect.png delete mode 100644 classes/novnc/images/ctrlaltdel.png delete mode 100644 classes/novnc/images/disconnect.png delete mode 100644 classes/novnc/images/drag.png delete mode 100644 classes/novnc/images/favicon.ico delete mode 100644 classes/novnc/images/favicon.png delete mode 100644 classes/novnc/images/keyboard.png delete mode 100644 classes/novnc/images/mouse_left.png delete mode 100644 classes/novnc/images/mouse_middle.png delete mode 100644 classes/novnc/images/mouse_none.png delete mode 100644 classes/novnc/images/mouse_right.png delete mode 100644 classes/novnc/images/screen_320x460.png delete mode 100644 classes/novnc/images/screen_57x57.png delete mode 100644 classes/novnc/images/screen_700x700.png delete mode 100644 classes/novnc/images/settings.png delete mode 100644 classes/novnc/include/Orbitron700.ttf delete mode 100644 classes/novnc/include/Orbitron700.woff delete mode 100644 classes/novnc/include/base.css delete mode 100644 classes/novnc/include/base64.js delete mode 100644 classes/novnc/include/black.css delete mode 100644 classes/novnc/include/blue.css delete mode 100644 classes/novnc/include/des.js delete mode 100644 classes/novnc/include/display.js delete mode 100644 classes/novnc/include/input.js delete mode 100644 classes/novnc/include/logo.js delete mode 100644 classes/novnc/include/playback.js delete mode 100644 classes/novnc/include/rfb.js delete mode 100644 classes/novnc/include/ui.js delete mode 100644 classes/novnc/include/util.js delete mode 100644 classes/novnc/include/vnc.js delete mode 100644 classes/novnc/include/web-socket-js/README.txt delete mode 100644 classes/novnc/include/web-socket-js/WebSocketMain.swf delete mode 100644 classes/novnc/include/web-socket-js/swfobject.js delete mode 100644 classes/novnc/include/web-socket-js/web_socket.js delete mode 100644 classes/novnc/include/websock.js delete mode 100644 classes/novnc/include/webutil.js delete mode 100644 classes/novnc/vnc.html delete mode 100644 classes/novnc/vnc_auto.html delete mode 100644 classes/ssl/Makefile.am delete mode 100644 classes/ssl/README delete mode 100644 classes/ssl/SignedUltraViewerSSL.jar delete mode 100644 classes/ssl/SignedVncViewer.jar delete mode 100644 classes/ssl/UltraViewerSSL.jar delete mode 100644 classes/ssl/VncViewer.jar delete mode 100644 classes/ssl/index.vnc delete mode 100755 classes/ssl/onetimekey delete mode 100644 classes/ssl/proxy.vnc delete mode 100755 classes/ssl/ss_vncviewer delete mode 100644 classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch delete mode 100644 classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch delete mode 100644 classes/ssl/ultra.vnc delete mode 100644 classes/ssl/ultraproxy.vnc delete mode 100644 classes/ssl/ultrasigned.vnc delete mode 100644 classes/ssl/ultravnc-102-JavaViewer-ssl-etc.patch create mode 100644 webclients/Makefile.am create mode 100644 webclients/VncViewer.jar create mode 100644 webclients/index.vnc create mode 100644 webclients/javaviewer.pseudo_proxy.patch create mode 100644 webclients/novnc/LICENSE.txt create mode 100644 webclients/novnc/README.md create mode 120000 webclients/novnc/favicon.ico create mode 100644 webclients/novnc/images/clipboard.png create mode 100644 webclients/novnc/images/connect.png create mode 100644 webclients/novnc/images/ctrlaltdel.png create mode 100644 webclients/novnc/images/disconnect.png create mode 100644 webclients/novnc/images/drag.png create mode 100644 webclients/novnc/images/favicon.ico create mode 100644 webclients/novnc/images/favicon.png create mode 100644 webclients/novnc/images/keyboard.png create mode 100644 webclients/novnc/images/mouse_left.png create mode 100644 webclients/novnc/images/mouse_middle.png create mode 100644 webclients/novnc/images/mouse_none.png create mode 100644 webclients/novnc/images/mouse_right.png create mode 100644 webclients/novnc/images/screen_320x460.png create mode 100644 webclients/novnc/images/screen_57x57.png create mode 100644 webclients/novnc/images/screen_700x700.png create mode 100644 webclients/novnc/images/settings.png create mode 100644 webclients/novnc/include/Orbitron700.ttf create mode 100644 webclients/novnc/include/Orbitron700.woff create mode 100644 webclients/novnc/include/base.css create mode 100644 webclients/novnc/include/base64.js create mode 100644 webclients/novnc/include/black.css create mode 100644 webclients/novnc/include/blue.css create mode 100644 webclients/novnc/include/des.js create mode 100644 webclients/novnc/include/display.js create mode 100644 webclients/novnc/include/input.js create mode 100644 webclients/novnc/include/logo.js create mode 100644 webclients/novnc/include/playback.js create mode 100644 webclients/novnc/include/rfb.js create mode 100644 webclients/novnc/include/ui.js create mode 100644 webclients/novnc/include/util.js create mode 100644 webclients/novnc/include/vnc.js create mode 100644 webclients/novnc/include/web-socket-js/README.txt create mode 100644 webclients/novnc/include/web-socket-js/WebSocketMain.swf create mode 100644 webclients/novnc/include/web-socket-js/swfobject.js create mode 100644 webclients/novnc/include/web-socket-js/web_socket.js create mode 100644 webclients/novnc/include/websock.js create mode 100644 webclients/novnc/include/webutil.js create mode 100644 webclients/novnc/vnc.html create mode 100644 webclients/novnc/vnc_auto.html create mode 100644 webclients/ssl/Makefile.am create mode 100644 webclients/ssl/README create mode 100644 webclients/ssl/SignedUltraViewerSSL.jar create mode 100644 webclients/ssl/SignedVncViewer.jar create mode 100644 webclients/ssl/UltraViewerSSL.jar create mode 100644 webclients/ssl/VncViewer.jar create mode 100644 webclients/ssl/index.vnc create mode 100755 webclients/ssl/onetimekey create mode 100644 webclients/ssl/proxy.vnc create mode 100755 webclients/ssl/ss_vncviewer create mode 100644 webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch create mode 100644 webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch create mode 100644 webclients/ssl/ultra.vnc create mode 100644 webclients/ssl/ultraproxy.vnc create mode 100644 webclients/ssl/ultrasigned.vnc create mode 100644 webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch diff --git a/LibVNCServer.spec.in b/LibVNCServer.spec.in index 3e93334..13fe351 100755 --- a/LibVNCServer.spec.in +++ b/LibVNCServer.spec.in @@ -59,7 +59,7 @@ make %makeinstall includedir="%{buildroot}%{_includedir}/rfb" %{__install} -d -m0755 %{buildroot}%{_datadir}/x11vnc/classes -%{__install} classes/VncViewer.jar classes/index.vnc \ +%{__install} webclients/VncViewer.jar webclients/index.vnc \ %{buildroot}%{_datadir}/x11vnc/classes %clean diff --git a/Makefile.am b/Makefile.am index 0125b5b..e244fe8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,8 +2,8 @@ if WITH_X11VNC X11VNC=x11vnc endif -SUBDIRS=libvncserver examples libvncclient vncterm classes client_examples test $(X11VNC) -DIST_SUBDIRS=libvncserver examples libvncclient vncterm classes client_examples test +SUBDIRS=libvncserver examples libvncclient vncterm webclients client_examples test $(X11VNC) +DIST_SUBDIRS=libvncserver examples libvncclient vncterm webclients client_examples test EXTRA_DIST = CMakeLists.txt rfb/rfbint.h.cmake rfb/rfbconfig.h.cmake bin_SCRIPTS = libvncserver-config diff --git a/README b/README index f062225..499b72b 100644 --- a/README +++ b/README @@ -163,7 +163,7 @@ If you already have a socket to talk to, just set rfbScreen->inetdSock To also start an HTTP server (running on port 5800+display_number), you have to set rfbScreen->httpdDir to a directory containing vncviewer.jar and -index.vnc (like the included "classes" directory). +index.vnc (like the included "webclients" directory). Hooks and IO functions ---------------------- diff --git a/classes/Makefile.am b/classes/Makefile.am deleted file mode 100644 index c5497a8..0000000 --- a/classes/Makefile.am +++ /dev/null @@ -1,5 +0,0 @@ -EXTRA_DIST=VncViewer.jar index.vnc javaviewer.pseudo_proxy.patch - -SUBDIRS = ssl -DIST_SUBDIRS = ssl - diff --git a/classes/VncViewer.jar b/classes/VncViewer.jar deleted file mode 100644 index 602fdb9..0000000 Binary files a/classes/VncViewer.jar and /dev/null differ diff --git a/classes/index.vnc b/classes/index.vnc deleted file mode 100644 index 34ad25d..0000000 --- a/classes/index.vnc +++ /dev/null @@ -1,37 +0,0 @@ - - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -
-
- -If the above Java applet does not work, you can also try the new JavaScript-only noVNC viewer. You will need a HTML5-capable browser though. - -
- -
- -
-
-
-LibVNCServer/LibVNCClient Homepage - diff --git a/classes/javaviewer.pseudo_proxy.patch b/classes/javaviewer.pseudo_proxy.patch deleted file mode 100644 index 4d2f36e..0000000 --- a/classes/javaviewer.pseudo_proxy.patch +++ /dev/null @@ -1,141 +0,0 @@ -diff -ru vnc_javasrc/OptionsFrame.java proxy_vnc_javasrc/OptionsFrame.java ---- vnc_javasrc/OptionsFrame.java Fri Jul 5 08:17:23 2002 -+++ proxy_vnc_javasrc/OptionsFrame.java Thu Aug 22 23:24:44 2002 -@@ -70,6 +70,12 @@ - - Label[] labels = new Label[names.length]; - Choice[] choices = new Choice[names.length]; -+ -+ Label proxyHostLabel; -+ TextField proxyHostEdit; -+ Label proxyPortLabel; -+ TextField proxyPortEdit; -+ - Button closeButton; - VncViewer viewer; - -@@ -93,6 +99,9 @@ - boolean shareDesktop; - boolean viewOnly; - -+ String proxyHost; -+ int proxyPort; -+ - // - // Constructor. Set up the labels and choices from the names and values - // arrays. -@@ -126,6 +135,32 @@ - } - } - -+ // TODO: find a way to set these to defaults from browser -+ proxyPort = viewer.readIntParameter("Use Proxy Port", -1); -+ if(proxyPort>-1) { -+ proxyHost = viewer.readParameter("Use Proxy Host", false); -+ if(proxyHost == null) -+ proxyHost = viewer.host; -+ -+ proxyHostLabel = new Label("Proxy Host"); -+ gbc.gridwidth = 1; -+ gridbag.setConstraints(proxyHostLabel,gbc); -+ add(proxyHostLabel); -+ proxyHostEdit = new TextField(); -+ gbc.gridwidth = GridBagConstraints.REMAINDER; -+ gridbag.setConstraints(proxyHostEdit,gbc); -+ add(proxyHostEdit); -+ -+ proxyPortLabel = new Label("Proxy Port"); -+ gbc.gridwidth = 1; -+ gridbag.setConstraints(proxyPortLabel,gbc); -+ add(proxyPortLabel); -+ proxyPortEdit = new TextField(); -+ gbc.gridwidth = GridBagConstraints.REMAINDER; -+ gridbag.setConstraints(proxyPortEdit,gbc); -+ add(proxyPortEdit); -+ } -+ - closeButton = new Button("Close"); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gridbag.setConstraints(closeButton, gbc); -@@ -161,6 +196,11 @@ - } - } - -+ if(proxyPort>-1) { -+ proxyPortEdit.setText(Integer.toString(proxyPort)); -+ proxyHostEdit.setText(proxyHost); -+ } -+ - // Make the booleans and encodings array correspond to the state of the GUI - - setEncodings(); -@@ -361,8 +401,12 @@ - // - - public void actionPerformed(ActionEvent evt) { -- if (evt.getSource() == closeButton) -+ if (evt.getSource() == closeButton) { - setVisible(false); -+ proxyHost = proxyHostEdit.getText(); -+ proxyPort = Integer.parseInt(proxyPortEdit.getText()); -+ System.err.println("proxy is " + proxyHost + ":" + proxyPort); -+ } - } - - // -diff -ru vnc_javasrc/RfbProto.java proxy_vnc_javasrc/RfbProto.java ---- vnc_javasrc/RfbProto.java Sun Aug 4 18:39:35 2002 -+++ proxy_vnc_javasrc/RfbProto.java Thu Aug 22 22:53:53 2002 -@@ -119,12 +119,51 @@ - viewer = v; - host = h; - port = p; -- sock = new Socket(host, port); -+ if(viewer.options.proxyPort>-1) -+ sock = new Socket(viewer.options.proxyHost, viewer.options.proxyPort); -+ else -+ sock = new Socket(host, port); - is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), - 16384)); - os = sock.getOutputStream(); -+ if(viewer.options.proxyPort>-1) -+ negotiateProxy(host,port); - } - -+ // this is inefficient as hell, but only used once per connection -+ String readLine() { -+ byte[] ba = new byte[1]; -+ String s = new String(""); -+ -+ ba[0]=0; -+ try { -+ while(ba[0] != 0xa) { -+ ba[0] = (byte)is.readUnsignedByte(); -+ s += new String(ba); -+ } -+ } catch(Exception e) { -+ e.printStackTrace(); -+ } -+ return s; -+ } -+ -+ void negotiateProxy(String realHost,int realPort) throws IOException { -+ String line; -+ -+ // this would be the correct way, but we want to trick strict proxies. -+ // line = "CONNECT " + realHost + ":" + realPort + " HTTP/1.1\r\nHost: " + realHost + ":" + realPort + "\r\n\r\n"; -+ line = "GET " + realHost + ":" + realPort + "/proxied.connection HTTP/1.0\r\nPragma: No-Cache\r\nProxy-Connection: Keep-Alive\r\n\r\n"; -+ os.write(line.getBytes()); -+ -+ line = readLine(); -+ System.err.println("Proxy said: " + line); -+ if(!(line.substring(0,7)+line.substring(8,12)).equalsIgnoreCase("HTTP/1. 200")) { -+ IOException e = new IOException(line); -+ throw e; -+ } -+ while(!line.equals("\r\n") && !line.equals("\n")) -+ line = readLine(); -+ } - - void close() { - try { diff --git a/classes/novnc/LICENSE.txt b/classes/novnc/LICENSE.txt deleted file mode 100644 index 755ace3..0000000 --- a/classes/novnc/LICENSE.txt +++ /dev/null @@ -1,33 +0,0 @@ -noVNC is Copyright (C) 2011 Joel Martin - -Some portions of noVNC are copyright to their individual authors. -Please refer to the individual source files and/or to the noVNC commit -history: https://github.com/kanaka/noVNC/commits/master - -noVNC is licensed under the LGPL (GNU Lesser General Public License) -version 3 with the following exceptions (all LGPL-3 compatible): - - include/input.js : LGPL-2 or any later version - - include/base64.js : Dual GPL-2 or LGPL-2.1 - - include/des.js : Various BSD style licenses - - include/web-socket-js/ : New BSD license. Source code at - http://github.com/gimite/web-socket-js - - include/Orbitron* : SIL Open Font License 1.1 - (Copyright 2009 Matt McInerney) - - images/ : Creative Commons Attribution-ShareAlike - http://creativecommons.org/licenses/by-sa/3.0/ - -The license texts are included at: - docs/LICENSE.LGPL-3 and - docs/LICENSE.GPL-3 - docs/LICENSE.OFL-1.1 - -Or alternatively the license texts may be found here: - http://www.gnu.org/licenses/lgpl.html and - http://www.gnu.org/licenses/gpl.html - http://scripts.sil.org/OFL diff --git a/classes/novnc/README.md b/classes/novnc/README.md deleted file mode 100644 index 4672969..0000000 --- a/classes/novnc/README.md +++ /dev/null @@ -1,93 +0,0 @@ -## noVNC: HTML5 VNC Client - - -### Description - -noVNC is a VNC client implemented using HTML5 technologies, -specifically Canvas and WebSockets (supports 'wss://' encryption). -noVNC is licensed under the -[LGPLv3](http://www.gnu.org/licenses/lgpl.html). - -Special thanks to [Sentry Data Systems](http://www.sentryds.com) for -sponsoring ongoing development of this project (and for employing me). - -There are many companies/projects that have integrated noVNC into -their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), and [SlapOS](http://www.slapos.org). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links. - -Notable commits, announcements and news are posted to -@noVNC - - -### Screenshots - -Running in Chrome before and after connecting: - -  - -See more screenshots here. - - -### Browser Requirements - -* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS - Safari, Opera 11+, Internet Explorer 9+, etc. - -* HTML5 WebSockets: For browsers that do not have builtin - WebSockets support, the project includes - web-socket-js, - a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in - WebSocket support. - -* Fast Javascript Engine: noVNC avoids using new Javascript - functionality so it will run on older browsers, but decode and - rendering happen in Javascript, so a slow Javascript engine will - mean noVNC is painfully slow. - -* I maintain a more detailed browser compatibility list here. - - -### Server Requirements - -Unless you are using a VNC server with support for WebSockets -connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver) -currently), you need to use a WebSockets to TCP socket proxy. There is -a python proxy included ('websockify'). One advantage of using the -proxy is that it has builtin support for SSL/TLS encryption (i.e. -"wss://"). - -There a few reasons why a proxy is required: - - 1. WebSockets is not a pure socket protocol. There is an initial HTTP - like handshake to allow easy hand-off by web servers and allow - some origin policy exchange. Also, each WebSockets frame begins - with 0 ('\x00') and ends with 255 ('\xff'). - - 2. Javascript itself does not have the ability to handle pure byte - arrays. The python proxy encodes the data as base64 so that the - Javascript client can decode the data as an integer array. - - -### Quick Start - -* Use the launch script to start a mini-webserver and the WebSockets - proxy (websockify). The `--vnc` option is used to specify the location of - a running VNC server: - - `./utils/launch.sh --vnc localhost:5901` - -* Point your browser to the cut-and-paste URL that is output by the - launch script. Enter a password if the VNC server has one - configured. Hit the Connect button and enjoy! - - -### Other Pages - -* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL - certificate, starting a VNC server, advanced websockify usage, etc. - -* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects. - -* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems. - - diff --git a/classes/novnc/favicon.ico b/classes/novnc/favicon.ico deleted file mode 120000 index 45399c8..0000000 --- a/classes/novnc/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -images/favicon.ico \ No newline at end of file diff --git a/classes/novnc/images/clipboard.png b/classes/novnc/images/clipboard.png deleted file mode 100644 index 24df33c..0000000 Binary files a/classes/novnc/images/clipboard.png and /dev/null differ diff --git a/classes/novnc/images/connect.png b/classes/novnc/images/connect.png deleted file mode 100644 index 79e71ad..0000000 Binary files a/classes/novnc/images/connect.png and /dev/null differ diff --git a/classes/novnc/images/ctrlaltdel.png b/classes/novnc/images/ctrlaltdel.png deleted file mode 100644 index 31922e5..0000000 Binary files a/classes/novnc/images/ctrlaltdel.png and /dev/null differ diff --git a/classes/novnc/images/disconnect.png b/classes/novnc/images/disconnect.png deleted file mode 100644 index 8832f5e..0000000 Binary files a/classes/novnc/images/disconnect.png and /dev/null differ diff --git a/classes/novnc/images/drag.png b/classes/novnc/images/drag.png deleted file mode 100644 index 433f896..0000000 Binary files a/classes/novnc/images/drag.png and /dev/null differ diff --git a/classes/novnc/images/favicon.ico b/classes/novnc/images/favicon.ico deleted file mode 100644 index c999634..0000000 Binary files a/classes/novnc/images/favicon.ico and /dev/null differ diff --git a/classes/novnc/images/favicon.png b/classes/novnc/images/favicon.png deleted file mode 100644 index e2bdb19..0000000 Binary files a/classes/novnc/images/favicon.png and /dev/null differ diff --git a/classes/novnc/images/keyboard.png b/classes/novnc/images/keyboard.png deleted file mode 100644 index f797952..0000000 Binary files a/classes/novnc/images/keyboard.png and /dev/null differ diff --git a/classes/novnc/images/mouse_left.png b/classes/novnc/images/mouse_left.png deleted file mode 100644 index 1de7a48..0000000 Binary files a/classes/novnc/images/mouse_left.png and /dev/null differ diff --git a/classes/novnc/images/mouse_middle.png b/classes/novnc/images/mouse_middle.png deleted file mode 100644 index 81fbd9b..0000000 Binary files a/classes/novnc/images/mouse_middle.png and /dev/null differ diff --git a/classes/novnc/images/mouse_none.png b/classes/novnc/images/mouse_none.png deleted file mode 100644 index 93dbf57..0000000 Binary files a/classes/novnc/images/mouse_none.png and /dev/null differ diff --git a/classes/novnc/images/mouse_right.png b/classes/novnc/images/mouse_right.png deleted file mode 100644 index 355b25d..0000000 Binary files a/classes/novnc/images/mouse_right.png and /dev/null differ diff --git a/classes/novnc/images/screen_320x460.png b/classes/novnc/images/screen_320x460.png deleted file mode 100644 index 172ec55..0000000 Binary files a/classes/novnc/images/screen_320x460.png and /dev/null differ diff --git a/classes/novnc/images/screen_57x57.png b/classes/novnc/images/screen_57x57.png deleted file mode 100644 index e2085f2..0000000 Binary files a/classes/novnc/images/screen_57x57.png and /dev/null differ diff --git a/classes/novnc/images/screen_700x700.png b/classes/novnc/images/screen_700x700.png deleted file mode 100644 index ae67768..0000000 Binary files a/classes/novnc/images/screen_700x700.png and /dev/null differ diff --git a/classes/novnc/images/settings.png b/classes/novnc/images/settings.png deleted file mode 100644 index a43f5e1..0000000 Binary files a/classes/novnc/images/settings.png and /dev/null differ diff --git a/classes/novnc/include/Orbitron700.ttf b/classes/novnc/include/Orbitron700.ttf deleted file mode 100644 index e28729d..0000000 Binary files a/classes/novnc/include/Orbitron700.ttf and /dev/null differ diff --git a/classes/novnc/include/Orbitron700.woff b/classes/novnc/include/Orbitron700.woff deleted file mode 100644 index 61db630..0000000 Binary files a/classes/novnc/include/Orbitron700.woff and /dev/null differ diff --git a/classes/novnc/include/base.css b/classes/novnc/include/base.css deleted file mode 100644 index 0a62a1b..0000000 --- a/classes/novnc/include/base.css +++ /dev/null @@ -1,380 +0,0 @@ -body { - margin:0; - padding:0; - font-family: Helvetica; - /*Background image with light grey curve.*/ - background-color:#494949; - background-repeat:no-repeat; - background-position:right bottom; - height:100%; -} - -html { - height:100%; -} - -#noVNC_controls ul { - list-style: none; - margin: 0px; - padding: 0px; -} -#noVNC_controls li { - padding-bottom:8px; -} - -#noVNC_host { - width:150px; -} -#noVNC_port { - width: 80px; -} -#noVNC_password { - width: 150px; -} -#noVNC_encrypt { -} -#noVNC_connectTimeout { - width: 30px; -} -#noVNC_path { - width: 100px; -} -#noVNC_connect_button { - width: 110px; - float:right; -} - - -#noVNC_view_drag_button { - display: none; -} -#sendCtrlAltDelButton { - display: none; -} -#noVNC_mobile_buttons { - display: none; -} - -.noVNC-buttons-left { - float: left; - padding-left:10px; - padding-top:4px; -} - -.noVNC-buttons-right { - float:right; - right: 0px; - padding-right:10px; - padding-top:4px; -} - -#noVNC_status_bar { - margin-top: 0px; - padding: 0px; -} - -#noVNC_status_bar div { - font-size: 12px; - padding-top: 4px; - width:100%; -} - -#noVNC_status { - height:20px; - text-align: center; -} -#noVNC_settings_menu { - margin: 3px; - text-align: left; -} -#noVNC_settings_menu ul { - list-style: none; - margin: 0px; - padding: 0px; -} - -#noVNC_apply { - float:right; -} - -.noVNC_status_normal { - background: #eee; -} -.noVNC_status_error { - background: #f44; -} -.noVNC_status_warn { - background: #ff4; -} - -/* Do not set width/height for VNC_screen or VNC_canvas or incorrect - * scaling will occur. Canvas resizes to remote VNC settings */ -#noVNC_screen_pad { - margin: 0px; - padding: 0px; - height: 44px; -} -#noVNC_screen { - text-align: center; - display: table; - width:100%; - height:100%; - background-color:#313131; - border-bottom-right-radius: 800px 600px; - /*border-top-left-radius: 800px 600px;*/ -} - -#noVNC_container, #noVNC_canvas { - margin: 0px; - padding: 0px; -} - -#noVNC_canvas { - left: 0px; -} - -#VNC_clipboard_clear_button { - float:right; -} -#VNC_clipboard_text { - font-size: 11px; -} - -#noVNC_clipboard_clear_button { - float:right; -} - -/*Bubble contents divs*/ -#noVNC_settings { - display:none; - margin-top:77px; - right:20px; - position:fixed; -} - -#noVNC_controls { - margin-top:77px; - right:12px; - position:fixed; -} -#noVNC_controls.top:after { - right:15px; -} - -#noVNC_clipboard { - display:none; - margin-top:77px; - right:30px; - position:fixed; -} -#noVNC_clipboard.top:after { - right:85px; -} - -#keyboardinput { - width:1px; - height:1px; - background-color:#fff; - color:#fff; - border:0; - position: relative; - left: -40px; - z-index: -1; -} - -.noVNC_status_warn { - background-color:yellow; -} - -/* - * Advanced Styling - */ - -/* Control bar */ -#noVNC-control-bar { - position:fixed; - background: #b2bdcd; /* Old browsers */ - background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ - - display:block; - height:44px; - left:0; - top:0; - width:100%; - z-index:200; -} - -.noVNC_status_button { - padding: 4px 4px; - vertical-align: middle; - border:1px solid #869dbc; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - border-radius: 6px; - background: #b2bdcd; /* Old browsers */ - background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ - /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ -} - -.noVNC_status_button_selected { - padding: 4px 4px; - vertical-align: middle; - border:1px solid #4366a9; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; - background: #779ced; /* Old browsers */ - background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ - /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ -} - - -/*Settings Bubble*/ -.triangle-right { - position:relative; - padding:15px; - margin:1em 0 3em; - color:#fff; - background:#fff; /* default background for browsers without gradient support */ - /* css3 */ - /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); - background:-moz-linear-gradient(#2e88c4, #075698); - background:-o-linear-gradient(#2e88c4, #075698); - background:linear-gradient(#2e88c4, #075698);*/ - -webkit-border-radius:10px; - -moz-border-radius:10px; - border-radius:10px; - color:#000; - border:2px solid #E0E0E0; -} - -.triangle-right.top:after { - border-color: transparent #E0E0E0; - border-width: 20px 20px 0 0; - bottom: auto; - left: auto; - right: 50px; - top: -20px; -} - -.triangle-right:after { - content:""; - position:absolute; - bottom:-20px; /* value = - border-top-width - border-bottom-width */ - left:50px; /* controls horizontal position */ - border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ - border-style:solid; - border-color:#E0E0E0 transparent; - /* reduce the damage in FF3.0 */ - display:block; - width:0; -} - -.triangle-right.top:after { - top:-40px; /* value = - border-top-width - border-bottom-width */ - right:50px; /* controls horizontal position */ - bottom:auto; - left:auto; - border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ - border-color:transparent #E0E0E0; -} - -/*Default noVNC logo.*/ -/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ -@font-face { - font-family: 'Orbitron'; - font-style: normal; - font-weight: 700; - src: local('?'), url('Orbitron700.woff') format('woff'), - url('Orbitron700.ttf') format('truetype'); -} - -#noVNC_logo { - margin-top: 170px; - margin-left: 10px; - color:yellow; - text-align:left; - font-family: 'Orbitron', 'OrbitronTTF', sans-serif; - line-height:90%; - text-shadow: - 5px 5px 0 #000, - -1px -1px 0 #000, - 1px -1px 0 #000, - -1px 1px 0 #000, - 1px 1px 0 #000; -} - - -#noVNC_logo span{ - color:green; -} - -/* ---------------------------------------- - * Media sizing - * ---------------------------------------- - */ - - -.noVNC_status_button { - font-size: 12px; -} - -#noVNC_clipboard_text { - width: 500px; -} - -#noVNC_logo { - font-size: 180px; -} - -@media screen and (min-width: 481px) and (max-width: 640px) { - .noVNC_status_button { - font-size: 10px; - } - #noVNC_clipboard_text { - width: 410px; - } - #noVNC_logo { - font-size: 150px; - } -} - -@media screen and (min-width: 321px) and (max-width: 480px) { - .noVNC_status_button { - font-size: 10px; - } - #noVNC_clipboard_text { - width: 250px; - } - #noVNC_logo { - font-size: 110px; - } -} - -@media screen and (max-width: 320px) { - .noVNC_status_button { - font-size: 9px; - } - #noVNC_clipboard_text { - width: 220px; - } - #noVNC_logo { - font-size: 90px; - } -} diff --git a/classes/novnc/include/base64.js b/classes/novnc/include/base64.js deleted file mode 100644 index c68b33a..0000000 --- a/classes/novnc/include/base64.js +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Modified from: - * http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 - */ - -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla XML-RPC Client component. - * - * The Initial Developer of the Original Code is - * Digital Creations 2, Inc. - * Portions created by the Initial Developer are Copyright (C) 2000 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Martijn Pieters (original author) - * Samuel Sieb - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/*jslint white: false, bitwise: false, plusplus: false */ -/*global console */ - -var Base64 = { - -/* Convert data (an array of integers) to a Base64 string. */ -toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', -base64Pad : '=', - -encode: function (data) { - "use strict"; - var result = '', - chrTable = Base64.toBase64Table.split(''), - pad = Base64.base64Pad, - length = data.length, - i; - // Convert every three bytes to 4 ascii characters. - for (i = 0; i < (length - 2); i += 3) { - result += chrTable[data[i] >> 2]; - result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; - result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; - result += chrTable[data[i+2] & 0x3f]; - } - - // Convert the remaining 1 or 2 bytes, pad out to 4 characters. - if (length%3) { - i = length - (length%3); - result += chrTable[data[i] >> 2]; - if ((length%3) === 2) { - result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; - result += chrTable[(data[i+1] & 0x0f) << 2]; - result += pad; - } else { - result += chrTable[(data[i] & 0x03) << 4]; - result += pad + pad; - } - } - - return result; -}, - -/* Convert Base64 data to a string */ -toBinaryTable : [ - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, - -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, - 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, - -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, - 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, - -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, - 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 -], - -decode: function (data, offset) { - "use strict"; - offset = typeof(offset) !== 'undefined' ? offset : 0; - var binTable = Base64.toBinaryTable, - pad = Base64.base64Pad, - result, result_length, idx, i, c, padding, - leftbits = 0, // number of bits decoded, but yet to be appended - leftdata = 0, // bits decoded, but yet to be appended - data_length = data.indexOf('=') - offset; - - if (data_length < 0) { data_length = data.length - offset; } - - /* Every four characters is 3 resulting numbers */ - result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); - result = new Array(result_length); - - // Convert one by one. - for (idx = 0, i = offset; i < data.length; i++) { - c = binTable[data.charCodeAt(i) & 0x7f]; - padding = (data.charAt(i) === pad); - // Skip illegal characters and whitespace - if (c === -1) { - console.error("Illegal character '" + data.charCodeAt(i) + "'"); - continue; - } - - // Collect data into leftdata, update bitcount - leftdata = (leftdata << 6) | c; - leftbits += 6; - - // If we have 8 or more bits, append 8 bits to the result - if (leftbits >= 8) { - leftbits -= 8; - // Append if not padding. - if (!padding) { - result[idx++] = (leftdata >> leftbits) & 0xff; - } - leftdata &= (1 << leftbits) - 1; - } - } - - // If there are any bits left, the base64 string was corrupted - if (leftbits) { - throw {name: 'Base64-Error', - message: 'Corrupted base64 string'}; - } - - return result; -} - -}; /* End of Base64 namespace */ diff --git a/classes/novnc/include/black.css b/classes/novnc/include/black.css deleted file mode 100644 index 8f80f66..0000000 --- a/classes/novnc/include/black.css +++ /dev/null @@ -1,45 +0,0 @@ -#keyboardinput { - background-color:#000; -} - -#noVNC-control-bar { - background: #4c4c4c; /* Old browsers */ - background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ -} - -.triangle-right { - border:2px solid #fff; - background:#000; - color:#fff; -} - -.noVNC_status_button { - font-size: 12px; - vertical-align: middle; - border:1px solid #4c4c4c; - - background: #4c4c4c; /* Old browsers */ - background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ -} - -.noVNC_status_button_selected { - background: #9dd53a; /* Old browsers */ - background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ - background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ - background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ -} diff --git a/classes/novnc/include/blue.css b/classes/novnc/include/blue.css deleted file mode 100644 index a8baf70..0000000 --- a/classes/novnc/include/blue.css +++ /dev/null @@ -1,27 +0,0 @@ - -#noVNC-control-bar { - background-color:#04073d; - background-image: -webkit-gradient( - linear, - left bottom, - left top, - color-stop(0.54, rgb(10,15,79)), - color-stop(0.5, rgb(4,7,61)) - ); - background-image: -moz-linear-gradient( - center bottom, - rgb(10,15,79) 54%, - rgb(4,7,61) 50% - ); -} - -.triangle-right { - border:2px solid #fff; - background:#04073d; - color:#fff; -} - -#keyboardinput { - background-color:#04073d; -} - diff --git a/classes/novnc/include/des.js b/classes/novnc/include/des.js deleted file mode 100644 index 1f95285..0000000 --- a/classes/novnc/include/des.js +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Ported from Flashlight VNC ActionScript implementation: - * http://www.wizhelp.com/flashlight-vnc/ - * - * Full attribution follows: - * - * ------------------------------------------------------------------------- - * - * This DES class has been extracted from package Acme.Crypto for use in VNC. - * The unnecessary odd parity code has been removed. - * - * These changes are: - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * 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. - * - - * DesCipher - the DES encryption method - * - * The meat of this code is by Dave Zimmerman , and is: - * - * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. - * - * Permission to use, copy, modify, and distribute this software - * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and - * without fee is hereby granted, provided that this copyright notice is kept - * intact. - * - * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY - * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED - * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE - * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR - * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. - * - * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE - * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE - * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT - * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE - * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE - * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE - * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP - * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR - * HIGH RISK ACTIVITIES. - * - * - * The rest is: - * - * Copyright (C) 1996 by Jef Poskanzer . All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Visit the ACME Labs Java page for up-to-date versions of this and other - * fine Java utilities: http://www.acme.com/java/ - */ - -"use strict"; -/*jslint white: false, bitwise: false, plusplus: false */ - -function DES(passwd) { - -// Tables, permutations, S-boxes, etc. -var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, - 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, - 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], - totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], - z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, - keys = []; - -a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; -SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, - z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, - a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, - c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; -a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; -SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, - a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, - z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, - z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; -a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; -SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, - b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, - c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, - b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; -a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; -SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, - z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, - b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, - c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; -a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; -SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, - a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, - z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, - c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; -a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; -SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, - z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, - b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, - a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; -a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; -SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, - b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, - b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, - z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; -a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; -SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, - c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, - a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, - z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; - -// Set the key. -function setKeys(keyBlock) { - var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], - raw0, raw1, rawi, KnLi; - - for (j = 0, l = 56; j < 56; ++j, l-=8) { - l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1 - m = l & 0x7; - pc1m[j] = ((keyBlock[l >>> 3] & (1<>> 10; - keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; - ++KnLi; - keys[KnLi] = (raw0 & 0x0003f000) << 12; - keys[KnLi] |= (raw0 & 0x0000003f) << 16; - keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; - keys[KnLi] |= (raw1 & 0x0000003f); - ++KnLi; - } -} - -// Encrypt 8 bytes of text -function enc8(text) { - var i = 0, b = text.slice(), fval, keysi = 0, - l, r, x; // left, right, accumulator - - // Squash 8 bytes to 2 ints - l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; - r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; - - x = ((l >>> 4) ^ r) & 0x0f0f0f0f; - r ^= x; - l ^= (x << 4); - x = ((l >>> 16) ^ r) & 0x0000ffff; - r ^= x; - l ^= (x << 16); - x = ((r >>> 2) ^ l) & 0x33333333; - l ^= x; - r ^= (x << 2); - x = ((r >>> 8) ^ l) & 0x00ff00ff; - l ^= x; - r ^= (x << 8); - r = (r << 1) | ((r >>> 31) & 1); - x = (l ^ r) & 0xaaaaaaaa; - l ^= x; - r ^= x; - l = (l << 1) | ((l >>> 31) & 1); - - for (i = 0; i < 8; ++i) { - x = (r << 28) | (r >>> 4); - x ^= keys[keysi++]; - fval = SP7[x & 0x3f]; - fval |= SP5[(x >>> 8) & 0x3f]; - fval |= SP3[(x >>> 16) & 0x3f]; - fval |= SP1[(x >>> 24) & 0x3f]; - x = r ^ keys[keysi++]; - fval |= SP8[x & 0x3f]; - fval |= SP6[(x >>> 8) & 0x3f]; - fval |= SP4[(x >>> 16) & 0x3f]; - fval |= SP2[(x >>> 24) & 0x3f]; - l ^= fval; - x = (l << 28) | (l >>> 4); - x ^= keys[keysi++]; - fval = SP7[x & 0x3f]; - fval |= SP5[(x >>> 8) & 0x3f]; - fval |= SP3[(x >>> 16) & 0x3f]; - fval |= SP1[(x >>> 24) & 0x3f]; - x = l ^ keys[keysi++]; - fval |= SP8[x & 0x0000003f]; - fval |= SP6[(x >>> 8) & 0x3f]; - fval |= SP4[(x >>> 16) & 0x3f]; - fval |= SP2[(x >>> 24) & 0x3f]; - r ^= fval; - } - - r = (r << 31) | (r >>> 1); - x = (l ^ r) & 0xaaaaaaaa; - l ^= x; - r ^= x; - l = (l << 31) | (l >>> 1); - x = ((l >>> 8) ^ r) & 0x00ff00ff; - r ^= x; - l ^= (x << 8); - x = ((l >>> 2) ^ r) & 0x33333333; - r ^= x; - l ^= (x << 2); - x = ((r >>> 16) ^ l) & 0x0000ffff; - l ^= x; - r ^= (x << 16); - x = ((r >>> 4) ^ l) & 0x0f0f0f0f; - l ^= x; - r ^= (x << 4); - - // Spread ints to bytes - x = [r, l]; - for (i = 0; i < 8; i++) { - b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256; - if (b[i] < 0) { b[i] += 256; } // unsigned - } - return b; -} - -// Encrypt 16 bytes of text using passwd as key -function encrypt(t) { - return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16))); -} - -setKeys(passwd); // Setup keys -return {'encrypt': encrypt}; // Public interface - -} // function DES diff --git a/classes/novnc/include/display.js b/classes/novnc/include/display.js deleted file mode 100644 index 2cf262d..0000000 --- a/classes/novnc/include/display.js +++ /dev/null @@ -1,671 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/*jslint browser: true, white: false, bitwise: false */ -/*global Util, Base64, changeCursor */ - -function Display(defaults) { -"use strict"; - -var that = {}, // Public API methods - conf = {}, // Configuration attributes - - // Private Display namespace variables - c_ctx = null, - c_forceCanvas = false, - - // Predefine function variables (jslint) - imageDataGet, rgbxImageData, cmapImageData, - setFillColor, rescale, - - // The full frame buffer (logical canvas) size - fb_width = 0, - fb_height = 0, - // The visible "physical canvas" viewport - viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 }, - cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1}, - - c_prevStyle = "", - tile = null, - tile16x16 = null, - tile_x = 0, - tile_y = 0; - - -// Configuration attributes -Util.conf_defaults(conf, that, defaults, [ - ['target', 'wo', 'dom', null, 'Canvas element for rendering'], - ['context', 'ro', 'raw', null, 'Canvas 2D context for rendering (read-only)'], - ['logo', 'rw', 'raw', null, 'Logo to display when cleared: {"width": width, "height": height, "data": data}'], - ['true_color', 'rw', 'bool', true, 'Use true-color pixel data'], - ['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'], - ['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'], - ['viewport', 'rw', 'bool', false, 'Use a viewport set with viewportChange()'], - ['width', 'rw', 'int', null, 'Display area width'], - ['height', 'rw', 'int', null, 'Display area height'], - - ['render_mode', 'ro', 'str', '', 'Canvas rendering mode (read-only)'], - - ['prefer_js', 'rw', 'str', null, 'Prefer Javascript over canvas methods'], - ['cursor_uri', 'rw', 'raw', null, 'Can we render cursor using data URI'] - ]); - -// Override some specific getters/setters -that.get_context = function () { return c_ctx; }; - -that.set_scale = function(scale) { rescale(scale); }; - -that.set_width = function (val) { that.resize(val, fb_height); }; -that.get_width = function() { return fb_width; }; - -that.set_height = function (val) { that.resize(fb_width, val); }; -that.get_height = function() { return fb_height; }; - - - -// -// Private functions -// - -// Create the public API interface -function constructor() { - Util.Debug(">> Display.constructor"); - - var c, func, i, curDat, curSave, - has_imageData = false, UE = Util.Engine; - - if (! conf.target) { throw("target must be set"); } - - if (typeof conf.target === 'string') { - throw("target must be a DOM element"); - } - - c = conf.target; - - if (! c.getContext) { throw("no getContext method"); } - - if (! c_ctx) { c_ctx = c.getContext('2d'); } - - Util.Debug("User Agent: " + navigator.userAgent); - if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); } - if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); } - if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); } - if (UE.presto) { Util.Debug("Browser: presto " + UE.presto); } - - that.clear(); - - // Check canvas features - if ('createImageData' in c_ctx) { - conf.render_mode = "canvas rendering"; - } else { - throw("Canvas does not support createImageData"); - } - if (conf.prefer_js === null) { - Util.Info("Prefering javascript operations"); - conf.prefer_js = true; - } - - // Initialize cached tile imageData - tile16x16 = c_ctx.createImageData(16, 16); - - /* - * Determine browser support for setting the cursor via data URI - * scheme - */ - curDat = []; - for (i=0; i < 8 * 8 * 4; i += 1) { - curDat.push(255); - } - try { - curSave = c.style.cursor; - changeCursor(conf.target, curDat, curDat, 2, 2, 8, 8); - if (c.style.cursor) { - if (conf.cursor_uri === null) { - conf.cursor_uri = true; - } - Util.Info("Data URI scheme cursor supported"); - } else { - if (conf.cursor_uri === null) { - conf.cursor_uri = false; - } - Util.Warn("Data URI scheme cursor not supported"); - } - c.style.cursor = curSave; - } catch (exc2) { - Util.Error("Data URI scheme cursor test exception: " + exc2); - conf.cursor_uri = false; - } - - Util.Debug("<< Display.constructor"); - return that ; -} - -rescale = function(factor) { - var c, tp, x, y, - properties = ['transform', 'WebkitTransform', 'MozTransform', null]; - c = conf.target; - tp = properties.shift(); - while (tp) { - if (typeof c.style[tp] !== 'undefined') { - break; - } - tp = properties.shift(); - } - - if (tp === null) { - Util.Debug("No scaling support"); - return; - } - - - if (typeof(factor) === "undefined") { - factor = conf.scale; - } else if (factor > 1.0) { - factor = 1.0; - } else if (factor < 0.1) { - factor = 0.1; - } - - if (conf.scale === factor) { - //Util.Debug("Display already scaled to '" + factor + "'"); - return; - } - - conf.scale = factor; - x = c.width - c.width * factor; - y = c.height - c.height * factor; - c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)"; -}; - -setFillColor = function(color) { - var rgb, newStyle; - if (conf.true_color) { - rgb = color; - } else { - rgb = conf.colourMap[color[0]]; - } - newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; - if (newStyle !== c_prevStyle) { - c_ctx.fillStyle = newStyle; - c_prevStyle = newStyle; - } -}; - - -// -// Public API interface functions -// - -// Shift and/or resize the visible viewport -that.viewportChange = function(deltaX, deltaY, width, height) { - var c = conf.target, v = viewport, cr = cleanRect, - saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h; - - if (!conf.viewport) { - Util.Debug("Setting viewport to full display region"); - deltaX = -v.w; // Clamped later if out of bounds - deltaY = -v.h; // Clamped later if out of bounds - width = fb_width; - height = fb_height; - } - - if (typeof(deltaX) === "undefined") { deltaX = 0; } - if (typeof(deltaY) === "undefined") { deltaY = 0; } - if (typeof(width) === "undefined") { width = v.w; } - if (typeof(height) === "undefined") { height = v.h; } - - // Size change - - if (width > fb_width) { width = fb_width; } - if (height > fb_height) { height = fb_height; } - - if ((v.w !== width) || (v.h !== height)) { - // Change width - if ((width < v.w) && (cr.x2 > v.x + width -1)) { - cr.x2 = v.x + width - 1; - } - v.w = width; - - // Change height - if ((height < v.h) && (cr.y2 > v.y + height -1)) { - cr.y2 = v.y + height - 1; - } - v.h = height; - - - if (v.w > 0 && v.h > 0 && c.width > 0 && c.height > 0) { - saveImg = c_ctx.getImageData(0, 0, - (c.width < v.w) ? c.width : v.w, - (c.height < v.h) ? c.height : v.h); - } - - c.width = v.w; - c.height = v.h; - - if (saveImg) { - c_ctx.putImageData(saveImg, 0, 0); - } - } - - vx2 = v.x + v.w - 1; - vy2 = v.y + v.h - 1; - - - // Position change - - if ((deltaX < 0) && ((v.x + deltaX) < 0)) { - deltaX = - v.x; - } - if ((vx2 + deltaX) >= fb_width) { - deltaX -= ((vx2 + deltaX) - fb_width + 1); - } - - if ((v.y + deltaY) < 0) { - deltaY = - v.y; - } - if ((vy2 + deltaY) >= fb_height) { - deltaY -= ((vy2 + deltaY) - fb_height + 1); - } - - if ((deltaX === 0) && (deltaY === 0)) { - //Util.Debug("skipping viewport change"); - return; - } - Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); - - v.x += deltaX; - vx2 += deltaX; - v.y += deltaY; - vy2 += deltaY; - - // Update the clean rectangle - if (v.x > cr.x1) { - cr.x1 = v.x; - } - if (vx2 < cr.x2) { - cr.x2 = vx2; - } - if (v.y > cr.y1) { - cr.y1 = v.y; - } - if (vy2 < cr.y2) { - cr.y2 = vy2; - } - - if (deltaX < 0) { - // Shift viewport left, redraw left section - x1 = 0; - w = - deltaX; - } else { - // Shift viewport right, redraw right section - x1 = v.w - deltaX; - w = deltaX; - } - if (deltaY < 0) { - // Shift viewport up, redraw top section - y1 = 0; - h = - deltaY; - } else { - // Shift viewport down, redraw bottom section - y1 = v.h - deltaY; - h = deltaY; - } - - // Copy the valid part of the viewport to the shifted location - saveStyle = c_ctx.fillStyle; - c_ctx.fillStyle = "rgb(255,255,255)"; - if (deltaX !== 0) { - //that.copyImage(0, 0, -deltaX, 0, v.w, v.h); - //that.fillRect(x1, 0, w, v.h, [255,255,255]); - c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h); - c_ctx.fillRect(x1, 0, w, v.h); - } - if (deltaY !== 0) { - //that.copyImage(0, 0, 0, -deltaY, v.w, v.h); - //that.fillRect(0, y1, v.w, h, [255,255,255]); - c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h); - c_ctx.fillRect(0, y1, v.w, h); - } - c_ctx.fillStyle = saveStyle; -}; - - -// Return a map of clean and dirty areas of the viewport and reset the -// tracking of clean and dirty areas. -// -// Returns: {'cleanBox': {'x': x, 'y': y, 'w': w, 'h': h}, -// 'dirtyBoxes': [{'x': x, 'y': y, 'w': w, 'h': h}, ...]} -that.getCleanDirtyReset = function() { - var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [], - vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1; - - - // Copy the cleanRect - cleanBox = {'x': c.x1, 'y': c.y1, - 'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1}; - - if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) { - // Whole viewport is dirty - dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h}); - } else { - // Redraw dirty regions - if (v.x < c.x1) { - // left side dirty region - dirtyBoxes.push({'x': v.x, 'y': v.y, - 'w': c.x1 - v.x + 1, 'h': v.h}); - } - if (vx2 > c.x2) { - // right side dirty region - dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y, - 'w': vx2 - c.x2, 'h': v.h}); - } - if (v.y < c.y1) { - // top/middle dirty region - dirtyBoxes.push({'x': c.x1, 'y': v.y, - 'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y}); - } - if (vy2 > c.y2) { - // bottom/middle dirty region - dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1, - 'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2}); - } - } - - // Reset the cleanRect to the whole viewport - cleanRect = {'x1': v.x, 'y1': v.y, - 'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1}; - - return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; -}; - -// Translate viewport coordinates to absolute coordinates -that.absX = function(x) { - return x + viewport.x; -} -that.absY = function(y) { - return y + viewport.y; -} - - -that.resize = function(width, height) { - c_prevStyle = ""; - - fb_width = width; - fb_height = height; - - rescale(conf.scale); - that.viewportChange(); -}; - -that.clear = function() { - - if (conf.logo) { - that.resize(conf.logo.width, conf.logo.height); - that.blitStringImage(conf.logo.data, 0, 0); - } else { - that.resize(640, 20); - c_ctx.clearRect(0, 0, viewport.w, viewport.h); - } - - // No benefit over default ("source-over") in Chrome and firefox - //c_ctx.globalCompositeOperation = "copy"; -}; - -that.fillRect = function(x, y, width, height, color) { - setFillColor(color); - c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height); -}; - -that.copyImage = function(old_x, old_y, new_x, new_y, w, h) { - var x1 = old_x - viewport.x, y1 = old_y - viewport.y, - x2 = new_x - viewport.x, y2 = new_y - viewport.y; - c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h); -}; - - -// Start updating a tile -that.startTile = function(x, y, width, height, color) { - var data, rgb, red, green, blue, i; - tile_x = x; - tile_y = y; - if ((width === 16) && (height === 16)) { - tile = tile16x16; - } else { - tile = c_ctx.createImageData(width, height); - } - data = tile.data; - if (conf.prefer_js) { - if (conf.true_color) { - rgb = color; - } else { - rgb = conf.colourMap[color[0]]; - } - red = rgb[0]; - green = rgb[1]; - blue = rgb[2]; - for (i = 0; i < (width * height * 4); i+=4) { - data[i ] = red; - data[i + 1] = green; - data[i + 2] = blue; - data[i + 3] = 255; - } - } else { - that.fillRect(x, y, width, height, color); - } -}; - -// Update sub-rectangle of the current tile -that.subTile = function(x, y, w, h, color) { - var data, p, rgb, red, green, blue, width, j, i, xend, yend; - if (conf.prefer_js) { - data = tile.data; - width = tile.width; - if (conf.true_color) { - rgb = color; - } else { - rgb = conf.colourMap[color[0]]; - } - red = rgb[0]; - green = rgb[1]; - blue = rgb[2]; - xend = x + w; - yend = y + h; - for (j = y; j < yend; j += 1) { - for (i = x; i < xend; i += 1) { - p = (i + (j * width) ) * 4; - data[p ] = red; - data[p + 1] = green; - data[p + 2] = blue; - data[p + 3] = 255; - } - } - } else { - that.fillRect(tile_x + x, tile_y + y, w, h, color); - } -}; - -// Draw the current tile to the screen -that.finishTile = function() { - if (conf.prefer_js) { - c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y) - } - // else: No-op, if not prefer_js then already done by setSubTile -}; - -rgbxImageData = function(x, y, width, height, arr, offset) { - var img, i, j, data, v = viewport; - /* - if ((x - v.x >= v.w) || (y - v.y >= v.h) || - (x - v.x + width < 0) || (y - v.y + height < 0)) { - // Skipping because outside of viewport - return; - } - */ - img = c_ctx.createImageData(width, height); - data = img.data; - for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) { - data[i ] = arr[j ]; - data[i + 1] = arr[j + 1]; - data[i + 2] = arr[j + 2]; - data[i + 3] = 255; // Set Alpha - } - c_ctx.putImageData(img, x - v.x, y - v.y); -}; - -cmapImageData = function(x, y, width, height, arr, offset) { - var img, i, j, data, rgb, cmap; - img = c_ctx.createImageData(width, height); - data = img.data; - cmap = conf.colourMap; - for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) { - rgb = cmap[arr[j]]; - data[i ] = rgb[0]; - data[i + 1] = rgb[1]; - data[i + 2] = rgb[2]; - data[i + 3] = 255; // Set Alpha - } - c_ctx.putImageData(img, x - viewport.x, y - viewport.y); -}; - -that.blitImage = function(x, y, width, height, arr, offset) { - if (conf.true_color) { - rgbxImageData(x, y, width, height, arr, offset); - } else { - cmapImageData(x, y, width, height, arr, offset); - } -}; - -that.blitStringImage = function(str, x, y) { - var img = new Image(); - img.onload = function () { - c_ctx.drawImage(img, x - viewport.x, y - viewport.y); - }; - img.src = str; -}; - -that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { - if (conf.cursor_uri === false) { - Util.Warn("changeCursor called but no cursor data URI support"); - return; - } - - if (conf.true_color) { - changeCursor(conf.target, pixels, mask, hotx, hoty, w, h); - } else { - changeCursor(conf.target, pixels, mask, hotx, hoty, w, h, conf.colourMap); - } -}; - -that.defaultCursor = function() { - conf.target.style.cursor = "default"; -}; - -return constructor(); // Return the public API interface - -} // End of Display() - - -/* Set CSS cursor property using data URI encoded cursor file */ -function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) { - "use strict"; - var cur = [], rgb, IHDRsz, RGBsz, ANDsz, XORsz, url, idx, alpha, x, y; - //Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h); - - // Push multi-byte little-endian values - cur.push16le = function (num) { - this.push((num ) & 0xFF, - (num >> 8) & 0xFF ); - }; - cur.push32le = function (num) { - this.push((num ) & 0xFF, - (num >> 8) & 0xFF, - (num >> 16) & 0xFF, - (num >> 24) & 0xFF ); - }; - - IHDRsz = 40; - RGBsz = w * h * 4; - XORsz = Math.ceil( (w * h) / 8.0 ); - ANDsz = Math.ceil( (w * h) / 8.0 ); - - // Main header - cur.push16le(0); // 0: Reserved - cur.push16le(2); // 2: .CUR type - cur.push16le(1); // 4: Number of images, 1 for non-animated ico - - // Cursor #1 header (ICONDIRENTRY) - cur.push(w); // 6: width - cur.push(h); // 7: height - cur.push(0); // 8: colors, 0 -> true-color - cur.push(0); // 9: reserved - cur.push16le(hotx); // 10: hotspot x coordinate - cur.push16le(hoty); // 12: hotspot y coordinate - cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); - // 14: cursor data byte size - cur.push32le(22); // 18: offset of cursor data in the file - - - // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) - cur.push32le(IHDRsz); // 22: Infoheader size - cur.push32le(w); // 26: Cursor width - cur.push32le(h*2); // 30: XOR+AND height - cur.push16le(1); // 34: number of planes - cur.push16le(32); // 36: bits per pixel - cur.push32le(0); // 38: Type of compression - - cur.push32le(XORsz + ANDsz); // 43: Size of Image - // Gimp leaves this as 0 - - cur.push32le(0); // 46: reserved - cur.push32le(0); // 50: reserved - cur.push32le(0); // 54: reserved - cur.push32le(0); // 58: reserved - - // 62: color data (RGBQUAD icColors[]) - for (y = h-1; y >= 0; y -= 1) { - for (x = 0; x < w; x += 1) { - idx = y * Math.ceil(w / 8) + Math.floor(x/8); - alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; - - if (cmap) { - idx = (w * y) + x; - rgb = cmap[pixels[idx]]; - cur.push(rgb[2]); // blue - cur.push(rgb[1]); // green - cur.push(rgb[0]); // red - cur.push(alpha); // alpha - } else { - idx = ((w * y) + x) * 4; - cur.push(pixels[idx + 2]); // blue - cur.push(pixels[idx + 1]); // green - cur.push(pixels[idx ]); // red - cur.push(alpha); // alpha - } - } - } - - // XOR/bitmask data (BYTE icXOR[]) - // (ignored, just needs to be right size) - for (y = 0; y < h; y += 1) { - for (x = 0; x < Math.ceil(w / 8); x += 1) { - cur.push(0x00); - } - } - - // AND/bitmask data (BYTE icAND[]) - // (ignored, just needs to be right size) - for (y = 0; y < h; y += 1) { - for (x = 0; x < Math.ceil(w / 8); x += 1) { - cur.push(0x00); - } - } - - url = "data:image/x-icon;base64," + Base64.encode(cur); - target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default"; - //Util.Debug("<< changeCursor, cur.length: " + cur.length); -} diff --git a/classes/novnc/include/input.js b/classes/novnc/include/input.js deleted file mode 100644 index 3124d08..0000000 --- a/classes/novnc/include/input.js +++ /dev/null @@ -1,1884 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-2 or any later version (see LICENSE.txt) - */ - -/*jslint browser: true, white: false, bitwise: false */ -/*global window, Util */ - - -// -// Keyboard event handler -// - -function Keyboard(defaults) { -"use strict"; - -var that = {}, // Public API methods - conf = {}, // Configuration attributes - - keyDownList = []; // List of depressed keys - // (even if they are happy) - -// Configuration attributes -Util.conf_defaults(conf, that, defaults, [ - ['target', 'wo', 'dom', document, 'DOM element that captures keyboard input'], - ['focused', 'rw', 'bool', true, 'Capture and send key events'], - - ['onKeyPress', 'rw', 'func', null, 'Handler for key press/release'] - ]); - - -// -// Private functions -// - -// From the event keyCode return the keysym value for keys that need -// to be suppressed otherwise they may trigger unintended browser -// actions -function getKeysymSpecial(evt) { - var keysym = null; - - switch ( evt.keyCode ) { - // These generate a keyDown and keyPress in Firefox and Opera - case 8 : keysym = 0xFF08; break; // BACKSPACE - case 13 : keysym = 0xFF0D; break; // ENTER - - // This generates a keyDown and keyPress in Opera - case 9 : keysym = 0xFF09; break; // TAB - default : break; - } - - if (evt.type === 'keydown') { - switch ( evt.keyCode ) { - case 27 : keysym = 0xFF1B; break; // ESCAPE - case 46 : keysym = 0xFFFF; break; // DELETE - - case 36 : keysym = 0xFF50; break; // HOME - case 35 : keysym = 0xFF57; break; // END - case 33 : keysym = 0xFF55; break; // PAGE_UP - case 34 : keysym = 0xFF56; break; // PAGE_DOWN - case 45 : keysym = 0xFF63; break; // INSERT - // '-' during keyPress - case 37 : keysym = 0xFF51; break; // LEFT - case 38 : keysym = 0xFF52; break; // UP - case 39 : keysym = 0xFF53; break; // RIGHT - case 40 : keysym = 0xFF54; break; // DOWN - case 16 : keysym = 0xFFE1; break; // SHIFT - case 17 : keysym = 0xFFE3; break; // CONTROL - //case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option) - case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command) - - case 112 : keysym = 0xFFBE; break; // F1 - case 113 : keysym = 0xFFBF; break; // F2 - case 114 : keysym = 0xFFC0; break; // F3 - case 115 : keysym = 0xFFC1; break; // F4 - case 116 : keysym = 0xFFC2; break; // F5 - case 117 : keysym = 0xFFC3; break; // F6 - case 118 : keysym = 0xFFC4; break; // F7 - case 119 : keysym = 0xFFC5; break; // F8 - case 120 : keysym = 0xFFC6; break; // F9 - case 121 : keysym = 0xFFC7; break; // F10 - case 122 : keysym = 0xFFC8; break; // F11 - case 123 : keysym = 0xFFC9; break; // F12 - - default : break; - } - } - - if ((!keysym) && (evt.ctrlKey || evt.altKey)) { - if ((typeof(evt.which) !== "undefined") && (evt.which > 0)) { - keysym = evt.which; - } else { - // IE9 always - // Firefox and Opera when ctrl/alt + special - Util.Warn("which not set, using keyCode"); - keysym = evt.keyCode; - } - - /* Remap symbols */ - switch (keysym) { - case 186 : keysym = 59; break; // ; (IE) - case 187 : keysym = 61; break; // = (IE) - case 188 : keysym = 44; break; // , (Mozilla, IE) - case 109 : // - (Mozilla, Opera) - if (Util.Engine.gecko || Util.Engine.presto) { - keysym = 45; } - break; - case 189 : keysym = 45; break; // - (IE) - case 190 : keysym = 46; break; // . (Mozilla, IE) - case 191 : keysym = 47; break; // / (Mozilla, IE) - case 192 : keysym = 96; break; // ` (Mozilla, IE) - case 219 : keysym = 91; break; // [ (Mozilla, IE) - case 220 : keysym = 92; break; // \ (Mozilla, IE) - case 221 : keysym = 93; break; // ] (Mozilla, IE) - case 222 : keysym = 39; break; // ' (Mozilla, IE) - } - - /* Remap shifted and unshifted keys */ - if (!!evt.shiftKey) { - switch (keysym) { - case 48 : keysym = 41 ; break; // ) (shifted 0) - case 49 : keysym = 33 ; break; // ! (shifted 1) - case 50 : keysym = 64 ; break; // @ (shifted 2) - case 51 : keysym = 35 ; break; // # (shifted 3) - case 52 : keysym = 36 ; break; // $ (shifted 4) - case 53 : keysym = 37 ; break; // % (shifted 5) - case 54 : keysym = 94 ; break; // ^ (shifted 6) - case 55 : keysym = 38 ; break; // & (shifted 7) - case 56 : keysym = 42 ; break; // * (shifted 8) - case 57 : keysym = 40 ; break; // ( (shifted 9) - - case 59 : keysym = 58 ; break; // : (shifted `) - case 61 : keysym = 43 ; break; // + (shifted ;) - case 44 : keysym = 60 ; break; // < (shifted ,) - case 45 : keysym = 95 ; break; // _ (shifted -) - case 46 : keysym = 62 ; break; // > (shifted .) - case 47 : keysym = 63 ; break; // ? (shifted /) - case 96 : keysym = 126; break; // ~ (shifted `) - case 91 : keysym = 123; break; // { (shifted [) - case 92 : keysym = 124; break; // | (shifted \) - case 93 : keysym = 125; break; // } (shifted ]) - case 39 : keysym = 34 ; break; // " (shifted ') - } - } else if ((keysym >= 65) && (keysym <=90)) { - /* Remap unshifted A-Z */ - keysym += 32; - } else if (evt.keyLocation === 3) { - // numpad keys - switch (keysym) { - case 96 : keysym = 48; break; // 0 - case 97 : keysym = 49; break; // 1 - case 98 : keysym = 50; break; // 2 - case 99 : keysym = 51; break; // 3 - case 100: keysym = 52; break; // 4 - case 101: keysym = 53; break; // 5 - case 102: keysym = 54; break; // 6 - case 103: keysym = 55; break; // 7 - case 104: keysym = 56; break; // 8 - case 105: keysym = 57; break; // 9 - case 109: keysym = 45; break; // - - case 110: keysym = 46; break; // . - case 111: keysym = 47; break; // / - } - } - } - - return keysym; -} - -/* Translate DOM keyPress event to keysym value */ -function getKeysym(evt) { - var keysym, msg; - - if (typeof(evt.which) !== "undefined") { - // WebKit, Firefox, Opera - keysym = evt.which; - } else { - // IE9 - Util.Warn("which not set, using keyCode"); - keysym = evt.keyCode; - } - - if ((keysym > 255) && (keysym < 0xFF00)) { - msg = "Mapping character code " + keysym; - // Map Unicode outside Latin 1 to X11 keysyms - keysym = unicodeTable[keysym]; - if (typeof(keysym) === 'undefined') { - keysym = 0; - } - Util.Debug(msg + " to " + keysym); - } - - return keysym; -} - -function show_keyDownList(kind) { - var c; - var msg = "keyDownList (" + kind + "):\n"; - for (c = 0; c < keyDownList.length; c++) { - msg = msg + " " + c + " - keyCode: " + keyDownList[c].keyCode + - " - which: " + keyDownList[c].which + "\n"; - } - Util.Debug(msg); -} - -function copyKeyEvent(evt) { - var members = ['type', 'keyCode', 'charCode', 'which', - 'altKey', 'ctrlKey', 'shiftKey', - 'keyLocation', 'keyIdentifier'], i, obj = {}; - for (i = 0; i < members.length; i++) { - if (typeof(evt[members[i]]) !== "undefined") { - obj[members[i]] = evt[members[i]]; - } - } - return obj; -} - -function pushKeyEvent(fevt) { - keyDownList.push(fevt); -} - -function getKeyEvent(keyCode, pop) { - var i, fevt = null; - for (i = keyDownList.length-1; i >= 0; i--) { - if (keyDownList[i].keyCode === keyCode) { - if ((typeof(pop) !== "undefined") && (pop)) { - fevt = keyDownList.splice(i, 1)[0]; - } else { - fevt = keyDownList[i]; - } - break; - } - } - return fevt; -} - -function ignoreKeyEvent(evt) { - // Blarg. Some keys have a different keyCode on keyDown vs keyUp - if (evt.keyCode === 229) { - // French AZERTY keyboard dead key. - // Lame thing is that the respective keyUp is 219 so we can't - // properly ignore the keyUp event - return true; - } - return false; -} - - -// -// Key Event Handling: -// -// There are several challenges when dealing with key events: -// - The meaning and use of keyCode, charCode and which depends on -// both the browser and the event type (keyDown/Up vs keyPress). -// - We cannot automatically determine the keyboard layout -// - The keyDown and keyUp events have a keyCode value that has not -// been translated by modifier keys. -// - The keyPress event has a translated (for layout and modifiers) -// character code but the attribute containing it differs. keyCode -// contains the translated value in WebKit (Chrome/Safari), Opera -// 11 and IE9. charCode contains the value in WebKit and Firefox. -// The which attribute contains the value on WebKit, Firefox and -// Opera 11. -// - The keyDown/Up keyCode value indicates (sort of) the physical -// key was pressed but only for standard US layout. On a US -// keyboard, the '-' and '_' characters are on the same key and -// generate a keyCode value of 189. But on an AZERTY keyboard even -// though they are different physical keys they both still -// generate a keyCode of 189! -// - To prevent a key event from propagating to the browser and -// causing unwanted default actions (such as closing a tab, -// opening a menu, shifting focus, etc) we must suppress this -// event in both keyDown and keyPress because not all key strokes -// generate on a keyPress event. Also, in WebKit and IE9 -// suppressing the keyDown prevents a keyPress but other browsers -// still generated a keyPress even if keyDown is suppressed. -// -// For safe key events, we wait until the keyPress event before -// reporting a key down event. For unsafe key events, we report a key -// down event when the keyDown event fires and we suppress any further -// actions (including keyPress). -// -// In order to report a key up event that matches what we reported -// for the key down event, we keep a list of keys that are currently -// down. When the keyDown event happens, we add the key event to the -// list. If it is a safe key event, then we update the which attribute -// in the most recent item on the list when we received a keyPress -// event (keyPress should immediately follow keyDown). When we -// received a keyUp event we search for the event on the list with -// a matching keyCode and we report the character code using the value -// in the 'which' attribute that was stored with that key. -// - -function onKeyDown(e) { - if (! conf.focused) { - return true; - } - var fevt = null, evt = (e ? e : window.event), - keysym = null, suppress = false; - //Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); - - fevt = copyKeyEvent(evt); - - keysym = getKeysymSpecial(evt); - // Save keysym decoding for use in keyUp - fevt.keysym = keysym; - if (keysym) { - // If it is a key or key combination that might trigger - // browser behaviors or it has no corresponding keyPress - // event, then send it immediately - if (conf.onKeyPress && !ignoreKeyEvent(evt)) { - Util.Debug("onKeyPress down, keysym: " + keysym + - " (onKeyDown key: " + evt.keyCode + - ", which: " + evt.which + ")"); - conf.onKeyPress(keysym, 1, evt); - } - suppress = true; - } - - if (! ignoreKeyEvent(evt)) { - // Add it to the list of depressed keys - pushKeyEvent(fevt); - //show_keyDownList('down'); - } - - if (suppress) { - // Suppress bubbling/default actions - Util.stopEvent(e); - return false; - } else { - // Allow the event to bubble and become a keyPress event which - // will have the character code translated - return true; - } -} - -function onKeyPress(e) { - if (! conf.focused) { - return true; - } - var evt = (e ? e : window.event), - kdlen = keyDownList.length, keysym = null; - //Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); - - if (((evt.which !== "undefined") && (evt.which === 0)) || - (getKeysymSpecial(evt))) { - // Firefox and Opera generate a keyPress event even if keyDown - // is suppressed. But the keys we want to suppress will have - // either: - // - the which attribute set to 0 - // - getKeysymSpecial() will identify it - Util.Debug("Ignoring special key in keyPress"); - Util.stopEvent(e); - return false; - } - - keysym = getKeysym(evt); - - // Modify the the which attribute in the depressed keys list so - // that the keyUp event will be able to have the character code - // translation available. - if (kdlen > 0) { - keyDownList[kdlen-1].keysym = keysym; - } else { - Util.Warn("keyDownList empty when keyPress triggered"); - } - - //show_keyDownList('press'); - - // Send the translated keysym - if (conf.onKeyPress && (keysym > 0)) { - Util.Debug("onKeyPress down, keysym: " + keysym + - " (onKeyPress key: " + evt.keyCode + - ", which: " + evt.which + ")"); - conf.onKeyPress(keysym, 1, evt); - } - - // Stop keypress events just in case - Util.stopEvent(e); - return false; -} - -function onKeyUp(e) { - if (! conf.focused) { - return true; - } - var fevt = null, evt = (e ? e : window.event), keysym; - //Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); - - fevt = getKeyEvent(evt.keyCode, true); - - if (fevt) { - keysym = fevt.keysym; - } else { - Util.Warn("Key event (keyCode = " + evt.keyCode + - ") not found on keyDownList"); - keysym = 0; - } - - //show_keyDownList('up'); - - if (conf.onKeyPress && (keysym > 0)) { - //Util.Debug("keyPress up, keysym: " + keysym + - // " (key: " + evt.keyCode + ", which: " + evt.which + ")"); - Util.Debug("onKeyPress up, keysym: " + keysym + - " (onKeyPress key: " + evt.keyCode + - ", which: " + evt.which + ")"); - conf.onKeyPress(keysym, 0, evt); - } - Util.stopEvent(e); - return false; -} - -// -// Public API interface functions -// - -that.grab = function() { - //Util.Debug(">> Keyboard.grab"); - var c = conf.target; - - Util.addEvent(c, 'keydown', onKeyDown); - Util.addEvent(c, 'keyup', onKeyUp); - Util.addEvent(c, 'keypress', onKeyPress); - - //Util.Debug("<< Keyboard.grab"); -}; - -that.ungrab = function() { - //Util.Debug(">> Keyboard.ungrab"); - var c = conf.target; - - Util.removeEvent(c, 'keydown', onKeyDown); - Util.removeEvent(c, 'keyup', onKeyUp); - Util.removeEvent(c, 'keypress', onKeyPress); - - //Util.Debug(">> Keyboard.ungrab"); -}; - -return that; // Return the public API interface - -} // End of Keyboard() - - -// -// Mouse event handler -// - -function Mouse(defaults) { -"use strict"; - -var that = {}, // Public API methods - conf = {}; // Configuration attributes - -// Configuration attributes -Util.conf_defaults(conf, that, defaults, [ - ['target', 'ro', 'dom', document, 'DOM element that captures mouse input'], - ['focused', 'rw', 'bool', true, 'Capture and send mouse clicks/movement'], - ['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'], - - ['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'], - ['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'], - ['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)'] - ]); - - -// -// Private functions -// - -function onMouseButton(e, down) { - var evt, pos, bmask; - if (! conf.focused) { - return true; - } - evt = (e ? e : window.event); - pos = Util.getEventPosition(e, conf.target, conf.scale); - if (e.touches || e.changedTouches) { - // Touch device - bmask = conf.touchButton; - // If bmask is set - } else if (evt.which) { - /* everything except IE */ - bmask = 1 << evt.button; - } else { - /* IE including 9 */ - bmask = (evt.button & 0x1) + // Left - (evt.button & 0x2) * 2 + // Right - (evt.button & 0x4) / 2; // Middle - } - //Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down + - // " bmask: " + bmask + "(evt.button: " + evt.button + ")"); - if (bmask > 0 && conf.onMouseButton) { - Util.Debug("onMouseButton " + (down ? "down" : "up") + - ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); - conf.onMouseButton(pos.x, pos.y, down, bmask); - } - Util.stopEvent(e); - return false; -} - -function onMouseDown(e) { - onMouseButton(e, 1); -} - -function onMouseUp(e) { - onMouseButton(e, 0); -} - -function onMouseWheel(e) { - var evt, pos, bmask, wheelData; - if (! conf.focused) { - return true; - } - evt = (e ? e : window.event); - pos = Util.getEventPosition(e, conf.target, conf.scale); - wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40; - if (wheelData > 0) { - bmask = 1 << 3; - } else { - bmask = 1 << 4; - } - //Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y); - if (conf.onMouseButton) { - conf.onMouseButton(pos.x, pos.y, 1, bmask); - conf.onMouseButton(pos.x, pos.y, 0, bmask); - } - Util.stopEvent(e); - return false; -} - -function onMouseMove(e) { - var evt, pos; - if (! conf.focused) { - return true; - } - evt = (e ? e : window.event); - pos = Util.getEventPosition(e, conf.target, conf.scale); - //Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y); - if (conf.onMouseMove) { - conf.onMouseMove(pos.x, pos.y); - } - Util.stopEvent(e); - return false; -} - -function onMouseDisable(e) { - var evt, pos; - if (! conf.focused) { - return true; - } - evt = (e ? e : window.event); - pos = Util.getEventPosition(e, conf.target, conf.scale); - /* Stop propagation if inside canvas area */ - if ((pos.x >= 0) && (pos.y >= 0) && - (pos.x < conf.target.offsetWidth) && - (pos.y < conf.target.offsetHeight)) { - //Util.Debug("mouse event disabled"); - Util.stopEvent(e); - return false; - } - //Util.Debug("mouse event not disabled"); - return true; -} - -// -// Public API interface functions -// - -that.grab = function() { - //Util.Debug(">> Mouse.grab"); - var c = conf.target; - - if ('ontouchstart' in document.documentElement) { - Util.addEvent(c, 'touchstart', onMouseDown); - Util.addEvent(c, 'touchend', onMouseUp); - Util.addEvent(c, 'touchmove', onMouseMove); - } else { - Util.addEvent(c, 'mousedown', onMouseDown); - Util.addEvent(c, 'mouseup', onMouseUp); - Util.addEvent(c, 'mousemove', onMouseMove); - Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', - onMouseWheel); - } - - /* Work around right and middle click browser behaviors */ - Util.addEvent(document, 'click', onMouseDisable); - Util.addEvent(document.body, 'contextmenu', onMouseDisable); - - //Util.Debug("<< Mouse.grab"); -}; - -that.ungrab = function() { - //Util.Debug(">> Mouse.ungrab"); - var c = conf.target; - - if ('ontouchstart' in document.documentElement) { - Util.removeEvent(c, 'touchstart', onMouseDown); - Util.removeEvent(c, 'touchend', onMouseUp); - Util.removeEvent(c, 'touchmove', onMouseMove); - } else { - Util.removeEvent(c, 'mousedown', onMouseDown); - Util.removeEvent(c, 'mouseup', onMouseUp); - Util.removeEvent(c, 'mousemove', onMouseMove); - Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', - onMouseWheel); - } - - /* Work around right and middle click browser behaviors */ - Util.removeEvent(document, 'click', onMouseDisable); - Util.removeEvent(document.body, 'contextmenu', onMouseDisable); - - //Util.Debug(">> Mouse.ungrab"); -}; - -return that; // Return the public API interface - -} // End of Mouse() - - -/* - * Browser keypress to X11 keysym for Unicode characters > U+00FF - */ -unicodeTable = { - 0x0104 : 0x01a1, - 0x02D8 : 0x01a2, - 0x0141 : 0x01a3, - 0x013D : 0x01a5, - 0x015A : 0x01a6, - 0x0160 : 0x01a9, - 0x015E : 0x01aa, - 0x0164 : 0x01ab, - 0x0179 : 0x01ac, - 0x017D : 0x01ae, - 0x017B : 0x01af, - 0x0105 : 0x01b1, - 0x02DB : 0x01b2, - 0x0142 : 0x01b3, - 0x013E : 0x01b5, - 0x015B : 0x01b6, - 0x02C7 : 0x01b7, - 0x0161 : 0x01b9, - 0x015F : 0x01ba, - 0x0165 : 0x01bb, - 0x017A : 0x01bc, - 0x02DD : 0x01bd, - 0x017E : 0x01be, - 0x017C : 0x01bf, - 0x0154 : 0x01c0, - 0x0102 : 0x01c3, - 0x0139 : 0x01c5, - 0x0106 : 0x01c6, - 0x010C : 0x01c8, - 0x0118 : 0x01ca, - 0x011A : 0x01cc, - 0x010E : 0x01cf, - 0x0110 : 0x01d0, - 0x0143 : 0x01d1, - 0x0147 : 0x01d2, - 0x0150 : 0x01d5, - 0x0158 : 0x01d8, - 0x016E : 0x01d9, - 0x0170 : 0x01db, - 0x0162 : 0x01de, - 0x0155 : 0x01e0, - 0x0103 : 0x01e3, - 0x013A : 0x01e5, - 0x0107 : 0x01e6, - 0x010D : 0x01e8, - 0x0119 : 0x01ea, - 0x011B : 0x01ec, - 0x010F : 0x01ef, - 0x0111 : 0x01f0, - 0x0144 : 0x01f1, - 0x0148 : 0x01f2, - 0x0151 : 0x01f5, - 0x0171 : 0x01fb, - 0x0159 : 0x01f8, - 0x016F : 0x01f9, - 0x0163 : 0x01fe, - 0x02D9 : 0x01ff, - 0x0126 : 0x02a1, - 0x0124 : 0x02a6, - 0x0130 : 0x02a9, - 0x011E : 0x02ab, - 0x0134 : 0x02ac, - 0x0127 : 0x02b1, - 0x0125 : 0x02b6, - 0x0131 : 0x02b9, - 0x011F : 0x02bb, - 0x0135 : 0x02bc, - 0x010A : 0x02c5, - 0x0108 : 0x02c6, - 0x0120 : 0x02d5, - 0x011C : 0x02d8, - 0x016C : 0x02dd, - 0x015C : 0x02de, - 0x010B : 0x02e5, - 0x0109 : 0x02e6, - 0x0121 : 0x02f5, - 0x011D : 0x02f8, - 0x016D : 0x02fd, - 0x015D : 0x02fe, - 0x0138 : 0x03a2, - 0x0156 : 0x03a3, - 0x0128 : 0x03a5, - 0x013B : 0x03a6, - 0x0112 : 0x03aa, - 0x0122 : 0x03ab, - 0x0166 : 0x03ac, - 0x0157 : 0x03b3, - 0x0129 : 0x03b5, - 0x013C : 0x03b6, - 0x0113 : 0x03ba, - 0x0123 : 0x03bb, - 0x0167 : 0x03bc, - 0x014A : 0x03bd, - 0x014B : 0x03bf, - 0x0100 : 0x03c0, - 0x012E : 0x03c7, - 0x0116 : 0x03cc, - 0x012A : 0x03cf, - 0x0145 : 0x03d1, - 0x014C : 0x03d2, - 0x0136 : 0x03d3, - 0x0172 : 0x03d9, - 0x0168 : 0x03dd, - 0x016A : 0x03de, - 0x0101 : 0x03e0, - 0x012F : 0x03e7, - 0x0117 : 0x03ec, - 0x012B : 0x03ef, - 0x0146 : 0x03f1, - 0x014D : 0x03f2, - 0x0137 : 0x03f3, - 0x0173 : 0x03f9, - 0x0169 : 0x03fd, - 0x016B : 0x03fe, - 0x1E02 : 0x1001e02, - 0x1E03 : 0x1001e03, - 0x1E0A : 0x1001e0a, - 0x1E80 : 0x1001e80, - 0x1E82 : 0x1001e82, - 0x1E0B : 0x1001e0b, - 0x1EF2 : 0x1001ef2, - 0x1E1E : 0x1001e1e, - 0x1E1F : 0x1001e1f, - 0x1E40 : 0x1001e40, - 0x1E41 : 0x1001e41, - 0x1E56 : 0x1001e56, - 0x1E81 : 0x1001e81, - 0x1E57 : 0x1001e57, - 0x1E83 : 0x1001e83, - 0x1E60 : 0x1001e60, - 0x1EF3 : 0x1001ef3, - 0x1E84 : 0x1001e84, - 0x1E85 : 0x1001e85, - 0x1E61 : 0x1001e61, - 0x0174 : 0x1000174, - 0x1E6A : 0x1001e6a, - 0x0176 : 0x1000176, - 0x0175 : 0x1000175, - 0x1E6B : 0x1001e6b, - 0x0177 : 0x1000177, - 0x0152 : 0x13bc, - 0x0153 : 0x13bd, - 0x0178 : 0x13be, - 0x203E : 0x047e, - 0x3002 : 0x04a1, - 0x300C : 0x04a2, - 0x300D : 0x04a3, - 0x3001 : 0x04a4, - 0x30FB : 0x04a5, - 0x30F2 : 0x04a6, - 0x30A1 : 0x04a7, - 0x30A3 : 0x04a8, - 0x30A5 : 0x04a9, - 0x30A7 : 0x04aa, - 0x30A9 : 0x04ab, - 0x30E3 : 0x04ac, - 0x30E5 : 0x04ad, - 0x30E7 : 0x04ae, - 0x30C3 : 0x04af, - 0x30FC : 0x04b0, - 0x30A2 : 0x04b1, - 0x30A4 : 0x04b2, - 0x30A6 : 0x04b3, - 0x30A8 : 0x04b4, - 0x30AA : 0x04b5, - 0x30AB : 0x04b6, - 0x30AD : 0x04b7, - 0x30AF : 0x04b8, - 0x30B1 : 0x04b9, - 0x30B3 : 0x04ba, - 0x30B5 : 0x04bb, - 0x30B7 : 0x04bc, - 0x30B9 : 0x04bd, - 0x30BB : 0x04be, - 0x30BD : 0x04bf, - 0x30BF : 0x04c0, - 0x30C1 : 0x04c1, - 0x30C4 : 0x04c2, - 0x30C6 : 0x04c3, - 0x30C8 : 0x04c4, - 0x30CA : 0x04c5, - 0x30CB : 0x04c6, - 0x30CC : 0x04c7, - 0x30CD : 0x04c8, - 0x30CE : 0x04c9, - 0x30CF : 0x04ca, - 0x30D2 : 0x04cb, - 0x30D5 : 0x04cc, - 0x30D8 : 0x04cd, - 0x30DB : 0x04ce, - 0x30DE : 0x04cf, - 0x30DF : 0x04d0, - 0x30E0 : 0x04d1, - 0x30E1 : 0x04d2, - 0x30E2 : 0x04d3, - 0x30E4 : 0x04d4, - 0x30E6 : 0x04d5, - 0x30E8 : 0x04d6, - 0x30E9 : 0x04d7, - 0x30EA : 0x04d8, - 0x30EB : 0x04d9, - 0x30EC : 0x04da, - 0x30ED : 0x04db, - 0x30EF : 0x04dc, - 0x30F3 : 0x04dd, - 0x309B : 0x04de, - 0x309C : 0x04df, - 0x06F0 : 0x10006f0, - 0x06F1 : 0x10006f1, - 0x06F2 : 0x10006f2, - 0x06F3 : 0x10006f3, - 0x06F4 : 0x10006f4, - 0x06F5 : 0x10006f5, - 0x06F6 : 0x10006f6, - 0x06F7 : 0x10006f7, - 0x06F8 : 0x10006f8, - 0x06F9 : 0x10006f9, - 0x066A : 0x100066a, - 0x0670 : 0x1000670, - 0x0679 : 0x1000679, - 0x067E : 0x100067e, - 0x0686 : 0x1000686, - 0x0688 : 0x1000688, - 0x0691 : 0x1000691, - 0x060C : 0x05ac, - 0x06D4 : 0x10006d4, - 0x0660 : 0x1000660, - 0x0661 : 0x1000661, - 0x0662 : 0x1000662, - 0x0663 : 0x1000663, - 0x0664 : 0x1000664, - 0x0665 : 0x1000665, - 0x0666 : 0x1000666, - 0x0667 : 0x1000667, - 0x0668 : 0x1000668, - 0x0669 : 0x1000669, - 0x061B : 0x05bb, - 0x061F : 0x05bf, - 0x0621 : 0x05c1, - 0x0622 : 0x05c2, - 0x0623 : 0x05c3, - 0x0624 : 0x05c4, - 0x0625 : 0x05c5, - 0x0626 : 0x05c6, - 0x0627 : 0x05c7, - 0x0628 : 0x05c8, - 0x0629 : 0x05c9, - 0x062A : 0x05ca, - 0x062B : 0x05cb, - 0x062C : 0x05cc, - 0x062D : 0x05cd, - 0x062E : 0x05ce, - 0x062F : 0x05cf, - 0x0630 : 0x05d0, - 0x0631 : 0x05d1, - 0x0632 : 0x05d2, - 0x0633 : 0x05d3, - 0x0634 : 0x05d4, - 0x0635 : 0x05d5, - 0x0636 : 0x05d6, - 0x0637 : 0x05d7, - 0x0638 : 0x05d8, - 0x0639 : 0x05d9, - 0x063A : 0x05da, - 0x0640 : 0x05e0, - 0x0641 : 0x05e1, - 0x0642 : 0x05e2, - 0x0643 : 0x05e3, - 0x0644 : 0x05e4, - 0x0645 : 0x05e5, - 0x0646 : 0x05e6, - 0x0647 : 0x05e7, - 0x0648 : 0x05e8, - 0x0649 : 0x05e9, - 0x064A : 0x05ea, - 0x064B : 0x05eb, - 0x064C : 0x05ec, - 0x064D : 0x05ed, - 0x064E : 0x05ee, - 0x064F : 0x05ef, - 0x0650 : 0x05f0, - 0x0651 : 0x05f1, - 0x0652 : 0x05f2, - 0x0653 : 0x1000653, - 0x0654 : 0x1000654, - 0x0655 : 0x1000655, - 0x0698 : 0x1000698, - 0x06A4 : 0x10006a4, - 0x06A9 : 0x10006a9, - 0x06AF : 0x10006af, - 0x06BA : 0x10006ba, - 0x06BE : 0x10006be, - 0x06CC : 0x10006cc, - 0x06D2 : 0x10006d2, - 0x06C1 : 0x10006c1, - 0x0492 : 0x1000492, - 0x0493 : 0x1000493, - 0x0496 : 0x1000496, - 0x0497 : 0x1000497, - 0x049A : 0x100049a, - 0x049B : 0x100049b, - 0x049C : 0x100049c, - 0x049D : 0x100049d, - 0x04A2 : 0x10004a2, - 0x04A3 : 0x10004a3, - 0x04AE : 0x10004ae, - 0x04AF : 0x10004af, - 0x04B0 : 0x10004b0, - 0x04B1 : 0x10004b1, - 0x04B2 : 0x10004b2, - 0x04B3 : 0x10004b3, - 0x04B6 : 0x10004b6, - 0x04B7 : 0x10004b7, - 0x04B8 : 0x10004b8, - 0x04B9 : 0x10004b9, - 0x04BA : 0x10004ba, - 0x04BB : 0x10004bb, - 0x04D8 : 0x10004d8, - 0x04D9 : 0x10004d9, - 0x04E2 : 0x10004e2, - 0x04E3 : 0x10004e3, - 0x04E8 : 0x10004e8, - 0x04E9 : 0x10004e9, - 0x04EE : 0x10004ee, - 0x04EF : 0x10004ef, - 0x0452 : 0x06a1, - 0x0453 : 0x06a2, - 0x0451 : 0x06a3, - 0x0454 : 0x06a4, - 0x0455 : 0x06a5, - 0x0456 : 0x06a6, - 0x0457 : 0x06a7, - 0x0458 : 0x06a8, - 0x0459 : 0x06a9, - 0x045A : 0x06aa, - 0x045B : 0x06ab, - 0x045C : 0x06ac, - 0x0491 : 0x06ad, - 0x045E : 0x06ae, - 0x045F : 0x06af, - 0x2116 : 0x06b0, - 0x0402 : 0x06b1, - 0x0403 : 0x06b2, - 0x0401 : 0x06b3, - 0x0404 : 0x06b4, - 0x0405 : 0x06b5, - 0x0406 : 0x06b6, - 0x0407 : 0x06b7, - 0x0408 : 0x06b8, - 0x0409 : 0x06b9, - 0x040A : 0x06ba, - 0x040B : 0x06bb, - 0x040C : 0x06bc, - 0x0490 : 0x06bd, - 0x040E : 0x06be, - 0x040F : 0x06bf, - 0x044E : 0x06c0, - 0x0430 : 0x06c1, - 0x0431 : 0x06c2, - 0x0446 : 0x06c3, - 0x0434 : 0x06c4, - 0x0435 : 0x06c5, - 0x0444 : 0x06c6, - 0x0433 : 0x06c7, - 0x0445 : 0x06c8, - 0x0438 : 0x06c9, - 0x0439 : 0x06ca, - 0x043A : 0x06cb, - 0x043B : 0x06cc, - 0x043C : 0x06cd, - 0x043D : 0x06ce, - 0x043E : 0x06cf, - 0x043F : 0x06d0, - 0x044F : 0x06d1, - 0x0440 : 0x06d2, - 0x0441 : 0x06d3, - 0x0442 : 0x06d4, - 0x0443 : 0x06d5, - 0x0436 : 0x06d6, - 0x0432 : 0x06d7, - 0x044C : 0x06d8, - 0x044B : 0x06d9, - 0x0437 : 0x06da, - 0x0448 : 0x06db, - 0x044D : 0x06dc, - 0x0449 : 0x06dd, - 0x0447 : 0x06de, - 0x044A : 0x06df, - 0x042E : 0x06e0, - 0x0410 : 0x06e1, - 0x0411 : 0x06e2, - 0x0426 : 0x06e3, - 0x0414 : 0x06e4, - 0x0415 : 0x06e5, - 0x0424 : 0x06e6, - 0x0413 : 0x06e7, - 0x0425 : 0x06e8, - 0x0418 : 0x06e9, - 0x0419 : 0x06ea, - 0x041A : 0x06eb, - 0x041B : 0x06ec, - 0x041C : 0x06ed, - 0x041D : 0x06ee, - 0x041E : 0x06ef, - 0x041F : 0x06f0, - 0x042F : 0x06f1, - 0x0420 : 0x06f2, - 0x0421 : 0x06f3, - 0x0422 : 0x06f4, - 0x0423 : 0x06f5, - 0x0416 : 0x06f6, - 0x0412 : 0x06f7, - 0x042C : 0x06f8, - 0x042B : 0x06f9, - 0x0417 : 0x06fa, - 0x0428 : 0x06fb, - 0x042D : 0x06fc, - 0x0429 : 0x06fd, - 0x0427 : 0x06fe, - 0x042A : 0x06ff, - 0x0386 : 0x07a1, - 0x0388 : 0x07a2, - 0x0389 : 0x07a3, - 0x038A : 0x07a4, - 0x03AA : 0x07a5, - 0x038C : 0x07a7, - 0x038E : 0x07a8, - 0x03AB : 0x07a9, - 0x038F : 0x07ab, - 0x0385 : 0x07ae, - 0x2015 : 0x07af, - 0x03AC : 0x07b1, - 0x03AD : 0x07b2, - 0x03AE : 0x07b3, - 0x03AF : 0x07b4, - 0x03CA : 0x07b5, - 0x0390 : 0x07b6, - 0x03CC : 0x07b7, - 0x03CD : 0x07b8, - 0x03CB : 0x07b9, - 0x03B0 : 0x07ba, - 0x03CE : 0x07bb, - 0x0391 : 0x07c1, - 0x0392 : 0x07c2, - 0x0393 : 0x07c3, - 0x0394 : 0x07c4, - 0x0395 : 0x07c5, - 0x0396 : 0x07c6, - 0x0397 : 0x07c7, - 0x0398 : 0x07c8, - 0x0399 : 0x07c9, - 0x039A : 0x07ca, - 0x039B : 0x07cb, - 0x039C : 0x07cc, - 0x039D : 0x07cd, - 0x039E : 0x07ce, - 0x039F : 0x07cf, - 0x03A0 : 0x07d0, - 0x03A1 : 0x07d1, - 0x03A3 : 0x07d2, - 0x03A4 : 0x07d4, - 0x03A5 : 0x07d5, - 0x03A6 : 0x07d6, - 0x03A7 : 0x07d7, - 0x03A8 : 0x07d8, - 0x03A9 : 0x07d9, - 0x03B1 : 0x07e1, - 0x03B2 : 0x07e2, - 0x03B3 : 0x07e3, - 0x03B4 : 0x07e4, - 0x03B5 : 0x07e5, - 0x03B6 : 0x07e6, - 0x03B7 : 0x07e7, - 0x03B8 : 0x07e8, - 0x03B9 : 0x07e9, - 0x03BA : 0x07ea, - 0x03BB : 0x07eb, - 0x03BC : 0x07ec, - 0x03BD : 0x07ed, - 0x03BE : 0x07ee, - 0x03BF : 0x07ef, - 0x03C0 : 0x07f0, - 0x03C1 : 0x07f1, - 0x03C3 : 0x07f2, - 0x03C2 : 0x07f3, - 0x03C4 : 0x07f4, - 0x03C5 : 0x07f5, - 0x03C6 : 0x07f6, - 0x03C7 : 0x07f7, - 0x03C8 : 0x07f8, - 0x03C9 : 0x07f9, - 0x23B7 : 0x08a1, - 0x2320 : 0x08a4, - 0x2321 : 0x08a5, - 0x23A1 : 0x08a7, - 0x23A3 : 0x08a8, - 0x23A4 : 0x08a9, - 0x23A6 : 0x08aa, - 0x239B : 0x08ab, - 0x239D : 0x08ac, - 0x239E : 0x08ad, - 0x23A0 : 0x08ae, - 0x23A8 : 0x08af, - 0x23AC : 0x08b0, - 0x2264 : 0x08bc, - 0x2260 : 0x08bd, - 0x2265 : 0x08be, - 0x222B : 0x08bf, - 0x2234 : 0x08c0, - 0x221D : 0x08c1, - 0x221E : 0x08c2, - 0x2207 : 0x08c5, - 0x223C : 0x08c8, - 0x2243 : 0x08c9, - 0x21D4 : 0x08cd, - 0x21D2 : 0x08ce, - 0x2261 : 0x08cf, - 0x221A : 0x08d6, - 0x2282 : 0x08da, - 0x2283 : 0x08db, - 0x2229 : 0x08dc, - 0x222A : 0x08dd, - 0x2227 : 0x08de, - 0x2228 : 0x08df, - 0x2202 : 0x08ef, - 0x0192 : 0x08f6, - 0x2190 : 0x08fb, - 0x2191 : 0x08fc, - 0x2192 : 0x08fd, - 0x2193 : 0x08fe, - 0x25C6 : 0x09e0, - 0x2592 : 0x09e1, - 0x2409 : 0x09e2, - 0x240C : 0x09e3, - 0x240D : 0x09e4, - 0x240A : 0x09e5, - 0x2424 : 0x09e8, - 0x240B : 0x09e9, - 0x2518 : 0x09ea, - 0x2510 : 0x09eb, - 0x250C : 0x09ec, - 0x2514 : 0x09ed, - 0x253C : 0x09ee, - 0x23BA : 0x09ef, - 0x23BB : 0x09f0, - 0x2500 : 0x09f1, - 0x23BC : 0x09f2, - 0x23BD : 0x09f3, - 0x251C : 0x09f4, - 0x2524 : 0x09f5, - 0x2534 : 0x09f6, - 0x252C : 0x09f7, - 0x2502 : 0x09f8, - 0x2003 : 0x0aa1, - 0x2002 : 0x0aa2, - 0x2004 : 0x0aa3, - 0x2005 : 0x0aa4, - 0x2007 : 0x0aa5, - 0x2008 : 0x0aa6, - 0x2009 : 0x0aa7, - 0x200A : 0x0aa8, - 0x2014 : 0x0aa9, - 0x2013 : 0x0aaa, - 0x2026 : 0x0aae, - 0x2025 : 0x0aaf, - 0x2153 : 0x0ab0, - 0x2154 : 0x0ab1, - 0x2155 : 0x0ab2, - 0x2156 : 0x0ab3, - 0x2157 : 0x0ab4, - 0x2158 : 0x0ab5, - 0x2159 : 0x0ab6, - 0x215A : 0x0ab7, - 0x2105 : 0x0ab8, - 0x2012 : 0x0abb, - 0x215B : 0x0ac3, - 0x215C : 0x0ac4, - 0x215D : 0x0ac5, - 0x215E : 0x0ac6, - 0x2122 : 0x0ac9, - 0x2018 : 0x0ad0, - 0x2019 : 0x0ad1, - 0x201C : 0x0ad2, - 0x201D : 0x0ad3, - 0x211E : 0x0ad4, - 0x2032 : 0x0ad6, - 0x2033 : 0x0ad7, - 0x271D : 0x0ad9, - 0x2663 : 0x0aec, - 0x2666 : 0x0aed, - 0x2665 : 0x0aee, - 0x2720 : 0x0af0, - 0x2020 : 0x0af1, - 0x2021 : 0x0af2, - 0x2713 : 0x0af3, - 0x2717 : 0x0af4, - 0x266F : 0x0af5, - 0x266D : 0x0af6, - 0x2642 : 0x0af7, - 0x2640 : 0x0af8, - 0x260E : 0x0af9, - 0x2315 : 0x0afa, - 0x2117 : 0x0afb, - 0x2038 : 0x0afc, - 0x201A : 0x0afd, - 0x201E : 0x0afe, - 0x22A4 : 0x0bc2, - 0x230A : 0x0bc4, - 0x2218 : 0x0bca, - 0x2395 : 0x0bcc, - 0x22A5 : 0x0bce, - 0x25CB : 0x0bcf, - 0x2308 : 0x0bd3, - 0x22A3 : 0x0bdc, - 0x22A2 : 0x0bfc, - 0x2017 : 0x0cdf, - 0x05D0 : 0x0ce0, - 0x05D1 : 0x0ce1, - 0x05D2 : 0x0ce2, - 0x05D3 : 0x0ce3, - 0x05D4 : 0x0ce4, - 0x05D5 : 0x0ce5, - 0x05D6 : 0x0ce6, - 0x05D7 : 0x0ce7, - 0x05D8 : 0x0ce8, - 0x05D9 : 0x0ce9, - 0x05DA : 0x0cea, - 0x05DB : 0x0ceb, - 0x05DC : 0x0cec, - 0x05DD : 0x0ced, - 0x05DE : 0x0cee, - 0x05DF : 0x0cef, - 0x05E0 : 0x0cf0, - 0x05E1 : 0x0cf1, - 0x05E2 : 0x0cf2, - 0x05E3 : 0x0cf3, - 0x05E4 : 0x0cf4, - 0x05E5 : 0x0cf5, - 0x05E6 : 0x0cf6, - 0x05E7 : 0x0cf7, - 0x05E8 : 0x0cf8, - 0x05E9 : 0x0cf9, - 0x05EA : 0x0cfa, - 0x0E01 : 0x0da1, - 0x0E02 : 0x0da2, - 0x0E03 : 0x0da3, - 0x0E04 : 0x0da4, - 0x0E05 : 0x0da5, - 0x0E06 : 0x0da6, - 0x0E07 : 0x0da7, - 0x0E08 : 0x0da8, - 0x0E09 : 0x0da9, - 0x0E0A : 0x0daa, - 0x0E0B : 0x0dab, - 0x0E0C : 0x0dac, - 0x0E0D : 0x0dad, - 0x0E0E : 0x0dae, - 0x0E0F : 0x0daf, - 0x0E10 : 0x0db0, - 0x0E11 : 0x0db1, - 0x0E12 : 0x0db2, - 0x0E13 : 0x0db3, - 0x0E14 : 0x0db4, - 0x0E15 : 0x0db5, - 0x0E16 : 0x0db6, - 0x0E17 : 0x0db7, - 0x0E18 : 0x0db8, - 0x0E19 : 0x0db9, - 0x0E1A : 0x0dba, - 0x0E1B : 0x0dbb, - 0x0E1C : 0x0dbc, - 0x0E1D : 0x0dbd, - 0x0E1E : 0x0dbe, - 0x0E1F : 0x0dbf, - 0x0E20 : 0x0dc0, - 0x0E21 : 0x0dc1, - 0x0E22 : 0x0dc2, - 0x0E23 : 0x0dc3, - 0x0E24 : 0x0dc4, - 0x0E25 : 0x0dc5, - 0x0E26 : 0x0dc6, - 0x0E27 : 0x0dc7, - 0x0E28 : 0x0dc8, - 0x0E29 : 0x0dc9, - 0x0E2A : 0x0dca, - 0x0E2B : 0x0dcb, - 0x0E2C : 0x0dcc, - 0x0E2D : 0x0dcd, - 0x0E2E : 0x0dce, - 0x0E2F : 0x0dcf, - 0x0E30 : 0x0dd0, - 0x0E31 : 0x0dd1, - 0x0E32 : 0x0dd2, - 0x0E33 : 0x0dd3, - 0x0E34 : 0x0dd4, - 0x0E35 : 0x0dd5, - 0x0E36 : 0x0dd6, - 0x0E37 : 0x0dd7, - 0x0E38 : 0x0dd8, - 0x0E39 : 0x0dd9, - 0x0E3A : 0x0dda, - 0x0E3F : 0x0ddf, - 0x0E40 : 0x0de0, - 0x0E41 : 0x0de1, - 0x0E42 : 0x0de2, - 0x0E43 : 0x0de3, - 0x0E44 : 0x0de4, - 0x0E45 : 0x0de5, - 0x0E46 : 0x0de6, - 0x0E47 : 0x0de7, - 0x0E48 : 0x0de8, - 0x0E49 : 0x0de9, - 0x0E4A : 0x0dea, - 0x0E4B : 0x0deb, - 0x0E4C : 0x0dec, - 0x0E4D : 0x0ded, - 0x0E50 : 0x0df0, - 0x0E51 : 0x0df1, - 0x0E52 : 0x0df2, - 0x0E53 : 0x0df3, - 0x0E54 : 0x0df4, - 0x0E55 : 0x0df5, - 0x0E56 : 0x0df6, - 0x0E57 : 0x0df7, - 0x0E58 : 0x0df8, - 0x0E59 : 0x0df9, - 0x0587 : 0x1000587, - 0x0589 : 0x1000589, - 0x055D : 0x100055d, - 0x058A : 0x100058a, - 0x055C : 0x100055c, - 0x055B : 0x100055b, - 0x055E : 0x100055e, - 0x0531 : 0x1000531, - 0x0561 : 0x1000561, - 0x0532 : 0x1000532, - 0x0562 : 0x1000562, - 0x0533 : 0x1000533, - 0x0563 : 0x1000563, - 0x0534 : 0x1000534, - 0x0564 : 0x1000564, - 0x0535 : 0x1000535, - 0x0565 : 0x1000565, - 0x0536 : 0x1000536, - 0x0566 : 0x1000566, - 0x0537 : 0x1000537, - 0x0567 : 0x1000567, - 0x0538 : 0x1000538, - 0x0568 : 0x1000568, - 0x0539 : 0x1000539, - 0x0569 : 0x1000569, - 0x053A : 0x100053a, - 0x056A : 0x100056a, - 0x053B : 0x100053b, - 0x056B : 0x100056b, - 0x053C : 0x100053c, - 0x056C : 0x100056c, - 0x053D : 0x100053d, - 0x056D : 0x100056d, - 0x053E : 0x100053e, - 0x056E : 0x100056e, - 0x053F : 0x100053f, - 0x056F : 0x100056f, - 0x0540 : 0x1000540, - 0x0570 : 0x1000570, - 0x0541 : 0x1000541, - 0x0571 : 0x1000571, - 0x0542 : 0x1000542, - 0x0572 : 0x1000572, - 0x0543 : 0x1000543, - 0x0573 : 0x1000573, - 0x0544 : 0x1000544, - 0x0574 : 0x1000574, - 0x0545 : 0x1000545, - 0x0575 : 0x1000575, - 0x0546 : 0x1000546, - 0x0576 : 0x1000576, - 0x0547 : 0x1000547, - 0x0577 : 0x1000577, - 0x0548 : 0x1000548, - 0x0578 : 0x1000578, - 0x0549 : 0x1000549, - 0x0579 : 0x1000579, - 0x054A : 0x100054a, - 0x057A : 0x100057a, - 0x054B : 0x100054b, - 0x057B : 0x100057b, - 0x054C : 0x100054c, - 0x057C : 0x100057c, - 0x054D : 0x100054d, - 0x057D : 0x100057d, - 0x054E : 0x100054e, - 0x057E : 0x100057e, - 0x054F : 0x100054f, - 0x057F : 0x100057f, - 0x0550 : 0x1000550, - 0x0580 : 0x1000580, - 0x0551 : 0x1000551, - 0x0581 : 0x1000581, - 0x0552 : 0x1000552, - 0x0582 : 0x1000582, - 0x0553 : 0x1000553, - 0x0583 : 0x1000583, - 0x0554 : 0x1000554, - 0x0584 : 0x1000584, - 0x0555 : 0x1000555, - 0x0585 : 0x1000585, - 0x0556 : 0x1000556, - 0x0586 : 0x1000586, - 0x055A : 0x100055a, - 0x10D0 : 0x10010d0, - 0x10D1 : 0x10010d1, - 0x10D2 : 0x10010d2, - 0x10D3 : 0x10010d3, - 0x10D4 : 0x10010d4, - 0x10D5 : 0x10010d5, - 0x10D6 : 0x10010d6, - 0x10D7 : 0x10010d7, - 0x10D8 : 0x10010d8, - 0x10D9 : 0x10010d9, - 0x10DA : 0x10010da, - 0x10DB : 0x10010db, - 0x10DC : 0x10010dc, - 0x10DD : 0x10010dd, - 0x10DE : 0x10010de, - 0x10DF : 0x10010df, - 0x10E0 : 0x10010e0, - 0x10E1 : 0x10010e1, - 0x10E2 : 0x10010e2, - 0x10E3 : 0x10010e3, - 0x10E4 : 0x10010e4, - 0x10E5 : 0x10010e5, - 0x10E6 : 0x10010e6, - 0x10E7 : 0x10010e7, - 0x10E8 : 0x10010e8, - 0x10E9 : 0x10010e9, - 0x10EA : 0x10010ea, - 0x10EB : 0x10010eb, - 0x10EC : 0x10010ec, - 0x10ED : 0x10010ed, - 0x10EE : 0x10010ee, - 0x10EF : 0x10010ef, - 0x10F0 : 0x10010f0, - 0x10F1 : 0x10010f1, - 0x10F2 : 0x10010f2, - 0x10F3 : 0x10010f3, - 0x10F4 : 0x10010f4, - 0x10F5 : 0x10010f5, - 0x10F6 : 0x10010f6, - 0x1E8A : 0x1001e8a, - 0x012C : 0x100012c, - 0x01B5 : 0x10001b5, - 0x01E6 : 0x10001e6, - 0x01D2 : 0x10001d1, - 0x019F : 0x100019f, - 0x1E8B : 0x1001e8b, - 0x012D : 0x100012d, - 0x01B6 : 0x10001b6, - 0x01E7 : 0x10001e7, - 0x01D2 : 0x10001d2, - 0x0275 : 0x1000275, - 0x018F : 0x100018f, - 0x0259 : 0x1000259, - 0x1E36 : 0x1001e36, - 0x1E37 : 0x1001e37, - 0x1EA0 : 0x1001ea0, - 0x1EA1 : 0x1001ea1, - 0x1EA2 : 0x1001ea2, - 0x1EA3 : 0x1001ea3, - 0x1EA4 : 0x1001ea4, - 0x1EA5 : 0x1001ea5, - 0x1EA6 : 0x1001ea6, - 0x1EA7 : 0x1001ea7, - 0x1EA8 : 0x1001ea8, - 0x1EA9 : 0x1001ea9, - 0x1EAA : 0x1001eaa, - 0x1EAB : 0x1001eab, - 0x1EAC : 0x1001eac, - 0x1EAD : 0x1001ead, - 0x1EAE : 0x1001eae, - 0x1EAF : 0x1001eaf, - 0x1EB0 : 0x1001eb0, - 0x1EB1 : 0x1001eb1, - 0x1EB2 : 0x1001eb2, - 0x1EB3 : 0x1001eb3, - 0x1EB4 : 0x1001eb4, - 0x1EB5 : 0x1001eb5, - 0x1EB6 : 0x1001eb6, - 0x1EB7 : 0x1001eb7, - 0x1EB8 : 0x1001eb8, - 0x1EB9 : 0x1001eb9, - 0x1EBA : 0x1001eba, - 0x1EBB : 0x1001ebb, - 0x1EBC : 0x1001ebc, - 0x1EBD : 0x1001ebd, - 0x1EBE : 0x1001ebe, - 0x1EBF : 0x1001ebf, - 0x1EC0 : 0x1001ec0, - 0x1EC1 : 0x1001ec1, - 0x1EC2 : 0x1001ec2, - 0x1EC3 : 0x1001ec3, - 0x1EC4 : 0x1001ec4, - 0x1EC5 : 0x1001ec5, - 0x1EC6 : 0x1001ec6, - 0x1EC7 : 0x1001ec7, - 0x1EC8 : 0x1001ec8, - 0x1EC9 : 0x1001ec9, - 0x1ECA : 0x1001eca, - 0x1ECB : 0x1001ecb, - 0x1ECC : 0x1001ecc, - 0x1ECD : 0x1001ecd, - 0x1ECE : 0x1001ece, - 0x1ECF : 0x1001ecf, - 0x1ED0 : 0x1001ed0, - 0x1ED1 : 0x1001ed1, - 0x1ED2 : 0x1001ed2, - 0x1ED3 : 0x1001ed3, - 0x1ED4 : 0x1001ed4, - 0x1ED5 : 0x1001ed5, - 0x1ED6 : 0x1001ed6, - 0x1ED7 : 0x1001ed7, - 0x1ED8 : 0x1001ed8, - 0x1ED9 : 0x1001ed9, - 0x1EDA : 0x1001eda, - 0x1EDB : 0x1001edb, - 0x1EDC : 0x1001edc, - 0x1EDD : 0x1001edd, - 0x1EDE : 0x1001ede, - 0x1EDF : 0x1001edf, - 0x1EE0 : 0x1001ee0, - 0x1EE1 : 0x1001ee1, - 0x1EE2 : 0x1001ee2, - 0x1EE3 : 0x1001ee3, - 0x1EE4 : 0x1001ee4, - 0x1EE5 : 0x1001ee5, - 0x1EE6 : 0x1001ee6, - 0x1EE7 : 0x1001ee7, - 0x1EE8 : 0x1001ee8, - 0x1EE9 : 0x1001ee9, - 0x1EEA : 0x1001eea, - 0x1EEB : 0x1001eeb, - 0x1EEC : 0x1001eec, - 0x1EED : 0x1001eed, - 0x1EEE : 0x1001eee, - 0x1EEF : 0x1001eef, - 0x1EF0 : 0x1001ef0, - 0x1EF1 : 0x1001ef1, - 0x1EF4 : 0x1001ef4, - 0x1EF5 : 0x1001ef5, - 0x1EF6 : 0x1001ef6, - 0x1EF7 : 0x1001ef7, - 0x1EF8 : 0x1001ef8, - 0x1EF9 : 0x1001ef9, - 0x01A0 : 0x10001a0, - 0x01A1 : 0x10001a1, - 0x01AF : 0x10001af, - 0x01B0 : 0x10001b0, - 0x20A0 : 0x10020a0, - 0x20A1 : 0x10020a1, - 0x20A2 : 0x10020a2, - 0x20A3 : 0x10020a3, - 0x20A4 : 0x10020a4, - 0x20A5 : 0x10020a5, - 0x20A6 : 0x10020a6, - 0x20A7 : 0x10020a7, - 0x20A8 : 0x10020a8, - 0x20A9 : 0x10020a9, - 0x20AA : 0x10020aa, - 0x20AB : 0x10020ab, - 0x20AC : 0x20ac, - 0x2070 : 0x1002070, - 0x2074 : 0x1002074, - 0x2075 : 0x1002075, - 0x2076 : 0x1002076, - 0x2077 : 0x1002077, - 0x2078 : 0x1002078, - 0x2079 : 0x1002079, - 0x2080 : 0x1002080, - 0x2081 : 0x1002081, - 0x2082 : 0x1002082, - 0x2083 : 0x1002083, - 0x2084 : 0x1002084, - 0x2085 : 0x1002085, - 0x2086 : 0x1002086, - 0x2087 : 0x1002087, - 0x2088 : 0x1002088, - 0x2089 : 0x1002089, - 0x2202 : 0x1002202, - 0x2205 : 0x1002205, - 0x2208 : 0x1002208, - 0x2209 : 0x1002209, - 0x220B : 0x100220B, - 0x221A : 0x100221A, - 0x221B : 0x100221B, - 0x221C : 0x100221C, - 0x222C : 0x100222C, - 0x222D : 0x100222D, - 0x2235 : 0x1002235, - 0x2245 : 0x1002248, - 0x2247 : 0x1002247, - 0x2262 : 0x1002262, - 0x2263 : 0x1002263, - 0x2800 : 0x1002800, - 0x2801 : 0x1002801, - 0x2802 : 0x1002802, - 0x2803 : 0x1002803, - 0x2804 : 0x1002804, - 0x2805 : 0x1002805, - 0x2806 : 0x1002806, - 0x2807 : 0x1002807, - 0x2808 : 0x1002808, - 0x2809 : 0x1002809, - 0x280a : 0x100280a, - 0x280b : 0x100280b, - 0x280c : 0x100280c, - 0x280d : 0x100280d, - 0x280e : 0x100280e, - 0x280f : 0x100280f, - 0x2810 : 0x1002810, - 0x2811 : 0x1002811, - 0x2812 : 0x1002812, - 0x2813 : 0x1002813, - 0x2814 : 0x1002814, - 0x2815 : 0x1002815, - 0x2816 : 0x1002816, - 0x2817 : 0x1002817, - 0x2818 : 0x1002818, - 0x2819 : 0x1002819, - 0x281a : 0x100281a, - 0x281b : 0x100281b, - 0x281c : 0x100281c, - 0x281d : 0x100281d, - 0x281e : 0x100281e, - 0x281f : 0x100281f, - 0x2820 : 0x1002820, - 0x2821 : 0x1002821, - 0x2822 : 0x1002822, - 0x2823 : 0x1002823, - 0x2824 : 0x1002824, - 0x2825 : 0x1002825, - 0x2826 : 0x1002826, - 0x2827 : 0x1002827, - 0x2828 : 0x1002828, - 0x2829 : 0x1002829, - 0x282a : 0x100282a, - 0x282b : 0x100282b, - 0x282c : 0x100282c, - 0x282d : 0x100282d, - 0x282e : 0x100282e, - 0x282f : 0x100282f, - 0x2830 : 0x1002830, - 0x2831 : 0x1002831, - 0x2832 : 0x1002832, - 0x2833 : 0x1002833, - 0x2834 : 0x1002834, - 0x2835 : 0x1002835, - 0x2836 : 0x1002836, - 0x2837 : 0x1002837, - 0x2838 : 0x1002838, - 0x2839 : 0x1002839, - 0x283a : 0x100283a, - 0x283b : 0x100283b, - 0x283c : 0x100283c, - 0x283d : 0x100283d, - 0x283e : 0x100283e, - 0x283f : 0x100283f, - 0x2840 : 0x1002840, - 0x2841 : 0x1002841, - 0x2842 : 0x1002842, - 0x2843 : 0x1002843, - 0x2844 : 0x1002844, - 0x2845 : 0x1002845, - 0x2846 : 0x1002846, - 0x2847 : 0x1002847, - 0x2848 : 0x1002848, - 0x2849 : 0x1002849, - 0x284a : 0x100284a, - 0x284b : 0x100284b, - 0x284c : 0x100284c, - 0x284d : 0x100284d, - 0x284e : 0x100284e, - 0x284f : 0x100284f, - 0x2850 : 0x1002850, - 0x2851 : 0x1002851, - 0x2852 : 0x1002852, - 0x2853 : 0x1002853, - 0x2854 : 0x1002854, - 0x2855 : 0x1002855, - 0x2856 : 0x1002856, - 0x2857 : 0x1002857, - 0x2858 : 0x1002858, - 0x2859 : 0x1002859, - 0x285a : 0x100285a, - 0x285b : 0x100285b, - 0x285c : 0x100285c, - 0x285d : 0x100285d, - 0x285e : 0x100285e, - 0x285f : 0x100285f, - 0x2860 : 0x1002860, - 0x2861 : 0x1002861, - 0x2862 : 0x1002862, - 0x2863 : 0x1002863, - 0x2864 : 0x1002864, - 0x2865 : 0x1002865, - 0x2866 : 0x1002866, - 0x2867 : 0x1002867, - 0x2868 : 0x1002868, - 0x2869 : 0x1002869, - 0x286a : 0x100286a, - 0x286b : 0x100286b, - 0x286c : 0x100286c, - 0x286d : 0x100286d, - 0x286e : 0x100286e, - 0x286f : 0x100286f, - 0x2870 : 0x1002870, - 0x2871 : 0x1002871, - 0x2872 : 0x1002872, - 0x2873 : 0x1002873, - 0x2874 : 0x1002874, - 0x2875 : 0x1002875, - 0x2876 : 0x1002876, - 0x2877 : 0x1002877, - 0x2878 : 0x1002878, - 0x2879 : 0x1002879, - 0x287a : 0x100287a, - 0x287b : 0x100287b, - 0x287c : 0x100287c, - 0x287d : 0x100287d, - 0x287e : 0x100287e, - 0x287f : 0x100287f, - 0x2880 : 0x1002880, - 0x2881 : 0x1002881, - 0x2882 : 0x1002882, - 0x2883 : 0x1002883, - 0x2884 : 0x1002884, - 0x2885 : 0x1002885, - 0x2886 : 0x1002886, - 0x2887 : 0x1002887, - 0x2888 : 0x1002888, - 0x2889 : 0x1002889, - 0x288a : 0x100288a, - 0x288b : 0x100288b, - 0x288c : 0x100288c, - 0x288d : 0x100288d, - 0x288e : 0x100288e, - 0x288f : 0x100288f, - 0x2890 : 0x1002890, - 0x2891 : 0x1002891, - 0x2892 : 0x1002892, - 0x2893 : 0x1002893, - 0x2894 : 0x1002894, - 0x2895 : 0x1002895, - 0x2896 : 0x1002896, - 0x2897 : 0x1002897, - 0x2898 : 0x1002898, - 0x2899 : 0x1002899, - 0x289a : 0x100289a, - 0x289b : 0x100289b, - 0x289c : 0x100289c, - 0x289d : 0x100289d, - 0x289e : 0x100289e, - 0x289f : 0x100289f, - 0x28a0 : 0x10028a0, - 0x28a1 : 0x10028a1, - 0x28a2 : 0x10028a2, - 0x28a3 : 0x10028a3, - 0x28a4 : 0x10028a4, - 0x28a5 : 0x10028a5, - 0x28a6 : 0x10028a6, - 0x28a7 : 0x10028a7, - 0x28a8 : 0x10028a8, - 0x28a9 : 0x10028a9, - 0x28aa : 0x10028aa, - 0x28ab : 0x10028ab, - 0x28ac : 0x10028ac, - 0x28ad : 0x10028ad, - 0x28ae : 0x10028ae, - 0x28af : 0x10028af, - 0x28b0 : 0x10028b0, - 0x28b1 : 0x10028b1, - 0x28b2 : 0x10028b2, - 0x28b3 : 0x10028b3, - 0x28b4 : 0x10028b4, - 0x28b5 : 0x10028b5, - 0x28b6 : 0x10028b6, - 0x28b7 : 0x10028b7, - 0x28b8 : 0x10028b8, - 0x28b9 : 0x10028b9, - 0x28ba : 0x10028ba, - 0x28bb : 0x10028bb, - 0x28bc : 0x10028bc, - 0x28bd : 0x10028bd, - 0x28be : 0x10028be, - 0x28bf : 0x10028bf, - 0x28c0 : 0x10028c0, - 0x28c1 : 0x10028c1, - 0x28c2 : 0x10028c2, - 0x28c3 : 0x10028c3, - 0x28c4 : 0x10028c4, - 0x28c5 : 0x10028c5, - 0x28c6 : 0x10028c6, - 0x28c7 : 0x10028c7, - 0x28c8 : 0x10028c8, - 0x28c9 : 0x10028c9, - 0x28ca : 0x10028ca, - 0x28cb : 0x10028cb, - 0x28cc : 0x10028cc, - 0x28cd : 0x10028cd, - 0x28ce : 0x10028ce, - 0x28cf : 0x10028cf, - 0x28d0 : 0x10028d0, - 0x28d1 : 0x10028d1, - 0x28d2 : 0x10028d2, - 0x28d3 : 0x10028d3, - 0x28d4 : 0x10028d4, - 0x28d5 : 0x10028d5, - 0x28d6 : 0x10028d6, - 0x28d7 : 0x10028d7, - 0x28d8 : 0x10028d8, - 0x28d9 : 0x10028d9, - 0x28da : 0x10028da, - 0x28db : 0x10028db, - 0x28dc : 0x10028dc, - 0x28dd : 0x10028dd, - 0x28de : 0x10028de, - 0x28df : 0x10028df, - 0x28e0 : 0x10028e0, - 0x28e1 : 0x10028e1, - 0x28e2 : 0x10028e2, - 0x28e3 : 0x10028e3, - 0x28e4 : 0x10028e4, - 0x28e5 : 0x10028e5, - 0x28e6 : 0x10028e6, - 0x28e7 : 0x10028e7, - 0x28e8 : 0x10028e8, - 0x28e9 : 0x10028e9, - 0x28ea : 0x10028ea, - 0x28eb : 0x10028eb, - 0x28ec : 0x10028ec, - 0x28ed : 0x10028ed, - 0x28ee : 0x10028ee, - 0x28ef : 0x10028ef, - 0x28f0 : 0x10028f0, - 0x28f1 : 0x10028f1, - 0x28f2 : 0x10028f2, - 0x28f3 : 0x10028f3, - 0x28f4 : 0x10028f4, - 0x28f5 : 0x10028f5, - 0x28f6 : 0x10028f6, - 0x28f7 : 0x10028f7, - 0x28f8 : 0x10028f8, - 0x28f9 : 0x10028f9, - 0x28fa : 0x10028fa, - 0x28fb : 0x10028fb, - 0x28fc : 0x10028fc, - 0x28fd : 0x10028fd, - 0x28fe : 0x10028fe, - 0x28ff : 0x10028ff -}; \ No newline at end of file diff --git a/classes/novnc/include/logo.js b/classes/novnc/include/logo.js deleted file mode 100644 index befa598..0000000 --- a/classes/novnc/include/logo.js +++ /dev/null @@ -1 +0,0 @@ -noVNC_logo = {"width": 640, "height": 435, "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAGzCAYAAAC/y6a9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAStAAAErQBBHTWggAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N13fBvlwQfw3522ZMm2vPdIGCFkA4GyoYyGsCmjk+7dQksHL2/H2/dtC4W2tLTlfelu2VA2lEILFCgQIHEGJCQkdjzkLdmWZGvfvX8oOkmJEy/pNO73/Xz44DtLzz2RT7qfnnXC8uXLZUxDlqfdnUYQhIP+bjbPn+5xhypzrmUf6rGzOc5cjzVduXN9/nTPyfRrMt/jzOcY05U5n3L2f95s/34LPW4m/p6FbLp/73xe+5nKnWuZs/07ZOOcnusx5nucbJU727LneuxslDmdTBxn/2NmusyEuZS7kHMxG/XP5Gf3TOVmQzY+u/PhPMnkMcSsH5WIiIiI8goDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHG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"}; diff --git a/classes/novnc/include/playback.js b/classes/novnc/include/playback.js deleted file mode 100644 index 22a00a3..0000000 --- a/classes/novnc/include/playback.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.LGPL-3) - */ - -"use strict"; -/*jslint browser: true, white: false */ -/*global Util, VNC_frame_data, finish */ - -var rfb, mode, test_state, frame_idx, frame_length, - iteration, iterations, istart_time, - - // Pre-declarations for jslint - send_array, next_iteration, queue_next_packet, do_packet; - -// Override send_array -send_array = function (arr) { - // Stub out send_array -}; - -next_iteration = function () { - if (iteration === 0) { - frame_length = VNC_frame_data.length; - test_state = 'running'; - } else { - rfb.disconnect(); - } - - if (test_state !== 'running') { return; } - - iteration += 1; - if (iteration > iterations) { - finish(); - return; - } - - frame_idx = 0; - istart_time = (new Date()).getTime(); - rfb.connect('test', 0, "bogus"); - - queue_next_packet(); - -}; - -queue_next_packet = function () { - var frame, foffset, toffset, delay; - if (test_state !== 'running') { return; } - - frame = VNC_frame_data[frame_idx]; - while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) { - //Util.Debug("Send frame " + frame_idx); - frame_idx += 1; - frame = VNC_frame_data[frame_idx]; - } - - if (frame === 'EOF') { - Util.Debug("Finished, found EOF"); - next_iteration(); - return; - } - if (frame_idx >= frame_length) { - Util.Debug("Finished, no more frames"); - next_iteration(); - return; - } - - if (mode === 'realtime') { - foffset = frame.slice(1, frame.indexOf('{', 1)); - toffset = (new Date()).getTime() - istart_time; - delay = foffset - toffset; - if (delay < 1) { - delay = 1; - } - - setTimeout(do_packet, delay); - } else { - setTimeout(do_packet, 1); - } -}; - -do_packet = function () { - //Util.Debug("Processing frame: " + frame_idx); - var frame = VNC_frame_data[frame_idx]; - rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)}); - frame_idx += 1; - - queue_next_packet(); -}; - diff --git a/classes/novnc/include/rfb.js b/classes/novnc/include/rfb.js deleted file mode 100644 index b7aa3f6..0000000 --- a/classes/novnc/include/rfb.js +++ /dev/null @@ -1,1613 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/*jslint white: false, browser: true, bitwise: false, plusplus: false */ -/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */ - - -function RFB(defaults) { -"use strict"; - -var that = {}, // Public API methods - conf = {}, // Configuration attributes - - // Pre-declare private functions used before definitions (jslint) - init_vars, updateState, fail, handle_message, - init_msg, normal_msg, framebufferUpdate, print_stats, - - pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests, - keyEvent, pointerEvent, clientCutText, - - extract_data_uri, scan_tight_imgQ, - keyPress, mouseButton, mouseMove, - - checkEvents, // Overridable for testing - - - // - // Private RFB namespace variables - // - rfb_host = '', - rfb_port = 5900, - rfb_password = '', - rfb_path = '', - - rfb_state = 'disconnected', - rfb_version = 0, - rfb_max_version= 3.8, - rfb_auth_scheme= '', - - - // In preference order - encodings = [ - ['COPYRECT', 0x01 ], - ['TIGHT_PNG', -260 ], - ['HEXTILE', 0x05 ], - ['RRE', 0x02 ], - ['RAW', 0x00 ], - ['DesktopSize', -223 ], - ['Cursor', -239 ], - - // Psuedo-encoding settings - ['JPEG_quality_lo', -32 ], - //['JPEG_quality_hi', -23 ], - ['compress_lo', -255 ] - //['compress_hi', -247 ] - ], - - encHandlers = {}, - encNames = {}, - encStats = {}, // [rectCnt, rectCntTot] - - ws = null, // Websock object - display = null, // Display object - keyboard = null, // Keyboard input handler object - mouse = null, // Mouse input handler object - sendTimer = null, // Send Queue check timer - connTimer = null, // connection timer - disconnTimer = null, // disconnection timer - msgTimer = null, // queued handle_message timer - - // Frame buffer update state - FBU = { - rects : 0, - subrects : 0, // RRE - lines : 0, // RAW - tiles : 0, // HEXTILE - bytes : 0, - x : 0, - y : 0, - width : 0, - height : 0, - encoding : 0, - subencoding : -1, - background : null, - imgQ : [] // TIGHT_PNG image queue - }, - - fb_Bpp = 4, - fb_depth = 3, - fb_width = 0, - fb_height = 0, - fb_name = "", - - scan_imgQ_rate = 40, // 25 times per second or so - last_req_time = 0, - rre_chunk_sz = 100, - - timing = { - last_fbu : 0, - fbu_total : 0, - fbu_total_cnt : 0, - full_fbu_total : 0, - full_fbu_cnt : 0, - - fbu_rt_start : 0, - fbu_rt_total : 0, - fbu_rt_cnt : 0 - }, - - test_mode = false, - - def_con_timeout = Websock_native ? 2 : 5, - - /* Mouse state */ - mouse_buttonMask = 0, - mouse_arr = [], - viewportDragging = false, - viewportDragPos = {}; - -// Configuration attributes -Util.conf_defaults(conf, that, defaults, [ - ['target', 'wo', 'dom', null, 'VNC display rendering Canvas object'], - ['focusContainer', 'wo', 'dom', document, 'DOM element that captures keyboard input'], - - ['encrypt', 'rw', 'bool', false, 'Use TLS/SSL/wss encryption'], - ['true_color', 'rw', 'bool', true, 'Request true color pixel data'], - ['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'], - ['shared', 'rw', 'bool', true, 'Request shared mode'], - - ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'], - ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'], - - ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'], - - ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'], - ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'], - - // Callback functions - ['onUpdateState', 'rw', 'func', function() { }, - 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '], - ['onPasswordRequired', 'rw', 'func', function() { }, - 'onPasswordRequired(rfb): VNC password is required '], - ['onClipboard', 'rw', 'func', function() { }, - 'onClipboard(rfb, text): RFB clipboard contents received'], - ['onBell', 'rw', 'func', function() { }, - 'onBell(rfb): RFB Bell message received '], - ['onFBUReceive', 'rw', 'func', function() { }, - 'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '], - ['onFBUComplete', 'rw', 'func', function() { }, - 'onFBUComplete(rfb, fbu): RFB FBU received and processed '], - - // These callback names are deprecated - ['updateState', 'rw', 'func', function() { }, - 'obsolete, use onUpdateState'], - ['clipboardReceive', 'rw', 'func', function() { }, - 'obsolete, use onClipboard'] - ]); - - -// Override/add some specific configuration getters/setters -that.set_local_cursor = function(cursor) { - if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) { - conf.local_cursor = false; - } else { - if (display.get_cursor_uri()) { - conf.local_cursor = true; - } else { - Util.Warn("Browser does not support local cursor"); - } - } -}; - -// These are fake configuration getters -that.get_display = function() { return display; }; - -that.get_keyboard = function() { return keyboard; }; - -that.get_mouse = function() { return mouse; }; - - - -// -// Setup routines -// - -// Create the public API interface and initialize values that stay -// constant across connect/disconnect -function constructor() { - var i, rmode; - Util.Debug(">> RFB.constructor"); - - // Create lookup tables based encoding number - for (i=0; i < encodings.length; i+=1) { - encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]]; - encNames[encodings[i][1]] = encodings[i][0]; - encStats[encodings[i][1]] = [0, 0]; - } - // Initialize display, mouse, keyboard, and websock - try { - display = new Display({'target': conf.target}); - } catch (exc) { - Util.Error("Display exception: " + exc); - updateState('fatal', "No working Display"); - } - keyboard = new Keyboard({'target': conf.focusContainer, - 'onKeyPress': keyPress}); - mouse = new Mouse({'target': conf.target, - 'onMouseButton': mouseButton, - 'onMouseMove': mouseMove}); - - rmode = display.get_render_mode(); - - ws = new Websock(); - ws.on('message', handle_message); - ws.on('open', function() { - if (rfb_state === "connect") { - updateState('ProtocolVersion', "Starting VNC handshake"); - } else { - fail("Got unexpected WebSockets connection"); - } - }); - ws.on('close', function() { - if (rfb_state === 'disconnect') { - updateState('disconnected', 'VNC disconnected'); - } else if (rfb_state === 'ProtocolVersion') { - fail('Failed to connect to server'); - } else if (rfb_state in {'failed':1, 'disconnected':1}) { - Util.Error("Received onclose while disconnected"); - } else { - fail('Server disconnected'); - } - }); - ws.on('error', function(e) { - fail("WebSock error: " + e); - }); - - - init_vars(); - - /* Check web-socket-js if no builtin WebSocket support */ - if (Websock_native) { - Util.Info("Using native WebSockets"); - updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode); - } else { - Util.Warn("Using web-socket-js bridge. Flash version: " + - Util.Flash.version); - if ((! Util.Flash) || - (Util.Flash.version < 9)) { - updateState('fatal', "WebSockets or Adobe Flash<\/a> is required"); - } else if (document.location.href.substr(0, 7) === "file://") { - updateState('fatal', - "'file://' URL is incompatible with Adobe Flash"); - } else { - updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode); - } - } - - Util.Debug("<< RFB.constructor"); - return that; // Return the public API interface -} - -function connect() { - Util.Debug(">> RFB.connect"); - - var uri = ""; - if (conf.encrypt) { - uri = "wss://"; - } else { - uri = "ws://"; - } - uri += rfb_host + ":" + rfb_port + "/" + rfb_path; - Util.Info("connecting to " + uri); - ws.open(uri); - - Util.Debug("<< RFB.connect"); -} - -// Initialize variables that are reset before each connection -init_vars = function() { - var i; - - /* Reset state */ - ws.init(); - - FBU.rects = 0; - FBU.subrects = 0; // RRE and HEXTILE - FBU.lines = 0; // RAW - FBU.tiles = 0; // HEXTILE - FBU.imgQ = []; // TIGHT_PNG image queue - mouse_buttonMask = 0; - mouse_arr = []; - - // Clear the per connection encoding stats - for (i=0; i < encodings.length; i+=1) { - encStats[encodings[i][1]][0] = 0; - } -}; - -// Print statistics -print_stats = function() { - var i, s; - Util.Info("Encoding stats for this connection:"); - for (i=0; i < encodings.length; i+=1) { - s = encStats[encodings[i][1]]; - if ((s[0] + s[1]) > 0) { - Util.Info(" " + encodings[i][0] + ": " + - s[0] + " rects"); - } - } - Util.Info("Encoding stats since page load:"); - for (i=0; i < encodings.length; i+=1) { - s = encStats[encodings[i][1]]; - if ((s[0] + s[1]) > 0) { - Util.Info(" " + encodings[i][0] + ": " + - s[1] + " rects"); - } - } -}; - -// -// Utility routines -// - - -/* - * Page states: - * loaded - page load, equivalent to disconnected - * disconnected - idle state - * connect - starting to connect (to ProtocolVersion) - * normal - connected - * disconnect - starting to disconnect - * failed - abnormal disconnect - * fatal - failed to load page, or fatal error - * - * RFB protocol initialization states: - * ProtocolVersion - * Security - * Authentication - * password - waiting for password, not part of RFB - * SecurityResult - * ClientInitialization - not triggered by server message - * ServerInitialization (to normal) - */ -updateState = function(state, statusMsg) { - var func, cmsg, oldstate = rfb_state; - - if (state === oldstate) { - /* Already here, ignore */ - Util.Debug("Already in state '" + state + "', ignoring."); - return; - } - - /* - * These are disconnected states. A previous connect may - * asynchronously cause a connection so make sure we are closed. - */ - if (state in {'disconnected':1, 'loaded':1, 'connect':1, - 'disconnect':1, 'failed':1, 'fatal':1}) { - if (sendTimer) { - clearInterval(sendTimer); - sendTimer = null; - } - - if (msgTimer) { - clearInterval(msgTimer); - msgTimer = null; - } - - if (display && display.get_context()) { - keyboard.ungrab(); - mouse.ungrab(); - display.defaultCursor(); - if ((Util.get_logging() !== 'debug') || - (state === 'loaded')) { - // Show noVNC logo on load and when disconnected if - // debug is off - display.clear(); - } - } - - ws.close(); - } - - if (oldstate === 'fatal') { - Util.Error("Fatal error, cannot continue"); - } - - if ((state === 'failed') || (state === 'fatal')) { - func = Util.Error; - } else { - func = Util.Warn; - } - - if ((oldstate === 'failed') && (state === 'disconnected')) { - // Do disconnect action, but stay in failed state. - rfb_state = 'failed'; - } else { - rfb_state = state; - } - - cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : ""; - func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg); - - if (connTimer && (rfb_state !== 'connect')) { - Util.Debug("Clearing connect timer"); - clearInterval(connTimer); - connTimer = null; - } - - if (disconnTimer && (rfb_state !== 'disconnect')) { - Util.Debug("Clearing disconnect timer"); - clearInterval(disconnTimer); - disconnTimer = null; - } - - switch (state) { - case 'normal': - if ((oldstate === 'disconnected') || (oldstate === 'failed')) { - Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'"); - } - - break; - - - case 'connect': - - connTimer = setTimeout(function () { - fail("Connect timeout"); - }, conf.connectTimeout * 1000); - - init_vars(); - connect(); - - // WebSocket.onopen transitions to 'ProtocolVersion' - break; - - - case 'disconnect': - - if (! test_mode) { - disconnTimer = setTimeout(function () { - fail("Disconnect timeout"); - }, conf.disconnectTimeout * 1000); - } - - print_stats(); - - // WebSocket.onclose transitions to 'disconnected' - break; - - - case 'failed': - if (oldstate === 'disconnected') { - Util.Error("Invalid transition from 'disconnected' to 'failed'"); - } - if (oldstate === 'normal') { - Util.Error("Error while connected."); - } - if (oldstate === 'init') { - Util.Error("Error while initializing."); - } - - // Make sure we transition to disconnected - setTimeout(function() { updateState('disconnected'); }, 50); - - break; - - - default: - // No state change action to take - - } - - if ((oldstate === 'failed') && (state === 'disconnected')) { - // Leave the failed message - conf.updateState(that, state, oldstate); // Obsolete - conf.onUpdateState(that, state, oldstate); - } else { - conf.updateState(that, state, oldstate, statusMsg); // Obsolete - conf.onUpdateState(that, state, oldstate, statusMsg); - } -}; - -fail = function(msg) { - updateState('failed', msg); - return false; -}; - -handle_message = function() { - //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen()); - //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); - if (ws.rQlen() === 0) { - Util.Warn("handle_message called on empty receive queue"); - return; - } - switch (rfb_state) { - case 'disconnected': - case 'failed': - Util.Error("Got data while disconnected"); - break; - case 'normal': - if (normal_msg() && ws.rQlen() > 0) { - // true means we can continue processing - // Give other events a chance to run - if (msgTimer === null) { - Util.Debug("More data to process, creating timer"); - msgTimer = setTimeout(function () { - msgTimer = null; - handle_message(); - }, 10); - } else { - Util.Debug("More data to process, existing timer"); - } - } - break; - default: - init_msg(); - break; - } -}; - - -function genDES(password, challenge) { - var i, passwd = []; - for (i=0; i < password.length; i += 1) { - passwd.push(password.charCodeAt(i)); - } - return (new DES(passwd)).encrypt(challenge); -} - -function flushClient() { - if (mouse_arr.length > 0) { - //send(mouse_arr.concat(fbUpdateRequests())); - ws.send(mouse_arr); - setTimeout(function() { - ws.send(fbUpdateRequests()); - }, 50); - - mouse_arr = []; - return true; - } else { - return false; - } -} - -// overridable for testing -checkEvents = function() { - var now; - if (rfb_state === 'normal' && !viewportDragging) { - if (! flushClient()) { - now = new Date().getTime(); - if (now > last_req_time + conf.fbu_req_rate) { - last_req_time = now; - ws.send(fbUpdateRequests()); - } - } - } - setTimeout(checkEvents, conf.check_rate); -}; - -keyPress = function(keysym, down) { - var arr; - arr = keyEvent(keysym, down); - arr = arr.concat(fbUpdateRequests()); - ws.send(arr); -}; - -mouseButton = function(x, y, down, bmask) { - if (down) { - mouse_buttonMask |= bmask; - } else { - mouse_buttonMask ^= bmask; - } - - if (conf.viewportDrag) { - if (down && !viewportDragging) { - viewportDragging = true; - viewportDragPos = {'x': x, 'y': y}; - - // Skip sending mouse events - return; - } else { - viewportDragging = false; - } - } - - mouse_arr = mouse_arr.concat( - pointerEvent(display.absX(x), display.absY(y)) ); - flushClient(); -}; - -mouseMove = function(x, y) { - //Util.Debug('>> mouseMove ' + x + "," + y); - var deltaX, deltaY; - - if (viewportDragging) { - //deltaX = x - viewportDragPos.x; // drag viewport - deltaX = viewportDragPos.x - x; // drag frame buffer - //deltaY = y - viewportDragPos.y; // drag viewport - deltaY = viewportDragPos.y - y; // drag frame buffer - viewportDragPos = {'x': x, 'y': y}; - - display.viewportChange(deltaX, deltaY); - - // Skip sending mouse events - return; - } - - mouse_arr = mouse_arr.concat( - pointerEvent(display.absX(x), display.absY(y)) ); -}; - - -// -// Server message handlers -// - -// RFB/VNC initialisation message handler -init_msg = function() { - //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']"); - - var strlen, reason, length, sversion, cversion, - i, types, num_types, challenge, response, bpp, depth, - big_endian, red_max, green_max, blue_max, red_shift, - green_shift, blue_shift, true_color, name_length; - - //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0)); - switch (rfb_state) { - - case 'ProtocolVersion' : - if (ws.rQlen() < 12) { - return fail("Incomplete protocol version"); - } - sversion = ws.rQshiftStr(12).substr(4,7); - Util.Info("Server ProtocolVersion: " + sversion); - switch (sversion) { - case "003.003": rfb_version = 3.3; break; - case "003.006": rfb_version = 3.3; break; // UltraVNC - case "003.007": rfb_version = 3.7; break; - case "003.008": rfb_version = 3.8; break; - default: - return fail("Invalid server version " + sversion); - } - if (rfb_version > rfb_max_version) { - rfb_version = rfb_max_version; - } - - if (! test_mode) { - sendTimer = setInterval(function() { - // Send updates either at a rate of one update - // every 50ms, or whatever slower rate the network - // can handle. - ws.flush(); - }, 50); - } - - cversion = "00" + parseInt(rfb_version,10) + - ".00" + ((rfb_version * 10) % 10); - ws.send_string("RFB " + cversion + "\n"); - updateState('Security', "Sent ProtocolVersion: " + cversion); - break; - - case 'Security' : - if (rfb_version >= 3.7) { - // Server sends supported list, client decides - num_types = ws.rQshift8(); - if (ws.rQwait("security type", num_types, 1)) { return false; } - if (num_types === 0) { - strlen = ws.rQshift32(); - reason = ws.rQshiftStr(strlen); - return fail("Security failure: " + reason); - } - rfb_auth_scheme = 0; - types = ws.rQshiftBytes(num_types); - Util.Debug("Server security types: " + types); - for (i=0; i < types.length; i+=1) { - if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) { - rfb_auth_scheme = types[i]; - } - } - if (rfb_auth_scheme === 0) { - return fail("Unsupported security types: " + types); - } - - ws.send([rfb_auth_scheme]); - } else { - // Server decides - if (ws.rQwait("security scheme", 4)) { return false; } - rfb_auth_scheme = ws.rQshift32(); - } - updateState('Authentication', - "Authenticating using scheme: " + rfb_auth_scheme); - init_msg(); // Recursive fallthrough (workaround JSLint complaint) - break; - - // Triggered by fallthough, not by server message - case 'Authentication' : - //Util.Debug("Security auth scheme: " + rfb_auth_scheme); - switch (rfb_auth_scheme) { - case 0: // connection failed - if (ws.rQwait("auth reason", 4)) { return false; } - strlen = ws.rQshift32(); - reason = ws.rQshiftStr(strlen); - return fail("Auth failure: " + reason); - case 1: // no authentication - if (rfb_version >= 3.8) { - updateState('SecurityResult'); - return; - } - // Fall through to ClientInitialisation - break; - case 2: // VNC authentication - if (rfb_password.length === 0) { - // Notify via both callbacks since it is kind of - // a RFB state change and a UI interface issue. - updateState('password', "Password Required"); - conf.onPasswordRequired(that); - return; - } - if (ws.rQwait("auth challenge", 16)) { return false; } - challenge = ws.rQshiftBytes(16); - //Util.Debug("Password: " + rfb_password); - //Util.Debug("Challenge: " + challenge + - // " (" + challenge.length + ")"); - response = genDES(rfb_password, challenge); - //Util.Debug("Response: " + response + - // " (" + response.length + ")"); - - //Util.Debug("Sending DES encrypted auth response"); - ws.send(response); - updateState('SecurityResult'); - return; - default: - fail("Unsupported auth scheme: " + rfb_auth_scheme); - return; - } - updateState('ClientInitialisation', "No auth required"); - init_msg(); // Recursive fallthrough (workaround JSLint complaint) - break; - - case 'SecurityResult' : - if (ws.rQwait("VNC auth response ", 4)) { return false; } - switch (ws.rQshift32()) { - case 0: // OK - // Fall through to ClientInitialisation - break; - case 1: // failed - if (rfb_version >= 3.8) { - length = ws.rQshift32(); - if (ws.rQwait("SecurityResult reason", length, 8)) { - return false; - } - reason = ws.rQshiftStr(length); - fail(reason); - } else { - fail("Authentication failed"); - } - return; - case 2: // too-many - return fail("Too many auth attempts"); - } - updateState('ClientInitialisation', "Authentication OK"); - init_msg(); // Recursive fallthrough (workaround JSLint complaint) - break; - - // Triggered by fallthough, not by server message - case 'ClientInitialisation' : - ws.send([conf.shared ? 1 : 0]); // ClientInitialisation - updateState('ServerInitialisation', "Authentication OK"); - break; - - case 'ServerInitialisation' : - if (ws.rQwait("server initialization", 24)) { return false; } - - /* Screen size */ - fb_width = ws.rQshift16(); - fb_height = ws.rQshift16(); - - /* PIXEL_FORMAT */ - bpp = ws.rQshift8(); - depth = ws.rQshift8(); - big_endian = ws.rQshift8(); - true_color = ws.rQshift8(); - - red_max = ws.rQshift16(); - green_max = ws.rQshift16(); - blue_max = ws.rQshift16(); - red_shift = ws.rQshift8(); - green_shift = ws.rQshift8(); - blue_shift = ws.rQshift8(); - ws.rQshiftStr(3); // padding - - Util.Info("Screen: " + fb_width + "x" + fb_height + - ", bpp: " + bpp + ", depth: " + depth + - ", big_endian: " + big_endian + - ", true_color: " + true_color + - ", red_max: " + red_max + - ", green_max: " + green_max + - ", blue_max: " + blue_max + - ", red_shift: " + red_shift + - ", green_shift: " + green_shift + - ", blue_shift: " + blue_shift); - - /* Connection name/title */ - name_length = ws.rQshift32(); - fb_name = ws.rQshiftStr(name_length); - - display.set_true_color(conf.true_color); - display.resize(fb_width, fb_height); - keyboard.grab(); - mouse.grab(); - - if (conf.true_color) { - fb_Bpp = 4; - fb_depth = 3; - } else { - fb_Bpp = 1; - fb_depth = 1; - } - - response = pixelFormat(); - response = response.concat(clientEncodings()); - response = response.concat(fbUpdateRequests()); - timing.fbu_rt_start = (new Date()).getTime(); - ws.send(response); - - /* Start pushing/polling */ - setTimeout(checkEvents, conf.check_rate); - setTimeout(scan_tight_imgQ, scan_imgQ_rate); - - if (conf.encrypt) { - updateState('normal', "Connected (encrypted) to: " + fb_name); - } else { - updateState('normal', "Connected (unencrypted) to: " + fb_name); - } - break; - } - //Util.Debug("<< init_msg"); -}; - - -/* Normal RFB/VNC server message handler */ -normal_msg = function() { - //Util.Debug(">> normal_msg"); - - var ret = true, msg_type, length, text, - c, first_colour, num_colours, red, green, blue; - - if (FBU.rects > 0) { - msg_type = 0; - } else { - msg_type = ws.rQshift8(); - } - switch (msg_type) { - case 0: // FramebufferUpdate - ret = framebufferUpdate(); // false means need more data - break; - case 1: // SetColourMapEntries - Util.Debug("SetColourMapEntries"); - ws.rQshift8(); // Padding - first_colour = ws.rQshift16(); // First colour - num_colours = ws.rQshift16(); - for (c=0; c < num_colours; c+=1) { - red = ws.rQshift16(); - //Util.Debug("red before: " + red); - red = parseInt(red / 256, 10); - //Util.Debug("red after: " + red); - green = parseInt(ws.rQshift16() / 256, 10); - blue = parseInt(ws.rQshift16() / 256, 10); - display.set_colourMap([red, green, blue], first_colour + c); - } - Util.Debug("colourMap: " + display.get_colourMap()); - Util.Info("Registered " + num_colours + " colourMap entries"); - //Util.Debug("colourMap: " + display.get_colourMap()); - break; - case 2: // Bell - Util.Debug("Bell"); - conf.onBell(that); - break; - case 3: // ServerCutText - Util.Debug("ServerCutText"); - if (ws.rQwait("ServerCutText header", 7, 1)) { return false; } - ws.rQshiftBytes(3); // Padding - length = ws.rQshift32(); - if (ws.rQwait("ServerCutText", length, 8)) { return false; } - - text = ws.rQshiftStr(length); - conf.clipboardReceive(that, text); // Obsolete - conf.onClipboard(that, text); - break; - default: - fail("Disconnected: illegal server message type " + msg_type); - Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); - break; - } - //Util.Debug("<< normal_msg"); - return ret; -}; - -framebufferUpdate = function() { - var now, hdr, fbu_rt_diff, ret = true; - - if (FBU.rects === 0) { - //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20)); - if (ws.rQwait("FBU header", 3)) { - ws.rQunshift8(0); // FBU msg_type - return false; - } - ws.rQshift8(); // padding - FBU.rects = ws.rQshift16(); - //Util.Debug("FramebufferUpdate, rects:" + FBU.rects); - FBU.bytes = 0; - timing.cur_fbu = 0; - if (timing.fbu_rt_start > 0) { - now = (new Date()).getTime(); - Util.Info("First FBU latency: " + (now - timing.fbu_rt_start)); - } - } - - while (FBU.rects > 0) { - if (rfb_state !== "normal") { - return false; - } - if (ws.rQwait("FBU", FBU.bytes)) { return false; } - if (FBU.bytes === 0) { - if (ws.rQwait("rect header", 12)) { return false; } - /* New FramebufferUpdate */ - - hdr = ws.rQshiftBytes(12); - FBU.x = (hdr[0] << 8) + hdr[1]; - FBU.y = (hdr[2] << 8) + hdr[3]; - FBU.width = (hdr[4] << 8) + hdr[5]; - FBU.height = (hdr[6] << 8) + hdr[7]; - FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) + - (hdr[10] << 8) + hdr[11], 10); - - conf.onFBUReceive(that, - {'x': FBU.x, 'y': FBU.y, - 'width': FBU.width, 'height': FBU.height, - 'encoding': FBU.encoding, - 'encodingName': encNames[FBU.encoding]}); - - if (encNames[FBU.encoding]) { - // Debug: - /* - var msg = "FramebufferUpdate rects:" + FBU.rects; - msg += " x: " + FBU.x + " y: " + FBU.y; - msg += " width: " + FBU.width + " height: " + FBU.height; - msg += " encoding:" + FBU.encoding; - msg += "(" + encNames[FBU.encoding] + ")"; - msg += ", ws.rQlen(): " + ws.rQlen(); - Util.Debug(msg); - */ - } else { - fail("Disconnected: unsupported encoding " + - FBU.encoding); - return false; - } - } - - timing.last_fbu = (new Date()).getTime(); - - ret = encHandlers[FBU.encoding](); - - now = (new Date()).getTime(); - timing.cur_fbu += (now - timing.last_fbu); - - if (ret) { - encStats[FBU.encoding][0] += 1; - encStats[FBU.encoding][1] += 1; - } - - if (FBU.rects === 0) { - if (((FBU.width === fb_width) && - (FBU.height === fb_height)) || - (timing.fbu_rt_start > 0)) { - timing.full_fbu_total += timing.cur_fbu; - timing.full_fbu_cnt += 1; - Util.Info("Timing of full FBU, cur: " + - timing.cur_fbu + ", total: " + - timing.full_fbu_total + ", cnt: " + - timing.full_fbu_cnt + ", avg: " + - (timing.full_fbu_total / - timing.full_fbu_cnt)); - } - if (timing.fbu_rt_start > 0) { - fbu_rt_diff = now - timing.fbu_rt_start; - timing.fbu_rt_total += fbu_rt_diff; - timing.fbu_rt_cnt += 1; - Util.Info("full FBU round-trip, cur: " + - fbu_rt_diff + ", total: " + - timing.fbu_rt_total + ", cnt: " + - timing.fbu_rt_cnt + ", avg: " + - (timing.fbu_rt_total / - timing.fbu_rt_cnt)); - timing.fbu_rt_start = 0; - } - } - if (! ret) { - return ret; // false ret means need more data - } - } - - conf.onFBUComplete(that, - {'x': FBU.x, 'y': FBU.y, - 'width': FBU.width, 'height': FBU.height, - 'encoding': FBU.encoding, - 'encodingName': encNames[FBU.encoding]}); - - return true; // We finished this FBU -}; - -// -// FramebufferUpdate encodings -// - -encHandlers.RAW = function display_raw() { - //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)"); - - var cur_y, cur_height; - - if (FBU.lines === 0) { - FBU.lines = FBU.height; - } - FBU.bytes = FBU.width * fb_Bpp; // At least a line - if (ws.rQwait("RAW", FBU.bytes)) { return false; } - cur_y = FBU.y + (FBU.height - FBU.lines); - cur_height = Math.min(FBU.lines, - Math.floor(ws.rQlen()/(FBU.width * fb_Bpp))); - display.blitImage(FBU.x, cur_y, FBU.width, cur_height, - ws.get_rQ(), ws.get_rQi()); - ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp); - FBU.lines -= cur_height; - - if (FBU.lines > 0) { - FBU.bytes = FBU.width * fb_Bpp; // At least another line - } else { - FBU.rects -= 1; - FBU.bytes = 0; - } - //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)"); - return true; -}; - -encHandlers.COPYRECT = function display_copy_rect() { - //Util.Debug(">> display_copy_rect"); - - var old_x, old_y; - - if (ws.rQwait("COPYRECT", 4)) { return false; } - old_x = ws.rQshift16(); - old_y = ws.rQshift16(); - display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height); - FBU.rects -= 1; - FBU.bytes = 0; - return true; -}; - -encHandlers.RRE = function display_rre() { - //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)"); - var color, x, y, width, height, chunk; - - if (FBU.subrects === 0) { - if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; } - FBU.subrects = ws.rQshift32(); - color = ws.rQshiftBytes(fb_Bpp); // Background - display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); - } - while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) { - color = ws.rQshiftBytes(fb_Bpp); - x = ws.rQshift16(); - y = ws.rQshift16(); - width = ws.rQshift16(); - height = ws.rQshift16(); - display.fillRect(FBU.x + x, FBU.y + y, width, height, color); - FBU.subrects -= 1; - } - //Util.Debug(" display_rre: rects: " + FBU.rects + - // ", FBU.subrects: " + FBU.subrects); - - if (FBU.subrects > 0) { - chunk = Math.min(rre_chunk_sz, FBU.subrects); - FBU.bytes = (fb_Bpp + 8) * chunk; - } else { - FBU.rects -= 1; - FBU.bytes = 0; - } - //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes); - return true; -}; - -encHandlers.HEXTILE = function display_hextile() { - //Util.Debug(">> display_hextile"); - var subencoding, subrects, color, cur_tile, - tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh, - rQ = ws.get_rQ(), rQi = ws.get_rQi(); - - if (FBU.tiles === 0) { - FBU.tiles_x = Math.ceil(FBU.width/16); - FBU.tiles_y = Math.ceil(FBU.height/16); - FBU.total_tiles = FBU.tiles_x * FBU.tiles_y; - FBU.tiles = FBU.total_tiles; - } - - /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */ - while (FBU.tiles > 0) { - FBU.bytes = 1; - if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; } - subencoding = rQ[rQi]; // Peek - if (subencoding > 30) { // Raw - fail("Disconnected: illegal hextile subencoding " + subencoding); - //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); - return false; - } - subrects = 0; - cur_tile = FBU.total_tiles - FBU.tiles; - tile_x = cur_tile % FBU.tiles_x; - tile_y = Math.floor(cur_tile / FBU.tiles_x); - x = FBU.x + tile_x * 16; - y = FBU.y + tile_y * 16; - w = Math.min(16, (FBU.x + FBU.width) - x); - h = Math.min(16, (FBU.y + FBU.height) - y); - - /* Figure out how much we are expecting */ - if (subencoding & 0x01) { // Raw - //Util.Debug(" Raw subencoding"); - FBU.bytes += w * h * fb_Bpp; - } else { - if (subencoding & 0x02) { // Background - FBU.bytes += fb_Bpp; - } - if (subencoding & 0x04) { // Foreground - FBU.bytes += fb_Bpp; - } - if (subencoding & 0x08) { // AnySubrects - FBU.bytes += 1; // Since we aren't shifting it off - if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; } - subrects = rQ[rQi + FBU.bytes-1]; // Peek - if (subencoding & 0x10) { // SubrectsColoured - FBU.bytes += subrects * (fb_Bpp + 2); - } else { - FBU.bytes += subrects * 2; - } - } - } - - /* - Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + - " (" + tile_x + "," + tile_y + ")" + - " [" + x + "," + y + "]@" + w + "x" + h + - ", subenc:" + subencoding + - "(last: " + FBU.lastsubencoding + "), subrects:" + - subrects + - ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes + - " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) + - " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10)); - */ - if (ws.rQwait("hextile", FBU.bytes)) { return false; } - - /* We know the encoding and have a whole tile */ - FBU.subencoding = rQ[rQi]; - rQi += 1; - if (FBU.subencoding === 0) { - if (FBU.lastsubencoding & 0x01) { - /* Weird: ignore blanks after RAW */ - Util.Debug(" Ignoring blank after RAW"); - } else { - display.fillRect(x, y, w, h, FBU.background); - } - } else if (FBU.subencoding & 0x01) { // Raw - display.blitImage(x, y, w, h, rQ, rQi); - rQi += FBU.bytes - 1; - } else { - if (FBU.subencoding & 0x02) { // Background - FBU.background = rQ.slice(rQi, rQi + fb_Bpp); - rQi += fb_Bpp; - } - if (FBU.subencoding & 0x04) { // Foreground - FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp); - rQi += fb_Bpp; - } - - display.startTile(x, y, w, h, FBU.background); - if (FBU.subencoding & 0x08) { // AnySubrects - subrects = rQ[rQi]; - rQi += 1; - for (s = 0; s < subrects; s += 1) { - if (FBU.subencoding & 0x10) { // SubrectsColoured - color = rQ.slice(rQi, rQi + fb_Bpp); - rQi += fb_Bpp; - } else { - color = FBU.foreground; - } - xy = rQ[rQi]; - rQi += 1; - sx = (xy >> 4); - sy = (xy & 0x0f); - - wh = rQ[rQi]; - rQi += 1; - sw = (wh >> 4) + 1; - sh = (wh & 0x0f) + 1; - - display.subTile(sx, sy, sw, sh, color); - } - } - display.finishTile(); - } - ws.set_rQi(rQi); - FBU.lastsubencoding = FBU.subencoding; - FBU.bytes = 0; - FBU.tiles -= 1; - } - - if (FBU.tiles === 0) { - FBU.rects -= 1; - } - - //Util.Debug("<< display_hextile"); - return true; -}; - - -encHandlers.TIGHT_PNG = function display_tight_png() { - //Util.Debug(">> display_tight_png"); - var ctl, cmode, clength, getCLength, color, img; - //Util.Debug(" FBU.rects: " + FBU.rects); - //Util.Debug(" starting ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); - - FBU.bytes = 1; // compression-control byte - if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; } - - // Get 'compact length' header and data size - getCLength = function (arr) { - var header = 1, data = 0; - data += arr[0] & 0x7f; - if (arr[0] & 0x80) { - header += 1; - data += (arr[1] & 0x7f) << 7; - if (arr[1] & 0x80) { - header += 1; - data += arr[2] << 14; - } - } - return [header, data]; - }; - - ctl = ws.rQpeek8(); - switch (ctl >> 4) { - case 0x08: cmode = "fill"; break; - case 0x09: cmode = "jpeg"; break; - case 0x0A: cmode = "png"; break; - default: throw("Illegal basic compression received, ctl: " + ctl); - } - switch (cmode) { - // fill uses fb_depth because TPIXELs drop the padding byte - case "fill": FBU.bytes += fb_depth; break; // TPIXEL - case "jpeg": FBU.bytes += 3; break; // max clength - case "png": FBU.bytes += 3; break; // max clength - } - - if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } - - //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); - //Util.Debug(" cmode: " + cmode); - - // Determine FBU.bytes - switch (cmode) { - case "fill": - ws.rQshift8(); // shift off ctl - color = ws.rQshiftBytes(fb_depth); - FBU.imgQ.push({ - 'type': 'fill', - 'img': {'complete': true}, - 'x': FBU.x, - 'y': FBU.y, - 'width': FBU.width, - 'height': FBU.height, - 'color': color}); - break; - case "jpeg": - case "png": - clength = getCLength(ws.rQslice(1, 4)); - FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data - if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } - - // We have everything, render it - //Util.Debug(" png, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]); - ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length - img = new Image(); - //img.onload = scan_tight_imgQ; - FBU.imgQ.push({ - 'type': 'img', - 'img': img, - 'x': FBU.x, - 'y': FBU.y}); - img.src = "data:image/" + cmode + - extract_data_uri(ws.rQshiftBytes(clength[1])); - img = null; - break; - } - FBU.bytes = 0; - FBU.rects -= 1; - //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); - //Util.Debug("<< display_tight_png"); - return true; -}; - -extract_data_uri = function(arr) { - //var i, stra = []; - //for (i=0; i< arr.length; i += 1) { - // stra.push(String.fromCharCode(arr[i])); - //} - //return "," + escape(stra.join('')); - return ";base64," + Base64.encode(arr); -}; - -scan_tight_imgQ = function() { - var data, imgQ, ctx; - ctx = display.get_context(); - if (rfb_state === 'normal') { - imgQ = FBU.imgQ; - while ((imgQ.length > 0) && (imgQ[0].img.complete)) { - data = imgQ.shift(); - if (data['type'] === 'fill') { - display.fillRect(data.x, data.y, data.width, data.height, data.color); - } else { - ctx.drawImage(data.img, data.x, data.y); - } - } - setTimeout(scan_tight_imgQ, scan_imgQ_rate); - } -}; - -encHandlers.DesktopSize = function set_desktopsize() { - Util.Debug(">> set_desktopsize"); - fb_width = FBU.width; - fb_height = FBU.height; - display.resize(fb_width, fb_height); - timing.fbu_rt_start = (new Date()).getTime(); - // Send a new non-incremental request - ws.send(fbUpdateRequests()); - - FBU.bytes = 0; - FBU.rects -= 1; - - Util.Debug("<< set_desktopsize"); - return true; -}; - -encHandlers.Cursor = function set_cursor() { - var x, y, w, h, pixelslength, masklength; - //Util.Debug(">> set_cursor"); - x = FBU.x; // hotspot-x - y = FBU.y; // hotspot-y - w = FBU.width; - h = FBU.height; - - pixelslength = w * h * fb_Bpp; - masklength = Math.floor((w + 7) / 8) * h; - - FBU.bytes = pixelslength + masklength; - if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; } - - //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h); - - display.changeCursor(ws.rQshiftBytes(pixelslength), - ws.rQshiftBytes(masklength), - x, y, w, h); - - FBU.bytes = 0; - FBU.rects -= 1; - - //Util.Debug("<< set_cursor"); - return true; -}; - -encHandlers.JPEG_quality_lo = function set_jpeg_quality() { - Util.Error("Server sent jpeg_quality pseudo-encoding"); -}; - -encHandlers.compress_lo = function set_compress_level() { - Util.Error("Server sent compress level pseudo-encoding"); -}; - -/* - * Client message routines - */ - -pixelFormat = function() { - //Util.Debug(">> pixelFormat"); - var arr; - arr = [0]; // msg-type - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding - - arr.push8(fb_Bpp * 8); // bits-per-pixel - arr.push8(fb_depth * 8); // depth - arr.push8(0); // little-endian - arr.push8(conf.true_color ? 1 : 0); // true-color - - arr.push16(255); // red-max - arr.push16(255); // green-max - arr.push16(255); // blue-max - arr.push8(0); // red-shift - arr.push8(8); // green-shift - arr.push8(16); // blue-shift - - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding - //Util.Debug("<< pixelFormat"); - return arr; -}; - -clientEncodings = function() { - //Util.Debug(">> clientEncodings"); - var arr, i, encList = []; - - for (i=0; i> fbUpdateRequest"); - if (typeof(x) === "undefined") { x = 0; } - if (typeof(y) === "undefined") { y = 0; } - if (typeof(xw) === "undefined") { xw = fb_width; } - if (typeof(yw) === "undefined") { yw = fb_height; } - var arr; - arr = [3]; // msg-type - arr.push8(incremental); - arr.push16(x); - arr.push16(y); - arr.push16(xw); - arr.push16(yw); - //Util.Debug("<< fbUpdateRequest"); - return arr; -}; - -// Based on clean/dirty areas, generate requests to send -fbUpdateRequests = function() { - var cleanDirty = display.getCleanDirtyReset(), - arr = [], i, cb, db; - - cb = cleanDirty.cleanBox; - if (cb.w > 0 && cb.h > 0) { - // Request incremental for clean box - arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h)); - } - for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) { - db = cleanDirty.dirtyBoxes[i]; - // Force all (non-incremental for dirty box - arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h)); - } - return arr; -}; - - - -keyEvent = function(keysym, down) { - //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down); - var arr; - arr = [4]; // msg-type - arr.push8(down); - arr.push16(0); - arr.push32(keysym); - //Util.Debug("<< keyEvent"); - return arr; -}; - -pointerEvent = function(x, y) { - //Util.Debug(">> pointerEvent, x,y: " + x + "," + y + - // " , mask: " + mouse_buttonMask); - var arr; - arr = [5]; // msg-type - arr.push8(mouse_buttonMask); - arr.push16(x); - arr.push16(y); - //Util.Debug("<< pointerEvent"); - return arr; -}; - -clientCutText = function(text) { - //Util.Debug(">> clientCutText"); - var arr, i, n; - arr = [6]; // msg-type - arr.push8(0); // padding - arr.push8(0); // padding - arr.push8(0); // padding - arr.push32(text.length); - n = text.length; - for (i=0; i < n; i+=1) { - arr.push(text.charCodeAt(i)); - } - //Util.Debug("<< clientCutText:" + arr); - return arr; -}; - - - -// -// Public API interface functions -// - -that.connect = function(host, port, password, path) { - //Util.Debug(">> connect"); - - rfb_host = host; - rfb_port = port; - rfb_password = (password !== undefined) ? password : ""; - rfb_path = (path !== undefined) ? path : ""; - - if ((!rfb_host) || (!rfb_port)) { - return fail("Must set host and port"); - } - - updateState('connect'); - //Util.Debug("<< connect"); - -}; - -that.disconnect = function() { - //Util.Debug(">> disconnect"); - updateState('disconnect', 'Disconnecting'); - //Util.Debug("<< disconnect"); -}; - -that.sendPassword = function(passwd) { - rfb_password = passwd; - rfb_state = "Authentication"; - setTimeout(init_msg, 1); -}; - -that.sendCtrlAltDel = function() { - if (rfb_state !== "normal") { return false; } - Util.Info("Sending Ctrl-Alt-Del"); - var arr = []; - arr = arr.concat(keyEvent(0xFFE3, 1)); // Control - arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt - arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete - arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete - arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt - arr = arr.concat(keyEvent(0xFFE3, 0)); // Control - arr = arr.concat(fbUpdateRequests()); - ws.send(arr); -}; - -// Send a key press. If 'down' is not specified then send a down key -// followed by an up key. -that.sendKey = function(code, down) { - if (rfb_state !== "normal") { return false; } - var arr = []; - if (typeof down !== 'undefined') { - Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code); - arr = arr.concat(keyEvent(code, down ? 1 : 0)); - } else { - Util.Info("Sending key code (down + up): " + code); - arr = arr.concat(keyEvent(code, 1)); - arr = arr.concat(keyEvent(code, 0)); - } - arr = arr.concat(fbUpdateRequests()); - ws.send(arr); -}; - -that.clipboardPasteFrom = function(text) { - if (rfb_state !== "normal") { return; } - //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "..."); - ws.send(clientCutText(text)); - //Util.Debug("<< clipboardPasteFrom"); -}; - -// Override internal functions for testing -that.testMode = function(override_send) { - test_mode = true; - that.recv_message = ws.testMode(override_send); - - checkEvents = function () { /* Stub Out */ }; - that.connect = function(host, port, password) { - rfb_host = host; - rfb_port = port; - rfb_password = password; - updateState('ProtocolVersion', "Starting VNC handshake"); - }; -}; - - -return constructor(); // Return the public API interface - -} // End of RFB() diff --git a/classes/novnc/include/ui.js b/classes/novnc/include/ui.js deleted file mode 100644 index 74a0005..0000000 --- a/classes/novnc/include/ui.js +++ /dev/null @@ -1,629 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -"use strict"; -/*jslint white: false, browser: true */ -/*global window, $D, Util, WebUtil, RFB, Display */ - -var UI = { - -rfb_state : 'loaded', -settingsOpen : false, -connSettingsOpen : true, -clipboardOpen: false, -keyboardVisible: false, - -// Render default UI and initialize settings menu -load: function() { - var html = '', i, sheet, sheets, llevels; - - // Stylesheet selection dropdown - sheet = WebUtil.selectStylesheet(); - sheets = WebUtil.getStylesheets(); - for (i = 0; i < sheets.length; i += 1) { - UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title); - } - - // Logging selection dropdown - llevels = ['error', 'warn', 'info', 'debug']; - for (i = 0; i < llevels.length; i += 1) { - UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]); - } - - // Settings with immediate effects - UI.initSetting('logging', 'warn'); - WebUtil.init_logging(UI.getSetting('logging')); - - UI.initSetting('stylesheet', 'default'); - WebUtil.selectStylesheet(null); - // call twice to get around webkit bug - WebUtil.selectStylesheet(UI.getSetting('stylesheet')); - - /* Populate the controls if defaults are provided in the URL */ - UI.initSetting('host', ''); - UI.initSetting('port', ''); - UI.initSetting('password', ''); - UI.initSetting('encrypt', false); - UI.initSetting('true_color', true); - UI.initSetting('cursor', false); - UI.initSetting('shared', true); - UI.initSetting('connectTimeout', 2); - UI.initSetting('path', ''); - - UI.rfb = RFB({'target': $D('noVNC_canvas'), - 'onUpdateState': UI.updateState, - 'onClipboard': UI.clipReceive}); - UI.updateVisualState(); - - // Unfocus clipboard when over the VNC area - //$D('VNC_screen').onmousemove = function () { - // var keyboard = UI.rfb.get_keyboard(); - // if ((! keyboard) || (! keyboard.get_focused())) { - // $D('VNC_clipboard_text').blur(); - // } - // }; - - // Show mouse selector buttons on touch screen devices - if ('ontouchstart' in document.documentElement) { - // Show mobile buttons - $D('noVNC_mobile_buttons').style.display = "inline"; - UI.setMouseButton(); - // Remove the address bar - setTimeout(function() { window.scrollTo(0, 1); }, 100); - UI.forceSetting('clip', true); - $D('noVNC_clip').disabled = true; - } else { - UI.initSetting('clip', false); - } - - //iOS Safari does not support CSS position:fixed. - //This detects iOS devices and enables javascript workaround. - if ((navigator.userAgent.match(/iPhone/i)) || - (navigator.userAgent.match(/iPod/i)) || - (navigator.userAgent.match(/iPad/i))) { - //UI.setOnscroll(); - //UI.setResize(); - } - - $D('noVNC_host').focus(); - - UI.setViewClip(); - Util.addEvent(window, 'resize', UI.setViewClip); - - Util.addEvent(window, 'beforeunload', function () { - if (UI.rfb_state === 'normal') { - return "You are currently connected."; - } - } ); - -}, - -// Read form control compatible setting from cookie -getSetting: function(name) { - var val, ctrl = $D('noVNC_' + name); - val = WebUtil.readCookie(name); - if (ctrl.type === 'checkbox') { - if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) { - val = false; - } else { - val = true; - } - } - return val; -}, - -// Update cookie and form control setting. If value is not set, then -// updates from control to current cookie setting. -updateSetting: function(name, value) { - - var i, ctrl = $D('noVNC_' + name); - // Save the cookie for this session - if (typeof value !== 'undefined') { - WebUtil.createCookie(name, value); - } - - // Update the settings control - value = UI.getSetting(name); - - if (ctrl.type === 'checkbox') { - ctrl.checked = value; - - } else if (typeof ctrl.options !== 'undefined') { - for (i = 0; i < ctrl.options.length; i += 1) { - if (ctrl.options[i].value === value) { - ctrl.selectedIndex = i; - break; - } - } - } else { - /*Weird IE9 error leads to 'null' appearring - in textboxes instead of ''.*/ - if (value === null) { - value = ""; - } - ctrl.value = value; - } -}, - -// Save control setting to cookie -saveSetting: function(name) { - var val, ctrl = $D('noVNC_' + name); - if (ctrl.type === 'checkbox') { - val = ctrl.checked; - } else if (typeof ctrl.options !== 'undefined') { - val = ctrl.options[ctrl.selectedIndex].value; - } else { - val = ctrl.value; - } - WebUtil.createCookie(name, val); - //Util.Debug("Setting saved '" + name + "=" + val + "'"); - return val; -}, - -// Initial page load read/initialization of settings -initSetting: function(name, defVal) { - var val; - - // Check Query string followed by cookie - val = WebUtil.getQueryVar(name); - if (val === null) { - val = WebUtil.readCookie(name, defVal); - } - UI.updateSetting(name, val); - //Util.Debug("Setting '" + name + "' initialized to '" + val + "'"); - return val; -}, - -// Force a setting to be a certain value -forceSetting: function(name, val) { - UI.updateSetting(name, val); - return val; -}, - - -// Show the clipboard panel -toggleClipboardPanel: function() { - //Close settings if open - if (UI.settingsOpen == true) { - UI.settingsApply(); - UI.closeSettingsMenu(); - } - //Close connection settings if open - if (UI.connSettingsOpen == true) { - UI.toggleConnectPanel(); - } - //Toggle Clipboard Panel - if (UI.clipboardOpen == true) { - $D('noVNC_clipboard').style.display = "none"; - $D('clipboardButton').className = "noVNC_status_button"; - UI.clipboardOpen = false; - } else { - $D('noVNC_clipboard').style.display = "block"; - $D('clipboardButton').className = "noVNC_status_button_selected"; - UI.clipboardOpen = true; - } -}, - -// Show the connection settings panel/menu -toggleConnectPanel: function() { - //Close connection settings if open - if (UI.settingsOpen == true) { - UI.settingsApply(); - UI.closeSettingsMenu(); - $D('connectButton').className = "noVNC_status_button"; - } - if (UI.clipboardOpen == true) { - UI.toggleClipboardPanel(); - } - - //Toggle Connection Panel - if (UI.connSettingsOpen == true) { - $D('noVNC_controls').style.display = "none"; - $D('connectButton').className = "noVNC_status_button"; - UI.connSettingsOpen = false; - } else { - $D('noVNC_controls').style.display = "block"; - $D('connectButton').className = "noVNC_status_button_selected"; - UI.connSettingsOpen = true; - $D('noVNC_host').focus(); - } -}, - -// Toggle the settings menu: -// On open, settings are refreshed from saved cookies. -// On close, settings are applied -toggleSettingsPanel: function() { - if (UI.settingsOpen) { - UI.settingsApply(); - UI.closeSettingsMenu(); - } else { - UI.updateSetting('encrypt'); - UI.updateSetting('true_color'); - if (UI.rfb.get_display().get_cursor_uri()) { - UI.updateSetting('cursor'); - } else { - UI.updateSetting('cursor', false); - $D('noVNC_cursor').disabled = true; - } - UI.updateSetting('clip'); - UI.updateSetting('shared'); - UI.updateSetting('connectTimeout'); - UI.updateSetting('path'); - UI.updateSetting('stylesheet'); - UI.updateSetting('logging'); - - UI.openSettingsMenu(); - } -}, - -// Open menu -openSettingsMenu: function() { - if (UI.clipboardOpen == true) { - UI.toggleClipboardPanel(); - } - //Close connection settings if open - if (UI.connSettingsOpen == true) { - UI.toggleConnectPanel(); - } - $D('noVNC_settings').style.display = "block"; - $D('settingsButton').className = "noVNC_status_button_selected"; - UI.settingsOpen = true; -}, - -// Close menu (without applying settings) -closeSettingsMenu: function() { - $D('noVNC_settings').style.display = "none"; - $D('settingsButton').className = "noVNC_status_button"; - UI.settingsOpen = false; -}, - -// Save/apply settings when 'Apply' button is pressed -settingsApply: function() { - //Util.Debug(">> settingsApply"); - UI.saveSetting('encrypt'); - UI.saveSetting('true_color'); - if (UI.rfb.get_display().get_cursor_uri()) { - UI.saveSetting('cursor'); - } - UI.saveSetting('clip'); - UI.saveSetting('shared'); - UI.saveSetting('connectTimeout'); - UI.saveSetting('path'); - UI.saveSetting('stylesheet'); - UI.saveSetting('logging'); - - // Settings with immediate (non-connected related) effect - WebUtil.selectStylesheet(UI.getSetting('stylesheet')); - WebUtil.init_logging(UI.getSetting('logging')); - UI.setViewClip(); - UI.setViewDrag(UI.rfb.get_viewportDrag()); - //Util.Debug("<< settingsApply"); -}, - - - -setPassword: function() { - UI.rfb.sendPassword($D('noVNC_password').value); - //Reset connect button. - $D('noVNC_connect_button').value = "Connect"; - $D('noVNC_connect_button').onclick = UI.Connect; - //Hide connection panel. - UI.toggleConnectPanel(); - return false; -}, - -sendCtrlAltDel: function() { - UI.rfb.sendCtrlAltDel(); -}, - -setMouseButton: function(num) { - var b, blist = [0, 1,2,4], button; - - if (typeof num === 'undefined') { - // Disable mouse buttons - num = -1; - } - if (UI.rfb) { - UI.rfb.get_mouse().set_touchButton(num); - } - - for (b = 0; b < blist.length; b++) { - button = $D('noVNC_mouse_button' + blist[b]); - if (blist[b] === num) { - button.style.display = ""; - } else { - button.style.display = "none"; - /* - button.style.backgroundColor = "black"; - button.style.color = "lightgray"; - button.style.backgroundColor = ""; - button.style.color = ""; - */ - } - } -}, - -updateState: function(rfb, state, oldstate, msg) { - var s, sb, c, d, cad, vd, klass; - UI.rfb_state = state; - s = $D('noVNC_status'); - sb = $D('noVNC_status_bar'); - switch (state) { - case 'failed': - case 'fatal': - klass = "noVNC_status_error"; - break; - case 'normal': - klass = "noVNC_status_normal"; - break; - case 'disconnected': - $D('noVNC_logo').style.display = "block"; - case 'loaded': - klass = "noVNC_status_normal"; - break; - case 'password': - UI.toggleConnectPanel(); - - $D('noVNC_connect_button').value = "Send Password"; - $D('noVNC_connect_button').onclick = UI.setPassword; - $D('noVNC_password').focus(); - - klass = "noVNC_status_warn"; - break; - default: - klass = "noVNC_status_warn"; - break; - } - - if (typeof(msg) !== 'undefined') { - s.setAttribute("class", klass); - sb.setAttribute("class", klass); - s.innerHTML = msg; - } - - UI.updateVisualState(); -}, - -// Disable/enable controls depending on connection state -updateVisualState: function() { - var connected = UI.rfb_state === 'normal' ? true : false; - - //Util.Debug(">> updateVisualState"); - $D('noVNC_encrypt').disabled = connected; - $D('noVNC_true_color').disabled = connected; - if (UI.rfb && UI.rfb.get_display() && - UI.rfb.get_display().get_cursor_uri()) { - $D('noVNC_cursor').disabled = connected; - } else { - UI.updateSetting('cursor', false); - $D('noVNC_cursor').disabled = true; - } - $D('noVNC_shared').disabled = connected; - $D('noVNC_connectTimeout').disabled = connected; - $D('noVNC_path').disabled = connected; - - if (connected) { - UI.setViewClip(); - UI.setMouseButton(1); - $D('showKeyboard').style.display = "inline"; - $D('sendCtrlAltDelButton').style.display = "inline"; - } else { - UI.setMouseButton(); - $D('showKeyboard').style.display = "none"; - $D('sendCtrlAltDelButton').style.display = "none"; - } - // State change disables viewport dragging. - // It is enabled (toggled) by direct click on the button - UI.setViewDrag(false); - - switch (UI.rfb_state) { - case 'fatal': - case 'failed': - case 'loaded': - case 'disconnected': - $D('connectButton').style.display = ""; - $D('disconnectButton').style.display = "none"; - break; - default: - $D('connectButton').style.display = "none"; - $D('disconnectButton').style.display = ""; - break; - } - - //Util.Debug("<< updateVisualState"); -}, - - -clipReceive: function(rfb, text) { - Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); - $D('noVNC_clipboard_text').value = text; - Util.Debug("<< UI.clipReceive"); -}, - - -connect: function() { - var host, port, password, path; - - UI.closeSettingsMenu(); - UI.toggleConnectPanel(); - - host = $D('noVNC_host').value; - port = $D('noVNC_port').value; - password = $D('noVNC_password').value; - path = $D('noVNC_path').value; - if ((!host) || (!port)) { - throw("Must set host and port"); - } - - UI.rfb.set_encrypt(UI.getSetting('encrypt')); - UI.rfb.set_true_color(UI.getSetting('true_color')); - UI.rfb.set_local_cursor(UI.getSetting('cursor')); - UI.rfb.set_shared(UI.getSetting('shared')); - UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout')); - - UI.rfb.connect(host, port, password, path); - //Close dialog. - setTimeout(UI.setBarPosition, 100); - $D('noVNC_logo').style.display = "none"; -}, - -disconnect: function() { - UI.closeSettingsMenu(); - UI.rfb.disconnect(); - - $D('noVNC_logo').style.display = "block"; - UI.connSettingsOpen = false; - UI.toggleConnectPanel(); -}, - -displayBlur: function() { - UI.rfb.get_keyboard().set_focused(false); - UI.rfb.get_mouse().set_focused(false); -}, - -displayFocus: function() { - UI.rfb.get_keyboard().set_focused(true); - UI.rfb.get_mouse().set_focused(true); -}, - -clipClear: function() { - $D('noVNC_clipboard_text').value = ""; - UI.rfb.clipboardPasteFrom(""); -}, - -clipSend: function() { - var text = $D('noVNC_clipboard_text').value; - Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "..."); - UI.rfb.clipboardPasteFrom(text); - Util.Debug("<< UI.clipSend"); -}, - - -// Enable/disable and configure viewport clipping -setViewClip: function(clip) { - var display, cur_clip, pos, new_w, new_h; - - if (UI.rfb) { - display = UI.rfb.get_display(); - } else { - return; - } - - cur_clip = display.get_viewport(); - - if (typeof(clip) !== 'boolean') { - // Use current setting - clip = UI.getSetting('clip'); - } - - if (clip && !cur_clip) { - // Turn clipping on - UI.updateSetting('clip', true); - } else if (!clip && cur_clip) { - // Turn clipping off - UI.updateSetting('clip', false); - display.set_viewport(false); - $D('noVNC_canvas').style.position = 'static'; - display.viewportChange(); - } - if (UI.getSetting('clip')) { - // If clipping, update clipping settings - $D('noVNC_canvas').style.position = 'absolute'; - pos = Util.getPosition($D('noVNC_canvas')); - new_w = window.innerWidth - pos.x; - new_h = window.innerHeight - pos.y; - display.set_viewport(true); - display.viewportChange(0, 0, new_w, new_h); - } -}, - -// Toggle/set/unset the viewport drag/move button -setViewDrag: function(drag) { - var vmb = $D('noVNC_view_drag_button'); - if (!UI.rfb) { return; } - - if (UI.rfb_state === 'normal' && - UI.rfb.get_display().get_viewport()) { - vmb.style.display = "inline"; - } else { - vmb.style.display = "none"; - } - - if (typeof(drag) === "undefined") { - // If not specified, then toggle - drag = !UI.rfb.get_viewportDrag(); - } - if (drag) { - vmb.className = "noVNC_status_button_selected"; - UI.rfb.set_viewportDrag(true); - } else { - vmb.className = "noVNC_status_button"; - UI.rfb.set_viewportDrag(false); - } -}, - -// On touch devices, show the OS keyboard -showKeyboard: function() { - if(UI.keyboardVisible == false) { - $D('keyboardinput').focus(); - UI.keyboardVisible = true; - $D('showKeyboard').className = "noVNC_status_button_selected"; - } else if(UI.keyboardVisible == true) { - $D('keyboardinput').blur(); - $D('showKeyboard').className = "noVNC_status_button"; - UI.keyboardVisible = false; - } -}, - -keyInputBlur: function() { - $D('showKeyboard').className = "noVNC_status_button"; - //Weird bug in iOS if you change keyboardVisible - //here it does not actually occur so next time - //you click keyboard icon it doesnt work. - setTimeout("UI.setKeyboard()",100) -}, - -setKeyboard: function() { - UI.keyboardVisible = false; -}, - -// iOS < Version 5 does not support position fixed. Javascript workaround: -setOnscroll: function() { - window.onscroll = function() { - UI.setBarPosition(); - }; -}, - -setResize: function () { - window.onResize = function() { - UI.setBarPosition(); - }; -}, - -//Helper to add options to dropdown. -addOption: function(selectbox,text,value ) -{ - var optn = document.createElement("OPTION"); - optn.text = text; - optn.value = value; - selectbox.options.add(optn); -}, - -setBarPosition: function() { - $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px'; - $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; - - var vncwidth = $D('noVNC_screen').style.offsetWidth; - $D('noVNC-control-bar').style.width = vncwidth + 'px'; -} - -}; - - - - diff --git a/classes/novnc/include/util.js b/classes/novnc/include/util.js deleted file mode 100644 index 0a9e0e0..0000000 --- a/classes/novnc/include/util.js +++ /dev/null @@ -1,276 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -"use strict"; -/*jslint bitwise: false, white: false */ -/*global window, console, document, navigator, ActiveXObject */ - -// Globals defined here -var Util = {}; - - -/* - * Make arrays quack - */ - -Array.prototype.push8 = function (num) { - this.push(num & 0xFF); -}; - -Array.prototype.push16 = function (num) { - this.push((num >> 8) & 0xFF, - (num ) & 0xFF ); -}; -Array.prototype.push32 = function (num) { - this.push((num >> 24) & 0xFF, - (num >> 16) & 0xFF, - (num >> 8) & 0xFF, - (num ) & 0xFF ); -}; - -/* - * ------------------------------------------------------ - * Namespaced in Util - * ------------------------------------------------------ - */ - -/* - * Logging/debug routines - */ - -Util._log_level = 'warn'; -Util.init_logging = function (level) { - if (typeof level === 'undefined') { - level = Util._log_level; - } else { - Util._log_level = level; - } - if (typeof window.console === "undefined") { - if (typeof window.opera !== "undefined") { - window.console = { - 'log' : window.opera.postError, - 'warn' : window.opera.postError, - 'error': window.opera.postError }; - } else { - window.console = { - 'log' : function(m) {}, - 'warn' : function(m) {}, - 'error': function(m) {}}; - } - } - - Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; - switch (level) { - case 'debug': Util.Debug = function (msg) { console.log(msg); }; - case 'info': Util.Info = function (msg) { console.log(msg); }; - case 'warn': Util.Warn = function (msg) { console.warn(msg); }; - case 'error': Util.Error = function (msg) { console.error(msg); }; - case 'none': - break; - default: - throw("invalid logging type '" + level + "'"); - } -}; -Util.get_logging = function () { - return Util._log_level; -}; -// Initialize logging level -Util.init_logging(); - - -// Set configuration default for Crockford style function namespaces -Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { - var getter, setter; - - // Default getter function - getter = function (idx) { - if ((type in {'arr':1, 'array':1}) && - (typeof idx !== 'undefined')) { - return cfg[v][idx]; - } else { - return cfg[v]; - } - }; - - // Default setter function - setter = function (val, idx) { - if (type in {'boolean':1, 'bool':1}) { - if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { - val = false; - } else { - val = true; - } - } else if (type in {'integer':1, 'int':1}) { - val = parseInt(val, 10); - } else if (type === 'func') { - if (!val) { - val = function () {}; - } - } - if (typeof idx !== 'undefined') { - cfg[v][idx] = val; - } else { - cfg[v] = val; - } - }; - - // Set the description - api[v + '_description'] = desc; - - // Set the getter function - if (typeof api['get_' + v] === 'undefined') { - api['get_' + v] = getter; - } - - // Set the setter function with extra sanity checks - if (typeof api['set_' + v] === 'undefined') { - api['set_' + v] = function (val, idx) { - if (mode in {'RO':1, 'ro':1}) { - throw(v + " is read-only"); - } else if ((mode in {'WO':1, 'wo':1}) && - (typeof cfg[v] !== 'undefined')) { - throw(v + " can only be set once"); - } - setter(val, idx); - }; - } - - // Set the default value - if (typeof defaults[v] !== 'undefined') { - defval = defaults[v]; - } else if ((type in {'arr':1, 'array':1}) && - (! (defval instanceof Array))) { - defval = []; - } - // Coerce existing setting to the right type - //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]); - setter(defval); -}; - -// Set group of configuration defaults -Util.conf_defaults = function(cfg, api, defaults, arr) { - var i; - for (i = 0; i < arr.length; i++) { - Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], - arr[i][2], arr[i][3], arr[i][4]); - } -} - - -/* - * Cross-browser routines - */ - -// Get DOM element position on page -Util.getPosition = function (obj) { - var x = 0, y = 0; - if (obj.offsetParent) { - do { - x += obj.offsetLeft; - y += obj.offsetTop; - obj = obj.offsetParent; - } while (obj); - } - return {'x': x, 'y': y}; -}; - -// Get mouse event position in DOM element -Util.getEventPosition = function (e, obj, scale) { - var evt, docX, docY, pos; - //if (!e) evt = window.event; - evt = (e ? e : window.event); - evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); - if (evt.pageX || evt.pageY) { - docX = evt.pageX; - docY = evt.pageY; - } else if (evt.clientX || evt.clientY) { - docX = evt.clientX + document.body.scrollLeft + - document.documentElement.scrollLeft; - docY = evt.clientY + document.body.scrollTop + - document.documentElement.scrollTop; - } - pos = Util.getPosition(obj); - if (typeof scale === "undefined") { - scale = 1; - } - return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; -}; - - -// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events -Util.addEvent = function (obj, evType, fn){ - if (obj.attachEvent){ - var r = obj.attachEvent("on"+evType, fn); - return r; - } else if (obj.addEventListener){ - obj.addEventListener(evType, fn, false); - return true; - } else { - throw("Handler could not be attached"); - } -}; - -Util.removeEvent = function(obj, evType, fn){ - if (obj.detachEvent){ - var r = obj.detachEvent("on"+evType, fn); - return r; - } else if (obj.removeEventListener){ - obj.removeEventListener(evType, fn, false); - return true; - } else { - throw("Handler could not be removed"); - } -}; - -Util.stopEvent = function(e) { - if (e.stopPropagation) { e.stopPropagation(); } - else { e.cancelBubble = true; } - - if (e.preventDefault) { e.preventDefault(); } - else { e.returnValue = false; } -}; - - -// Set browser engine versions. Based on mootools. -Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; - -Util.Engine = { - 'presto': (function() { - return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), - 'trident': (function() { - return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), - 'webkit': (function() { - try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), - //'webkit': (function() { - // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), - 'gecko': (function() { - return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) -}; -if (Util.Engine.webkit) { - // Extract actual webkit version if available - Util.Engine.webkit = (function(v) { - var re = new RegExp('WebKit/([0-9\.]*) '); - v = (navigator.userAgent.match(re) || ['', v])[1]; - return parseFloat(v, 10); - })(Util.Engine.webkit); -} - -Util.Flash = (function(){ - var v, version; - try { - v = navigator.plugins['Shockwave Flash'].description; - } catch(err1) { - try { - v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); - } catch(err2) { - v = '0 r0'; - } - } - version = v.match(/\d+/g); - return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; -}()); diff --git a/classes/novnc/include/vnc.js b/classes/novnc/include/vnc.js deleted file mode 100644 index f938be7..0000000 --- a/classes/novnc/include/vnc.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2011 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -/*jslint evil: true */ -/*global window, document, INCLUDE_URI */ - -/* - * Load supporting scripts - */ -function get_INCLUDE_URI() { - return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; -} - -(function () { - "use strict"; - - var extra = "", start, end; - - start = " - - - - - -
- -
- -
- - - - - - -
-
- - -
- - - - - -
- - -
- -
- -
- - -
- -
    -
  • Encrypt
  • -
  • True Color
  • -
  • Local Cursor
  • -
  • Clip to window
  • -
  • Shared Mode
  • -
  • Connect Timeout (s)
  • -
  • Path
  • -
    - -
  • -
  • - - -
  • -
  • -
    -
  • -
-
-
- - -
-
    -
  • -
  • -
  • -
  • -
-
- -
- - -
-
- -
-
Loading
-
- -

no
VNC

- - -
- - Canvas not supported. - -
- -
- - - - diff --git a/classes/novnc/vnc_auto.html b/classes/novnc/vnc_auto.html deleted file mode 100644 index a500b79..0000000 --- a/classes/novnc/vnc_auto.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - noVNC - - - - - - - -
-
- - - -
Loading
- -
-
- - Canvas not supported. - -
- - - - - - diff --git a/classes/ssl/Makefile.am b/classes/ssl/Makefile.am deleted file mode 100644 index fd1c201..0000000 --- a/classes/ssl/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc - diff --git a/classes/ssl/README b/classes/ssl/README deleted file mode 100644 index b244cf1..0000000 --- a/classes/ssl/README +++ /dev/null @@ -1,338 +0,0 @@ -This directory contains a patched Java applet VNC viewer that is SSL -enabled. - -The patches in the *.patch files are relative to the source tarball: - - tightvnc-1.3dev7_javasrc.tar.gz - -currently (4/06) available here: - - http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download - -It also includes some simple patches to: - - - fix richcursor colors - - - make the Java Applet cursor (not the cursor drawn to the canvas - framebuffer) invisible when it is inside the canvas. - - - allow Tab (and some other) keystrokes to be sent to the vnc - server instead of doing widget traversal. - - -This SSL applet should work with any VNC viewer that has an SSL tunnel in -front of it. It has been tested on x11vnc and using the stunnel tunnel -to other VNC servers. - -By default this Vnc Viewer will only do SSL. To do unencrypted traffic -see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc). - -Proxies: they are a general problem with java socket applets (a socket -connection does not go through the proxy). See the info in the proxy.vnc -file for a workaround. It uses SignedVncViewer.jar which is simply -a signed version of VncViewer.jar. The basic idea is the user clicks -"Yes" to trust the applet and then it can connect directly to the proxy -and issue a CONNECT request. - -This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun -Java plugin. It may not work on older releases or different vendor VM's. -Send full Java Console output for failures. - ---------------------------------------------------------------- -Tips: - -When doing single-port proxy connections (e.g. both VNC and HTTPS -thru port 5900) it helps to move through the 'do you trust this site' -dialogs quickly. x11vnc has to wait to see if the traffic is VNC or -HTTP and this can cause timeouts if you don't move thru them quickly. - -You may have to restart your browser completely if it gets into a -weird state. For one case we saw the JVM requesting VncViewer.class -even when no such file exists. - - ---------------------------------------------------------------- -Extras: - -ss_vncviewer (not Java): - - Wrapper script for native VNC viewer to connect to x11vnc in - SSL mode. Script launches stunnel(8) and then connects to it - via localhost which in turn is then redirected to x11vnc via an - SSL tunnel. stunnel(8) must be installed and available in PATH. - - -Running Java SSL VncViewer from the command line: - - From this directory: - - java -cp ./VncViewer.jar VncViewer HOST PORT - - substitute and with the actual values. - You can add any other parameters, e.g.: ignoreProxy yes - ---------------------------------------------------------------- -UltraVNC: - -The UltraVNC java viewer has also been patched to support SSL. Various -bugs in the UltraVNC java viewer were also fixed. This viewer can be -useful because is support UltraVNC filetransfer, and so it works on -Unix, etc. - -UltraViewerSSL.jar -SignedUltraViewerSSL.jar -ultra.vnc -ultraproxy.vnc -ultravnc-102-JavaViewer-ssl-etc.patch - ---------------------------------------------------------------- -Applet Parameters: - -Some additional applet parameters can be set via the URL, e.g. - - http://host:5800/?param=value - http://host:5800/ultra.vnc?param=value - https://host:5900/ultra.vnc?param=value - -etc. If running java from command line as show above, it comes -in as java ... VncViewer param value ... - -There is a limitation with libvncserver that param and value can -only be alphanumeric, underscore, "+" (for space), or "." - -We have added some applet parameters to the stock VNC java -viewers. Here are the applet parameters: - -Both TightVNC and UltraVNC Java viewers: - - HOST - string, default: none. - The Hostname to connect to. - - PORT - number, default: 0 - The VNC server port to connect to. - - Open New Window - yes/no, default: no - Run applet in separate frame. - - Show Controls - yes/no, default: yes - Show Controls button panel. - - Show Offline Desktop - yes/no, default: no - Do we continue showing desktop on remote disconnect? - - Defer screen updates - number, default: 20 - Milliseconds delay - - Defer cursor updates - number, default: 10 - Milliseconds delay - - Defer update requests - number, default: 50 - Milliseconds delay - - PASSWORD - string, default: none - VNC session password in plain text. - - ENCPASSWORD - string, default: none - VNC session password in encrypted in DES with KNOWN FIXED - key. It is a hex string. This is like the ~/.vnc/passwd format. - - - The following are added by x11vnc and/or ssvnc project - - VNCSERVERPORT - number, default: 0 - Like PORT, but if there is a firewall this is the Actual VNC - server port. PORT might be a redir port on the firewall. - - DisableSSL - yes/no, default: no - Do unencrypted connection, no SSL. - - httpsPort - number, default: none - When checking for proxy, use this at the url port number. - - CONNECT - string, default: none - Sets to host:port for the CONNECT line to a Web proxy. - The Web proxy should connect us to it. - - GET - yes/no, default: no - Set to do a special HTTP GET (/request.https.vnc.connection) - to the vnc server that will cause it to switch to VNC instead. - This is to speedup/make more robust, the single port HTTPS and VNC - mode of x11vnc (e.g. both services thru port 5900, etc) - - urlPrefix - string, default: none - set to a string that will be prefixed to all URL's when contacting - the VNC server. Idea is a special proxy will use this to indicate - internal hostname, etc. - - oneTimeKey - string, default: none - set a special hex "key" to correspond to an SSL X.509 cert+key. - See the 'onetimekey' helper script. Can also be PROMPT to prompt - the user to paste the hex key string in. - - This provides a Client-Side cert+key that the client will use to - authenticate itself by SSL To the VNC Server. - - This is to try to work around the problem that the Java applet - cannot keep an SSL keystore on disk, etc. E.g. if they log - into an HTTPS website via password they are authenticated and - encrypted, then the website can safely put oneTimeKey=... on the - URL. The Vncviewer authenticates the VNC server with this key. - - Note that there is currently a problem in that if x11vnc requires - Client Certificates the user cannot download the index.vnc HTML - and VncViewer.jar from the same x11vnc. Those need to come from - a different x11vnc or from a web server. - - Note that the HTTPS website can also put the VNC Password - (e.g. a temporary/one-time one) in the parameter PASSWORD. - The Java Applet will automatically supply this VNC password - instead of prompting. - - serverCert - string, default: none - set a special hex "cert" to correspond to an SSL X.509 cert - See the 'onetimekey -certonly' helper script. - - This provides a Server-Side cert that the client will authenticate - the VNC Server against by SSL. - - This is to try to work around the problem that the Java applet - cannot keep an SSL keystore on disk, etc. E.g. if they log - into an HTTPS website via password they are authenticated and - encrypted, then the website can safely put serverCert=... on the - URL. - - Of course the VNC Server is sending this string to the Java - Applet, so this is only reasonable security if the VNC Viewer - already trusts the HTTPS retrieval of the URL + serverCert param - that it gets. This should be done over HTTPS not HTTP. - - proxyHost - string, default: none - Do not try to guess the proxy's hostname, use the value in - proxyHost. Does not imply forceProxy (below.) - - proxyPort - string, default: none - Do not try to guess the proxy's port number, use the value in - proxyPort. Does not imply forceProxy (below.) - - forceProxy - yes/no, default: no - Assume there is a proxy and force its use. - - If a string other than "yes" or "no" is given, it implies "yes" - and uses the string for proxyHost and proxyPort (see above). - In this case the string must be of the form "hostname+port". - Note that it is "+" and not ":" before the port number. - - ignoreProxy - yes/no, default: no - Don't check for a proxy, assume there is none. - - trustAllVncCerts - yes/no, default: no - Automatically trust any cert received from the VNC server - (obviously this could be dangerous and lead to man in the - middle attack). Do not ask the user to verify any of these - certs from the VNC server. - - trustUrlVncCert - yes/no, default: no - Automatically trust any cert that the web browsers has accepted. - E.g. the user said "Yes" or "Continue" to a web browser dialog - regarding a certificate. If we get the same cert (chain) from - the VNC server we trust it without prompting the user. - - debugCerts - yes/no, default: no - Print out every cert in the Server, TrustUrl, TrustAll chains. - - -TightVNC Java viewer only: - - Offer Relogin - yes/no, default: yes - "Offer Relogin" set to "No" disables "Login again" - - SocketFactory - string, default: none - set Java Socket class factory. - -UltraVNC Java viewer only: - - None. - - The following are added by x11vnc and/or ssvnc project - - ftpDropDown - string, default: none - Sets the file transfer "drives" dropdown to the "." separated - list. Use "+" for space. The default is - - My+Documents.Desktop.Home - - for 3 entries in the dropdown in addition to the "drives" - (e.g. C:\) These items should be expanded properly by the VNC - Server. x11vnc will prepend $HOME to them, which is normally - what one wants. To include a "/" use "_2F_". Another example: - - Home.Desktop.bin_2F_linux - - If an item is prefixed with "TOP_" then the item is inserted at - the top of the drop down rather than being appended to the end. - E.g. to try to initially load the user homedir instead of /: - - TOP_Home.My+Documents.Desktop - - If ftpDropDown is set to the empty string, "", then no special - locations, [Desktop] etc., are placed in the drop down. Only the - ultravnc "drives" will appear. - - ftpOnly - yes/no, default: no - The VNC viewer only shows the filetransfer panel, no desktop - is displayed. - - graftFtp - yes/no, default: no - As ftpOnly, the VNC viewer only shows the filetransfer panel, - no desktop is displayed, however it is "grafted" onto an existing - SSVNC unix vncviewer. The special SSVNC vncviewer merges the two - channels. - - dsmActive - yes/no, default: no - Special usage mode with the SSVNC unix vncviewer. The UltraVNC - DSM encryption is active. Foolishly, UltraVNC DSM encryption - *MODIFIES* the VNC protocol when active (it is not a pure tunnel). - This option indicates to modify the VNC protocol to make this work. - Usually only used with graftFtp and SSVNC unix vncviewer. - - delayAuthPanel - yes/no, default: no - This is another special usage mode with the SSVNC unix vncviewer. - A login panel is delayed (not shown at startup.) Could be useful - for non SSVNC usage too. - - ignoreMSLogonCheck - yes/no, default: no - Similar to delayAuthPanel, do not put up a popup asking for - Windows username, etc. diff --git a/classes/ssl/SignedUltraViewerSSL.jar b/classes/ssl/SignedUltraViewerSSL.jar deleted file mode 100644 index 6c18737..0000000 Binary files a/classes/ssl/SignedUltraViewerSSL.jar and /dev/null differ diff --git a/classes/ssl/SignedVncViewer.jar b/classes/ssl/SignedVncViewer.jar deleted file mode 100644 index 95c0b0b..0000000 Binary files a/classes/ssl/SignedVncViewer.jar and /dev/null differ diff --git a/classes/ssl/UltraViewerSSL.jar b/classes/ssl/UltraViewerSSL.jar deleted file mode 100644 index 45259fd..0000000 Binary files a/classes/ssl/UltraViewerSSL.jar and /dev/null differ diff --git a/classes/ssl/VncViewer.jar b/classes/ssl/VncViewer.jar deleted file mode 100644 index 9453c6f..0000000 Binary files a/classes/ssl/VncViewer.jar and /dev/null differ diff --git a/classes/ssl/index.vnc b/classes/ssl/index.vnc deleted file mode 100644 index ec520dc..0000000 --- a/classes/ssl/index.vnc +++ /dev/null @@ -1,26 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - -$PARAMS - -
-
x11vnc site - diff --git a/classes/ssl/onetimekey b/classes/ssl/onetimekey deleted file mode 100755 index bf57c8f..0000000 --- a/classes/ssl/onetimekey +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -# -# usage: onetimekey path/to/mycert.pem -# onetimekey -certonly path/to/mycert.pem -# -# Takes an openssl cert+key pem file and turns into a long string -# for the x11vnc SSL VNC Java Viewer. -# -# The Java applet URL parameter can be oneTimeKey= where str is -# the output of this program, or can be oneTimeKey=PROMPT in which -# case the applet will ask you to paste in the string. -# -# The problem trying to be solved here is it is difficult to get -# the Java applet to have or use a keystore with the key saved -# in it. Also, as the name implies, an HTTPS server can create -# a one time key to send to the applet (the user has already -# logged in via password to the HTTPS server). -# -# Note oneTimeKey is to provide a CLIENT Certificate for the viewer -# to authenticate itself to the VNC Server. -# -# There is also the serverCert= Applet parameter. This is -# a cert to authenticate the VNC server against. To create that -# string with this tool specify -certonly as the first argument. - -certonly="" -if [ "X$1" = "X-certonly" ]; then - shift - certonly=1 -fi - -in=$1 -der=/tmp/1time$$.der -touch $der -chmod 600 $der - -openssl pkcs8 -topk8 -nocrypt -in "$in" -out "$der" -outform der - -pbinhex=/tmp/pbinhex.$$ -cat > $pbinhex < - - - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - -$PARAMS - -
-x11vnc site - diff --git a/classes/ssl/ss_vncviewer b/classes/ssl/ss_vncviewer deleted file mode 100755 index 7e793ff..0000000 --- a/classes/ssl/ss_vncviewer +++ /dev/null @@ -1,3676 +0,0 @@ -#!/bin/sh -# -# ss_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel -# or an SSH tunnel. -# -# Copyright (c) 2006-2009 by Karl J. Runge -# -# ss_vncviewer 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. -# -# ss_vncviewer 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 ss_vncviewer; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -# or see . -# -# -# You must have stunnel(8) installed on the system and in your PATH -# (however, see the -ssh option below, in which case you will need ssh(1) -# installed) Note: stunnel is usually installed in an "sbin" subdirectory. -# -# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." -# already running as the VNC server on the remote machine. -# (or use stunnel on the server side for any other VNC server) -# -# -# Usage: ss_vncviewer [cert-args] host:display -# -# e.g.: ss_vncviewer snoopy:0 -# ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" -# -# [cert-args] can be: -# -# -verify /path/to/cacert.pem -# -mycert /path/to/mycert.pem -# -crl /path/to/my_crl.pem (or directory) -# -proxy host:port -# -# -verify specifies a CA cert PEM file (or a self-signed one) for -# authenticating the VNC server. -# -# -mycert specifies this client's cert+key PEM file for the VNC server to -# authenticate this client. -# -# -proxy try host:port as a Web proxy to use the CONNECT method -# to reach the VNC server (e.g. your firewall requires a proxy). -# -# For the "double proxy" case use -proxy host1:port1,host2:port2 -# (the first CONNECT is done through host1:port1 to host2:port2 -# and then a 2nd CONNECT to the destination VNC server.) -# -# Use socks://host:port, socks4://host:port, or socks5://host,port -# to force usage of a SOCKS proxy. Also repeater://host:port and -# sslrepeater://host:port. -# -# -showcert Only fetch the certificate using the 'openssl s_client' -# command (openssl(1) must in installed). On ssvnc 1.0.27 and -# later the bundled command 'ultravnc_dsm_helper' is used. -# -# See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on -# SSL certificates with VNC. -# -# A few other args (not related to SSL and certs): -# -# -2nd Run the vncviewer a 2nd time if the first connections fails. -# -# -ssh Use ssh instead of stunnel SSL. ssh(1) must be installed and you -# must be able to log into the remote machine via ssh. -# -# In this case "host:display" may be of the form "user@host:display" -# where "user@host" is used for the ssh login (see ssh(1) manpage). -# -# If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port" -# "user@gwhost" or "user@gwhost:port". "gwhost" is an incoming ssh -# gateway machine (the VNC server is not running there), an ssh -L -# redir is used to "host" in "host:display" from "gwhost". Any "user@" -# part must be in the -proxy string (not in "host:display"). -# -# Under -proxy use "gwhost:port" if connecting to any ssh port -# other than the default (22). (even for the non-gateway case, -# -proxy must be used to specify a non-standard ssh port) -# -# A "double ssh" can be specified via a -proxy string with the two -# hosts separated by a comma: -# -# [user1@]host1[:port1],[user2@]host2[:port2] -# -# in which case a ssh to host1 and thru it via a -L redir a 2nd -# ssh is established to host2. -# -# Examples: -# -# ss_vncviewer -ssh bob@bobs-home.net:0 -# ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0 -# -# ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0 -# ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0 -# -# ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0 -# -# -sshcmd cmd Run "cmd" via ssh instead of the default "sleep 15" -# e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900' -# -# -sshargs "args" pass "args" to the ssh process, e.g. -L/-R port redirs. -# -# -sshssl Tunnel the SSL connection thru a SSH connection. The tunnel as -# under -ssh is set up and the SSL connection goes thru it. Use -# this if you want to have and end-to-end SSL connection but must -# go thru a SSH gateway host (e.g. not the vnc server). Or use -# this if you need to tunnel additional services via -R and -L -# (see -sshargs above). -# -# ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0 -# -# -listen (or -reverse) set up a reverse connection. -# -# -alpha turn on cursor alphablending hack if you are using the -# enhanced tightvnc vncviewer. -# -# -grab turn on XGrabServer hack if you are using the enhanced tightvnc -# vncviewer (e.g. for fullscreen mode in some windowmanagers like -# fvwm that do not otherwise work in fullscreen mode) -# -# -# set VNCVIEWERCMD to whatever vncviewer command you want to use. -# -VNCIPCMD=${VNCVIEWERCMD:-vncip} -VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer} -if [ "X$SSVNC_TURBOVNC" != "X" ]; then - if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then - : - else - if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then - VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc" - fi - fi -fi -# -# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc. -# - -# turn on verbose debugging output -if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then - set -xv -fi - -PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH - -localhost="localhost" -if uname | grep Darwin >/dev/null; then - localhost="127.0.0.1" -fi - -# work out which stunnel to use (debian installs as stunnel4) -stunnel_set_here="" -if [ "X$STUNNEL" = "X" ]; then - check_stunnel=1 - if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then - if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then - type stunnel > /dev/null 2>&1 - if [ $? = 0 ]; then - # found ours - STUNNEL=stunnel - check_stunnel=0 - fi - fi - fi - if [ "X$check_stunnel" = "X1" ]; then - type stunnel4 > /dev/null 2>&1 - if [ $? = 0 ]; then - STUNNEL=stunnel4 - else - STUNNEL=stunnel - fi - fi - stunnel_set_here=1 -fi - -help() { - tail -n +2 "$0" | sed -e '/^$/ q' -} - -secondtry="" -gotalpha="" -use_ssh="" -use_sshssl="" -direct_connect="" -ssh_sleep=15 - -# sleep longer in -listen mode: -if echo "$*" | grep '.*-listen' > /dev/null; then - ssh_sleep=1800 -fi - - -ssh_cmd="" -# env override of ssh_cmd: -if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then - ssh_cmd="$SS_VNCVIEWER_SSH_CMD" -fi - -ssh_args="" -showcert="" -reverse="" - -ciphers="" -anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH" -anondh_set="" -stunnel_debug="6" -if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then - stunnel_debug="7" -fi - -if [ "X$1" = "X-viewerflavor" ]; then - # special case, try to guess which viewer: - # - if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - echo "unknown" - exit 0 - fi - if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then - echo "cotvnc" - exit 0 - fi - if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then - echo "ultravnc" - exit 0 - fi - # OK, run it for help output... - str=`$VNCVIEWERCMD -h 2>&1 | head -n 5` - if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then - echo "tightvnc" - elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then - echo "realvnc3" - elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then - echo "realvnc4" - elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then - echo "realvnc4" - else - echo "unknown" - fi - exit 0 -fi -if [ "X$1" = "X-viewerhelp" ]; then - $VNCVIEWERCMD -h 2>&1 - exit 0 -fi - -# grab our cmdline options: -while [ "X$1" != "X" ] -do - case $1 in - "-verify") shift; verify="$1" - ;; - "-mycert") shift; mycert="$1" - ;; - "-crl") shift; crl="$1" - ;; - "-proxy") shift; proxy="$1" - ;; - "-ssh") use_ssh=1 - ;; - "-sshssl") use_ssh=1 - use_sshssl=1 - ;; - "-sshcmd") shift; ssh_cmd="$1" - ;; - "-sshargs") shift; ssh_args="$1" - ;; - "-anondh") ciphers="ciphers=$anondh" - ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1 - export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH - anondh_set=1 - ;; - "-ciphers") shift; ciphers="ciphers=$1" - ;; - "-alpha") gotalpha=1 - ;; - "-showcert") showcert=1 - ;; - "-listen") reverse=1 - ;; - "-reverse") reverse=1 - ;; - "-2nd") secondtry=1 - ;; - "-grab") VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER - ;; - "-x11cursor") VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR - ;; - "-rawlocal") VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL - ;; - "-scale") shift; SSVNC_SCALE="$1"; export SSVNC_SCALE - ;; - "-onelisten") SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - ;; - "-sendclipboard") VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD - ;; - "-sendalways") VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS - ;; - "-recvtext") shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT - ;; - "-escape") shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE - ;; - "-ssvnc_encodings") shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS - ;; - "-ssvnc_extra_opts") shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS - ;; - "-rfbversion") shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION - ;; - "-nobell") VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL - ;; - "-popupfix") VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX - ;; - "-realvnc4") VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4 - ;; - "-h"*) help; exit 0 - ;; - "--h"*) help; exit 0 - ;; - *) break - ;; - esac - shift -done - -# maxconn is something we added to stunnel, this disables it: -if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -elif [ "X$reverse" != "X" ]; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -else - # new way (our patches). other than the above, we set these: - if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then - STUNNEL_ONCE=1; export STUNNEL_ONCE - fi - if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then - STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS - fi -fi -# always set this one: -if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then - STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG -fi - -# this is the -t ssh option (gives better keyboard response thru SSH tunnel) -targ="-t" -if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then - targ="" -fi - -# set the alpha blending env. hack: -if [ "X$gotalpha" = "X1" ]; then - VNCVIEWER_ALPHABLEND=1 - export VNCVIEWER_ALPHABLEND -else - NO_ALPHABLEND=1 - export NO_ALPHABLEND -fi - -if [ "X$reverse" != "X" ]; then - ssh_sleep=1800 - if [ "X$proxy" != "X" ]; then - # check proxy usage under reverse connection: - if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then - echo "" - if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then - : - else - echo "*Warning*: SSL -listen and a Web proxy does not make sense." - sleep 2 - fi - elif echo "$proxy" | grep "," > /dev/null; then - : - else - echo "" - echo "*Warning*: -listen and a single proxy/gateway does not make sense." - sleep 2 - fi - - # we now try to PPROXY_LOOP_THYSELF, set this var to disable that. - #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - fi -fi -if [ "X$ssh_cmd" = "X" ]; then - # if no remote ssh cmd, sleep a bit: - ssh_cmd="sleep $ssh_sleep" -fi - -# this should be a host:display: -# -orig="$1" -shift - -dL="-L" -if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then - dL="-h" -fi - -have_uvnc_dsm_helper_showcert="" -if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then - if type ultravnc_dsm_helper >/dev/null 2>&1; then - if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then - have_uvnc_dsm_helper_showcert=1 - fi - fi -fi -have_uvnc_dsm_helper_ipv6="" -if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if type ultravnc_dsm_helper >/dev/null 2>&1; then - if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then - have_uvnc_dsm_helper_ipv6=1 - fi - fi -fi - -rchk() { - # a kludge to set $RANDOM if we are not bash: - if [ "X$BASH_VERSION" = "X" ]; then - RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'` - fi -} -rchk - -# a portable, but not absolutely safe, tmp file creator -mytmp() { - tf=$1 - if type mktemp > /dev/null 2>&1; then - # if we have mktemp(1), use it: - tf2="$tf.XXXXXX" - tf2=`mktemp "$tf2"` - if [ "X$tf2" != "X" -a -f "$tf2" ]; then - if [ "X$DEBUG_MKTEMP" != "X" ]; then - echo "mytmp-mktemp: $tf2" 1>&2 - fi - echo "$tf2" - return - fi - fi - # fallback to multiple cmds: - rm -rf "$tf" || exit 1 - if [ -d "$tf" ]; then - echo "tmp file $tf still exists as a directory." - exit 1 - elif [ $dL "$tf" ]; then - echo "tmp file $tf still exists as a symlink." - exit 1 - elif [ -f "$tf" ]; then - echo "tmp file $tf still exists." - exit 1 - fi - touch "$tf" || exit 1 - chmod 600 "$tf" || exit 1 - rchk - if [ "X$DEBUG_MKTEMP" != "X" ]; then - echo "mytmp-touch: $tf" 1>&2 - fi - echo "$tf" -} - -# set up special case of ultravnc single click III mode: -if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then - pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'` - pstr1=`echo "$pstr" | sed -e 's/+.*$//'` - pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'` - SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER - orig=$pstr1 - echo - echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''" - proxy="" -fi -if echo "$proxy" | egrep "vencrypt://" > /dev/null; then - vtmp="/tmp/ss_handshake${RANDOM}.$$.txt" - vtmp=`mytmp "$vtmp"` - SSVNC_PREDIGESTED_HANDSHAKE="$vtmp" - export SSVNC_PREDIGESTED_HANDSHAKE - if [ "X$SSVNC_USE_OURS" = "X" ]; then - NEED_VENCRYPT_VIEWER_BRIDGE=1 - fi -fi -if [ "X$SSVNC_USE_OURS" = "X" ]; then - VNCVIEWERCMD_EXTRA_OPTS="" -fi - - -# check -ssh and -mycert/-verify conflict: -if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then - if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then - echo "-mycert and -verify cannot be used in -ssh mode" - exit 1 - fi -fi - -# direct mode Vnc:// means show no warnings. -# direct mode vnc:// will show warnings. -if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then - SSVNC_NO_ENC_WARN=1 - export SSVNC_NO_ENC_WARN - orig=`echo "$orig" | sed -e 's/^...:/vnc:/'` -fi - -# interprest the pseudo URL proto:// strings: -if echo "$orig" | grep '^vnc://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc://,,'` - verify="" - mycert="" - crl="" - use_ssh="" - use_sshssl="" - direct_connect=1 -elif echo "$orig" | grep '^vncs://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncs://,,'` -elif echo "$orig" | grep '^vncssl://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncssl://,,'` -elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'` -elif echo "$orig" | grep '^vncssh://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncssh://,,'` - use_ssh=1 -elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'` - use_ssh=1 -fi - -if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - verify="" - mycert="" - crl="" - use_ssh="" - use_sshssl="" - direct_connect=1 - if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then - SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM - fi -fi - -# rsh mode is an internal/secret thing only I use. -rsh="" -if echo "$orig" | grep '^rsh://' > /dev/null; then - use_ssh=1 - rsh=1 - orig=`echo "$orig" | sed -e 's,rsh://,,'` -elif echo "$orig" | grep '^rsh:' > /dev/null; then - use_ssh=1 - rsh=1 - orig=`echo "$orig" | sed -e 's,rsh:,,'` -fi - -# play around with host:display port: -if echo "$orig" | grep ':[0-9][0-9]*$' > /dev/null; then - : -else - # add or assume :0 if no ':' - if [ "X$reverse" = "X" ]; then - orig="$orig:0" - elif [ "X$orig" = "X" ]; then - orig=":0" - fi -fi - -# extract host and disp number: - -# try to see if it is ipv6 address: -ipv6=0 -if echo "$orig" | grep '\[' > /dev/null; then - # ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900 - host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'` - disp=`echo "$orig" | sed -e 's/^.*\]://'` - ipv6=1 -elif echo "$orig" | grep ':..*:' > /dev/null; then - # ipv6 fe80::219:dbff:fee5:3f92%eth1:5900 - host=`echo "$orig" | sed -e 's/:[^:]*$//'` - disp=`echo "$orig" | sed -e 's/^.*://'` - ipv6=1 -else - # regular host:port - host=`echo "$orig" | awk -F: '{print $1}'` - disp=`echo "$orig" | awk -F: '{print $2}'` -fi - -if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then - STUNNEL_LISTEN=$host - echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN" -fi - -if [ "X$host" = "X" ]; then - host=$localhost -fi - -if [ "X$SSVNC_IPV6" = "X0" ]; then - # disable checking for it. - ipv6=0 -#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then -# ipv6=0 -elif [ "X$ipv6" = "X1" ]; then - : -elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then - : -else - # regular hostname, can't be sure... - gout="" - if type getent > /dev/null 2>/dev/null; then - gout=`getent hosts "$host" 2>/dev/null` - fi - if echo "$gout" | grep ':.*:' > /dev/null; then - if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then - : - else - echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1` - ipv6=1 - fi - fi - if [ "X$ipv6" = "X0" ]; then - hout="" - if type host > /dev/null 2>/dev/null; then - host "$host" >/dev/null 2>&1 - host "$host" >/dev/null 2>&1 - hout=`host "$host" 2>/dev/null` - fi - if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then - if echo "$hout" | grep -i 'has address' > /dev/null; then - : - else - echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1` - ipv6=1 - fi - fi - fi - if [ "X$ipv6" = "X0" ]; then - dout="" - if type dig > /dev/null 2>/dev/null; then - dout=`dig -t any "$host" 2>/dev/null` - fi - if echo "$dout" | grep -i "^$host" | grep '[ ]AAAA[ ]' > /dev/null; then - if echo "$dout" | grep -i "^$host" | grep '[ ]A[ ]' > /dev/null; then - : - else - echo "ipv6: "`echo "$dout" | grep -i '[ ]AAAA[ ]' | head -n 1` - ipv6=1 - fi - fi - fi - if [ "X$ipv6" = "X0" ]; then - sout=`env LOOKUP="$host" \ - perl -e ' eval {use Socket}; exit 0 if $@; - eval {use Socket6}; exit 0 if $@; - @res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM); - $ipv4 = 0; - $ipv6 = 0; - $ip6 = ""; - while (scalar(@res) >= 5) { - ($family, $socktype, $proto, $saddr, $canon, @res) = @res; - $ipv4 = 1 if $family == AF_INET; - $ipv6 = 1 if $family == AF_INET6; - if ($family == AF_INET6 && $ip6 eq "") { - my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV); - $ip6 = $host; - } - } - if (! $ipv4 && $ipv6) { - print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n"; - } - exit 0; - ' 2>/dev/null` - if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then - echo "$sout" - ipv6=1 - fi - fi -fi -if [ "X$ipv6" = "X1" ]; then - echo "ipv6: addr=$host disp=$disp" -fi -if [ "X$disp" = "X" ]; then - port="" # probably -listen mode. -elif [ $disp -lt 0 ]; then - # negative means use |n| without question: - port=`expr 0 - $disp` -elif [ $disp -lt 200 ]; then - # less than 200 means 5900+n - if [ "X$reverse" = "X" ]; then - port=`expr $disp + 5900` - else - port=`expr $disp + 5500` - fi -else - # otherwise use the number directly, e.g. 443, 2345 - port=$disp -fi - -if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then - if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then - if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then - : - elif [ "X$SSVNC_USE_OURS" = "X1" ]; then - # requires 1.0.27 and later ssvncviewer binary - : - else - proxy="ipv6://$host:$port" - echo "direct connect: set proxy=$proxy" - fi - fi -fi - -# (possibly) tell the vncviewer to only listen on lo: -if [ "X$reverse" != "X" ]; then - if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then - VNCVIEWER_LISTEN_LOCALHOST=1 - export VNCVIEWER_LISTEN_LOCALHOST - fi -fi - -# try to find an open listening port via netstat(1): -inuse="" -if uname | grep Linux > /dev/null; then - inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'` -elif uname | grep SunOS > /dev/null; then - inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'` -elif uname | egrep -i 'bsd|darwin' > /dev/null; then - inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'` -# add others... -fi - -# this is a crude attempt for unique ports tags, etc. -date_sec=`date +%S` - -# these are special cases of no vnc, e.g. sleep or xmessage. -# these are for using ssvnc as a general port redirector. -if echo "$VNCVIEWERCMD" | grep '^sleep[ ][ ]*[0-9][0-9]*' > /dev/null; then - if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then - p=`echo "$VNCVIEWERCMD" | awk '{print $3}'` - if [ "X$p" != "X" ]; then - SS_VNCVIEWER_LISTEN_PORT=$p - fi - fi - p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'` - VNCVIEWERCMD="eval sleep $p2; echo Local " -elif echo "$VNCVIEWERCMD" | grep '^xmessage[ ][ ]*[0-9][0-9]*' > /dev/null; then - if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then - p=`echo "$VNCVIEWERCMD" | awk '{print $2}'` - SS_VNCVIEWER_LISTEN_PORT=$p - fi -fi - -# utility to find a free port to listen on. -findfree() { - try0=$1 - try=$try0 - use0="" - - if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then - echo "$SS_VNCVIEWER_LISTEN_PORT" - return - fi - if [ $try -ge 6000 ]; then - fmax=`expr $try + 1000` - else - fmax=6000 - fi - - while [ $try -lt $fmax ] - do - if [ "X$inuse" = "X" ]; then - break - fi - if echo "$inuse" | grep -w $try > /dev/null; then - : - else - use0=$try - break - fi - try=`expr $try + 1` - done - if [ "X$use0" = "X" ]; then - use0=`expr $date_sec + $try0` - fi - - echo $use0 -} - -# utility for exiting; kills some helper processes, -# removes files, etc. -final() { - echo "" - if [ "X$tmp_cfg" != "X" ]; then - rm -f $tmp_cfg - fi - if [ "X$SS_VNCVIEWER_RM" != "X" ]; then - rm -f $SS_VNCVIEWER_RM 2>/dev/null - fi - if [ "X$tcert" != "X" ]; then - rm -f $tcert - fi - if [ "X$pssh" != "X" ]; then - echo "Terminating background ssh process" - echo kill -TERM "$pssh" - kill -TERM "$pssh" 2>/dev/null - sleep 1 - kill -KILL "$pssh" 2>/dev/null - pssh="" - fi - if [ "X$stunnel_pid" != "X" ]; then - echo "Terminating background stunnel process" - echo kill -TERM "$stunnel_pid" - kill -TERM "$stunnel_pid" 2>/dev/null - sleep 1 - kill -KILL "$stunnel_pid" 2>/dev/null - stunnel_pid="" - fi - if [ "X$dsm_pid" != "X" ]; then - echo "Terminating background ultravnc_dsm_helper process" - echo kill -TERM "$dsm_pid" - kill -TERM "$dsm_pid" 2>/dev/null - sleep 1 - kill -KILL "$dsm_pid" 2>/dev/null - stunnel_pid="" - fi - if [ "X$tail_pid" != "X" ]; then - kill -TERM $tail_pid - fi - if [ "X$tail_pid2" != "X" ]; then - kill -TERM $tail_pid2 - fi -} - -if [ "X$reverse" = "X" ]; then - # normal connections try 5930-5999: - if [ "X$showcert" = "X" ]; then - use=`findfree 5930` - else - # move away from normal place for (possibly many) -showcert - pstart=`date +%S` - pstart=`expr 6130 + $pstart + $pstart` - use=`findfree $pstart` - fi - if [ $use -ge 5900 ]; then - N=`expr $use - 5900` - else - N=$use - fi -else - # reverse connections: - p2=`expr $port + 30` - use=`findfree $p2` - if [ $use -ge 5500 ]; then - N=`expr $use - 5500` - else - N=$use - fi -fi - -# this is for my special use of ss_vncip -> vncip viewer. -if echo "$0" | grep vncip > /dev/null; then - VNCVIEWERCMD="$VNCIPCMD" -fi - -if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - : -elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then - VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS" -fi - -# trick for the undocumented rsh://host:port method. -rsh_setup() { - if echo "$ssh_host" | grep '@' > /dev/null; then - ul=`echo "$ssh_host" | awk -F@ '{print $1}'` - ul="-l $ul" - ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'` - else - ul="" - fi - ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'` -} - -# trick for the undocumented rsh://host:port method. -rsh_viewer() { - trap "final" 0 2 15 - if [ "X$PORT" = "X" ]; then - exit 1 - elif [ $PORT -ge 5900 ]; then - vdpy=`expr $PORT - 5900` - else - vdpy=":$PORT" - fi - stty sane - echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy - echo "" - $VNCVIEWERCMD "$@" $ssh_host:$vdpy - if [ $? != 0 ]; then - sleep 2 - $VNCVIEWERCMD "$@" $ssh_host:$vdpy - fi -} - -check_perl() { - if type "$1" > /dev/null 2>&1; then - : - elif [ ! -x "$1" ]; then - echo "" - echo "*******************************************************" - echo "** Problem finding the Perl command '$1': **" - echo "" - type "perl" - echo "" - echo "** Perhaps you need to install the Perl package. **" - echo "*******************************************************" - echo "" - sleep 5 - fi -} - -# this is the PPROXY tool. used only here for now... -pcode() { - tf=$1 - PPROXY_PROXY=$proxy; export PPROXY_PROXY - PPROXY_DEST="$host:$port"; export PPROXY_DEST - check_perl /usr/bin/perl - - cod='#!/usr/bin/perl - -# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for -# client connections. -# Also acts as a VeNCrypt bridge (by redirecting to stunnel.) - -use IO::Socket::INET; - -my $have_inet6 = ""; -eval "use IO::Socket::INET6;"; -$have_inet6 = 1 if $@ eq ""; - -#my $have_sock6 = ""; -#eval "use Socket; use Socket6;"; -#$have_sock6 = 1 if $@ eq ""; - -if (exists $ENV{PPROXY_LOOP_THYSELF}) { - # used for reverse vnc, run a repeating outer loop. - print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n"; - my $rm = $ENV{PPROXY_REMOVE}; - my $lp = $ENV{PPROXY_LOOP_THYSELF}; - delete $ENV{PPROXY_REMOVE}; - delete $ENV{PPROXY_LOOP_THYSELF}; - $ENV{PPROXY_LOOP_THYSELF_MASTER} = $$; - my $pid = $$; - my $dbg = 0; - my $c = 0; - use POSIX ":sys_wait_h"; - while (1) { - $pid = fork(); - last if ! defined $pid; - if ($pid eq "0") { - last; - } - $c++; - print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n"; - while (1) { - waitpid(-1, WNOHANG); - fsleep(0.25); - if (! kill 0, $pid) { - print STDERR "PPROXY_LOOP: child=$pid gone.\n"; - last; - } - print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg; - if (! -f $lp) { - print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n"; - kill TERM, $pid; - fsleep(0.1); - wait; - last; - } - print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg; - } - last if ! -f $lp; - fsleep(0.25); - } - if ($pid ne "0") { - unlink($0) if $rm; - exit 0; - } -} - -if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) { - print STDERR "PPROXY_PID: $$\n"; - sleep $ENV{PPROXY_SLEEP}; -} - -foreach my $var (qw( - PPROXY_DEST - PPROXY_KILLPID - PPROXY_LISTEN - PPROXY_PROXY - PPROXY_REMOVE - PPROXY_REPEATER - PPROXY_REVERSE - PPROXY_SLEEP - PPROXY_SOCKS - PPROXY_VENCRYPT - PPROXY_VENCRYPT_VIEWER_BRIDGE - )) { - if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) { - print STDERR "$var: $ENV{$var}\n"; - } -} - -if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) { - if ($ENV{PPROXY_SOCKS} eq "5") { - $ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}"; - } else { - $ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}"; - } -} - -my $rfbSecTypeAnonTls = 18; -my $rfbSecTypeVencrypt = 19; - -my $rfbVencryptPlain = 256; -my $rfbVencryptTlsNone = 257; -my $rfbVencryptTlsVnc = 258; -my $rfbVencryptTlsPlain = 259; -my $rfbVencryptX509None = 260; -my $rfbVencryptX509Vnc = 261; -my $rfbVencryptX509Plain = 262; - -my $handshake_file = ""; -if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE}) { - $handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE}; -} - -my $have_gettimeofday = 0; -eval "use Time::HiRes;"; -if ($@ eq "") { - $have_gettimeofday = 1; -} -sub gettime { - my $t = "0.0"; - if ($have_gettimeofday) { - $t = Time::HiRes::gettimeofday(); - } - return $t; -} - -my $listen_handle = ""; -my $sock = ""; -my $parent = $$; - -my $initial_data = ""; - -if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { - my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}); - do_vencrypt_viewer_bridge($from, $to); - exit 0; -} - -my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3); -my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", ""); - -($first, $mode_1st) = url_parse($first); - -my ($proxy_host, $proxy_port) = ($first, ""); -if ($proxy_host =~ /^(.*):(\d+)$/) { - $proxy_host = $1; - $proxy_port = $2; -} -my $connect = $ENV{PPROXY_DEST}; - -if ($second ne "") { - ($second, $mode_2nd) = url_parse($second); -} - -if ($third ne "") { - ($third, $mode_3rd) = url_parse($third); -} - - -print STDERR "\n"; -print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n"; -print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n"; -print STDERR "proxy_host: $proxy_host\n"; -print STDERR "proxy_port: $proxy_port\n"; -print STDERR "proxy_connect: $connect\n"; -print STDERR "pproxy_params: $ENV{PPROXY_PROXY}\n"; -print STDERR "pproxy_listen: $ENV{PPROXY_LISTEN}\n"; -print STDERR "pproxy_reverse: $ENV{PPROXY_REVERSE}\n"; -print STDERR "io_socket_inet6: $have_inet6\n"; -print STDERR "\n"; -if (! $have_inet6) { - print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n"; -} - -if (1) { - print STDERR "pproxy 1st: $first\t- $mode_1st\n"; - print STDERR "pproxy 2nd: $second\t- $mode_2nd\n"; - print STDERR "pproxy 3rd: $third\t- $mode_3rd\n"; - print STDERR "\n"; -} - -sub pdie { - my $msg = shift; - kill_proxy_pids(); - die "$msg"; -} - -if ($ENV{PPROXY_REVERSE} ne "") { - my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, ""); - if ($rhost =~ /^(.*):(\d+)$/) { - $rhost = $1; - $rport = $2; - } - $rport = 5900 unless $rport; - my $emsg = ""; - $listen_handle = IO::Socket::INET->new( - PeerAddr => $rhost, - PeerPort => $rport, - Proto => "tcp" - ); - $emsg = $!; - if (! $listen_handle && $have_inet6) { - eval {$listen_handle = IO::Socket::INET6->new( - PeerAddr => $rhost, - PeerPort => $rport, - Proto => "tcp" - );}; - $emsg .= " / $!"; - } - if (! $listen_handle) { - pdie "pproxy: $emsg -- PPROXY_REVERSE\n"; - } - print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n"; - -} elsif ($ENV{PPROXY_LISTEN} ne "") { - my $listen_sock = ""; - my $maxtry = 12; - my $sleep = 5; - my $p2 = ""; - my $emsg = ""; - for (my $i=0; $i < $maxtry; $i++) { - my ($if, $p) = ("", $ENV{PPROXY_LISTEN}); - if ($p =~ /^(.*):(\d+)$/) { - $if = $1; - $p = $2; - } - $p2 = "*:$p"; - if ($if eq "") { - $if = "localhost"; - } - print STDERR "pproxy interface: $if\n"; - - $emsg = ""; - if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) { - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::", - LocalPort => $p, - Proto => "tcp" - );}; - $p2 = ":::$p"; - } elsif ($if =~ /^INADDR_ANY/) { - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalPort => $p, - Proto => "tcp" - ); - } elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) { - $p2 = "::1:$p"; - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::1", - LocalPort => $p, - Proto => "tcp" - );}; - $p2 = "::1:$p"; - } else { - $p2 = "$if:$p"; - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalAddr => $if, - LocalPort => $p, - Proto => "tcp" - ); - $emsg = $!; - - if (! $listen_sock && $have_inet6) { - print STDERR "PPROXY_LISTEN: retry with INET6\n"; - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => $if, - LocalPort => $p, - Proto => "tcp" - );}; - $emsg .= " / $!"; - } - } - if (! $listen_sock) { - if ($i < $maxtry - 1) { - warn "pproxy: $emsg $!\n"; - warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n"; - sleep $sleep; - } - } else { - last; - } - } - if (! $listen_sock) { - pdie "pproxy: $emsg -- PPROXY_LISTEN\n"; - } - print STDERR "pproxy: listening on $p2\n"; - my $ip; - ($listen_handle, $ip) = $listen_sock->accept(); - my $err = $!; - close $listen_sock; - if (! $listen_handle) { - pdie "pproxy: $err\n"; - } - - if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { - my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; - if ($sml ne "" && $sml ne "0") { - setpgrp(0, 0); - if (fork()) { - close $viewer_sock; - wait; - exit 0; - } - if (fork()) { - close $viewer_sock; - exit 0; - } - setpgrp(0, 0); - $parent = $$; - } - } -} - -$sock = IO::Socket::INET->new( - PeerAddr => $proxy_host, - PeerPort => $proxy_port, - Proto => "tcp" -); - -my $err = ""; - -if (! $sock && $have_inet6) { - $err = $!; - - print STDERR "pproxy: $!\n"; - - eval {$sock = IO::Socket::INET6->new( - PeerAddr => $proxy_host, - PeerPort => $proxy_port, - Proto => "tcp" - );}; - $err .= " / $!"; -} - -if (! $sock && ($proxy_host =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i || $proxy_host =~ /^::ffff:([\da-f]+:[\da-f]+)$/i)) { - print STDERR "pproxy: $!\n"; - my $ipv4_addr = $1; - if ($ipv4_addr =~ /:/) { - my ($a, $b) = split(/:/, $ipv4_addr); - $a = hex($a); - $b = hex($b); - $ipv4_addr = sprintf("%d.", ($a & 0xff00) >> 8); - $ipv4_addr .= sprintf("%d.", ($a & 0x00ff)); - $ipv4_addr .= sprintf("%d.", ($b & 0xff00) >> 8); - $ipv4_addr .= sprintf("%d", ($b & 0x00ff)); - } - - print STDERR "pproxy: re-trying with ipv4 addr: $ipv4_addr\n"; - - eval {$sock = IO::Socket::INET->new( - PeerAddr => $ipv4_addr, - PeerPort => $proxy_port, - Proto => "tcp" - );}; - $err .= " / $!"; -} - -if (! $sock) { - unlink($0) if $ENV{PPROXY_REMOVE}; - pdie "pproxy: $err\n"; -} - -unlink($0) if $ENV{PPROXY_REMOVE}; - -if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) { - print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n"; - my $tmp_swap = $sock; - $sock = $listen_handle; - $listen_handle = $tmp_swap; -} - -$cur_proxy = $first; -setmode($mode_1st); - -if ($second ne "") { - connection($second, 1); - - setmode($mode_2nd); - $cur_proxy = $second; - - if ($third ne "") { - connection($third, 2); - setmode($mode_3rd); - $cur_proxy = $third; - connection($connect, 3); - } else { - connection($connect, 2); - } -} else { - connection($connect, 1); -} - -sub kill_proxy_pids() { - if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { - return; - } - if ($ENV{PPROXY_KILLPID}) { - foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) { - if ($p =~ /^(\+|-)/) { - $p = $parent + $p; - } - print STDERR "kill TERM, $p (PPROXY_KILLPID)\n"; - kill "TERM", $p; - } - } -} - -sub xfer { - my($in, $out) = @_; - $RIN = $WIN = $EIN = ""; - $ROUT = ""; - vec($RIN, fileno($in), 1) = 1; - vec($WIN, fileno($in), 1) = 1; - $EIN = $RIN | $WIN; - - while (1) { - my $nf = 0; - while (! $nf) { - $nf = select($ROUT=$RIN, undef, undef, undef); - } - my $len = sysread($in, $buf, 8192); - if (! defined($len)) { - next if $! =~ /^Interrupted/; - print STDERR "pproxy[$$]: $!\n"; - last; - } elsif ($len == 0) { - print STDERR "pproxy[$$]: Input is EOF.\n"; - last; - } - my $offset = 0; - my $quit = 0; - while ($len) { - my $written = syswrite($out, $buf, $len, $offset); - if (! defined $written) { - print STDERR "pproxy[$$]: Output is EOF. $!\n"; - $quit = 1; - last; - } - $len -= $written; - $offset += $written; - } - last if $quit; - } - close($out); - close($in); - print STDERR "pproxy[$$]: finished xfer.\n"; -} - -sub handler { - print STDERR "pproxy[$$]: got SIGTERM.\n"; - close $listen_handle if $listen_handle; - close $sock if $sock; - exit; -} - -sub xfer_both { - $child = fork; - - if (! defined $child) { - kill_proxy_pids(); - exit 1; - } - - $SIG{TERM} = "handler"; - - if ($child) { - if ($listen_handle) { - print STDERR "pproxy parent[$$] listen_handle -> socket\n"; - xfer($listen_handle, $sock); - } else { - print STDERR "pproxy parent[$$] STDIN -> socket\n"; - xfer(STDIN, $sock); - } - select(undef, undef, undef, 0.25); - if (kill 0, $child) { - select(undef, undef, undef, 0.9); - if (kill 0, $child) { - print STDERR "pproxy[$$]: kill TERM child $child\n"; - kill "TERM", $child; - } else { - print STDERR "pproxy[$$]: child $child gone.\n"; - } - } - } else { - select(undef, undef, undef, 0.05); - if ($listen_handle) { - print STDERR "pproxy child [$$] socket -> listen_handle\n"; - if ($initial_data ne "") { - my $len = length $initial_data; - print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; - syswrite($listen_handle, $initial_data, $len); - } else { - print STDERR "\n"; - } - xfer($sock, $listen_handle); - } else { - print STDERR "pproxy child [$$] socket -> STDOUT\n"; - if ($initial_data ne "") { - my $len = length $initial_data; - print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; - syswrite(STDOUT, $initial_data, $len); - } else { - print STDERR "\n"; - } - xfer($sock, STDOUT); - } - select(undef, undef, undef, 0.25); - if (kill 0, $parent) { - select(undef, undef, undef, 0.8); - if (kill 0, $parent) { - print STDERR "pproxy[$$]: kill TERM parent $parent\n"; - kill "TERM", $parent; - } else { - print STDERR "pproxy[$$]: parent $parent gone.\n"; - } - } - } - - kill_proxy_pids(); -} - -xfer_both(); - -exit; - -sub fsleep { - select(undef, undef, undef, shift); -} - -sub url_parse { - my $hostport = shift; - my $mode = "http"; - if ($hostport =~ m,^socks4?://(\S*)$,i) { - $mode = "socks4"; - $hostport = $1; - } elsif ($hostport =~ m,^socks5://(\S*)$,i) { - $mode = "socks5"; - $hostport = $1; - } elsif ($hostport =~ m,^https?://(\S*)$,i) { - $mode = "http"; - $hostport = $1; - } elsif ($hostport =~ m,^ipv6://(\S*)$,i) { - $mode = "ipv6"; - $hostport = $1; - } elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) { - # ultravnc repeater proxy. - $hostport = $1; - $mode = "repeater:$2"; - if ($hostport !~ /:\d+$/) { - $hostport .= ":5900"; - } - } elsif ($hostport =~ m,^vencrypt://(\S*)$,i) { - # vencrypt handshake. - $hostport = $1; - my $m = "connect"; - if ($hostpost =~ /^(\S+)\+(\S+)$/) { - $hostport = $1; - $mode = $2; - } - $mode = "vencrypt:$m"; - if ($hostport !~ /:\d+$/) { - $hostport .= ":5900"; - } - } - return ($hostport, $mode); -} - -sub setmode { - my $mode = shift; - $ENV{PPROXY_REPEATER} = ""; - $ENV{PPROXY_VENCRYPT} = ""; - if ($mode =~ /^socks/) { - if ($mode =~ /^socks5/) { - $ENV{PPROXY_SOCKS} = 5; - } else { - $ENV{PPROXY_SOCKS} = 1; - } - } elsif ($mode =~ /^ipv6/i) { - $ENV{PPROXY_SOCKS} = 0; - } elsif ($mode =~ /^repeater:(.*)/) { - $ENV{PPROXY_REPEATER} = $1; - $ENV{PPROXY_SOCKS} = ""; - } elsif ($mode =~ /^vencrypt:(.*)/) { - $ENV{PPROXY_VENCRYPT} = $1; - $ENV{PPROXY_SOCKS} = ""; - } else { - $ENV{PPROXY_SOCKS} = ""; - } -} - -sub connection { - my ($CONNECT, $w) = @_; - - my $con = ""; - my $msg = ""; - - if ($ENV{PPROXY_SOCKS} eq "5") { - # SOCKS5 - my ($h, $p) = ($CONNECT, ""); - if ($h =~ /^(.*):(\d+)$/) { - $h = $1; - $p = $2; - } - $con .= pack("C", 0x05); - $con .= pack("C", 0x01); - $con .= pack("C", 0x00); - - $msg = "SOCKS5 via $cur_proxy to $h:$p\n\n"; - print STDERR "proxy_request$w: $msg"; - - syswrite($sock, $con, length($con)); - - my ($n1, $n2, $n3, $n4, $n5, $n6); - my ($r1, $r2, $r3, $r4, $r5, $r6); - my ($s1, $s2, $s3, $s4, $s5, $s6); - - $n1 = sysread($sock, $r1, 1); - $n2 = sysread($sock, $r2, 1); - - $s1 = unpack("C", $r1); - $s2 = unpack("C", $r2); - if ($s1 != 0x05 || $s2 != 0x00) { - print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n"; - close $sock; - exit(1); - } - - $con = ""; - $con .= pack("C", 0x05); - $con .= pack("C", 0x01); - $con .= pack("C", 0x00); - $con .= pack("C", 0x03); - $con .= pack("C", length($h)); - $con .= $h; - $con .= pack("C", $p >> 8); - $con .= pack("C", $p & 0xff); - - syswrite($sock, $con, length($con)); - - $n1 = sysread($sock, $r1, 1); - $n2 = sysread($sock, $r2, 1); - $n3 = sysread($sock, $r3, 1); - $n4 = sysread($sock, $r4, 1); - $s1 = unpack("C", $r1); - $s2 = unpack("C", $r2); - $s3 = unpack("C", $r3); - $s4 = unpack("C", $r4); - - if ($s4 == 0x1) { - sysread($sock, $r5, 4 + 2); - } elsif ($s4 == 0x3) { - sysread($sock, $r5, 1); - $s5 = unpack("C", $r5); - sysread($sock, $r6, $s5 + 2); - } elsif ($s4 == 0x4) { - sysread($sock, $r5, 16 + 2); - } - - if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) { - print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n"; - close $sock; - exit(1); - } - - } elsif ($ENV{PPROXY_SOCKS} eq "1") { - # SOCKS4 SOCKS4a - my ($h, $p) = ($CONNECT, ""); - if ($h =~ /^(.*):(\d+)$/) { - $h = $1; - $p = $2; - } - $con .= pack("C", 0x04); - $con .= pack("C", 0x01); - $con .= pack("n", $p); - - my $SOCKS_4a = 0; - if ($h eq "localhost" || $h eq "127.0.0.1") { - $con .= pack("C", 127); - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 1); - } elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { - $con .= pack("C", $1); - $con .= pack("C", $2); - $con .= pack("C", $3); - $con .= pack("C", $4); - } else { - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 3); - $SOCKS_4a = 1; - } - - $con .= "nobody"; - $con .= pack("C", 0); - - $msg = "SOCKS4 via $cur_proxy to $h:$p\n\n"; - if ($SOCKS_4a) { - $con .= $h; - $con .= pack("C", 0); - $msg =~ s/SOCKS4/SOCKS4a/; - } - print STDERR "proxy_request$w: $msg"; - syswrite($sock, $con, length($con)); - - my $ok = 1; - for (my $i = 0; $i < 8; $i++) { - my $c; - sysread($sock, $c, 1); - my $s = unpack("C", $c); - if ($i == 0) { - $ok = 0 if $s != 0x0; - } elsif ($i == 1) { - $ok = 0 if $s != 0x5a; - } - } - if (! $ok) { - print STDERR "SOCKS4 failed.\n"; - close $sock; - exit(1); - } - } elsif ($ENV{PPROXY_SOCKS} eq "0") { - # hack for ipv6 "proxy", nothing to do, assume INET6 call worked. - ; - } elsif ($ENV{PPROXY_REPEATER} ne "") { - my $rep = $ENV{PPROXY_REPEATER}; - print STDERR "repeater: $rep\n"; - $rep .= pack("x") x 250; - syswrite($sock, $rep, 250); - - my $rfb = ""; - - my $ok = 1; - for (my $i = 0; $i < 12; $i++) { - my $c; - last if $ENV{PPROXY_GENERIC_REPEATER}; - sysread($sock, $c, 1); - print STDERR $c; - $rfb .= $c; - } - if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) { - $initial_data = $rfb; - $rfb =~ s/\n//g; - print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n"; - } - } elsif ($ENV{PPROXY_VENCRYPT} ne "") { - my $vencrypt = $ENV{PPROXY_VENCRYPT}; - vencrypt_dialog($vencrypt); - - } else { - # Web Proxy: - $con = "CONNECT $CONNECT HTTP/1.1\r\n"; - $con .= "Host: $CONNECT\r\n"; - $con .= "Connection: close\r\n\r\n"; - $msg = $con; - - print STDERR "proxy_request$w: via $cur_proxy:\n$msg"; - syswrite($sock, $con, length($con)); - - my $rep = ""; - my $n = 0; - while ($rep !~ /\r\n\r\n/ && $n < 30000) { - my $c; - sysread($sock, $c, 1); - print STDERR $c; - $rep .= $c; - $n++; - } - if ($rep !~ m,HTTP/.* 200,) { - print STDERR "HTTP CONNECT failed.\n"; - close $sock; - exit(1); - } - } -} - -sub vdie { - append_handshake("done\n"); - close $sock; - kill_proxy_pids(); - exit(1); -} - -sub anontls_handshake { - my ($vmode, $db) = @_; - - print STDERR "\nPPROXY: Doing ANONTLS Handshake\n"; - - my $psec = pack("C", $rfbSecTypeAnonTls); - syswrite($sock, $psec, 1); - - append_handshake("done\n"); -} - -sub vencrypt_handshake { - - my ($vmode, $db) = @_; - - print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n"; - - my $psec = pack("C", $rfbSecTypeVencrypt); - - if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { - my $fake = $ENV{SSVNC_TEST_SEC_TYPE}; - print STDERR "PPROXY: sending sec-type: $fake\n"; - $psec = pack("C", $fake); - } - - syswrite($sock, $psec, 1); - - my $vmajor; - my $vminor; - sysread($sock, $vmajor, 1); - sysread($sock, $vminor, 1); - - vdie if $vmajor eq "" || $vminor eq ""; - - $vmajor = unpack("C", $vmajor); - $vminor = unpack("C", $vminor); - print STDERR "server vencrypt version $vmajor.$vminor\n" if $db; - - if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { - print STDERR "PPROXY: continuing on in test mode.\n"; - } else { - vdie if $vmajor ne 0; - vdie if $vminor < 2; - } - - $vmajor = pack("C", 0); - $vminor = pack("C", 2); - append_handshake("subversion=0.2\n"); - - syswrite($sock, $vmajor, 1); - syswrite($sock, $vminor, 1); - - my $result; - sysread($sock, $result, 1); - print STDERR "result empty\n" if $db && $result eq ""; - - vdie if $result eq ""; - $result = unpack("C", $result); - print STDERR "result=$result\n" if $db; - - vdie if $result ne 0; - - my $nsubtypes; - sysread($sock, $nsubtypes, 1); - - vdie if $nsubtypes eq ""; - $nsubtypes = unpack("C", $nsubtypes); - print STDERR "nsubtypes=$nsubtypes\n" if $db; - - my %subtypes; - - for (my $i = 0; $i < $nsubtypes; $i++) { - my $subtype = ""; - sysread($sock, $subtype, 4); - vdie if length($subtype) != 4; - - # XXX fix 64bit. - $subtype = unpack("N", $subtype); - print STDERR "subtype: $subtype\n" if $db; - $subtypes{$subtype} = 1; - append_handshake("sst$i=$subtype\n"); - } - - my $subtype = 0; - if (exists $subtypes{$rfbVencryptX509None}) { - $subtype = $rfbVencryptX509None; - print STDERR "selected rfbVencryptX509None\n" if $db; - } elsif (exists $subtypes{$rfbVencryptX509Vnc}) { - $subtype = $rfbVencryptX509Vnc; - print STDERR "selected rfbVencryptX509Vnc\n" if $db; - } elsif (exists $subtypes{$rfbVencryptX509Plain}) { - $subtype = $rfbVencryptX509Plain; - print STDERR "selected rfbVencryptX509Plain\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsNone}) { - $subtype = $rfbVencryptTlsNone; - print STDERR "selected rfbVencryptTlsNone\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsVnc}) { - $subtype = $rfbVencryptTlsVnc; - print STDERR "selected rfbVencryptTlsVnc\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsPlain}) { - $subtype = $rfbVencryptTlsPlain; - print STDERR "selected rfbVencryptTlsPlain\n" if $db; - } - - if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { - my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE}; - print STDERR "PPROXY: sending sec-subtype: $fake\n"; - $subtype = $fake; - } - - append_handshake("subtype=$subtype\n"); - - my $pst = pack("N", $subtype); - syswrite($sock, $pst, 4); - - if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { - print STDERR "PPROXY: continuing on in test mode.\n"; - } else { - vdie if $subtype == 0; - } - - my $ok; - sysread($sock, $ok, 1); - $ok = unpack("C", $ok); - print STDERR "ok=$ok\n" if $db; - - append_handshake("done\n"); - - vdie if $ok == 0; -} - -sub vencrypt_dialog { - my $vmode = shift; - my $db = 0; - - $db = 1 if exists $ENV{SS_DEBUG}; - $db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG}; - - append_handshake("mode=$vmode\n"); - - my $server_rfb = ""; - #syswrite($sock, $rep, 250); - for (my $i = 0; $i < 12; $i++) { - my $c; - sysread($sock, $c, 1); - $server_rfb .= $c; - print STDERR $c; - } - print STDERR "server_rfb: $server_rfb\n" if $db; - append_handshake("server=$server_rfb"); - - my $minor = ""; - if ($server_rfb =~ /^RFB 003\.(\d+)/) { - $minor = $1; - } else { - vdie; - } - my $viewer_rfb = "RFB 003.008\n"; - if ($minor < 7) { - vdie; - } elsif ($minor == 7) { - $viewer_rfb = "RFB 003.007\n"; - } - my $nsec; - my $t1 = gettime(); - my $t0 = gettime(); - - syswrite($sock, $viewer_rfb, 12); - sysread($sock, $nsec, 1); - - $t1 = gettime(); - $t1 = sprintf("%.6f", $t1 - $t0); - - append_handshake("viewer=$viewer_rfb"); - append_handshake("latency=$t1\n"); - - vdie if $nsec eq ""; - - $nsec = unpack("C", $nsec); - - print STDERR "nsec: $nsec\n" if $db; - vdie if $nsec eq 0 || $nsec > 100; - - my %sectypes = (); - - for (my $i = 0; $i < $nsec; $i++) { - my $sec; - sysread($sock, $sec, 1); - vdie if $sec eq ""; - $sec = unpack("C", $sec); - print STDERR "sec: $sec\n" if $db; - $sectypes{$sec} = 1; - } - - if (exists $sectypes{$rfbSecTypeVencrypt}) { - print STDERR "found rfbSecTypeVencrypt\n" if $db; - append_handshake("sectype=$rfbSecTypeVencrypt\n"); - vencrypt_handshake($vmode, $db); - } elsif (exists $sectypes{$rfbSecTypeAnonTls}) { - print STDERR "found rfbSecTypeAnonTls\n" if $db; - append_handshake("sectype=$rfbSecTypeAnonTls\n"); - anontls_handshake($vmode, $db); - } else { - print STDERR "No supported sec-type found\n" if $db; - vdie; - } -} - -sub append_handshake { - my $str = shift; - if ($handshake_file) { - if (open(HSF, ">>$handshake_file")) { - print HSF $str; - close HSF; - } - } -} - -sub do_vencrypt_viewer_bridge { - my ($listen, $connect) = @_; - print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n"; - my $db = 0; - my $backwards = 0; - if ($listen < 0) { - $backwards = 1; - $listen = -$listen; - } - if ($handshake_file eq "") { - die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n"; - } - my $listen_sock; - my $maxtry = 12; - my $sleep = 5; - for (my $i=0; $i < $maxtry; $i++) { - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalAddr => "127.0.0.1", - LocalPort => $listen, - Proto => "tcp" - ); - if (! $listen_sock) { - if ($i < $maxtry - 1) { - warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; - warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n"; - sleep $sleep; - } - } else { - last; - } - } - if (! $listen_sock) { - die "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n"; - my ($viewer_sock, $ip) = $listen_sock->accept(); - my $err = $!; - close $listen_sock; - if (! $viewer_sock) { - die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; - } - if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { - my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; - if ($sml ne "" && $sml ne "0") { - setpgrp(0, 0); - if (fork()) { - close $viewer_sock; - wait; - exit 0; - } - if (fork()) { - close $viewer_sock; - exit 0; - } - setpgrp(0, 0); - $parent = $$; - } - } - print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db; - - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n"; - my $server_sock = IO::Socket::INET->new( - PeerAddr => "127.0.0.1", - PeerPort => $connect, - Proto => "tcp" - ); - print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db; - if (! $server_sock) { - my $err = $!; - die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; - } - - if ($backwards) { - print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n"; - my $t = $viewer_sock; - $viewer_sock = $server_sock; - $server_sock = $t; - } - - my %hs = (); - my $dt = 0.2; - my $slept = 0.0; - while ($slept < 20.0) { - select(undef, undef, undef, $dt); - $slept += $dt; - if (-f $handshake_file && open(HSF, "<$handshake_file")) { - my $done = 0; - %hs = (); - my $str = ""; - while () { - print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG}; - $str .= "vencrypt_viewer_bridge[$$]: $_"; - chomp; - if ($_ eq "done") { - $done = 1; - } else { - my ($k, $v) = split(/=/, $_, 2); - if ($k ne "" && $v ne "") { - $hs{$k} = $v; - } - } - } - close HSF; - if ($done) { - print STDERR "\n" . $str; - last; - } - } - } - if (! exists $hs{server}) { - $hs{server} = "RFB 003.008"; - } - if (! exists $hs{sectype}) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n"; - } - syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1); - my $viewer_rfb = ""; - for (my $i = 0; $i < 12; $i++) { - my $c; - sysread($viewer_sock, $c, 1); - $viewer_rfb .= $c; - print STDERR $c; - } - my $viewer_major = 3; - my $viewer_minor = 8; - if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) { - $viewer_major = $1; - $viewer_minor = $2; - } - my $u0 = pack("C", 0); - my $u1 = pack("C", 1); - my $u2 = pack("C", 2); - if ($hs{sectype} == $rfbSecTypeAnonTls) { - unlink($handshake_file); - print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n"; - if ($viewer_major > 3 || $viewer_minor >= 7) { - ; # setup ok, proceed to xfer. - } else { - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; - my $n; - sysread($server_sock, $n, 1); - $n = unpack("C", $n); - if ($n == 0) { - die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n"; - } - my %types; - for (my $i = 0; $i < $n; $i++) { - my $t; - sysread($server_sock, $t, 1); - $t = unpack("C", $t); - $types{$t} = 1; - } - my $use = 1; # None - if (exists $types{1}) { - $use = 1; # None - } elsif (exists $types{2}) { - $use = 2; # VncAuth - } else { - die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n"; - } - - # send 4 bytes sectype to viewer: - # (note this should be MSB, network byte order...) - my $up = pack("C", $use); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $up, 1); - # and tell server the one we selected: - syswrite($server_sock, $up, 1); - if ($use == 1) { - # even None has security result, so read it here and discard it. - my $sr = ""; - sysread($server_sock, $sr, 4); - } - } - } elsif ($hs{sectype} == $rfbSecTypeVencrypt) { - print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n"; - if (! exists $hs{subtype}) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n"; - } - my $fake_type = "None"; - my $plain = 0; - my $sub_type = $hs{subtype}; - if ($sub_type == $rfbVencryptTlsNone) { - $fake_type = "None"; - } elsif ($sub_type == $rfbVencryptTlsVnc) { - $fake_type = "VncAuth"; - } elsif ($sub_type == $rfbVencryptTlsPlain) { - $fake_type = "None"; - $plain = 1; - } elsif ($sub_type == $rfbVencryptX509None) { - $fake_type = "None"; - } elsif ($sub_type == $rfbVencryptX509Vnc) { - $fake_type = "VncAuth"; - } elsif ($sub_type == $rfbVencryptX509Plain) { - $fake_type = "None"; - $plain = 1; - } - if ($plain) { - if (!open(W, ">$handshake_file")) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n"; - } - print W <<"END"; - - proc print_out {} { - global user pass env - - if [info exists env(SSVNC_UP_DEBUG)] { - toplevel .b - button .b.b -text "user=\$user pass=\$pass" -command {destroy .b} - pack .b.b - update - tkwait window .b - } - - if [info exists env(SSVNC_UP_FILE)] { - set fh "" - catch {set fh [open \$env(SSVNC_UP_FILE) w]} - if {\$fh != ""} { - puts \$fh user=\$user\\npass=\$pass - flush \$fh - close \$fh - return - } - } - puts stdout user=\$user\\npass=\$pass - flush stdout - } - - proc center_win {w} { - update - set W [winfo screenwidth \$w] - set W [expr \$W + 1] - wm geometry \$w +\$W+0 - update - set x [expr [winfo screenwidth \$w]/2 - [winfo width \$w]/2] - set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2] - - wm geometry \$w +\$x+\$y - wm deiconify \$w - update - } - - wm withdraw . - - global env - set up {} - if [info exists env(SSVNC_UNIXPW)] { - set rm 0 - set up \$env(SSVNC_UNIXPW) - if [regexp {^rm:} \$up] { - set rm 1 - regsub {^rm:} \$up {} up - } - if [file exists \$up] { - set fh "" - set f \$up - catch {set fh [open \$up r]} - if {\$fh != ""} { - gets \$fh u - gets \$fh p - close \$fh - set up "\$u@\$p" - } - if {\$rm} { - catch {file delete \$f} - } - } - } elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] { - set up \$env(SSVNC_VENCRYPT_USERPASS) - } - #puts stderr up=\$up - if {\$up != ""} { - if [regexp {@} \$up] { - global user pass - set user \$up - set pass \$up - regsub {@.*\$} \$user "" user - regsub {^[^@]*@} \$pass "" pass - print_out - exit - } - } - - wm title . {VeNCrypt Viewer Bridge User/Pass} - - set user {} - set pass {} - - label .l -text {SSVNC VeNCrypt Viewer Bridge} - - frame .f0 - frame .f0.fL - label .f0.fL.la -text {Username: } - label .f0.fL.lb -text {Password: } - - pack .f0.fL.la .f0.fL.lb -side top - - frame .f0.fR - entry .f0.fR.ea -width 24 -textvariable user - entry .f0.fR.eb -width 24 -textvariable pass -show * - - pack .f0.fR.ea .f0.fR.eb -side top -fill x - - pack .f0.fL -side left - pack .f0.fR -side right -expand 1 -fill x - - button .no -text Cancel -command {destroy .} - button .ok -text Done -command {print_out; destroy .} - - center_win . - pack .l .f0 .no .ok -side top -fill x - update - wm deiconify . - - bind .f0.fR.ea {focus .f0.fR.eb} - bind .f0.fR.eb {print_out; destroy .} - focus .f0.fR.ea - - wm resizable . 1 0 - wm minsize . [winfo reqwidth .] [winfo reqheight .] -END - close W; - - #system("cat $handshake_file"); - my $w = "wish"; - if ($ENV{WISH}) { - $w = $ENV{WISH}; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt VencryptPlain user and passwd.\n"; - my $res = ""; - if (`uname` =~ /Darwin/) { - my $mtmp = `mktemp /tmp/hsup.XXXXXX`; - chomp $mtmp; - system("env SSVNC_UP_FILE=$mtmp $w $handshake_file"); - $res = `cat $mtmp`; - unlink $mtmp; - } else { - $res = `$w $handshake_file`; - } - my $user = ""; - my $pass = ""; - if ($res =~ /user=(\S*)/) { - $user = $1; - } - if ($res =~ /pass=(\S*)/) { - $pass = $1; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n"; - my $ulen = pack("C", length($user)); - my $plen = pack("C", length($pass)); - # (note this should be MSB, network byte order...) - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $ulen, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $plen, 1); - syswrite($server_sock, $user, length($user)); - syswrite($server_sock, $pass, length($pass)); - } - unlink($handshake_file); - - my $ft = 0; - if ($fake_type eq "None") { - $ft = 1; - } elsif ($fake_type eq "VncAuth") { - $ft = 2; - } else { - die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n"; - } - my $fp = pack("C", $ft); - if ($viewer_major > 3 || $viewer_minor >= 7) { - syswrite($viewer_sock, $u1, 1); - syswrite($viewer_sock, $fp, 1); - my $cr; - sysread($viewer_sock, $cr, 1); - $cr = unpack("C", $cr); - if ($cr != $ft) { - die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n"; - } - } else { - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; - # send 4 bytes sect type to viewer: - # (note this should be MSB, network byte order...) - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $fp, 1); - if ($ft == 1) { - # even None has security result, so read it here and discard it. - my $sr = ""; - sysread($server_sock, $sr, 4); - } - } - } - - $listen_handle = $viewer_sock; - $sock = $server_sock; - - xfer_both(); -} -' - # ' - # xpg_echo will expand \n \r, etc. - # try to unset and then test for it. - if type shopt > /dev/null 2>&1; then - shopt -u xpg_echo >/dev/null 2>&1 - fi - v='print STDOUT "abc\n";' - echo "$v" > $tf - chmod 700 $tf - - lc=`wc -l $tf | awk '{print $1}'` - if [ "X$lc" = "X1" ]; then - echo "$cod" > $tf - else - printf "%s" "$cod" > $tf - echo "" >> $tf - fi - # prime perl - perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1 -} - -# make_tcert is no longer invoked via the ssvnc gui (Listen mode). -# make_tcert is for testing only now via -mycert BUILTIN -make_tcert() { - tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$" - tcert=`mytmp "$tcert"` - cat > $tcert < Advanced -> Private SSH KnownHosts file' (or set" - echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts" - echo "** Warning: file. That file holds the 'localhost' cert for this specific" - echo "** Warning: connection. This yields a both secure and convenient solution." - echo "" -} - -space_expand() { - str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'` - echo "$str" -} - -# handle ssh case: -# -if [ "X$use_ssh" = "X1" ]; then - # - # USING SSH - # - ssh_port="22" - ssh_host="$host" - vnc_host="$localhost" - ssh_UKHF="" - localhost_extra="" - # let user override ssh via $SSH - ssh=${SSH:-"ssh -x"} - - sshword=`echo "$ssh" | awk '{print $1}'` - if [ "X$sshword" != "X" ]; then - if [ -x "$sshword" ]; then - : - elif type "$sshword" > /dev/null 2>&1; then - : - else - echo "" - echo "*********************************************************" - echo "** Problem finding the SSH command '$sshword': **" - echo "" - type "$sshword" - echo "" - echo "** Perhaps you need to install the SSH client package. **" - echo "*********************************************************" - echo "" - sleep 5 - fi - fi - - ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes" - if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then - ssh_NHAFL="" - fi - if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then - ssh_NHAFL="" - - ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE" - ssh_args="$ssh_args $ssh_UKHF" - if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then - touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 - fi - chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 - fi - did_ssh_NHAFL="" - - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then - SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD" - fi - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then - echo "" - echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD" - fi - - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then - plvar=LD_PRELOAD - if uname | grep Darwin >/dev/null; then - plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES" - fi - ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh" - else - SSVNC_LIM_ACCEPT_PRELOAD="" - fi - - ssh_vencrypt_proxy="" - # We handle vencrypt for SSH+SSL mode. - if echo "$proxy" | grep 'vencrypt://' > /dev/null; then - proxynew="" - for part in `echo "$proxy" | tr ',' ' '` - do - if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then - ssh_vencrypt_proxy=$part - else - if [ "X$proxynew" = "X" ]; then - proxynew="$part" - else - proxynew="$proxynew,$part" - fi - fi - done - proxy=$proxynew - fi - Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy - - # note that user must supply http:// for web proxy in SSH and SSH+SSL. - # No xxxx:// implies ssh server+port. - # - if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then - # Handle Web or SOCKS proxy(ies) for the initial connect. - Kecho host=$host - Kecho port=$port - pproxy="" - sproxy1="" - sproxy_rest="" - for part in `echo "$proxy" | tr ',' ' '` - do - Kecho proxy_part=$part - if [ "X$part" = "X" ]; then - continue - elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then - pproxy="$pproxy,$part" - else - if [ "X$sproxy1" = "X" ]; then - sproxy1="$part" - else - sproxy_rest="$sproxy_rest,$part" - fi - fi - done - pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'` - sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'` - - Kecho pproxy=$pproxy - Kecho sproxy1=$sproxy1 - Kecho sproxy_rest=$sproxy_rest - - sproxy1_host="" - sproxy1_port="" - sproxy1_user="" - - if [ "X$sproxy1" != "X" ]; then - # XXX fix ipv6 ip adder here and below. - sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'` - sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` - sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` - if [ "X$sproxy1_host" = "X" ]; then - sproxy1_host=$sproxy1_user - sproxy1_user="" - else - sproxy1_user="${sproxy1_user}@" - fi - sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'` - if [ "X$sproxy1_port" = "X" ]; then - sproxy1_port="22" - fi - else - sproxy1_host=`echo "$host" | awk -F: '{print $1}'` - sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` - sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` - if [ "X$sproxy1_host" = "X" ]; then - sproxy1_host=$sproxy1_user - sproxy1_user="" - else - sproxy1_user="${sproxy1_user}@" - fi - sproxy1_port=`echo "$host" | awk -F: '{print $2}'` - if [ "X$sproxy1_port" = "X" ]; then - sproxy1_port="22" - fi - fi - - Kecho sproxy1_host=$sproxy1_host - Kecho sproxy1_port=$sproxy1_port - Kecho sproxy1_user=$sproxy1_user - - ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl" - ptmp=`mytmp "$ptmp"` - PPROXY_REMOVE=1; export PPROXY_REMOVE - proxy=$pproxy - port_save=$port - host_save=$host - if [ "X$sproxy1_host" != "X" ]; then - host=$sproxy1_host - fi - if [ "X$sproxy1_port" != "X" ]; then - port=$sproxy1_port - fi - host=`echo "$host" | sed -e 's/^.*@//'` - port=`echo "$port" | sed -e 's/^.*://'` - pcode "$ptmp" - port=$port_save - host=$host_save - - nd=`findfree 6600` - PPROXY_LISTEN=$nd; export PPROXY_LISTEN - # XXX no reverse forever PPROXY_LOOP_THYSELF ... - $ptmp & - sleep 1 - if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then - NHAFL_warning - ssh_args="$ssh_args $ssh_NHAFL" - did_ssh_NHAFL=1 - fi - sleep 1 - if [ "X$sproxy1" = "X" ]; then - u="" - if echo "$host" | grep '@' > /dev/null; then - u=`echo "$host" | sed -e 's/@.*$/@/'` - fi - - proxy="${u}$localhost:$nd" - else - proxy="${sproxy1_user}$localhost:$nd" - fi - localhost_extra=".2" - if [ "X$sproxy_rest" != "X" ]; then - proxy="$proxy,$sproxy_rest" - fi - Kecho proxy=$proxy - fi - - if echo "$proxy" | grep "," > /dev/null; then - - proxy1=`echo "$proxy" | awk -F, '{print $1}'` - proxy2=`echo "$proxy" | awk -F, '{print $2}'` - - # user1@gw1.com:port1,user2@ws2:port2 - ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'` - ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'` - if [ "X$ssh_port1" != "X" ]; then - ssh_port1="-p $ssh_port1" - fi - ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'` - ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'` - ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'` - if [ "X$ssh_host2" = "X" ]; then - ssh_host2=$ssh_user2 - ssh_user2="" - else - ssh_user2="${ssh_user2}@" - fi - ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'` - if [ "X$ssh_port2" = "X" ]; then - ssh_port2="22" - fi - proxport=`findfree 3500` - if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then - NHAFL_warning - did_ssh_NHAFL=1 - sleep 1 - fi - echo - echo "Running 1st ssh proxy:" - ukhf="" - if [ "X$ssh_UKHF" != "X" ]; then - ukhf="$ssh_UKHF$localhost_extra" - fi - if echo "$ssh_host1" | grep '%' > /dev/null; then - uath=`space_expand "$ssh_host1"` - else - uath="$ssh_host1" - fi - echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\"" - echo "" - $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30" - ssh_args="$ssh_args $ssh_NHAFL" - sleep 1 - stty sane - proxy="${ssh_user2}$localhost:$proxport" - fi - - if [ "X$proxy" != "X" ]; then - ssh_port=`echo "$proxy" | awk -F: '{print $2}'` - if [ "X$ssh_port" = "X" ]; then - ssh_port="22" - fi - ssh_host=`echo "$proxy" | awk -F: '{print $1}'` - vnc_host="$host" - fi - - echo "" - echo "Running ssh:" - sz=`echo "$ssh_cmd" | wc -c` - if [ "$sz" -gt 300 ]; then - info="..." - else - info="$ssh_cmd" - fi - - C="" - if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then - C="-C" - fi - - getport="" - teeport="" - if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then - getport=1 - if echo "$ssh_cmd" | egrep "P= " > /dev/null; then - teeport=1 - fi - - PORT="" - ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ ]*//' -e 's/P=//'` - SSVNC_NO_ENC_WARN=1 - if [ "X$use_sshssl" = "X" ]; then - direct_connect=1 - fi - fi - if [ "X$getport" != "X" ]; then - ssh_redir="-D ${use}" - elif [ "X$reverse" = "X" ]; then - ssh_redir="-L ${use}:${vnc_host}:${port}" - else - ssh_redir="-R ${port}:${vnc_host}:${use}" - fi - pmark=`sh -c 'echo $$'` - - # the -t option actually speeds up typing response via VNC!! - if [ "X$ssh_port" = "X22" ]; then - ssh_port="" - else - ssh_port="-p $ssh_port" - fi - - if echo "$ssh_host" | grep '%' > /dev/null; then - uath=`space_expand "$ssh_host"` - else - uath="$ssh_host" - fi - if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then - echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd" - exit $? - - elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then - echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" - rc=$? - - elif [ "X$getport" != "X" ]; then - tport=/tmp/ss_vncviewer_tport${RANDOM}.$$ - tport=`mytmp "$tport"` - tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$ - tport2=`mytmp "$tport2"` - - if [ "X$rsh" != "X1" ]; then - if echo "$ssh_cmd" | grep "sudo " > /dev/null; then - echo "" - echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one" - echo "will require no password..." - echo "" - targ="-t" - $ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty" - echo "" - fi - echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2 - if [ "X$teeport" = "X1" ]; then - tail -f $tport 1>&2 & - tail_pid=$! - tail -f $tport2 1>&2 & - tail_pid2=$! - fi - rc=$? - else - rsh_setup - echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" - echo "" - rsh $ul "$ssh_host" "$ssh_cmd" > $tport & - sleep 1 - rc=0 - fi - - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - - stty sane - i=0 - if type perl > /dev/null 2>&1; then - imax=50 - sleepit="perl -e 'select(undef, undef, undef, 0.20)'" - else - imax=10 - sleepit="sleep 1" - fi - while [ $i -lt $imax ]; do - #echo $sleepit - eval $sleepit - PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'` - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - break - fi - vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'` - if [ "X$vnss" != "X" ]; then - PORT=`echo "$vnss" | awk -F: '{print $2}'` - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - if [ $PORT -lt 100 ]; then - PORT=`expr $PORT + 5900` - fi - fi - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1` - echo "vncserver string: $vnss" 1>&2 - break - fi - fi - i=`expr $i + 1` - done - - echo "found: PORT='$PORT'" 1>&2 - lh6="" - if [ "X$SSVNC_PORT_IPV6" != "X" ]; then - lh6=1 - elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then - lh6=1 - fi - if [ "X$lh6" = "X1" ]; then - echo "set SOCKS5 localhost to ::1" 1>&2 - fi - rm -f $tport $tport2 - if [ "X$rsh" = "X1" ]; then - rsh_viewer "$@" - exit $? - fi - PPROXY_SOCKS=5 - if [ "X$SSVNC_SOCKS5" != "X" ]; then - PPROXY_SOCKS=5 - elif [ "X$SSVNC_SOCKS4" != "X" ]; then - PPROXY_SOCKS=1 - fi - export PPROXY_SOCKS - if [ "X$lh6" = "X" ]; then - host="$localhost" - else - host="::1" - fi - port="$PORT" - proxy="$localhost:$use" - - else - if [ "X$rsh" != "X1" ]; then - echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" - rc=$? - else - rsh_setup - echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" - echo "" - rsh $ul "$ssh_host" "$ssh_cmd" & - sleep 1 - PORT=$port - rsh_viewer "$@" - exit $? - fi - fi - - if [ "$rc" != "0" ]; then - echo "" - echo "ssh to \"$uath\" failed." - exit 1 - fi - stty sane - - c=0 - pssh="" - while [ $c -lt 40 ] - do - p=`expr $pmark + $c` - pout=`ps -p "$p" 2>/dev/null | grep -v '^[ ]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'` - if echo "$pout" | grep "ssh" > /dev/null; then - if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then - : - elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then - : - else - pssh=$p - break - fi - fi - c=`expr $c + 1` - done - if [ "X$getport" != "X" ]; then - : - elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then - sleep 2 - elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then - #echo T sleep 1 - sleep 1 - elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then - #echo T sleep 2 - sleep 2 - else - # let any command get started a bit. - #echo T sleep 5 - sleep 5 - fi - echo "" - #reset - stty sane - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - echo "ssh_pid='$pssh'"; echo - if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then - if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo - fi - echo "Running viewer:" - - trap "final" 0 2 15 - if [ "X$reverse" = "X" ]; then - echo "$VNCVIEWERCMD" "$@" $localhost:$N - echo "" - $VNCVIEWERCMD "$@" $localhost:$N - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" $localhost:$N - fi - fi - else - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - N2=$N - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - N2=`echo "$N2" | sed -e 's/://g'` - if [ $N2 -le 200 ]; then - N2=`expr $N2 + 5500` - fi - fi - echo "$VNCVIEWERCMD" "$@" -listen $N2 - echo "" - $VNCVIEWERCMD "$@" -listen $N2 - fi - - exit $? - else - use2=`findfree 5960` - host0=$host - port0=$port - host=$localhost - port=$use - use=$use2 - N=`expr $use - 5900` - if [ "X$getport" != "X" ]; then - host="$host0" - port="$port0" - else - proxy="" - fi - if [ "X$ssh_vencrypt_proxy" != "X" ]; then - ssh_vencrypt_proxy="vencrypt://$host:$port" - if [ "X$proxy" = "X" ]; then - proxy=$ssh_vencrypt_proxy - else - proxy="$proxy,$ssh_vencrypt_proxy" - fi - Kecho "proxy_now=$proxy" - unset PPROXY_LISTEN - fi - fi -fi - -if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then - if type $STUNNEL > /dev/null 2>&1; then - : - else - echo "" - echo "***************************************************************" - echo "** Problem finding the Stunnel command '$STUNNEL': **" - echo "" - type $STUNNEL - echo "" - echo "** Perhaps you need to install the stunnel/stunnel4 package. **" - echo "***************************************************************" - echo "" - sleep 5 - fi -fi - -# create the stunnel config file: -if [ "X$verify" != "X" ]; then - if [ -d $verify ]; then - verify="CApath = $verify" - else - verify="CAfile = $verify" - fi - verify="$verify -verify = 2" -fi -if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then - verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'` -fi -if [ "X$mycert" != "X" ]; then - cert="cert = $mycert" -fi -if [ "X$crl" != "X" ]; then - if [ -d $crl ]; then - crl="CRLpath = $crl" - else - crl="CRLfile = $crl" - fi -fi - -if [ "X$showcert" = "X1" ]; then - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then - : - elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then - proxy="ipv6://$host:$port" - fi -fi - -if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then - proxy=reverse_direct -fi - -ptmp="" -if [ "X$proxy" != "X" ]; then - ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl" - ptmp=`mytmp "$ptmp"` - PPROXY_REMOVE=1; export PPROXY_REMOVE - pcode "$ptmp" - if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then - if uname | egrep 'Darwin|SunOS' >/dev/null; then - vout=`echo "$proxy" | grep -i vencrypt` - if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then - # need to exec for reverse vencrypt - connect="exec = $ptmp" - else - # on mac and solaris we need to listen on socket instead of stdio: - nd=`findfree 6700` - PPROXY_LISTEN=$nd - export PPROXY_LISTEN - if [ "X$reverse" = "X" ]; then - $ptmp & - fi - sleep 2 - host="$localhost" - port="$nd" - connect="connect = $localhost:$nd" - fi - else - # otherwise on unix we can exec it: - connect="exec = $ptmp" - fi - else - connect="exec = $ptmp" - fi -else - connect="connect = $host:$port" -fi - -# handle showcert case: -# -if [ "X$showcert" = "X1" ]; then - if [ "X$proxy" != "X" ]; then - PPROXY_LISTEN=$use - export PPROXY_LISTEN - if [ "X$SS_DEBUG" != "X" ]; then - $ptmp & - else - $ptmp 2>/dev/null & - fi - sleep 1 - more_sleep=1 - if uname | grep Linux > /dev/null; then - if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then - more_sleep="" - fi - elif uname | grep SunOS > /dev/null; then - if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then - more_sleep="" - fi - elif uname | egrep -i 'bsd|darwin' > /dev/null; then - if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then - more_sleep="" - fi - fi - if [ "X$more_sleep" = "X1" ]; then - sleep 1 - fi - host="$localhost" - port="$use" - fi - cipher_args="" - if [ "X$ciphers" != "X" ]; then - cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'` - fi - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - : - elif type openssl > /dev/null 2>&1; then - : - else - echo "" - echo "********************************************************" - echo "** Problem finding the OpenSSL command 'openssl': **" - echo "" - type openssl 2>&1 - echo "" - echo "** Perhaps you need to install the 'openssl' package. **" - echo "********************************************************" - echo "" - fi - #echo "openssl s_client $cipher_args -connect $host:$port" - if [ "X$reverse" = "X" ]; then - if type host > /dev/null 2>/dev/null; then - host $host >/dev/null 2>&1 - host $host >/dev/null 2>&1 - fi - timeout=15 - if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then - timeout=$SSVNC_FETCH_TIMEOUT - fi - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - if type pkill >/dev/null 2>&1; then - (sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 & - fi - ultravnc_dsm_helper showcert $host:$port 2>&1 - else - if type pkill >/dev/null 2>&1; then - (sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 & - fi - openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null - fi - rc=$? - else - tcert="" - if [ "X$mycert" = "X" ]; then - tcert=`make_tcert` - cert_args="-cert $tcert -CAfile $tcert" - else - cert_args="-cert $mycert -CAfile $mycert" - fi - tmp_out=/tmp/showcert_out${RANDOM}.$$ - tmp_out=`mytmp "$tmp_out"` - tmp_err=/tmp/showcert_err${RANDOM}.$$ - tmp_err=`mytmp "$tmp_err"` - - #echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2 - - # assume we have perl: - check_perl perl - - perl -e " - \$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\"); - exit 1 unless \$p; - while (1) { - sleep 1; - if (!open(F, \"<$tmp_out\")) { - kill \$p; - exit 1; - } - while () { - if (/RFB 00/) { - fsleep(0.25); - print O \"RFB 000.000\\n\"; - fsleep(1.00); - kill \$p; - fsleep(0.25); - exit 0; - } - } - close F; - } - sub fsleep { - select(undef, undef, undef, shift); - } - "; - - echo "" - cat $tmp_out - echo "" - echo "----2----" - cat $tmp_err - if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then - rc=0 - else - rc=1 - fi - - rm -f $tmp_out $tmp_err - fi - if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then - rm -f $SSVNC_PREDIGESTED_HANDSHAKE - fi - if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then - exit 0 - else - exit $rc - fi -fi - -# handle direct connect case: -# -if [ "X$direct_connect" != "X" ]; then - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - SSVNC_NO_ENC_WARN=1 - echo "" - echo "Using UltraVNC DSM Plugin key for encryption:" - echo "" - ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo " $ustr PORT HOST:PORT" - echo "" - elif [ "X$getport" = "X" ]; then - echo "" - echo "Running viewer for direct connection:" - if echo X"$@" | grep chatonly > /dev/null; then - : - else - echo "" - echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **" - echo "" - fi - fi - x="" - if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then - if [ "X$getport" = "X" ]; then - sleep 1 - fi - elif type printf > /dev/null 2>&1; then - printf "Are you sure you want to continue? [y]/n " - read x - else - echo -n "Are you sure you want to continue? [y]/n " - read x - fi - if [ "X$x" = "Xn" ]; then - exit 1 - fi - echo "" - if [ "X$ptmp" != "X" ]; then - if [ "X$reverse" = "X" ]; then - PPROXY_LISTEN=$use - export PPROXY_LISTEN - else - if [ "X$proxy" = "Xreverse_direct" ]; then - PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`" - PPROXY_DEST="$localhost:$use" - PPROXY_PROXY="ipv6://$localhost:$use" # not always ipv6.. - export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY - pps=1 - else - PPROXY_REVERSE="$localhost:$use" - export PPROXY_LISTEN - pps=3 - fi - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - pps=2 - fi - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - pps=`expr $pps + $SSVNC_EXTRA_SLEEP` - fi - PPROXY_SLEEP=$pps; export PPROXY_SLEEP; - PPROXY_KILLPID=+1; export PPROXY_KILLPID; - fi - - $ptmp & - - if [ "X$reverse" = "X" ]; then - #sleep 2 - #echo T sleep 1 - sleep 1 - fi - host="$localhost" - disp="$N" - port=`expr $disp + 5900` - fi - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "T sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo - fi - if [ "X$reverse" = "X" ]; then - hostdisp="$host:$disp" - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if [ "X$SSVNC_USE_OURS" = "X1" ]; then - hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port" - else - pf=`findfree 5970` - cmd="$SSVNC_ULTRA_DSM -$pf $host:$port" - pf=`expr $pf - 5900` - hostdisp="$localhost:$pf" - ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "Running:" - echo - echo "$ustr &" - echo - $cmd & - dsm_pid=$! - sleep 2 - fi - fi - hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "$VNCVIEWERCMD" "$@" "$hostdisp2" - trap "final" 0 2 15 - echo "" - $VNCVIEWERCMD "$@" "$hostdisp" - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" "$hostdisp" - fi - fi - else - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - trap "final" 0 2 15 - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then - echo "NOTE: The ultravnc_dsm_helper only runs once. So after the first LISTEN" - echo " ends you must restart the Listening mode. You may also need to" - echo " Press Ctrl-C to stop the viewer and restart for another connection." - echo "" - fi - #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - VNCVIEWER_LISTEN_LOCALHOST=1 - export VNCVIEWER_LISTEN_LOCALHOST - dport=`expr 5500 + $disp` - cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use" - ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "Running:" - echo - echo "$ustr &" - echo - if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then - $cmd & - dsm_pid=$! - else - while [ 1 ]; do $cmd; sleep 1; done & - dsm_pid=$! - fi - sleep 2 - disp=$use - if [ $disp -ge 5500 ]; then - disp=`expr $disp - 5500` - fi - fi - disp2=$disp - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - disp2=`echo "$disp2" | sed -e 's/://g'` - if [ $disp2 -le 200 ]; then - disp2=`expr $disp2 + 5500` - fi - fi - echo "$VNCVIEWERCMD" "$@" -listen $disp2 - echo "" - $VNCVIEWERCMD "$@" -listen $disp2 - if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF - fi - fi - exit $? -fi - -tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$ -tmp_cfg=`mytmp "$tmp_cfg"` - -stunnel_exec="" -if [ "X$SSVNC_USE_OURS" != "X1" ]; then - : -elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then - stunnel_exec="#" -fi - -if [ "X$reverse" = "X" ]; then - - if echo "$proxy" | grep "^repeater://" > /dev/null; then - if [ "X$cert" = "XBUILTIN" ]; then - ttcert=`make_tcert` - cert="cert = $ttcert" - fi - # Note for listen mode, an empty cert will cause stunnel to fail. - # The ssvnc gui will have already taken care of this. - fi - - cat > "$tmp_cfg" < /dev/null; then - hloc="$localhost:" - pv=`findfree 5570` - proxy="vencrypt:$pv:$port" - port=$pv - if [ "X$anondh_set" = "X1" ]; then - # not needed for ANONDH in this mode - #ciphers="ciphers = ADH:@STRENGTH" - : - fi - fi - cat > "$tmp_cfg" < /dev/null 2>&1 - $STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty & - stunnel_pid=$! - echo "" - - # pause here to let the user supply a possible passphrase for the - # mycert key: - if [ "X$mycert" != "X" ]; then - nsl=10 - dsl=0 - if [ ! -f $mycert ]; then - dsl=0 - elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then - dsl=1 - fi - if [ "X$dsl" = "X1" ]; then - echo "" - echo "(** pausing $nsl secs for possible certificate passphrase dialog **)" - echo "" - sleep $nsl - echo "(** done pausing for passphrase **)" - echo "" - fi - fi - #echo T sleep 1 - sleep 1 - rm -f "$tmp_cfg" -fi - - -echo "" -if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP -fi -if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo -fi - -if [ "X$reverse" = "X" ]; then - if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then - port1=`expr 5900 + $N` # stunnel port - port2=`findfree 5970` # bridge port (viewer connects to it.) - N=`expr $port2 - 5900` - env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp & - sleep 1 - fi - echo "Running viewer:" - vnc_hp=$localhost:$N - if [ "X$stunnel_exec" != "X" ]; then - vnc_hp="exec=$STUNNEL $tmp_cfg" - fi - echo "$VNCVIEWERCMD" "$@" "$vnc_hp" - trap "final" 0 2 15 - echo "" - $VNCVIEWERCMD "$@" "$vnc_hp" - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" "$vnc_hp" - fi - fi -else - echo "Running viewer:" - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - trap "final" 0 2 15 - N2=$N - N2_trim=`echo "$N2" | sed -e 's/://g'` - if [ $N2_trim -le 200 ]; then - N2_trim=`expr $N2_trim + 5500` - fi - if [ "X$proxy" != "X" ]; then - if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then - pstunnel=`echo "$proxy" | awk -F: '{print $2}'` - plisten=`echo "$proxy" | awk -F: '{print $3}'` - IF=INADDR_ANY - if [ "X$STUNNEL_LISTEN" != "X" ]; then - IF=$STUNNEL_LISTEN - fi - PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE - PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN - PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY - PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST - STUNNEL_ONCE=1; export STUNNEL_ONCE - STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS - if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then - port1=`expr 5500 + $N2` - port2=`findfree 5580` - N2=`expr $port2 - 5500` - N2_trim=`echo "$N2" | sed -e 's/://g'` - if [ $N2_trim -le 200 ]; then - N2_trim=`expr $N2_trim + 5500` - fi - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF - fi - env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp & - sleep 1 - fi - else - PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE - PPROXY_SLEEP=1; export PPROXY_SLEEP; - fi - PPROXY_KILLPID=+1; export PPROXY_KILLPID; - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - fi - $ptmp & - # Important to have no extra pids generated between here and VNCVIEWERCMD - fi - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - N2=$N2_trim - fi - echo "$VNCVIEWERCMD" "$@" -listen $N2 - echo "" - $VNCVIEWERCMD "$@" -listen $N2 - - if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF - fi - if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF0 - fi -fi - -sleep 1 diff --git a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch b/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch deleted file mode 100644 index bc10f3c..0000000 --- a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch +++ /dev/null @@ -1,111 +0,0 @@ ---- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400 -+++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500 -@@ -28,13 +28,14 @@ - import java.lang.*; - import java.util.zip.*; - -+import java.util.Collections; - - // - // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. - // - - class VncCanvas extends Canvas -- implements KeyListener, MouseListener, MouseMotionListener { -+ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { - - VncViewer viewer; - RfbProto rfb; -@@ -81,6 +82,20 @@ - cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - -+ // 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); -+ - colors = new Color[256]; - for (int i = 0; i < 256; i++) - colors[i] = new Color(cm8.getRGB(i)); -@@ -169,6 +184,7 @@ - inputEnabled = true; - addMouseListener(this); - addMouseMotionListener(this); -+ addMouseWheelListener(this); - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(true); - } -@@ -177,6 +193,7 @@ - inputEnabled = false; - removeMouseListener(this); - removeMouseMotionListener(this); -+ removeMouseWheelListener(this); - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(false); - } -@@ -1190,6 +1207,9 @@ - public void mouseDragged(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } -+ public void mouseWheelMoved(MouseWheelEvent evt) { -+ processLocalMouseWheelEvent(evt); -+ } - - public void processLocalKeyEvent(KeyEvent evt) { - if (viewer.rfb != null && rfb.inNormalProtocol) { -@@ -1221,6 +1241,19 @@ - evt.consume(); - } - -+ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { -+ if (viewer.rfb != null && rfb.inNormalProtocol) { -+ synchronized(rfb) { -+ try { -+ rfb.writeWheelEvent(evt); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ } -+ rfb.notify(); -+ } -+ } -+ } -+ - public void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (moved) { -@@ -1387,9 +1420,9 @@ - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | -- (pixBuf[i * 4 + 1] & 0xFF) << 16 | -- (pixBuf[i * 4 + 2] & 0xFF) << 8 | -- (pixBuf[i * 4 + 3] & 0xFF); -+ (pixBuf[i * 4 + 2] & 0xFF) << 16 | -+ (pixBuf[i * 4 + 1] & 0xFF) << 8 | -+ (pixBuf[i * 4 + 0] & 0xFF); - } - } else { - result = 0; // Transparent pixel -@@ -1403,9 +1436,9 @@ - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | -- (pixBuf[i * 4 + 1] & 0xFF) << 16 | -- (pixBuf[i * 4 + 2] & 0xFF) << 8 | -- (pixBuf[i * 4 + 3] & 0xFF); -+ (pixBuf[i * 4 + 2] & 0xFF) << 16 | -+ (pixBuf[i * 4 + 1] & 0xFF) << 8 | -+ (pixBuf[i * 4 + 0] & 0xFF); - } - } else { - result = 0; // Transparent pixel diff --git a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch deleted file mode 100644 index 801234a..0000000 --- a/classes/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch +++ /dev/null @@ -1,2600 +0,0 @@ -diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile ---- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 -+++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400 -@@ -4,6 +4,7 @@ - - CP = cp - JC = javac -+JC_ARGS = -target 1.4 -source 1.4 - JAR = jar - ARCHIVE = VncViewer.jar - MANIFEST = MANIFEST.MF -@@ -15,25 +16,29 @@ - DesCipher.class CapabilityInfo.class CapsContainer.class \ - RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \ - SocketFactory.class HTTPConnectSocketFactory.class \ -- HTTPConnectSocket.class ReloginPanel.class -+ HTTPConnectSocket.class ReloginPanel.class \ -+ SSLSocketToMe.class -+ -+SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class - - SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ - OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ - DesCipher.java CapabilityInfo.java CapsContainer.java \ - RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \ - SocketFactory.java HTTPConnectSocketFactory.java \ -- HTTPConnectSocket.java ReloginPanel.java -+ HTTPConnectSocket.java ReloginPanel.java \ -+ SSLSocketToMe.java - - all: $(CLASSES) $(ARCHIVE) - - $(CLASSES): $(SOURCES) -- $(JC) -target 1.1 -O $(SOURCES) -+ $(JC) $(JC_ARGS) -O $(SOURCES) - - $(ARCHIVE): $(CLASSES) $(MANIFEST) -- $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) -+ $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES) - - install: $(CLASSES) $(ARCHIVE) -- $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) -+ $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) - - export:: $(CLASSES) $(ARCHIVE) $(PAGES) - @$(ExportJavaClasses) -diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java ---- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500 -+++ vnc_javasrc/RfbProto.java 2010-11-30 22:05:12.000000000 -0500 -@@ -199,7 +199,21 @@ - host = h; - port = p; - -- if (viewer.socketFactory == null) { -+ 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 if (viewer.socketFactory == null) { - sock = new Socket(host, port); - } else { - try { -@@ -255,7 +269,7 @@ - || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) - { - throw new Exception("Host " + host + " port " + port + -- " is not an RFB server"); -+ " is not an RFB server: " + b); - } - - serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); -@@ -892,6 +906,38 @@ - final static int ALT_MASK = InputEvent.ALT_MASK; - - -+ void writeWheelEvent(MouseWheelEvent evt) throws IOException { -+ -+ eventBufLen = 0; -+ -+ int x = evt.getX(); -+ int y = evt.getY(); -+ -+ if (x < 0) x = 0; -+ if (y < 0) y = 0; -+ -+ int ptrmask; -+ -+ int clicks = evt.getWheelRotation(); -+ System.out.println("writeWheelEvent: clicks: " + clicks); -+ if (clicks > 0) { -+ ptrmask = 16; -+ } else if (clicks < 0) { -+ ptrmask = 8; -+ } else { -+ return; -+ } -+ -+ eventBuf[eventBufLen++] = (byte) PointerEvent; -+ eventBuf[eventBufLen++] = (byte) ptrmask; -+ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (x & 0xff); -+ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (y & 0xff); -+ -+ os.write(eventBuf, 0, eventBufLen); -+ } -+ - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. -@@ -992,6 +1038,19 @@ - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - - int key; -+ if (viewer.debugKeyboard) { -+ System.out.println("----------------------------------------"); -+ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); -+ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); -+ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); -+ System.out.println("evt.getID: " + evt.getID()); -+ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); -+ System.out.println("evt.isActionKey: " + evt.isActionKey()); -+ System.out.println("evt.isControlDown: " + evt.isControlDown()); -+ System.out.println("evt.getModifiers: " + evt.getModifiers()); -+ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); -+ System.out.println("evt.paramString: " + evt.paramString()); -+ } - if (evt.isActionKey()) { - - // -@@ -1025,6 +1084,13 @@ - return; - } - -+ if(key == 0xffc2 && viewer.mapF5_to_atsign) { -+ if (viewer.debugKeyboard) { -+ System.out.println("Mapping: F5 -> AT "); -+ } -+ key = 0x40; -+ } -+ - } else { - - // -@@ -1036,6 +1102,7 @@ - - key = keyChar; - -+ - if (key < 0x20) { - if (evt.isControlDown()) { - key += 0x60; -@@ -1121,6 +1188,16 @@ - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { -+ if(viewer.forbid_Ctrl_Alt) { -+ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { -+ int orig = newModifiers; -+ newModifiers &= ~ALT_MASK; -+ newModifiers &= ~CTRL_MASK; -+ if (viewer.debugKeyboard) { -+ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); -+ } -+ } -+ } - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - -diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java ---- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 -+++ vnc_javasrc/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 -@@ -0,0 +1,2067 @@ -+/* -+ * 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, 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. -+ * -+ */ -+ -+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; -+ boolean debug_certs = false; -+ -+ /* sockets */ -+ SSLSocket socket = null; -+ SSLSocketFactory factory; -+ -+ /* fallback for Proxy connection */ -+ boolean proxy_in_use = false; -+ boolean proxy_failure = false; -+ public DataInputStream is = null; -+ public OutputStream os = null; -+ -+ /* strings from user WRT proxy: */ -+ String proxy_auth_string = 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 trustsrv_ctx; -+ SSLContext trusturl_ctx; -+ SSLContext trustone_ctx; -+ -+ /* corresponding trust managers */ -+ TrustManager[] trustAllCerts; -+ TrustManager[] trustSrvCert; -+ TrustManager[] trustUrlCert; -+ TrustManager[] trustOneCert; -+ -+ /* client-side SSL auth key (oneTimeKey=...) */ -+ KeyManager[] mykey = null; -+ -+ boolean user_wants_to_see_cert = true; -+ String cert_fail = null; -+ -+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ -+ java.security.cert.Certificate[] trustallCerts = null; -+ java.security.cert.Certificate[] trustsrvCerts = null; -+ java.security.cert.Certificate[] trusturlCerts = null; -+ -+ /* utility to decode hex oneTimeKey=... and serverCert=... */ -+ 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; -+ -+ debug_certs = v.debugCerts; -+ -+ /* we will first try default factory for certification: */ -+ -+ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); -+ -+ dbg("SSL startup: " + host + " " + port); -+ -+ -+ /* create trust managers to be used if initial handshake fails: */ -+ -+ trustAllCerts = new TrustManager[] { -+ /* -+ * this one accepts everything. Only used if user -+ * has disabled checking (trustAllVncCerts=yes) -+ * or when we grab the cert to show it to them in -+ * a dialog and ask them to manually verify/accept it. -+ */ -+ 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 and stored in -+ * trusturlCerts. -+ */ -+ 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 (URL)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trusturlCerts' */ -+ if (trusturlCerts == null) { -+ throw new CertificateException( -+ "No Trust url Certs array."); -+ } -+ if (trusturlCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust url Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trusturlCerts.length) { -+ throw new CertificateException( -+ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trusturlCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("URL: cert mismatch at i=" + i); -+ dbg("URL: cert mismatch cert" + certs[i]); -+ dbg("URL: cert mismatch url" + trusturlCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("URL: cert info at i=" + i); -+ dbg("URL: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("URL: cert info url" + trusturlCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != URL Cert Chain."); -+ } -+ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustSrvCert = new TrustManager[] { -+ /* -+ * this one accepts cert given to us in the serverCert -+ * Applet Parameter we were started with. It is -+ * currently a fatal error if the VNC Server's cert -+ * doesn't match it. -+ */ -+ 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 (SRV)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustsrvCerts' */ -+ if (trustsrvCerts == null) { -+ throw new CertificateException( -+ "No Trust srv Certs array."); -+ } -+ if (trustsrvCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust srv Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustsrvCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustsrvCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("SRV: cert mismatch at i=" + i); -+ dbg("SRV: cert mismatch cert" + certs[i]); -+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("SRV: cert info at i=" + i); -+ dbg("SRV: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("SRV: cert info srv" + trustsrvCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); -+ } -+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustOneCert = new TrustManager[] { -+ /* -+ * this one accepts only the retrieved server -+ * cert by SSLSocket by this applet we stored in -+ * trustallCerts that user has accepted or applet -+ * parameter trustAllVncCerts=yes is set. This is -+ * for when we reconnect after the user has manually -+ * accepted the trustall cert in the dialog (or set -+ * trustAllVncCerts=yes applet param.) -+ */ -+ 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 (ONE)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustallCerts' */ -+ 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 (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustallCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustallCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("ONE: cert mismatch at i=" + i); -+ dbg("ONE: cert mismatch cert" + certs[i]); -+ dbg("ONE: cert mismatch all" + trustallCerts[i]); -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("ONE: cert info at i=" + i); -+ dbg("ONE: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("ONE: cert info all" + trustallCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != TRUSTALL Cert Chain."); -+ } -+ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ /* -+ * The above TrustManagers are used: -+ * -+ * 1) to retrieve the server cert in case of failure to -+ * display it to the user in a dialog. -+ * 2) to subsequently connect to the server if user agrees. -+ */ -+ -+ /* -+ * build oneTimeKey cert+key if supplied in applet parameter: -+ */ -+ 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); -+ if (debug_certs) { -+ dbg("one time key " + ff); -+ } -+ -+ 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)); -+ if (debug_certs) { -+ dbg("one time cert" + 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(); -+ } -+ -+ /* -+ * build serverCert cert if supplied in applet parameter: -+ */ -+ if (viewer.serverCert != null) { -+ CertificateFactory cf = CertificateFactory.getInstance("X.509"); -+ byte[] cert = hex2bytes(viewer.serverCert); -+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); -+ trustsrvCerts = new Certificate[c.toArray().length]; -+ if (c.size() == 1) { -+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); -+ trustsrvCerts[0] = tmpcert; -+ } else { -+ trustsrvCerts = (Certificate[]) c.toArray(); -+ } -+ } -+ -+ /* the trust loc certs context: */ -+ try { -+ trustloc_ctx = SSLContext.getInstance("SSL"); -+ -+ /* -+ * below is a failed attempt to get jvm's default -+ * trust manager using null (below) makes it so -+ * for HttpsURLConnection the server cannot be -+ * verified (no prompting.) -+ */ -+ if (false) { -+ boolean didit = false; -+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); -+ tmf.init((KeyStore) null); -+ TrustManager [] tml = tmf.getTrustManagers(); -+ for (int i = 0; i < tml.length; i++) { -+ TrustManager tm = tml[i]; -+ if (tm instanceof X509TrustManager) { -+ TrustManager tm1[] = new TrustManager[1]; -+ tm1[0] = tm; -+ trustloc_ctx.init(mykey, tm1, null); -+ didit = true; -+ break; -+ } -+ } -+ if (!didit) { -+ trustloc_ctx.init(mykey, null, null); -+ } -+ } else { -+ /* we have to set trust manager to null */ -+ trustloc_ctx.init(mykey, null, null); -+ } -+ -+ } catch (Exception e) { -+ String msg = "SSL trustloc_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust all certs context: */ -+ 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); -+ } -+ -+ /* the trust url certs context: */ -+ 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); -+ } -+ -+ /* the trust srv certs context: */ -+ try { -+ trustsrv_ctx = SSLContext.getInstance("SSL"); -+ trustsrv_ctx.init(mykey, trustSrvCert, new -+ java.security.SecureRandom()); -+ -+ } catch (Exception e) { -+ String msg = "SSL trustsrv_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust the one cert from server context: */ -+ 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); -+ } -+ } -+ -+ /* -+ * we call this early on to 1) check for a proxy, 2) grab -+ * Browser/JVM accepted HTTPS cert. -+ */ -+ public void check_for_proxy_and_grab_vnc_server_cert() { -+ -+ trusturlCerts = null; -+ proxy_in_use = false; -+ -+ if (viewer.ignoreProxy) { -+ /* applet param says skip it. */ -+ /* the downside is we do not set trusturlCerts for comparison later... */ -+ /* nor do we autodetect x11vnc for GET=1. */ -+ return; -+ } -+ -+ dbg("------------------------------------------------"); -+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); -+ -+ dbg("TRYING HTTPS:"); -+ String ustr = "https://" + host + ":"; -+ if (viewer.httpsPort != null) { -+ ustr += viewer.httpsPort; -+ } else { -+ ustr += port; -+ } -+ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTPS URL connection to host:port */ -+ URL url = new URL(ustr); -+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); -+ -+ if (mykey != null) { -+ /* with oneTimeKey (mykey) we can't use the default SSL context */ -+ if (trustsrvCerts != null) { -+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); -+ } else if (trustloc_ctx != null) { -+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); -+ } -+ } -+ -+ https.setUseCaches(false); -+ https.setRequestMethod("GET"); -+ https.setRequestProperty("Pragma", "No-Cache"); -+ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); -+ https.setDoInput(true); -+ -+ dbg("trying https.connect()"); -+ https.connect(); -+ -+ dbg("trying https.getServerCertificates()"); -+ 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; -+ dbg("An HTTPS proxy is in use. There may be connection problems."); -+ } -+ -+ dbg("trying https.getContent()"); -+ Object output = https.getContent(); -+ dbg("trying https.disconnect()"); -+ https.disconnect(); -+ if (! viewer.GET) { -+ String header = https.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (1), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ -+ } catch(Exception e) { -+ dbg("HttpsURLConnection: " + e.getMessage()); -+ } -+ -+ if (proxy_in_use) { -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } else if (trusturlCerts != null && !viewer.forceProxy) { -+ /* Allow user to require HTTP check? use forceProxy for now. */ -+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } -+ -+ /* -+ * XXX need to remember scenario where this extra check -+ * gives useful info. User's Browser proxy settings? -+ */ -+ dbg("TRYING HTTP:"); -+ ustr = "http://" + host + ":" + port; -+ ustr += viewer.urlPrefix + "/index.vnc"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ -+ 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); -+ -+ dbg("trying http.connect()"); -+ http.connect(); -+ -+ if (http.usingProxy()) { -+ proxy_in_use = true; -+ dbg("An HTTP proxy is in use. There may be connection problems."); -+ } -+ dbg("trying http.getContent()"); -+ Object output = http.getContent(); -+ dbg("trying http.disconnect()"); -+ http.disconnect(); -+ if (! viewer.GET) { -+ String header = http.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (2), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ } catch(Exception e) { -+ dbg("HttpURLConnection: " + e.getMessage()); -+ } -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ } -+ -+ public Socket connectSock() throws IOException { -+ /* -+ * first try a https connection to detect a proxy, and -+ * grab the VNC server cert at the same time: -+ */ -+ check_for_proxy_and_grab_vnc_server_cert(); -+ -+ boolean srv_cert = false; -+ -+ if (trustsrvCerts != null) { -+ /* applet parameter suppled serverCert */ -+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); -+ factory = trustsrv_ctx.getSocketFactory(); -+ srv_cert = true; -+ } else if (viewer.trustAllVncCerts) { -+ /* trust all certs (no checking) */ -+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); -+ factory = trustall_ctx.getSocketFactory(); -+ } else if (trusturlCerts != null) { -+ /* trust certs the Browser/JVM accepted in check_for_proxy... */ -+ dbg("using trusturl_ctx"); -+ factory = trusturl_ctx.getSocketFactory(); -+ } else { -+ /* trust the local defaults */ -+ 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"); -+ } -+ -+ int timeout = 6; -+ if (timeout > 0) { -+ socket = (SSLSocket) factory.createSocket(); -+ InetSocketAddress inetaddr = new InetSocketAddress(host, port); -+ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); -+ socket.connect(inetaddr, timeout * 1000); -+ } else { -+ socket = (SSLSocket) factory.createSocket(host, port); -+ } -+ -+ } catch (Exception esock) { -+ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); -+ } -+ } else { -+ /* n.b. socket is left in error state to cause ex. below. */ -+ } -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The Server Connection Verified OK on 1st try."); -+ -+ java.security.cert.Certificate[] currentTrustedCerts; -+ BrowserCertsDialog bcd; -+ -+ SSLSession sess = socket.getSession(); -+ currentTrustedCerts = sess.getPeerCertificates(); -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-1 keeping socket."); -+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy."); -+ } -+ 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 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.serverCert != null && trustsrvCerts != null) { -+ dbg("viewer.serverCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.trustUrlVncCert) { -+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else { -+ /* have a dialog with the user: */ -+ bcd = new BrowserCertsDialog(serv, host + ":" + port); -+ dbg("browser certs dialog begin."); -+ bcd.queryUser(); -+ dbg("browser certs dialog finished."); -+ -+ if (bcd.showCertDialog) { -+ String msg = "user wants to see cert"; -+ dbg(msg); -+ user_wants_to_see_cert = true; -+ if (cert_fail == null) { -+ cert_fail = "user-view"; -+ } -+ throw new SSLHandshakeException(msg); -+ } else { -+ user_wants_to_see_cert = false; -+ dbg("browser certs dialog: user said yes, accept it"); -+ } -+ } -+ -+ } catch (SSLHandshakeException eh) { -+ dbg("SSLHandshakeException: could not automatically verify Server."); -+ dbg("msg: " + eh.getMessage()); -+ -+ -+ /* send a cleanup string just in case: */ -+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; -+ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!"); -+ } -+ -+ /* reload */ -+ -+ socket = null; -+ -+ String reason = null; -+ -+ if (srv_cert) { -+ /* for serverCert usage we make this a fatal error. */ -+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); -+ /* see below in TrustDialog were we describe this case to user anyway */ -+ } -+ -+ /* -+ * Reconnect, trusting any cert, so we can grab -+ * the cert to show it to the user in a dialog -+ * for him to manually accept. This 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); -+ } -+ -+ if (debug_certs) { -+ dbg("trusturlCerts: " + trusturlCerts); -+ dbg("trustsrvCerts: " + trustsrvCerts); -+ } -+ if (trusturlCerts == null && cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); -+ -+ /* grab the cert: */ -+ try { -+ SSLSession sess = socket.getSession(); -+ trustallCerts = sess.getPeerCertificates(); -+ } catch (Exception e) { -+ throw new Exception("Could not get " + -+ "Peer Certificate"); -+ } -+ if (debug_certs) { -+ dbg("trustallCerts: " + trustallCerts); -+ } -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); -+ } else if (! browser_cert_match()) { -+ /* -+ * close socket now, we will reopen after -+ * dialog if user agrees to use the cert. -+ */ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!"); -+ } -+ socket = null; -+ -+ /* dialog with user to accept cert or not: */ -+ -+ TrustDialog td= new TrustDialog(host, port, -+ trustallCerts); -+ -+ if (cert_fail == null) { -+ ; -+ } else if (cert_fail.equals("user-view")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " You Asked to View the Certificate."; -+ } else if (cert_fail.equals("server-cert-mismatch")) { -+ /* this is now fatal error, see above. */ -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Certificate\n" -+ + " specified in the supplied 'serverCert' Applet Parameter."; -+ } else if (cert_fail.equals("cert-mismatch")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Website's\n" -+ + " HTTPS Certificate (that you previously accepted; either\n" -+ + " manually or automatically via Certificate Authority.)"; -+ } else if (cert_fail.equals("missing-certs")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " Not all Certificates could be obtained to check."; -+ } -+ -+ if (! td.queryUser(reason)) { -+ 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()); -+ } -+ -+ /* reload again: */ -+ -+ if (socket != null) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!!"); -+ } -+ socket = null; -+ } -+ -+ /* -+ * Now connect a 3rd time, using the cert -+ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); -+ -+ } catch (Exception ehand3) { -+ dbg("** Could not TrustAll/TrustOne Verify Server #3."); -+ -+ throw new IOException(ehand3.getMessage()); -+ } -+ } -+ -+ /* we have socket (possibly null) at this point, so proceed: */ -+ -+ /* handle x11vnc GET=1, if applicable: */ -+ 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: " + str); -+ OutputStream os = socket.getOutputStream(); -+ String type = "os"; -+ -+ if (type == "os") { -+ os.write(str.getBytes()); -+ os.flush(); -+ System.out.println("used OutputStream"); -+ } else if (type == "bs") { -+ BufferedOutputStream bs = new BufferedOutputStream(os); -+ bs.write(str.getBytes()); -+ bs.flush(); -+ System.out.println("used BufferedOutputStream"); -+ } else if (type == "ds") { -+ DataOutputStream ds = new DataOutputStream(os); -+ ds.write(str.getBytes()); -+ ds.flush(); -+ System.out.println("used DataOutputStream"); -+ } -+ 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."); -+ dbg(""); -+ -+ /* could be null, let caller handle that. */ -+ return (Socket) socket; -+ } -+ -+ boolean browser_cert_match() { -+ String msg = "Browser URL accept previously accepted cert"; -+ -+ if (user_wants_to_see_cert) { -+ return false; -+ } -+ -+ if (viewer.serverCert != null || trustsrvCerts != null) { -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (trustallCerts != null && trusturlCerts != null) { -+ if (trustallCerts.length == trusturlCerts.length) { -+ boolean ok = true; -+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ -+ for (int i = 0; i < trusturlCerts.length; i++) { -+ if (! trustallCerts[i].equals(trusturlCerts[i])) { -+ dbg("BCM: cert mismatch at i=" + i); -+ dbg("BCM: cert mismatch url" + trusturlCerts[i]); -+ dbg("BCM: cert mismatch all" + trustallCerts[i]); -+ ok = false; -+ } -+ } -+ if (ok) { -+ System.out.println(msg); -+ if (cert_fail == null) { -+ cert_fail = "did-not-fail"; -+ } -+ return true; -+ } else { -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ return false; -+ } -+ } -+ } -+ if (cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ return false; -+ } -+ -+ 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; -+ } -+ -+ /* this will do the proxy CONNECT negotiation and hook us up. */ -+ -+ private void proxy_helper(String proxyHost, int proxyPort) { -+ -+ boolean proxy_auth = false; -+ String proxy_auth_basic_realm = ""; -+ String hp = host + ":" + port; -+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); -+ -+ /* we loop here a few times trying for the password case */ -+ for (int k=0; k < 2; k++) { -+ dbg("proxy_in_use psocket: " + k); -+ -+ if (proxySock != null) { -+ try { -+ proxySock.close(); -+ } catch (Exception e) { -+ dbg("proxy socket is grumpy."); -+ } -+ } -+ -+ proxySock = psocket(proxyHost, proxyPort); -+ if (proxySock == null) { -+ dbg("1-a sadly, returning a null socket"); -+ return; -+ } -+ -+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" -+ + "Host: " + hp + "\r\n"; -+ -+ dbg("requesting via proxy: " + req1); -+ -+ if (proxy_auth) { -+ if (proxy_auth_string == null) { -+ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); -+ pp.queryUser(); -+ proxy_auth_string = pp.getAuth(); -+ } -+ //dbg("auth1: " + proxy_auth_string); -+ -+ String auth2 = Base64Coder.encodeString(proxy_auth_string); -+ //dbg("auth2: " + auth2); -+ -+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; -+ //dbg("req1: " + req1); -+ -+ dbg("added Proxy-Authorization: Basic ... to request"); -+ } -+ req1 += "\r\n"; -+ -+ try { -+ proxy_os.write(req1.getBytes()); -+ String reply = readline(proxy_is); -+ -+ dbg("proxy replied: " + reply.trim()); -+ -+ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { -+ proxy_auth = true; -+ proxySock.close(); -+ } else 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; -+ } -+ } -+ } catch(Exception e) { -+ dbg("some proxy socket problem: " + e.getMessage()); -+ } -+ -+ /* read the rest of the HTTP headers */ -+ while (true) { -+ String line = readline(proxy_is); -+ dbg("proxy line: " + line.trim()); -+ if (proxy_auth) { -+ String uc = line.toLowerCase(); -+ if (uc.indexOf("proxy-authenticate:") == 0) { -+ if (uc.indexOf(" basic ") >= 0) { -+ int idx = uc.indexOf(" realm"); -+ if (idx >= 0) { -+ proxy_auth_basic_realm = uc.substring(idx+1); -+ } -+ } -+ } -+ } -+ if (line.equals("\r\n") || line.equals("\n")) { -+ break; -+ } -+ } -+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { -+ /* we only try once for the non-password case: */ -+ break; -+ } -+ } -+ } -+ -+ 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) { -+ /* sandboxed applet might not be able to read it. */ -+ dbg("props failed: " + e.getMessage()); -+ } -+ if (viewer.proxyHost != null) { -+ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); -+ proxyHost = viewer.proxyHost; -+ if (viewer.proxyPort != null) { -+ proxyPort = gint(viewer.proxyPort); -+ } else { -+ proxyPort = 8080; -+ } -+ -+ } else if (props != null) { -+ dbg("\n---------------\nAll props:"); -+ props.list(System.out); -+ dbg("\n---------------\n\n"); -+ -+ /* scrape throught properties looking for proxy info: */ -+ -+ 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.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; -+ } -+ } -+ -+ 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 (proxyHost != null && proxyPort > 0) { -+ break; -+ } -+ -+ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 -+ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { -+ continue; -+ } -+ if (v2.indexOf("http") < 0) { -+ 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) { -+ j = p.indexOf("http"); -+ 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()); -+ } -+ -+ proxy_helper(proxyHost, proxyPort); -+ if (proxySock == null) { -+ return null; -+ } -+ } 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("proxy socket problem-2: " + 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(String reason) { -+ -+ /* 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"); -+ } -+ if (reason != null) { -+ reason += "\n\n"; -+ } -+ -+ text = "\n" -++ "Unable to verify the identity of\n" -++ "\n" -++ " " + host + ":" + port + "\n" -++ "\n" -++ infostr -++ "\n" -++ "as a trusted VNC server.\n" -++ "\n" -++ reason -++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" -++ "is due to one of the following:\n" -++ "\n" -++ " - Your requesting to View the Certificate before accepting.\n" -++ "\n" -++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" -++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" -++ "\n" -++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" -++ " the Apache Web server has a certificate *different* from the VNC server's.\n" -++ "\n" -++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" -++ " obtained by this applet to compare the VNC Server Certificate against.\n" -++ "\n" -++ " - The VNC Server's Certificate does not match the one specified in the\n" -++ " supplied 'serverCert' Java Applet Parameter.\n" -++ "\n" -++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" -++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" -++ "automatically authenticate this VNC Server.\n" -++ "\n" -++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" -++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" -++ "certificate (except for the Apache portal case above where they don't match.)\n" -++ "\n" -++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" -++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" -++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); -+ dialog.hide(); -+ -+ } else if (evt.getSource() == cancel) { -+ /* Cancel button clicked */ -+ trust_this_session = false; -+ -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+ -+ 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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+class ProxyPasswdDialog implements ActionListener { -+ String guessedHost = null; -+ String guessedPort = null; -+ String guessedUser = null; -+ String guessedPasswd = null; -+ String realm = 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 entry1; -+ TextField entry2; -+ String reply1 = ""; -+ String reply2 = ""; -+ -+ ProxyPasswdDialog (String h, int p, String realm) { -+ guessedHost = h; -+ try { -+ guessedPort = Integer.toString(p); -+ } catch (Exception e) { -+ guessedPort = "8080"; -+ } -+ this.realm = realm; -+ } -+ -+ public void queryUser() { -+ -+ /* create and display the dialog for unverified cert. */ -+ -+ Frame frame = new Frame("Proxy Requires Username and Password"); -+ -+ dialog = new Dialog(frame, true); -+ -+ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); -+ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); -+ entry1 = new TextField(30); -+ entry2 = new TextField(30); -+ entry2.setEchoChar('*'); -+ ok = new Button("OK"); -+ ok.addActionListener(this); -+ -+ dialog.setLayout(new BorderLayout()); -+ dialog.add("North", label); -+ dialog.add("Center", entry1); -+ dialog.add("South", entry2); -+ dialog.add("East", ok); -+ dialog.pack(); -+ dialog.resize(dialog.preferredSize()); -+ -+ dialog.show(); /* block here til OK or Cancel pressed. */ -+ return; -+ } -+ -+ public String getAuth() { -+ return reply1 + ":" + reply2; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == ok) { -+ reply1 = entry1.getText(); -+ reply2 = entry2.getText(); -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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 and/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 += "If the VNC Server connection times out while you are reading this\n"; -+m += "dialog, then restart the connection and try again.\n"; -+m += "\n"; -+m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; -+m += "\n"; -+ -+ TextArea textarea = new TextArea(m, 22, 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. */ -+ System.out.println("done show()"); -+ return; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == yes) { -+ showCertDialog = false; -+ //dialog.dispose(); -+ dialog.hide(); -+ } else if (evt.getSource() == no) { -+ showCertDialog = true; -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ System.out.println("done actionPerformed()"); -+ } -+} -+ -+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 ""; -+ } -+ } -+} -+ -+class Base64Coder { -+ -+ // Mapping table from 6-bit nibbles to Base64 characters. -+ private static char[] map1 = new char[64]; -+ static { -+ int i=0; -+ for (char c='A'; c<='Z'; c++) map1[i++] = c; -+ for (char c='a'; c<='z'; c++) map1[i++] = c; -+ for (char c='0'; c<='9'; c++) map1[i++] = c; -+ map1[i++] = '+'; map1[i++] = '/'; } -+ -+ // Mapping table from Base64 characters to 6-bit nibbles. -+ private static byte[] map2 = new byte[128]; -+ static { -+ for (int i=0; iin. -+ * @return A character array with the Base64 encoded data. -+ */ -+ public static char[] encode (byte[] in, int iLen) { -+ int oDataLen = (iLen*4+2)/3; // output length without padding -+ int oLen = ((iLen+2)/3)*4; // output length including padding -+ char[] out = new char[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++] & 0xff; -+ int i1 = ip < iLen ? in[ip++] & 0xff : 0; -+ int i2 = ip < iLen ? in[ip++] & 0xff : 0; -+ int o0 = i0 >>> 2; -+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); -+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); -+ int o3 = i2 & 0x3F; -+ out[op++] = map1[o0]; -+ out[op++] = map1[o1]; -+ out[op] = op < oDataLen ? map1[o2] : '='; op++; -+ out[op] = op < oDataLen ? map1[o3] : '='; op++; } -+ return out; } -+ -+ /** -+ * Decodes a string from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return A String containing the decoded data. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static String decodeString (String s) { -+ return new String(decode(s)); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (String s) { -+ return decode(s.toCharArray()); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * No blanks or line breaks are allowed within the Base64 encoded data. -+ * @param in a character array containing the Base64 encoded data. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (char[] in) { -+ int iLen = in.length; -+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); -+ while (iLen > 0 && in[iLen-1] == '=') iLen--; -+ int oLen = (iLen*3) / 4; -+ byte[] out = new byte[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++]; -+ int i1 = in[ip++]; -+ int i2 = ip < iLen ? in[ip++] : 'A'; -+ int i3 = ip < iLen ? in[ip++] : 'A'; -+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int b0 = map2[i0]; -+ int b1 = map2[i1]; -+ int b2 = map2[i2]; -+ int b3 = map2[i3]; -+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int o0 = ( b0 <<2) | (b1>>>4); -+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); -+ int o2 = ((b2 & 3)<<6) | b3; -+ out[op++] = (byte)o0; -+ if (op= 2) { -+ proxyPort = new String(pieces[1]); -+ } else { -+ proxyPort = new String("8080"); -+ } -+ } -+ } -+ str = readParameter("proxyHost", false); -+ if (str != null) { -+ proxyHost = new String(str); -+ } -+ str = readParameter("proxyPort", false); -+ if (str != null) { -+ proxyPort = new String(str); -+ } -+ if (proxyHost != null && proxyPort == null) { -+ proxyPort = new String("8080"); -+ } -+ -+ 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; -+ } -+ debugCerts = false; -+ str = readParameter("debugCerts", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugCerts = true; -+ } -+ debugKeyboard = false; -+ str = readParameter("debugKeyboard", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugKeyboard = true; -+ } -+ mapF5_to_atsign = false; -+ str = readParameter("mapF5_to_atsign", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ mapF5_to_atsign = true; -+ } -+ forbid_Ctrl_Alt = false; -+ str = readParameter("forbid_Ctrl_Alt", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ forbid_Ctrl_Alt = true; -+ } - } - - public String readParameter(String name, boolean required) { diff --git a/classes/ssl/ultra.vnc b/classes/ssl/ultra.vnc deleted file mode 100644 index 3c57445..0000000 --- a/classes/ssl/ultra.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/classes/ssl/ultraproxy.vnc b/classes/ssl/ultraproxy.vnc deleted file mode 100644 index fd842c4..0000000 --- a/classes/ssl/ultraproxy.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/classes/ssl/ultrasigned.vnc b/classes/ssl/ultrasigned.vnc deleted file mode 100644 index a711655..0000000 --- a/classes/ssl/ultrasigned.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/classes/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/classes/ssl/ultravnc-102-JavaViewer-ssl-etc.patch deleted file mode 100644 index 3309860..0000000 --- a/classes/ssl/ultravnc-102-JavaViewer-ssl-etc.patch +++ /dev/null @@ -1,5494 +0,0 @@ -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-31 15:40:45.000000000 -0400 -@@ -43,30 +43,36 @@ - viewer = v; - - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); -- disconnectButton = new Button("Disconnect"); -+ if (v.ftpOnly) { -+ disconnectButton = new Button("Quit"); -+ } else { -+ disconnectButton = new Button("Close"); -+ } - disconnectButton.setEnabled(false); - add(disconnectButton); - disconnectButton.addActionListener(this); -- optionsButton = new Button("Options"); -- add(optionsButton); -- optionsButton.addActionListener(this); -- clipboardButton = new Button("Clipboard"); -- clipboardButton.setEnabled(false); -- add(clipboardButton); -- clipboardButton.addActionListener(this); -- if (viewer.rec != null) { -- recordButton = new Button("Record"); -- add(recordButton); -- recordButton.addActionListener(this); -- } -- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); -- ctrlAltDelButton.setEnabled(false); -- add(ctrlAltDelButton); -- ctrlAltDelButton.addActionListener(this); -- refreshButton = new Button("Refresh"); -- refreshButton.setEnabled(false); -- add(refreshButton); -- refreshButton.addActionListener(this); -+ if (!v.ftpOnly) { -+ optionsButton = new Button("Options"); -+ add(optionsButton); -+ optionsButton.addActionListener(this); -+ clipboardButton = new Button("Clipboard"); -+ clipboardButton.setEnabled(false); -+ add(clipboardButton); -+ clipboardButton.addActionListener(this); -+ if (viewer.rec != null) { -+ recordButton = new Button("Record"); -+ add(recordButton); -+ recordButton.addActionListener(this); -+ } -+ ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); -+ ctrlAltDelButton.setEnabled(false); -+ add(ctrlAltDelButton); -+ ctrlAltDelButton.addActionListener(this); -+ refreshButton = new Button("Refresh"); -+ refreshButton.setEnabled(false); -+ add(refreshButton); -+ refreshButton.addActionListener(this); -+ } - ftpButton = new Button("File Transfer"); - ftpButton.setEnabled(false); - add(ftpButton); -@@ -79,9 +85,10 @@ - - public void enableButtons() { - disconnectButton.setEnabled(true); -+ ftpButton.setEnabled(true); -+ if (viewer.ftpOnly) {return;} - clipboardButton.setEnabled(true); - refreshButton.setEnabled(true); -- ftpButton.setEnabled(true); - } - - // -@@ -89,6 +96,9 @@ - // - - public void disableButtonsOnDisconnect() { -+ ftpButton.setEnabled(false); -+ if (viewer.ftpOnly) {return;} -+ - remove(disconnectButton); - disconnectButton = new Button("Hide desktop"); - disconnectButton.setEnabled(true); -@@ -99,7 +109,6 @@ - clipboardButton.setEnabled(false); - ctrlAltDelButton.setEnabled(false); - refreshButton.setEnabled(false); -- ftpButton.setEnabled(false); - - validate(); - } -@@ -110,6 +119,7 @@ - // - - public void enableRemoteAccessControls(boolean enable) { -+ if (viewer.ftpOnly) {return;} - ctrlAltDelButton.setEnabled(enable); - } - -@@ -163,9 +173,19 @@ - } - else if (evt.getSource() == ftpButton) - { -- viewer.ftp.setVisible(!viewer.ftp.isVisible()); -+// begin runge/x11vnc -+ if (viewer.ftpOnly) { -+ viewer.vncFrame.setVisible(false); -+ } -+ viewer.ftp.setSavedLocations(); -+ if (viewer.ftp.isVisible()) { -+ viewer.ftp.doClose(); -+ } else { -+ viewer.ftp.doOpen(); -+ } -+// end runge/x11vnc - 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 2009-01-13 09:48:30.000000000 -0500 -@@ -24,8 +24,17 @@ - import java.io.*; - import java.util.ArrayList; - import java.util.Vector; -+import java.util.Date; - import javax.swing.*; - -+import java.nio.ByteBuffer; -+import java.nio.CharBuffer; -+import java.nio.charset.*; -+ -+// begin runge/x11vnc -+import java.util.Arrays; -+// end runge/x11vnc -+ - - /* - * Created on Feb 25, 2004 -@@ -74,12 +83,31 @@ - public javax.swing.JTextField connectionStatus = null; - public boolean updateDriveList; - private Vector remoteList = null; -+ private Vector remoteListInfo = null; - private Vector localList = null; -+ private Vector localListInfo = null; - private File currentLocalDirectory = null; // Holds the current local Directory - private File currentRemoteDirectory = null; // Holds the current remote Directory - 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; -+ private javax.swing.JButton refreshButton = 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; -+ private int lastRemoteIndex = -1; -+ private int lastLocalIndex = -1; -+ private boolean doingShortcutDir = false; -+ private boolean gotShortcutDir = false; -+ private boolean ignore_events = false; -+// end runge/x11vnc - - // sf@2004 - Separate directories and files for better lisibility - private ArrayList DirsList; -@@ -125,11 +153,61 @@ - - void refreshRemoteLocation() - { -+ -+//System.out.println("refreshRemoteLocation1"); - remoteList.clear(); -+ remoteListInfo.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 -@@ -143,6 +221,9 @@ - int size = str.length(); - String driveType = null; - String[] drive = new String[str.length() / 3]; -+ int idx = 0, C_drive = -1, O_drive = -1; -+ -+System.out.println("ComboBox: Str '" + str + "'"); - - // Loop through the string to create a String[] - for (int i = 0; i < size; i = i + 3) { -@@ -150,26 +231,68 @@ - driveType = str.substring(i + 2, i + 3); - if (driveType.compareTo("f") == 0) - drive[i / 3] += "\\ Floppy"; -- if (driveType.compareTo("l") == 0) -+ if (driveType.compareTo("l") == 0) { - drive[i / 3] += "\\ Local Disk"; -+ if (drive[i/3].substring(0,1).toUpperCase().equals("C")) { -+ C_drive = idx; -+ } else if (O_drive < 0) { -+ O_drive = idx; -+ } -+ } - if (driveType.compareTo("c") == 0) - drive[i / 3] += "\\ CD-ROM"; - if (driveType.compareTo("n") == 0) - drive[i / 3] += "\\ Network"; - - remoteDrivesComboBox.addItem(drive[i / 3]); -+System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'"); -+ idx++; -+ } -+ -+ // runge -+ if (viewer.ftpDropDown != null) { -+ String[] dd = viewer.ftpDropDown.split("\\."); -+ for (int i=0; i < dd.length; i++) { -+ if (!dd[i].equals("")) { -+ String s = dd[i]; -+ if (s.startsWith("TOP_")) { -+ s = s.substring(4); -+ remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0); -+ } else { -+ remoteDrivesComboBox.addItem(" [" + s + "]"); -+ } -+ } -+ } -+ } else { -+ remoteDrivesComboBox.addItem(" [My Documents]"); -+ remoteDrivesComboBox.addItem(" [Desktop]"); -+ remoteDrivesComboBox.addItem(" [Home]"); - } -+ - //sf@ - Select Drive C:as default if possible - boolean bFound = false; -- for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) -- { -- if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) -- { -- remoteDrivesComboBox.setSelectedIndex(i); -+ -+ if (false) { -+ for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) { -+ if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) { -+ remoteDrivesComboBox.setSelectedIndex(i); -+ bFound = true; -+ } -+ } -+ } else { -+ if (C_drive >= 0) { -+ remoteDrivesComboBox.setSelectedIndex(C_drive); -+ bFound = true; -+System.out.println("ComboBox: C_drive index: " + C_drive); -+ } else if (O_drive >= 0) { -+ remoteDrivesComboBox.setSelectedIndex(O_drive); - bFound = true; -+System.out.println("ComboBox: Other_drive index: " + O_drive); - } - } -+ - if (!bFound) remoteDrivesComboBox.setSelectedIndex(0); -+ - updateDriveList = false; - return drive; - } -@@ -185,6 +308,8 @@ - stopButton.setVisible(true); - stopButton.setEnabled(true); - receiveButton.setEnabled(false); -+ viewButton.setEnabled(false); // runge/x11vnc -+ refreshButton.setEnabled(false); - remoteTopButton.setEnabled(false); - sendButton.setEnabled(false); - remoteFileTable.setEnabled(false); -@@ -207,6 +332,8 @@ - stopButton.setVisible(false); - stopButton.setEnabled(false); - receiveButton.setEnabled(true); -+ viewButton.setEnabled(true); // runge/x11vnc -+ refreshButton.setEnabled(true); - remoteTopButton.setEnabled(true); - sendButton.setEnabled(true); - remoteFileTable.setEnabled(true); -@@ -221,10 +348,11 @@ - /* - * Print Directory prints out all the contents of a directory - */ -- void printDirectory(ArrayList a) { -+ void printDirectory(ArrayList a, ArrayList b) { - - for (int i = 0; i < a.size(); i++) { - remoteList.addElement(a.get(i)); -+ remoteListInfo.addElement(b.get(i)); - } - remoteFileTable.setListData(remoteList); - } -@@ -235,10 +363,12 @@ - * @return void - */ - private void initialize() { -+ ignore_events = true; - this.setSize(794, 500); - this.setContentPane(getJContentPane()); -+ ignore_events = false; - updateDriveList = true; -- } -+ } - /** - * This method initializes jContentPane. This is the main content pane - * -@@ -253,6 +383,33 @@ - jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST); - jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST); - jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER); -+ -+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); -+ AbstractAction escapeAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ System.out.println("Escape Pressed"); -+ if (viewer.ftpOnly) { -+ System.out.println("exiting..."); -+ System.exit(0); -+ } else { -+ doClose(); -+ } -+ } -+ }; -+ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); -+ jContentPane.getInputMap().put(stroke, "escapeAction"); -+ jContentPane.getActionMap().put("escapeAction", escapeAction); -+ -+ stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK); -+ AbstractAction resetAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ System.out.println("Ctrl-R Pressed"); -+ doReset(); -+ } -+ }; -+ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction"); -+ jContentPane.getInputMap().put(stroke, "resetAction"); -+ jContentPane.getActionMap().put("resetAction", resetAction); - } - return jContentPane; - } -@@ -270,6 +427,7 @@ - topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER); - topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST); - topPanelLocal.setBackground(java.awt.Color.lightGray); -+//System.out.println("getTopPanelLocal"); - } - return topPanelLocal; - } -@@ -288,6 +446,7 @@ - topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER); - topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST); - topPanelRemote.setBackground(java.awt.Color.lightGray); -+//System.out.println("getTopPanelRemote"); - } - return topPanelRemote; - } -@@ -301,6 +460,7 @@ - if (topPanelCenter == null) { - topPanelCenter = new javax.swing.JPanel(); - topPanelCenter.add(getDummyButton(), null); -+//System.out.println("getTopPanelCenter"); - } - return topPanelCenter; - } -@@ -328,6 +488,7 @@ - topPanel.add(getRemoteTopButton(), null); - topPanel.setBackground(java.awt.Color.lightGray); - */ -+//System.out.println("getTopPanel"); - } - return topPanel; - } -@@ -348,6 +509,7 @@ - statusPanel.add(getJProgressBar(), null); - statusPanel.add(getConnectionStatus(), null); - statusPanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getStatusPanel"); - - } - return statusPanel; -@@ -368,6 +530,7 @@ - remotePanel.add(getRemoteScrollPane(), null); - remotePanel.add(getRemoteStatus(), null); - remotePanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getRemotePanel"); - } - return remotePanel; - } -@@ -390,6 +553,7 @@ - localPanel.setComponentOrientation( - java.awt.ComponentOrientation.UNKNOWN); - localPanel.setName("localPanel"); -+//System.out.println("getLocalPanel"); - } - return localPanel; - } -@@ -405,12 +569,15 @@ - buttonPanel = new javax.swing.JPanel(); - buttonPanel.setLayout(null); - buttonPanel.add(getReceiveButton(), null); -+ buttonPanel.add(getRefreshButton(), null); // runge/x11vnc -+ buttonPanel.add(getViewButton(), null); // runge/x11vnc - buttonPanel.add(getNewFolderButton(), null); - buttonPanel.add(getCloseButton(), null); - buttonPanel.add(getDeleteButton(), null); - buttonPanel.add(getSendButton(), null); - buttonPanel.add(getStopButton(), null); - buttonPanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getButtonPanel"); - } - return buttonPanel; - } -@@ -422,10 +589,11 @@ - 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); -+//System.out.println("getSendButton"); - - } - return sendButton; -@@ -438,7 +606,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 +621,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 +636,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 +644,39 @@ - return newFolderButton; - } - -+// begin runge/x11vnc -+ /** -+ * This method initializes refreshButton -+ * -+ * @return javax.swing.JButton -+ */ -+ private javax.swing.JButton getRefreshButton() { -+ if (refreshButton == null) { -+ refreshButton = new javax.swing.JButton(); -+ refreshButton.setBounds(15, 170, 107, 25); -+ refreshButton.setText("Refresh"); -+ refreshButton.setName("refreshButton"); -+ refreshButton.addActionListener(this); -+ } -+ return refreshButton; -+ } -+ /** -+ * This method initializes viewButton -+ * -+ * @return javax.swing.JButton -+ */ -+ private javax.swing.JButton getViewButton() { -+ if (viewButton == null) { -+ viewButton = new javax.swing.JButton(); -+ viewButton.setBounds(15, 200, 107, 25); -+ viewButton.setText("View File"); -+ viewButton.setName("viewButton"); -+ viewButton.addActionListener(this); -+ } -+ return viewButton; -+ } -+// end runge/x11vnc -+ - /** - * This method initializes stopButton - * -@@ -486,7 +687,7 @@ - if (stopButton == null) - { - stopButton = new javax.swing.JButton(); -- stopButton.setBounds(20, 200, 97, 25); -+ stopButton.setBounds(15, 230, 107, 25); // runge/x11vnc - stopButton.setText("Stop"); - stopButton.setName("stopButton"); - stopButton.addActionListener(this); -@@ -503,8 +704,12 @@ - private javax.swing.JButton getCloseButton() { - if (closeButton == null) { - closeButton = new javax.swing.JButton(); -- closeButton.setBounds(20, 325, 97, 25); -- closeButton.setText("Close"); -+ closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc -+ if (viewer.ftpOnly) { -+ closeButton.setText("Quit"); -+ } else { -+ closeButton.setText("Close"); -+ } - closeButton.setName("closeButton"); - closeButton.addActionListener(this); - } -@@ -551,6 +756,7 @@ - //Select the second entry (e.g. C:\) - // localDrivesComboBox.setSelectedIndex(1); - localDrivesComboBox.addActionListener(this); -+//System.out.println("getLocalDrivesComboBox"); - } - updateDriveList = false; - return localDrivesComboBox; -@@ -567,6 +773,7 @@ - remoteDrivesComboBox.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - remoteDrivesComboBox.addActionListener(this); -+//System.out.println("getRemoteDrivesComboBox"); - - } - return remoteDrivesComboBox; -@@ -587,6 +794,7 @@ - localMachineLabel.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 11)); - localMachineLabel.setEditable(false); -+//System.out.println("getLocalMachineLabel"); - } - return localMachineLabel; - } -@@ -622,6 +830,7 @@ - localTopButton.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); - localTopButton.addActionListener(this); -+//System.out.println("getLocalTopButton"); - } - return localTopButton; - } -@@ -638,6 +847,7 @@ - remoteTopButton.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); - remoteTopButton.addActionListener(this); -+//System.out.println("getRemoteTopButton"); - } - return remoteTopButton; - } -@@ -650,9 +860,24 @@ - private javax.swing.JList getLocalFileTable() { - if (localFileTable == null) { - localList = new Vector(0); -+ localListInfo = new Vector(0); - localFileTable = new JList(localList); -+ MouseMotionListener mlisten = new MouseMotionAdapter() { -+ public void mouseMoved(MouseEvent e) { -+ int index = localFileTable.locationToIndex(e.getPoint()); -+ if (index == lastLocalIndex) { -+ return; -+ } else if (index < 0) { -+ return; -+ } -+ lastLocalIndex = index; -+ connectionStatus.setText((String) localListInfo.get(index)); -+ } -+ }; - localFileTable.addMouseListener(this); -+ localFileTable.addMouseMotionListener(mlisten); - localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); -+//System.out.println("getLocalFileTable"); - } - return localFileTable; - } -@@ -669,6 +894,7 @@ - localScrollPane.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - localScrollPane.setName("localFileList"); -+//System.out.println("getLocalScrollPane"); - } - return localScrollPane; - } -@@ -680,10 +906,25 @@ - private javax.swing.JList getRemoteFileTable() { - if (remoteFileTable == null) { - remoteList = new Vector(0); -+ remoteListInfo = new Vector(0); - remoteFileTable = new JList(remoteList); -+ MouseMotionListener mlisten = new MouseMotionAdapter() { -+ public void mouseMoved(MouseEvent e) { -+ int index = remoteFileTable.locationToIndex(e.getPoint()); -+ if (index == lastRemoteIndex) { -+ return; -+ } else if (index < 0) { -+ return; -+ } -+ lastRemoteIndex = index; -+ connectionStatus.setText((String) remoteListInfo.get(index)); -+ } -+ }; - remoteFileTable.addMouseListener(this); -+ remoteFileTable.addMouseMotionListener(mlisten); - remoteFileTable.setSelectedValue("C:\\", false); - remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); -+//System.out.println("getRemoteFileTable"); - - } - return remoteFileTable; -@@ -698,6 +939,7 @@ - remoteScrollPane = new javax.swing.JScrollPane(); - remoteScrollPane.setViewportView(getRemoteFileTable()); - remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418)); -+//System.out.println("getRemoteScrollPane"); - } - return remoteScrollPane; - } -@@ -716,6 +958,7 @@ - remoteLocation.setBackground(new Color(255,255,238)); - remoteLocation.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getRemoteLocation"); - } - return remoteLocation; - } -@@ -732,6 +975,7 @@ - localLocation.setBackground( new Color(255,255,238)); - localLocation.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getLocalLocation"); - } - return localLocation; - } -@@ -748,6 +992,7 @@ - localStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - localStatus.setEditable(false); -+//System.out.println("getLocalStatus"); - } - return localStatus; - } -@@ -764,6 +1009,7 @@ - remoteStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - remoteStatus.setEditable(false); -+//System.out.println("getRemoteStatus"); - } - return remoteStatus; - } -@@ -777,9 +1023,10 @@ - historyComboBox = new javax.swing.JComboBox(); - historyComboBox.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); -- historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0); -+ historyComboBox.insertItemAt(new String("Pulldown to view history; Press Escape to Close/Quit; Press Ctrl-R to Reset Panel."),0); - historyComboBox.setSelectedIndex(0); - historyComboBox.addActionListener(this); -+//System.out.println("getHistoryComboBox"); - } - return historyComboBox; - } -@@ -791,6 +1038,7 @@ - private javax.swing.JProgressBar getJProgressBar() { - if (jProgressBar == null) { - jProgressBar = new javax.swing.JProgressBar(); -+//System.out.println("getJProgressBar"); - } - return jProgressBar; - } -@@ -806,6 +1054,7 @@ - connectionStatus.setBackground(java.awt.Color.lightGray); - connectionStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getConnectionStatus"); - } - connectionStatus.setEditable(false); - return connectionStatus; -@@ -815,7 +1064,12 @@ - * Implements Action listener. - */ - public void actionPerformed(ActionEvent evt) { -- System.out.println(evt.getSource()); -+// System.out.println(evt.getSource()); -+ -+ if (ignore_events) { -+ System.out.println("ignore_events: " + evt.getSource()); -+ return; -+ } - - if (evt.getSource() == closeButton) - { // Close Button -@@ -829,15 +1083,27 @@ - { - 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); -+ -+ // are these really needed? changeRemoteDrive() does them at the end. -+ if (false) { -+ remoteList.clear(); -+ remoteListInfo.clear(); -+ remoteFileTable.setListData(remoteList); -+ } - } - else if (evt.getSource() == localTopButton) - { -@@ -845,12 +1111,17 @@ - } - else if (evt.getSource() == remoteTopButton) - { -+//System.out.println("remoteTopButton"); // runge/x11vnc - changeRemoteDrive(); - } - else if(evt.getSource() == deleteButton) - { - doDelete(); - } -+ else if(evt.getSource() == refreshButton) -+ { -+ doRefresh(); -+ } - else if(evt.getSource()==newFolderButton) - { - doNewFolder(); -@@ -864,7 +1135,7 @@ - - private void doNewFolder() - { -- String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); -+ String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); - if(selectedTable.equals("remote")) - { - name = remoteLocation.getText()+name; -@@ -880,34 +1151,106 @@ - historyComboBox.setSelectedIndex(0); - } - } -- private void doClose() -+ public void doClose() - { -+ if (viewer.ftpOnly) { -+ viewer.disconnect(); -+ return; -+ } - try { - this.setVisible(false); -- viewer.rfb.writeFramebufferUpdateRequest( -- 0, -- 0, -- viewer.rfb.framebufferWidth, -- viewer.rfb.framebufferHeight, -- true); -+ viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth, -+ viewer.rfb.framebufferHeight, true); -+ -+ if (false) { -+ this.dispose(); -+ jContentPane = null; -+ } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } -+ private void unSwing() { -+ jContentPane = null; -+ topPanel = null; -+ topPanelLocal = null; -+ topPanelRemote = null; -+ topPanelCenter = null; -+ statusPanel = null; -+ remotePanel = null; -+ localPanel = null; -+ buttonPanel = null; -+ sendButton = null; -+ receiveButton = null; -+ deleteButton = null; -+ newFolderButton = null; -+ stopButton = null; -+ closeButton = null; -+ dummyButton = null; -+ localDrivesComboBox = null; -+ remoteDrivesComboBox = null; -+ localMachineLabel = null; -+ remoteMachineLabel = null; -+ localTopButton = null; -+ remoteTopButton = null; -+ localScrollPane = null; -+ localFileTable = null; -+ remoteScrollPane = null; -+ remoteFileTable = null; -+ remoteLocation = null; -+ localLocation = null; -+ localStatus = null; -+ remoteStatus = null; -+ historyComboBox = null; -+ jProgressBar = null; -+ connectionStatus = null; -+ viewButton = null; -+ refreshButton = null; -+ } -+ -+ public void doReset() -+ { -+ try { -+ this.setVisible(false); -+ this.dispose(); -+ jContentPane = null; -+ try {Thread.sleep(500);} catch (InterruptedException e) {} -+ viewer.ftp_init(); -+ } catch (Exception e) { -+ // TODO Auto-generated catch block -+ e.printStackTrace(); -+ } -+ } - -+ public void doOpen() -+ { -+ try { -+ this.setVisible(true); -+ if (false) { -+ this.initialize(); -+ } -+ } catch (Exception e) { -+ // TODO Auto-generated catch block -+ e.printStackTrace(); -+ } -+ } - private void doDelete() - { -- System.out.println("Delete Button Pressed"); -+// System.out.println("Delete Button Pressed"); - //Call this method to delete a file at server - if(selectedTable.equals("remote")) - { -- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be deleted - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - -@@ -916,7 +1259,7 @@ - // sf@2004 - Delete prompt - if (remoteList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -926,18 +1269,22 @@ - } - else - { -- String sFileName = ((String) this.localFileTable.getSelectedValue()); -+ Object selected = this.localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be deleted - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - // sf@2004 - Delete prompt - if (localList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -952,21 +1299,25 @@ - - private void doReceive() - { -- System.out.println("Received Button Pressed"); -+// System.out.println("Received Button Pressed"); - -- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be transfered - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - - // sf@2004 - Overwrite prompt - if (localList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -979,23 +1330,101 @@ - viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); - } - -+// begin runge/x11vnc -+ private void doRefresh() -+ { -+ System.out.println("Refreshing Local and Remote."); -+ refreshLocalLocation(); -+ refreshRemoteLocation(); -+ } -+ -+ 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; -+ } -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return null; -+ } -+ String sFileName = ((String) selected); -+ -+ 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); -+ System.out.println("remoteFileName: " + remoteFileName); -+if (false) { -+ char[] b = remoteFileName.toCharArray(); -+ for (int n = 0; n < b.length; n++) { -+ System.out.print(Integer.toHexString(b[n]) + " "); -+ } -+ System.out.println(""); -+ for (int n = 0; n < b.length; n++) { -+ System.out.print(b[n]); -+ } -+ System.out.println(""); -+} -+ -+ 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"); -+// System.out.println("Send Button Pressed"); - -- String sFileName = ((String) this.localFileTable.getSelectedValue()); -+ Object selected = this.localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be transfered - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - - // sf@2004 - Overwrite prompt - if (remoteList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -1013,6 +1442,7 @@ - // - private void doStop() - { -+ System.out.println("** Current Transfer Aborted **"); - viewer.rfb.fAbort = true; - } - /** -@@ -1024,6 +1454,14 @@ - System.out.println("History: " + message); - historyComboBox.insertItemAt(new String(message), 0); - } -+ -+ public void receivedRemoteDirectoryName(String str) { -+ if (doingShortcutDir) { -+ if (str.length() > 1) { -+ remoteLocation.setText(str); -+ } -+ } -+ } - - /** - * This method updates the file table to the current selection of the remoteComboBox -@@ -1034,11 +1472,44 @@ - remoteSelection = null; - - if (!updateDriveList) { -- String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; -- viewer.rfb.readServerDirectory(drive); -- remoteLocation.setText(drive); -+//System.out.println("changeRemoteDrive-A " + drive); // begin runge/x11vnc -+ Object selected = remoteDrivesComboBox.getSelectedItem(); -+ if (selected != null) { -+ String instr = selected.toString(); -+ if (instr != null) { -+System.out.println("changeRemoteDrive: instr='" + instr + "'"); -+ String drive = instr.substring(0,1)+ ":\\"; -+ if (instr.startsWith(" [")) { -+ int idx = instr.lastIndexOf(']'); -+ if (idx > 2) { -+ drive = instr.substring(2, idx); -+ } else { -+ drive = instr.substring(2); -+ } -+ if (drive.equals("Home")) { -+ drive = ""; -+ } -+ drive += "\\"; -+ doingShortcutDir = true; -+ } else { -+ doingShortcutDir = false; -+ drive = saveRemoteHack(drive); -+ } -+ gotShortcutDir = false; -+ viewer.rfb.readServerDirectory(drive); -+ if (!gotShortcutDir) { -+ remoteLocation.setText(drive); -+ } -+ } else { -+System.out.println("changeRemoteDrive: instr null"); -+ } -+ } else { -+System.out.println("changeRemoteDrive: selection null"); -+ } -+//System.out.println("changeRemoteDrive-B " + drive); // end runge/x11vnc - } - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - /** -@@ -1048,6 +1519,7 @@ - private void changeLocalDrive() - { - File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); -+System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc - if(currentDrive.canRead()) - { - localSelection = null; -@@ -1057,9 +1529,11 @@ - else - { - localList.clear(); -+ localListInfo.clear(); - 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 -@@ -1098,10 +1572,18 @@ - selectedTable = "remote"; - localFileTable.setBackground(new Color(238, 238, 238)); - remoteFileTable.setBackground(new Color(255, 255, 255)); -- String name = (remoteFileTable.getSelectedValue().toString()).substring(1); -+ Object selected = remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String selstr = selected.toString(); -+ if (selstr == null) { -+ return; -+ } -+ String name = selstr.substring(1); - if( !name.substring(0, 2).equals(" [")) - remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); -- -+ - } - - /* -@@ -1115,10 +1597,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 - */ -@@ -1126,20 +1636,29 @@ - String name = null; - String action = null; - String drive = null; -- name = (remoteFileTable.getSelectedValue().toString()).substring(1); -+ Object selected = remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sname = selected.toString(); -+ if (sname == null) { -+ return; -+ } -+ name = sname.substring(1); - - if (name.equals("[..]")) - { - action = "up"; - remoteSelection = null; - drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1); -- // JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); -+ // JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); - int index = drive.lastIndexOf("\\"); - drive = drive.substring(0, index + 1); - - remoteLocation.setText(drive); - viewer.rfb.readServerDirectory(drive); - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]")) -@@ -1149,6 +1668,7 @@ - remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); - drive = remoteLocation.getText(); - // ?? -+ viewRemote(); // runge/x11vnc - } - else - { -@@ -1159,10 +1679,12 @@ - remoteLocation.setText(drive); - viewer.rfb.readServerDirectory(drive); - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - //remoteLocation.setText(drive); - } -+ - /** - * Updates the Local File Table based on selection. Called from MouseClicked handler - */ -@@ -1188,6 +1710,7 @@ - else if (currentSelection.isFile()) - { - localSelection = currentSelection.getAbsoluteFile(); -+ viewLocal(); // runge/x11vnc - } - else if (currentSelection.isDirectory()) - { -@@ -1201,13 +1724,22 @@ - * - */ - private String getTrimmedSelection(){ -- String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1); -- if(currentSelection.substring(0,1).equals("[") && -- currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ -- return currentSelection.substring(1,currentSelection.length()-1); -- } else { -- return currentSelection; -- } -+ String currentSelection = ""; -+ Object selected = localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return currentSelection; -+ } -+ String selstr = selected.toString(); -+ if (selstr == null) { -+ return currentSelection; -+ } -+ currentSelection = selstr.substring(1); -+ if(currentSelection.substring(0,1).equals("[") && -+ currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ -+ return currentSelection.substring(1,currentSelection.length()-1); -+ } else { -+ return currentSelection; -+ } - } - - /* -@@ -1241,36 +1773,148 @@ - return null; - } - -+ String timeStr(long t) { -+ Date date = new Date(t); -+ return date.toString(); -+ } -+ String dotPast(double f, int n) { -+ String fs = "" + f; -+ int i = fs.lastIndexOf(".") + n; -+ if (i >= 0) { -+ int len = fs.length(); -+ if (i >= len) { -+ i = len-1; -+ } -+ fs = fs.substring(0, i); -+ } -+ return fs; -+ } -+ String sizeStr(int s) { -+ if (s < 0) { -+ return s + "? B"; -+ } else if (s < 1024) { -+ return s + " B"; -+ } else if (s < 1024 * 1024) { -+ double k = s / 1024.0; -+ String ks = dotPast(k, 3); -+ -+ return s + " (" + ks + " KB)"; -+ } else { -+ double m = s / (1024.0*1024.0); -+ String ms = dotPast(m, 3); -+ return s + " (" + ms + " MB)"; -+ } -+ } -+ -+ int max_char(String text) { -+ int maxc = 0; -+ char chars[] = text.toCharArray(); -+ for (int n = 0; n < chars.length; n++) { -+ if ((int) chars[n] > maxc) { -+ maxc = (int) chars[n]; -+ } -+ } -+ return maxc; -+ } - - /* - * Navigates the local file structure up or down one directory - */ - public void changeLocalDirectory(File dir) - { -- currentLocalDirectory = dir; // Updates Global -+ dir = saveLocalHack(dir); // runge/x11vnc -+ -+ if (dir == null) { -+ connectionStatus.setText("Error changing local directory."); -+ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); -+ historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ - File allFiles[] = dir.listFiles(); // Reads files - String[] contents = dir.list(); - -+ if (contents == null || allFiles == null) { -+ connectionStatus.setText("Error changing local directory."); -+ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); -+ historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ -+ currentLocalDirectory = dir; // Updates Global -+// 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]); -+ } -+ } else { -+ return; -+ } -+// end runge/x11vnc -+ - localList.clear(); -+ localListInfo.clear(); - localList.addElement(" [..]"); -+ localListInfo.addElement(" [..]"); -+ -+ ArrayList DirInfo = new ArrayList(); -+ ArrayList FilInfo = new ArrayList(); -+ -+ Charset charset = Charset.forName("ISO-8859-1"); -+ CharsetDecoder decoder = charset.newDecoder(); -+ CharsetEncoder encoder = charset.newEncoder(); - - // Populate the Lists - for (int i = 0; i < contents.length; i++) - { -- if (allFiles[i].isDirectory()) -+ String f1 = contents[i]; -+ -+if (false) { -+ -+System.out.println("max_char: " + max_char(f1) + " " + f1); -+ if (max_char(f1) > 255) { -+ try { -+System.out.println("bbuf1"); -+ ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray())); -+System.out.println("bbuf2"); -+ CharBuffer cbuf = decoder.decode(bbuf); -+System.out.println("bbuf3"); -+ f1 = cbuf.toString(); -+System.out.println("did bbuf: " + f1); -+ } catch (Exception e) { -+ ; -+ } -+ } -+} -+ -+ String f2 = f1; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ String s = f2 + " \tLastmod: " + timeStr(allFiles[i].lastModified()) + " \t\tSize: " + sizeStr((int) allFiles[i].length()); -+ if (allFiles[i].isDirectory()) { - // localList.addElement("[" + contents[i] + "]"); -- DirsList.add(" [" + contents[i] + "]"); // sf@2004 -- else -- { -+ DirsList.add(" [" + f1 + "]"); // sf@2004 -+ DirInfo.add(s); -+ } else { - // localList.addElement(contents[i]); -- FilesList.add(" " + contents[i]); // sf@2004 -+ FilesList.add(" " + f1); // sf@2004 -+ FilInfo.add(s); - } - } - // sf@2004 -- for (int i = 0; i < DirsList.size(); i++) -+ for (int i = 0; i < DirsList.size(); i++) { - localList.addElement(DirsList.get(i)); -- for (int i = 0; i < FilesList.size(); i++) -+ localListInfo.addElement(DirInfo.get(i)); -+ } -+ for (int i = 0; i < FilesList.size(); i++) { - localList.addElement(FilesList.get(i)); -+ localListInfo.addElement(FilInfo.get(i)); -+ } - - FilesList.clear(); - DirsList.clear(); -@@ -1296,3 +1940,147 @@ - } - - } // @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; -+ TextViewer me; -+ -+ public TextViewer(String s, File f, boolean d) { -+ -+ delete_it = d; -+ file = f; -+ me = this; -+ -+ JScrollPane scrollPane = new JScrollPane(textArea, -+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, -+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); -+ -+ textArea.setEditable(false); -+ textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); -+ -+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK); -+ AbstractAction escapeAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ cleanse(); -+ me.dispose(); -+ } -+ }; -+ textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); -+ textArea.getInputMap().put(stroke, "escapeAction"); -+ textArea.getActionMap().put("escapeAction", escapeAction); -+ -+ 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/Makefile JavaViewer/Makefile ---- JavaViewer.orig/Makefile 2006-05-29 09:06:32.000000000 -0400 -+++ JavaViewer/Makefile 2010-05-18 20:53:32.000000000 -0400 -@@ -4,6 +4,7 @@ - - CP = cp - JC = javac -+JC_ARGS = -target 1.4 -source 1.4 - JAR = jar - ARCHIVE = VncViewer.jar - PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc -@@ -20,7 +21,7 @@ - all: $(CLASSES) $(ARCHIVE) - - $(CLASSES): $(SOURCES) -- $(JC) -O $(SOURCES) -+ $(JC) $(JC_ARGS) -O $(SOURCES) - - $(ARCHIVE): $(CLASSES) - $(JAR) cf $(ARCHIVE) $(CLASSES) -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 2010-11-30 22:13:58.000000000 -0500 -@@ -31,6 +31,7 @@ - import java.net.Socket; - import java.util.*; - import java.util.zip.*; -+import java.text.DateFormat; - - - class RfbProto { -@@ -86,8 +87,11 @@ - - // sf@2004 - FileTransfer part - ArrayList remoteDirsList; -+ ArrayList remoteDirsListInfo; - ArrayList remoteFilesList; -+ ArrayList remoteFilesListInfo; - ArrayList a; -+ ArrayList b; - boolean fFTInit = true; // sf@2004 - boolean fFTAllowed = true; - boolean fAbort = false; -@@ -199,6 +203,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 +215,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)); -@@ -215,9 +243,12 @@ - osw = new OutputStreamWriter(sock.getOutputStream()); - inDirectory2 = false; - a = new ArrayList(); -+ b = new ArrayList(); - // sf@2004 - remoteDirsList = new ArrayList(); -+ remoteDirsListInfo = new ArrayList(); - remoteFilesList = new ArrayList(); -+ remoteFilesListInfo = new ArrayList(); - - sendFileSource = ""; - } -@@ -420,7 +451,13 @@ - // - - int readServerMessageType() throws IOException { -- int msgType = is.readUnsignedByte(); -+ int msgType; -+ try { -+ msgType = is.readUnsignedByte(); -+ } catch (Exception e) { -+ viewer.disconnect(); -+ return -1; -+ } - - // If the session is being recorded: - if (rec != null) { -@@ -600,6 +637,7 @@ - contentParamT = is.readUnsignedByte(); - contentParamT = contentParamT << 8; - contentParam = contentParam | contentParamT; -+//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam); - if (contentType == rfbRDrivesList || contentType == rfbDirPacket) - { - readDriveOrDirectory(contentParam); -@@ -610,7 +648,7 @@ - } - else if (contentType == rfbFilePacket) - { -- receiveFileChunk(); -+ receiveFileChunk(); - } - else if (contentType == rfbEndOfFile) - { -@@ -618,6 +656,10 @@ - } - else if (contentType == rfbAbortFileTransfer) - { -+ System.out.println("rfbAbortFileTransfer: fFileReceptionRunning=" -+ + fFileReceptionRunning + " fAbort=" -+ + fAbort + " fFileReceptionError=" -+ + fFileReceptionError); - if (fFileReceptionRunning) - { - endOfReceiveFile(false); // Error -@@ -626,6 +668,11 @@ - { - // sf@2004 - Todo: Add TestPermission - // System.out.println("File Transfer Aborted!"); -+ -+ // runge: seems like we must at least read the remaining -+ // 8 bytes of the header, right? -+ int size = is.readInt(); -+ int length = is.readInt(); - } - - } -@@ -645,6 +692,7 @@ - { - System.out.println("ContentType: " + contentType); - } -+//System.out.println("FTM: done"); - } - - //Refactored from readRfbFileTransferMsg() -@@ -662,6 +710,7 @@ - - //Refactored from readRfbFileTransferMsg() - public void readDriveOrDirectory(int contentParam) throws IOException { -+//System.out.println("RDOD: " + contentParam + " " + inDirectory2); - if (contentParam == rfbADrivesList) - { - readFTPMsgDriveList(); -@@ -688,13 +737,21 @@ - - // Internally used. Write an Rfb message to the server - void writeRfbFileTransferMsg( -- int contentType, -- int contentParam, -- long size, // 0 : compression not supported - 1 : compression supported -- long length, -- String text) throws IOException -+ int contentType, -+ int contentParam, -+ long size, // 0 : compression not supported - 1 : compression supported -+ long length, -+ String text) throws IOException - { - byte b[] = new byte[12]; -+ byte byteArray[]; -+ -+ if (viewer.dsmActive) { -+ // need to send the rfbFileTransfer msg type twice for the plugin... -+ byte b2[] = new byte[1]; -+ b2[0] = (byte) rfbFileTransfer; -+ os.write(b2); -+ } - - b[0] = (byte) rfbFileTransfer; - b[1] = (byte) contentType; -@@ -702,7 +759,7 @@ - - byte by = 0; - long c = 0; -- length++; -+ - c = size & 0xFF000000; - by = (byte) (c >>> 24); - b[4] = by; -@@ -716,6 +773,32 @@ - by = (byte) c; - b[7] = by; - -+ if (text != null) { -+ byte byteArray0[] = text.getBytes(); -+ int maxc = max_char(text); -+ if (maxc > 255) { -+ System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")"); -+ byteArray0 = text.getBytes("UTF-8"); -+ } else if (maxc > 127) { -+ System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")"); -+ byteArray0 = text.getBytes("ISO-8859-1"); -+ } -+ byteArray = new byte[byteArray0.length + 1]; -+ for (int i = 0; i < byteArray0.length; i++) { -+ byteArray[i] = byteArray0[i]; -+ } -+ byteArray[byteArray.length - 1] = 0; -+System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length); -+ -+ // will equal length for ascii, ISO-8859-1, more for UTF-8 -+ length = byteArray.length; -+ -+ //length++; // used to not include null byte at end. -+ } else { -+ String moo = "moo"; -+ byteArray = moo.getBytes(); -+ } -+ - c = length & 0xFF000000; - by = (byte) (c >>> 24); - b[8] = by; -@@ -729,29 +812,91 @@ - by = (byte) c; - b[11] = by; - os.write(b); -+ -+//System.out.println("size: " + size + " length: " + length + " text: " + text); - - - if (text != null) - { -- byte byteArray[] = text.getBytes(); -- byte byteArray2[] = new byte[byteArray.length + 1]; -- for (int i = 0; i < byteArray.length; i++) { -- byteArray2[i] = byteArray[i]; -+ os.write(byteArray); -+ } -+ } -+ -+ int max_char(String text) { -+ int maxc = 0; -+ char chars[] = text.toCharArray(); -+ for (int n = 0; n < chars.length; n++) { -+ if ((int) chars[n] > maxc) { -+ maxc = (int) chars[n]; - } -- byteArray2[byteArray2.length - 1] = 0; -- os.write(byteArray2); - } -- -+ return maxc; - } - -+ String guess_encoding(char[] chars) { -+ boolean saw_high_char = false; -+ -+ for (int i = 0; i < chars.length; i++) { -+ if (chars[i] == '\0') { -+ break; -+ } -+ if (chars[i] >= 128) { -+ saw_high_char = true; -+ break; -+ } -+ } -+ if (!saw_high_char) { -+ return "ASCII"; -+ } -+ char prev = 1; -+ boolean valid_utf8 = true; -+ int n = 0; -+ for (int i = 0; i < chars.length; i++) { -+ if (chars[i] == '\0') { -+ break; -+ } -+ char c = chars[i]; -+ if (prev < 128 && c >= 128) { -+ if (c >> 5 == 0x6) { -+ n = 1; -+ } else if (c >> 4 == 0xe) { -+ n = 2; -+ } else if (c >> 3 == 0x1e) { -+ n = 3; -+ } else if (c >> 2 == 0x3e) { -+ n = 4; -+ } else { -+ valid_utf8 = false; -+ break; -+ } -+ } else { -+ if (n > 0) { -+ if (c < 128) { -+ valid_utf8 = false; -+ break; -+ } -+ n--; -+ } -+ } -+ -+ prev = c; -+ } -+ if (valid_utf8) { -+ return "UTF-8"; -+ } else { -+ return "ISO-8859-1"; -+ } -+ } -+ -+ - //Internally used. Write an rfb message to the server for sending files ONLY - int writeRfbFileTransferMsgForSendFile( -- int contentType, -- int contentParam, -- long size, -- long length, -- String source -- ) throws IOException -+ int contentType, -+ int contentParam, -+ long size, -+ long length, -+ String source -+ ) throws IOException - { - File f = new File(source); - fis = new FileInputStream(f); -@@ -768,50 +913,47 @@ - - while (bytesRead!=-1) - { -- counter += bytesRead; -- myDeflater.setInput(byteBuffer, 0, bytesRead); -- myDeflater.finish(); -- compressedSize = myDeflater.deflate(CompressionBuffer); -- myDeflater.reset(); -- // If the compressed data is larger than the original one, we're dealing with -- // already compressed data -- if (compressedSize > bytesRead) -- fCompress = false; -- this.writeRfbFileTransferMsg( -- contentType, -- contentParam, -- (fCompress ? 1 : 0), -- (fCompress ? compressedSize-1 : bytesRead-1), -- null -- ); -- // Todo: Test write error ! -- os.write( -- fCompress ? CompressionBuffer : byteBuffer, -- 0, -- fCompress ? compressedSize : bytesRead -- ); -- -- // Todo: test read error ! -- bytesRead = fis.read(byteBuffer); -- -- // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); -- viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); -- viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); -- -- if (fAbort == true) -- { -- fAbort = false; -- fError = true; -- break; -- } -- try -- { -- Thread.sleep(5); -- } -- catch(InterruptedException e) -- { -- System.err.println("Interrupted"); -- } -+ counter += bytesRead; -+ myDeflater.setInput(byteBuffer, 0, bytesRead); -+ myDeflater.finish(); -+ compressedSize = myDeflater.deflate(CompressionBuffer); -+ myDeflater.reset(); -+ // If the compressed data is larger than the original one, we're dealing with -+ // already compressed data -+ if (compressedSize > bytesRead) -+ fCompress = false; -+ this.writeRfbFileTransferMsg( -+ contentType, -+ contentParam, -+ (fCompress ? 1 : 0), -+// RUNGE (fCompress ? compressedSize-1 : bytesRead-1), -+ (fCompress ? compressedSize : bytesRead), -+ null -+ ); -+ // Todo: Test write error ! -+ os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead); -+ -+ // Todo: test read error ! -+ bytesRead = fis.read(byteBuffer); -+ -+ // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); -+ viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); -+ viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); -+ -+ if (fAbort == true) -+ { -+ fAbort = false; -+ fError = true; -+ break; -+ } -+ try -+ { -+ Thread.sleep(5); -+ } -+ catch(InterruptedException e) -+ { -+ System.err.println("Interrupted"); -+ } - } - - writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null); -@@ -831,24 +973,30 @@ - { - System.out.print((char) is.readUnsignedByte()); - } -+ System.out.println(""); -+ -+ if (size == rfbRErrorCmd || size == -1) { -+ viewer.ftp.enableButtons(); -+ viewer.ftp.connectionStatus.setText("Remote file not available for writing."); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ return; -+ } - -- int ret = writeRfbFileTransferMsgForSendFile( -- rfbFilePacket, -- 0, -- 0, -- 0, -- sendFileSource); -+ int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource); - - viewer.ftp.refreshRemoteLocation(); - if (ret != 1) - { - viewer.ftp.connectionStatus.setText(" > Error - File NOT sent"); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) -+ + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0); - } - else - { - viewer.ftp.connectionStatus.setText(" > File sent"); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) -+ + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0); - } - viewer.ftp.historyComboBox.setSelectedIndex(0); - viewer.ftp.enableButtons(); -@@ -907,7 +1055,7 @@ - //Handles acknowledgement that the file has been deleted on the server - void deleteRemoteFileFeedback() throws IOException - { -- is.readInt(); -+ int ret = is.readInt(); - int length = is.readInt(); - String f = ""; - for (int i = 0; i < length; i++) -@@ -916,7 +1064,11 @@ - } - - viewer.ftp.refreshRemoteLocation(); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); -+ if (ret == -1) { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0); -+ } else { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); -+ } - viewer.ftp.historyComboBox.setSelectedIndex(0); - } - -@@ -926,12 +1078,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbCommand, -- rfbCFileDelete, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -943,7 +1090,7 @@ - // Handles acknowledgement that the directory has been created on the server - void createRemoteDirectoryFeedback() throws IOException - { -- is.readInt(); -+ int ret = is.readInt(); - int length = is.readInt(); - String f=""; - for (int i = 0; i < length; i++) -@@ -951,7 +1098,11 @@ - f += (char)is.readUnsignedByte(); - } - viewer.ftp.refreshRemoteLocation(); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); -+ if (ret == -1) { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0); -+ } else { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); -+ } - viewer.ftp.historyComboBox.setSelectedIndex(0); - } - -@@ -961,12 +1112,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbCommand, -- rfbCDirCreate, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -979,15 +1125,13 @@ - { - try - { -+//System.out.println("requestRemoteFile text: " + text); -+//System.out.println("requestRemoteFile leng: " + text.length()); - String temp = text; - receivePath = localPath; - -- writeRfbFileTransferMsg( -- rfbFileTransferRequest, -- 0, -- 1, // 0 : compression not supported - 1 : compression supported -- temp.length(), -- temp); -+ // 0 : compression not supported - 1 : compression supported -+ writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp); - } - catch (IOException e) - { -@@ -1004,6 +1148,9 @@ - viewer.ftp.disableButtons(); - int size = is.readInt(); - int length = is.readInt(); -+ -+//System.out.println("receiveFileHeader size: " + size); -+//System.out.println("receiveFileHeader leng: " + length); - - String tempName = ""; - for (int i = 0; i < length; i++) -@@ -1011,6 +1158,15 @@ - tempName += (char) is.readUnsignedByte(); - } - -+ if (size == rfbRErrorCmd || size == -1) { -+ fFileReceptionRunning = false; -+ viewer.ftp.enableButtons(); -+ viewer.ftp.connectionStatus.setText("Remote file not available for reading."); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ - // sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for - // backward compatibility reasons...) - int sizeH = is.readInt(); -@@ -1021,7 +1177,16 @@ - fileSize=0; - fileChunkCounter = 0; - String fileName = receivePath; -- fos = new FileOutputStream(fileName); -+ try { -+ fos = new FileOutputStream(fileName); -+ } catch (Exception e) { -+ fFileReceptionRunning = false; -+ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ viewer.ftp.enableButtons(); -+ return; -+ } - writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null); - } - -@@ -1085,7 +1250,13 @@ - fAbort = false; - fFileReceptionError = true; - writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); -- -+ -+ //runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent. -+ try {Thread.sleep(500);} catch (InterruptedException e) {} -+ viewer.ftp.enableButtons(); -+ viewer.ftp.refreshLocalLocation(); -+ viewer.ftp.connectionStatus.setText(" > Error - File NOT received"); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0); - } - // sf@2004 - For old FT protocole only - /* -@@ -1104,7 +1275,7 @@ - int length = is.readInt(); - fileSize=0; - fos.close(); -- -+ - viewer.ftp.refreshLocalLocation(); - if (fReceptionOk && !fFileReceptionError) - { -@@ -1132,12 +1303,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbDirContentRequest, -- rfbRDirContent, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -1197,11 +1363,80 @@ - str += temp; - } - } -+ // runge -+ viewer.ftp.receivedRemoteDirectoryName(str); - // viewer.ftp.changeRemoteDirectory(str); - - } - } - -+ int zogswap(int n) { -+ long l = n; -+ if (l < 0) { -+ l += 0x100000000L; -+ } -+ l = l & 0xFFFFFFFF; -+ l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24); -+ return (int) l; -+ } -+ -+ int windozeToUnix(int L, int H) { -+ long L2 = zogswap(L); -+ long H2 = zogswap(H); -+ long unix = (H2 << 32) + L2; -+ unix -= 11644473600L * 10000000L; -+ unix /= 10000000L; -+ //System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2); -+ return (int) unix; -+ } -+ -+ String timeStr(int t, int h) { -+ if (h == 0) { -+ // x11vnc/libvncserver unix -+ t = zogswap(t); -+ } else { -+ // ultra (except if h==0 by chance) -+ t = windozeToUnix(t, h); -+ } -+ long tl = (long) t; -+ Date date = new Date(tl * 1000); -+ if (true) { -+ return date.toString(); -+ } else { -+ return DateFormat.getDateTimeInstance().format(date); -+ } -+ } -+ -+ String dotPast(double f, int n) { -+ String fs = "" + f; -+ int i = fs.lastIndexOf(".") + n; -+ if (i >= 0) { -+ int len = fs.length(); -+ if (i >= len) { -+ i = len-1; -+ } -+ fs = fs.substring(0, i); -+ } -+ return fs; -+ } -+ String sizeStr(int s) { -+ s = zogswap(s); -+ if (s < 0) { -+ return s + "? B"; -+ } else if (s < 1024) { -+ return s + " B"; -+ } else if (s < 1024 * 1024) { -+ double k = s / 1024.0; -+ String ks = dotPast(k, 3); -+ -+ return s + " (" + ks + " KB)"; -+ } else { -+ double m = s / (1024.0*1024.0); -+ String ms = dotPast(m, 3); -+ return s + " (" + ms + " MB)"; -+ } -+ } -+ - //Internally used to receive directory content from server - //Here, the server sends one file/directory with it's attributes - void readFTPMsgDirectoryListContent() throws IOException -@@ -1217,17 +1452,32 @@ - dwReserved0, - dwReserved1; - long ftCreationTime, ftLastAccessTime, ftLastWriteTime; -+ int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL; -+ int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH; - char cFileName, cAlternateFileName; - int length = 0; - is.readInt(); - length = is.readInt(); -+ -+ char[] chars = new char[4*length]; -+ int char_cnt = 0; -+ for (int i = 0; i < chars.length; i++) { -+ chars[i] = '\0'; -+ } -+ - dwFileAttributes = is.readInt(); - length -= 4; -- ftCreationTime = is.readLong(); -+ //ftCreationTime = is.readLong(); -+ ftCreationTimeL = is.readInt(); -+ ftCreationTimeH = is.readInt(); - length -= 8; -- ftLastAccessTime = is.readLong(); -+ //ftLastAccessTime = is.readLong(); -+ ftLastAccessTimeL = is.readInt(); -+ ftLastAccessTimeH = is.readInt(); - length -= 8; -- ftLastWriteTime = is.readLong(); -+ //ftLastWriteTime = is.readLong(); -+ ftLastWriteTimeL = is.readInt(); -+ ftLastWriteTimeH = is.readInt(); - length -= 8; - nFileSizeHigh = is.readInt(); - length -= 4; -@@ -1239,10 +1489,12 @@ - length -= 4; - cFileName = (char) is.readUnsignedByte(); - length--; -+ chars[char_cnt++] = cFileName; - while (cFileName != '\0') - { - fileName += cFileName; - cFileName = (char) is.readUnsignedByte(); -+ chars[char_cnt++] = cFileName; - length--; - } - cAlternateFileName = (char) is.readByte(); -@@ -1253,7 +1505,28 @@ - cAlternateFileName = (char) is.readUnsignedByte(); - length--; - } -- if (dwFileAttributes == 268435456 -+ String guessed = guess_encoding(chars); -+ if (!guessed.equals("ASCII")) { -+ System.out.println("guess: " + guessed + "\t" + fileName); -+ } -+ if (guessed.equals("UTF-8")) { -+ try { -+ byte[] bytes = new byte[char_cnt-1]; -+ for (int i=0; i < char_cnt-1; i++) { -+ bytes[i] = (byte) chars[i]; -+ } -+ String newstr = new String(bytes, "UTF-8"); -+ fileName = newstr; -+ } catch (Exception e) { -+ System.out.println("failed to convert bytes to UTF-8 based string"); -+ } -+ } -+ for (int i = 0; i < char_cnt; i++) { -+ //System.out.println("char[" + i + "]\t" + (int) chars[i]); -+ } -+ if (fileName.length() <= 0) { -+ ; -+ } else if (dwFileAttributes == 268435456 - || dwFileAttributes == 369098752 - || dwFileAttributes == 285212672 - || dwFileAttributes == 271056896 -@@ -1263,11 +1536,74 @@ - || dwFileAttributes == 369623040) - { - fileName = " [" + fileName + "]"; -- remoteDirsList.add(fileName); // sf@2004 -- } -- else -- { -- remoteFilesList.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; -+ } -+ } -+ //String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " " + fileName; -+ String f2 = fileName; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); -+ //s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL); -+ if (i >= 0) { -+ remoteDirsList.add(i, fileName); -+ remoteDirsListInfo.add(i, s); -+ } else { -+ remoteDirsList.add(fileName); -+ remoteDirsListInfo.add(s); -+ } -+// end runge/x11vnc -+ } else { -+// 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; -+ } -+ } -+ String f2 = fileName; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ -+if (false) { -+System.out.println("fileName: " + f2); -+System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL); -+System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH); -+System.out.println("nFileSizeLow: " + nFileSizeLow); -+} -+ -+ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); -+ //s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + " Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow); -+ if (i >= 0) { -+ remoteFilesList.add(i, fileName); -+ remoteFilesListInfo.add(i, s); -+ } else { -+ remoteFilesList.add(fileName); -+ remoteFilesListInfo.add(s); -+ } -+// end runge/x11vnc - } - - // a.add(fileName); -@@ -1282,14 +1618,32 @@ - - // sf@2004 - a.clear(); -- for (int i = 0; i < remoteDirsList.size(); i++) -+ b.clear(); -+ for (int i = 0; i < remoteDirsList.size(); i++) { - a.add(remoteDirsList.get(i)); -- for (int i = 0; i < remoteFilesList.size(); i++) -+ b.add(remoteDirsListInfo.get(i)); -+ } -+ for (int i = 0; i < remoteFilesList.size(); i++) { - a.add(remoteFilesList.get(i)); -+ -+ b.add(remoteFilesListInfo.get(i)); -+ } - remoteDirsList.clear(); -+ remoteDirsListInfo.clear(); - remoteFilesList.clear(); -+ remoteFilesListInfo.clear(); - -- viewer.ftp.printDirectory(a); -+// begin runge/x11vnc -+ // Hack for double listing at startup... probably libvncserver bug.. -+ readServerDriveListCnt++; -+ if (readServerDriveListCnt == 2) { -+ if (System.currentTimeMillis() - readServerDriveListTime < 2000) { -+//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); -+ return; -+ } -+ } -+// end runge/x11vnc -+ viewer.ftp.printDirectory(a, b); - } - - //Internally used to signify the drive requested is not ready -@@ -1299,6 +1653,8 @@ - System.out.println("Remote Drive unavailable"); - viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)"); - viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable"); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); - } - - //Call this method to request the list of drives on the server. -@@ -1306,12 +1662,11 @@ - { - try - { -- viewer.rfb.writeRfbFileTransferMsg( -- RfbProto.rfbDirContentRequest, -- RfbProto.rfbRDrivesList, -- 0, -- 0, -- null); -+ viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null); -+// begin runge/x11vnc -+ readServerDriveListCnt = 0; -+ readServerDriveListTime = System.currentTimeMillis(); -+// end runge/x11vnc - } - catch (IOException e) - { -@@ -1355,21 +1710,21 @@ - int h, - boolean incremental) - throws IOException { -- if (!viewer.ftp.isVisible()) { -- byte[] b = new byte[10]; -+ if (!viewer.ftp.isVisible()) { -+ byte[] b = new byte[10]; - -- b[0] = (byte) FramebufferUpdateRequest; -- b[1] = (byte) (incremental ? 1 : 0); -- b[2] = (byte) ((x >> 8) & 0xff); -- b[3] = (byte) (x & 0xff); -- b[4] = (byte) ((y >> 8) & 0xff); -- b[5] = (byte) (y & 0xff); -- b[6] = (byte) ((w >> 8) & 0xff); -- b[7] = (byte) (w & 0xff); -- b[8] = (byte) ((h >> 8) & 0xff); -- b[9] = (byte) (h & 0xff); -+ b[0] = (byte) FramebufferUpdateRequest; -+ b[1] = (byte) (incremental ? 1 : 0); -+ b[2] = (byte) ((x >> 8) & 0xff); -+ b[3] = (byte) (x & 0xff); -+ b[4] = (byte) ((y >> 8) & 0xff); -+ b[5] = (byte) (y & 0xff); -+ b[6] = (byte) ((w >> 8) & 0xff); -+ b[7] = (byte) (w & 0xff); -+ b[8] = (byte) ((h >> 8) & 0xff); -+ b[9] = (byte) (h & 0xff); - -- os.write(b); -+ os.write(b); - } - } - -@@ -1482,7 +1837,13 @@ - b[6] = (byte) ((text.length() >> 8) & 0xff); - b[7] = (byte) (text.length() & 0xff); - -- System.arraycopy(text.getBytes(), 0, b, 8, text.length()); -+ if (false && max_char(text) > 255) { -+ System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length()); -+ } else if (max_char(text) > 127) { -+ System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length()); -+ } else { -+ System.arraycopy(text.getBytes(), 0, b, 8, text.length()); -+ } - - os.write(b); - // } -@@ -1506,6 +1867,37 @@ - final static int META_MASK = InputEvent.META_MASK; - final static int ALT_MASK = InputEvent.ALT_MASK; - -+ void writeWheelEvent(MouseWheelEvent evt) throws IOException { -+ eventBufLen = 0; -+ -+ int x = evt.getX(); -+ int y = evt.getY(); -+ -+ if (x < 0) x = 0; -+ if (y < 0) y = 0; -+ -+ int ptrmask; -+ -+ int clicks = evt.getWheelRotation(); -+ System.out.println("writeWheelEvent: clicks: " + clicks); -+ if (clicks > 0) { -+ ptrmask = 16; -+ } else if (clicks < 0) { -+ ptrmask = 8; -+ } else { -+ return; -+ } -+ -+ eventBuf[eventBufLen++] = (byte) PointerEvent; -+ eventBuf[eventBufLen++] = (byte) ptrmask; -+ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (x & 0xff); -+ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (y & 0xff); -+ -+ os.write(eventBuf, 0, eventBufLen); -+ } -+ - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. -@@ -1610,6 +2002,21 @@ - - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - -+ if (viewer.debugKeyboard) { -+ System.out.println("----------------------------------------"); -+ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); -+ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); -+ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); -+ System.out.println("evt.getID: " + evt.getID()); -+ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); -+ System.out.println("evt.isActionKey: " + evt.isActionKey()); -+ System.out.println("evt.isControlDown: " + evt.isControlDown()); -+ System.out.println("evt.getModifiers: " + evt.getModifiers()); -+ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); -+ System.out.println("evt.paramString: " + evt.paramString()); -+ } -+ -+ - int key; - if (evt.isActionKey()) { - -@@ -1685,6 +2092,9 @@ - default : - return; - } -+ if (key == 0xffc2 && viewer.mapF5_to_atsign) { -+ key = 0x40; -+ } - - } else { - -@@ -1794,6 +2204,16 @@ - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { -+ if(viewer.forbid_Ctrl_Alt) { -+ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { -+ int orig = newModifiers; -+ newModifiers &= ~ALT_MASK; -+ newModifiers &= ~CTRL_MASK; -+ if (viewer.debugKeyboard) { -+ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); -+ } -+ } -+ } - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - -diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java ---- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 -+++ JavaViewer/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 -@@ -0,0 +1,2067 @@ -+/* -+ * 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, 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. -+ * -+ */ -+ -+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; -+ boolean debug_certs = false; -+ -+ /* sockets */ -+ SSLSocket socket = null; -+ SSLSocketFactory factory; -+ -+ /* fallback for Proxy connection */ -+ boolean proxy_in_use = false; -+ boolean proxy_failure = false; -+ public DataInputStream is = null; -+ public OutputStream os = null; -+ -+ /* strings from user WRT proxy: */ -+ String proxy_auth_string = 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 trustsrv_ctx; -+ SSLContext trusturl_ctx; -+ SSLContext trustone_ctx; -+ -+ /* corresponding trust managers */ -+ TrustManager[] trustAllCerts; -+ TrustManager[] trustSrvCert; -+ TrustManager[] trustUrlCert; -+ TrustManager[] trustOneCert; -+ -+ /* client-side SSL auth key (oneTimeKey=...) */ -+ KeyManager[] mykey = null; -+ -+ boolean user_wants_to_see_cert = true; -+ String cert_fail = null; -+ -+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ -+ java.security.cert.Certificate[] trustallCerts = null; -+ java.security.cert.Certificate[] trustsrvCerts = null; -+ java.security.cert.Certificate[] trusturlCerts = null; -+ -+ /* utility to decode hex oneTimeKey=... and serverCert=... */ -+ 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; -+ -+ debug_certs = v.debugCerts; -+ -+ /* we will first try default factory for certification: */ -+ -+ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); -+ -+ dbg("SSL startup: " + host + " " + port); -+ -+ -+ /* create trust managers to be used if initial handshake fails: */ -+ -+ trustAllCerts = new TrustManager[] { -+ /* -+ * this one accepts everything. Only used if user -+ * has disabled checking (trustAllVncCerts=yes) -+ * or when we grab the cert to show it to them in -+ * a dialog and ask them to manually verify/accept it. -+ */ -+ 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 and stored in -+ * trusturlCerts. -+ */ -+ 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 (URL)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trusturlCerts' */ -+ if (trusturlCerts == null) { -+ throw new CertificateException( -+ "No Trust url Certs array."); -+ } -+ if (trusturlCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust url Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trusturlCerts.length) { -+ throw new CertificateException( -+ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trusturlCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("URL: cert mismatch at i=" + i); -+ dbg("URL: cert mismatch cert" + certs[i]); -+ dbg("URL: cert mismatch url" + trusturlCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("URL: cert info at i=" + i); -+ dbg("URL: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("URL: cert info url" + trusturlCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != URL Cert Chain."); -+ } -+ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustSrvCert = new TrustManager[] { -+ /* -+ * this one accepts cert given to us in the serverCert -+ * Applet Parameter we were started with. It is -+ * currently a fatal error if the VNC Server's cert -+ * doesn't match it. -+ */ -+ 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 (SRV)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustsrvCerts' */ -+ if (trustsrvCerts == null) { -+ throw new CertificateException( -+ "No Trust srv Certs array."); -+ } -+ if (trustsrvCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust srv Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustsrvCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustsrvCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("SRV: cert mismatch at i=" + i); -+ dbg("SRV: cert mismatch cert" + certs[i]); -+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("SRV: cert info at i=" + i); -+ dbg("SRV: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("SRV: cert info srv" + trustsrvCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); -+ } -+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustOneCert = new TrustManager[] { -+ /* -+ * this one accepts only the retrieved server -+ * cert by SSLSocket by this applet we stored in -+ * trustallCerts that user has accepted or applet -+ * parameter trustAllVncCerts=yes is set. This is -+ * for when we reconnect after the user has manually -+ * accepted the trustall cert in the dialog (or set -+ * trustAllVncCerts=yes applet param.) -+ */ -+ 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 (ONE)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustallCerts' */ -+ 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 (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustallCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustallCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("ONE: cert mismatch at i=" + i); -+ dbg("ONE: cert mismatch cert" + certs[i]); -+ dbg("ONE: cert mismatch all" + trustallCerts[i]); -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("ONE: cert info at i=" + i); -+ dbg("ONE: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("ONE: cert info all" + trustallCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != TRUSTALL Cert Chain."); -+ } -+ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ /* -+ * The above TrustManagers are used: -+ * -+ * 1) to retrieve the server cert in case of failure to -+ * display it to the user in a dialog. -+ * 2) to subsequently connect to the server if user agrees. -+ */ -+ -+ /* -+ * build oneTimeKey cert+key if supplied in applet parameter: -+ */ -+ 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); -+ if (debug_certs) { -+ dbg("one time key " + ff); -+ } -+ -+ 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)); -+ if (debug_certs) { -+ dbg("one time cert" + 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(); -+ } -+ -+ /* -+ * build serverCert cert if supplied in applet parameter: -+ */ -+ if (viewer.serverCert != null) { -+ CertificateFactory cf = CertificateFactory.getInstance("X.509"); -+ byte[] cert = hex2bytes(viewer.serverCert); -+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); -+ trustsrvCerts = new Certificate[c.toArray().length]; -+ if (c.size() == 1) { -+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); -+ trustsrvCerts[0] = tmpcert; -+ } else { -+ trustsrvCerts = (Certificate[]) c.toArray(); -+ } -+ } -+ -+ /* the trust loc certs context: */ -+ try { -+ trustloc_ctx = SSLContext.getInstance("SSL"); -+ -+ /* -+ * below is a failed attempt to get jvm's default -+ * trust manager using null (below) makes it so -+ * for HttpsURLConnection the server cannot be -+ * verified (no prompting.) -+ */ -+ if (false) { -+ boolean didit = false; -+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); -+ tmf.init((KeyStore) null); -+ TrustManager [] tml = tmf.getTrustManagers(); -+ for (int i = 0; i < tml.length; i++) { -+ TrustManager tm = tml[i]; -+ if (tm instanceof X509TrustManager) { -+ TrustManager tm1[] = new TrustManager[1]; -+ tm1[0] = tm; -+ trustloc_ctx.init(mykey, tm1, null); -+ didit = true; -+ break; -+ } -+ } -+ if (!didit) { -+ trustloc_ctx.init(mykey, null, null); -+ } -+ } else { -+ /* we have to set trust manager to null */ -+ trustloc_ctx.init(mykey, null, null); -+ } -+ -+ } catch (Exception e) { -+ String msg = "SSL trustloc_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust all certs context: */ -+ 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); -+ } -+ -+ /* the trust url certs context: */ -+ 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); -+ } -+ -+ /* the trust srv certs context: */ -+ try { -+ trustsrv_ctx = SSLContext.getInstance("SSL"); -+ trustsrv_ctx.init(mykey, trustSrvCert, new -+ java.security.SecureRandom()); -+ -+ } catch (Exception e) { -+ String msg = "SSL trustsrv_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust the one cert from server context: */ -+ 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); -+ } -+ } -+ -+ /* -+ * we call this early on to 1) check for a proxy, 2) grab -+ * Browser/JVM accepted HTTPS cert. -+ */ -+ public void check_for_proxy_and_grab_vnc_server_cert() { -+ -+ trusturlCerts = null; -+ proxy_in_use = false; -+ -+ if (viewer.ignoreProxy) { -+ /* applet param says skip it. */ -+ /* the downside is we do not set trusturlCerts for comparison later... */ -+ /* nor do we autodetect x11vnc for GET=1. */ -+ return; -+ } -+ -+ dbg("------------------------------------------------"); -+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); -+ -+ dbg("TRYING HTTPS:"); -+ String ustr = "https://" + host + ":"; -+ if (viewer.httpsPort != null) { -+ ustr += viewer.httpsPort; -+ } else { -+ ustr += port; -+ } -+ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTPS URL connection to host:port */ -+ URL url = new URL(ustr); -+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); -+ -+ if (mykey != null) { -+ /* with oneTimeKey (mykey) we can't use the default SSL context */ -+ if (trustsrvCerts != null) { -+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); -+ } else if (trustloc_ctx != null) { -+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); -+ } -+ } -+ -+ https.setUseCaches(false); -+ https.setRequestMethod("GET"); -+ https.setRequestProperty("Pragma", "No-Cache"); -+ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); -+ https.setDoInput(true); -+ -+ dbg("trying https.connect()"); -+ https.connect(); -+ -+ dbg("trying https.getServerCertificates()"); -+ 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; -+ dbg("An HTTPS proxy is in use. There may be connection problems."); -+ } -+ -+ dbg("trying https.getContent()"); -+ Object output = https.getContent(); -+ dbg("trying https.disconnect()"); -+ https.disconnect(); -+ if (! viewer.GET) { -+ String header = https.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (1), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ -+ } catch(Exception e) { -+ dbg("HttpsURLConnection: " + e.getMessage()); -+ } -+ -+ if (proxy_in_use) { -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } else if (trusturlCerts != null && !viewer.forceProxy) { -+ /* Allow user to require HTTP check? use forceProxy for now. */ -+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } -+ -+ /* -+ * XXX need to remember scenario where this extra check -+ * gives useful info. User's Browser proxy settings? -+ */ -+ dbg("TRYING HTTP:"); -+ ustr = "http://" + host + ":" + port; -+ ustr += viewer.urlPrefix + "/index.vnc"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ -+ 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); -+ -+ dbg("trying http.connect()"); -+ http.connect(); -+ -+ if (http.usingProxy()) { -+ proxy_in_use = true; -+ dbg("An HTTP proxy is in use. There may be connection problems."); -+ } -+ dbg("trying http.getContent()"); -+ Object output = http.getContent(); -+ dbg("trying http.disconnect()"); -+ http.disconnect(); -+ if (! viewer.GET) { -+ String header = http.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (2), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ } catch(Exception e) { -+ dbg("HttpURLConnection: " + e.getMessage()); -+ } -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ } -+ -+ public Socket connectSock() throws IOException { -+ /* -+ * first try a https connection to detect a proxy, and -+ * grab the VNC server cert at the same time: -+ */ -+ check_for_proxy_and_grab_vnc_server_cert(); -+ -+ boolean srv_cert = false; -+ -+ if (trustsrvCerts != null) { -+ /* applet parameter suppled serverCert */ -+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); -+ factory = trustsrv_ctx.getSocketFactory(); -+ srv_cert = true; -+ } else if (viewer.trustAllVncCerts) { -+ /* trust all certs (no checking) */ -+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); -+ factory = trustall_ctx.getSocketFactory(); -+ } else if (trusturlCerts != null) { -+ /* trust certs the Browser/JVM accepted in check_for_proxy... */ -+ dbg("using trusturl_ctx"); -+ factory = trusturl_ctx.getSocketFactory(); -+ } else { -+ /* trust the local defaults */ -+ 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"); -+ } -+ -+ int timeout = 6; -+ if (timeout > 0) { -+ socket = (SSLSocket) factory.createSocket(); -+ InetSocketAddress inetaddr = new InetSocketAddress(host, port); -+ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); -+ socket.connect(inetaddr, timeout * 1000); -+ } else { -+ socket = (SSLSocket) factory.createSocket(host, port); -+ } -+ -+ } catch (Exception esock) { -+ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); -+ } -+ } else { -+ /* n.b. socket is left in error state to cause ex. below. */ -+ } -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The Server Connection Verified OK on 1st try."); -+ -+ java.security.cert.Certificate[] currentTrustedCerts; -+ BrowserCertsDialog bcd; -+ -+ SSLSession sess = socket.getSession(); -+ currentTrustedCerts = sess.getPeerCertificates(); -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-1 keeping socket."); -+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy."); -+ } -+ 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 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.serverCert != null && trustsrvCerts != null) { -+ dbg("viewer.serverCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.trustUrlVncCert) { -+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else { -+ /* have a dialog with the user: */ -+ bcd = new BrowserCertsDialog(serv, host + ":" + port); -+ dbg("browser certs dialog begin."); -+ bcd.queryUser(); -+ dbg("browser certs dialog finished."); -+ -+ if (bcd.showCertDialog) { -+ String msg = "user wants to see cert"; -+ dbg(msg); -+ user_wants_to_see_cert = true; -+ if (cert_fail == null) { -+ cert_fail = "user-view"; -+ } -+ throw new SSLHandshakeException(msg); -+ } else { -+ user_wants_to_see_cert = false; -+ dbg("browser certs dialog: user said yes, accept it"); -+ } -+ } -+ -+ } catch (SSLHandshakeException eh) { -+ dbg("SSLHandshakeException: could not automatically verify Server."); -+ dbg("msg: " + eh.getMessage()); -+ -+ -+ /* send a cleanup string just in case: */ -+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; -+ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!"); -+ } -+ -+ /* reload */ -+ -+ socket = null; -+ -+ String reason = null; -+ -+ if (srv_cert) { -+ /* for serverCert usage we make this a fatal error. */ -+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); -+ /* see below in TrustDialog were we describe this case to user anyway */ -+ } -+ -+ /* -+ * Reconnect, trusting any cert, so we can grab -+ * the cert to show it to the user in a dialog -+ * for him to manually accept. This 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); -+ } -+ -+ if (debug_certs) { -+ dbg("trusturlCerts: " + trusturlCerts); -+ dbg("trustsrvCerts: " + trustsrvCerts); -+ } -+ if (trusturlCerts == null && cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); -+ -+ /* grab the cert: */ -+ try { -+ SSLSession sess = socket.getSession(); -+ trustallCerts = sess.getPeerCertificates(); -+ } catch (Exception e) { -+ throw new Exception("Could not get " + -+ "Peer Certificate"); -+ } -+ if (debug_certs) { -+ dbg("trustallCerts: " + trustallCerts); -+ } -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); -+ } else if (! browser_cert_match()) { -+ /* -+ * close socket now, we will reopen after -+ * dialog if user agrees to use the cert. -+ */ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!"); -+ } -+ socket = null; -+ -+ /* dialog with user to accept cert or not: */ -+ -+ TrustDialog td= new TrustDialog(host, port, -+ trustallCerts); -+ -+ if (cert_fail == null) { -+ ; -+ } else if (cert_fail.equals("user-view")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " You Asked to View the Certificate."; -+ } else if (cert_fail.equals("server-cert-mismatch")) { -+ /* this is now fatal error, see above. */ -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Certificate\n" -+ + " specified in the supplied 'serverCert' Applet Parameter."; -+ } else if (cert_fail.equals("cert-mismatch")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Website's\n" -+ + " HTTPS Certificate (that you previously accepted; either\n" -+ + " manually or automatically via Certificate Authority.)"; -+ } else if (cert_fail.equals("missing-certs")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " Not all Certificates could be obtained to check."; -+ } -+ -+ if (! td.queryUser(reason)) { -+ 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()); -+ } -+ -+ /* reload again: */ -+ -+ if (socket != null) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!!"); -+ } -+ socket = null; -+ } -+ -+ /* -+ * Now connect a 3rd time, using the cert -+ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); -+ -+ } catch (Exception ehand3) { -+ dbg("** Could not TrustAll/TrustOne Verify Server #3."); -+ -+ throw new IOException(ehand3.getMessage()); -+ } -+ } -+ -+ /* we have socket (possibly null) at this point, so proceed: */ -+ -+ /* handle x11vnc GET=1, if applicable: */ -+ 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: " + str); -+ OutputStream os = socket.getOutputStream(); -+ String type = "os"; -+ -+ if (type == "os") { -+ os.write(str.getBytes()); -+ os.flush(); -+ System.out.println("used OutputStream"); -+ } else if (type == "bs") { -+ BufferedOutputStream bs = new BufferedOutputStream(os); -+ bs.write(str.getBytes()); -+ bs.flush(); -+ System.out.println("used BufferedOutputStream"); -+ } else if (type == "ds") { -+ DataOutputStream ds = new DataOutputStream(os); -+ ds.write(str.getBytes()); -+ ds.flush(); -+ System.out.println("used DataOutputStream"); -+ } -+ 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."); -+ dbg(""); -+ -+ /* could be null, let caller handle that. */ -+ return (Socket) socket; -+ } -+ -+ boolean browser_cert_match() { -+ String msg = "Browser URL accept previously accepted cert"; -+ -+ if (user_wants_to_see_cert) { -+ return false; -+ } -+ -+ if (viewer.serverCert != null || trustsrvCerts != null) { -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (trustallCerts != null && trusturlCerts != null) { -+ if (trustallCerts.length == trusturlCerts.length) { -+ boolean ok = true; -+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ -+ for (int i = 0; i < trusturlCerts.length; i++) { -+ if (! trustallCerts[i].equals(trusturlCerts[i])) { -+ dbg("BCM: cert mismatch at i=" + i); -+ dbg("BCM: cert mismatch url" + trusturlCerts[i]); -+ dbg("BCM: cert mismatch all" + trustallCerts[i]); -+ ok = false; -+ } -+ } -+ if (ok) { -+ System.out.println(msg); -+ if (cert_fail == null) { -+ cert_fail = "did-not-fail"; -+ } -+ return true; -+ } else { -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ return false; -+ } -+ } -+ } -+ if (cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ return false; -+ } -+ -+ 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; -+ } -+ -+ /* this will do the proxy CONNECT negotiation and hook us up. */ -+ -+ private void proxy_helper(String proxyHost, int proxyPort) { -+ -+ boolean proxy_auth = false; -+ String proxy_auth_basic_realm = ""; -+ String hp = host + ":" + port; -+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); -+ -+ /* we loop here a few times trying for the password case */ -+ for (int k=0; k < 2; k++) { -+ dbg("proxy_in_use psocket: " + k); -+ -+ if (proxySock != null) { -+ try { -+ proxySock.close(); -+ } catch (Exception e) { -+ dbg("proxy socket is grumpy."); -+ } -+ } -+ -+ proxySock = psocket(proxyHost, proxyPort); -+ if (proxySock == null) { -+ dbg("1-a sadly, returning a null socket"); -+ return; -+ } -+ -+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" -+ + "Host: " + hp + "\r\n"; -+ -+ dbg("requesting via proxy: " + req1); -+ -+ if (proxy_auth) { -+ if (proxy_auth_string == null) { -+ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); -+ pp.queryUser(); -+ proxy_auth_string = pp.getAuth(); -+ } -+ //dbg("auth1: " + proxy_auth_string); -+ -+ String auth2 = Base64Coder.encodeString(proxy_auth_string); -+ //dbg("auth2: " + auth2); -+ -+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; -+ //dbg("req1: " + req1); -+ -+ dbg("added Proxy-Authorization: Basic ... to request"); -+ } -+ req1 += "\r\n"; -+ -+ try { -+ proxy_os.write(req1.getBytes()); -+ String reply = readline(proxy_is); -+ -+ dbg("proxy replied: " + reply.trim()); -+ -+ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { -+ proxy_auth = true; -+ proxySock.close(); -+ } else 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; -+ } -+ } -+ } catch(Exception e) { -+ dbg("some proxy socket problem: " + e.getMessage()); -+ } -+ -+ /* read the rest of the HTTP headers */ -+ while (true) { -+ String line = readline(proxy_is); -+ dbg("proxy line: " + line.trim()); -+ if (proxy_auth) { -+ String uc = line.toLowerCase(); -+ if (uc.indexOf("proxy-authenticate:") == 0) { -+ if (uc.indexOf(" basic ") >= 0) { -+ int idx = uc.indexOf(" realm"); -+ if (idx >= 0) { -+ proxy_auth_basic_realm = uc.substring(idx+1); -+ } -+ } -+ } -+ } -+ if (line.equals("\r\n") || line.equals("\n")) { -+ break; -+ } -+ } -+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { -+ /* we only try once for the non-password case: */ -+ break; -+ } -+ } -+ } -+ -+ 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) { -+ /* sandboxed applet might not be able to read it. */ -+ dbg("props failed: " + e.getMessage()); -+ } -+ if (viewer.proxyHost != null) { -+ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); -+ proxyHost = viewer.proxyHost; -+ if (viewer.proxyPort != null) { -+ proxyPort = gint(viewer.proxyPort); -+ } else { -+ proxyPort = 8080; -+ } -+ -+ } else if (props != null) { -+ dbg("\n---------------\nAll props:"); -+ props.list(System.out); -+ dbg("\n---------------\n\n"); -+ -+ /* scrape throught properties looking for proxy info: */ -+ -+ 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.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; -+ } -+ } -+ -+ 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 (proxyHost != null && proxyPort > 0) { -+ break; -+ } -+ -+ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 -+ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { -+ continue; -+ } -+ if (v2.indexOf("http") < 0) { -+ 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) { -+ j = p.indexOf("http"); -+ 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()); -+ } -+ -+ proxy_helper(proxyHost, proxyPort); -+ if (proxySock == null) { -+ return null; -+ } -+ } 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("proxy socket problem-2: " + 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(String reason) { -+ -+ /* 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"); -+ } -+ if (reason != null) { -+ reason += "\n\n"; -+ } -+ -+ text = "\n" -++ "Unable to verify the identity of\n" -++ "\n" -++ " " + host + ":" + port + "\n" -++ "\n" -++ infostr -++ "\n" -++ "as a trusted VNC server.\n" -++ "\n" -++ reason -++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" -++ "is due to one of the following:\n" -++ "\n" -++ " - Your requesting to View the Certificate before accepting.\n" -++ "\n" -++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" -++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" -++ "\n" -++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" -++ " the Apache Web server has a certificate *different* from the VNC server's.\n" -++ "\n" -++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" -++ " obtained by this applet to compare the VNC Server Certificate against.\n" -++ "\n" -++ " - The VNC Server's Certificate does not match the one specified in the\n" -++ " supplied 'serverCert' Java Applet Parameter.\n" -++ "\n" -++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" -++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" -++ "automatically authenticate this VNC Server.\n" -++ "\n" -++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" -++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" -++ "certificate (except for the Apache portal case above where they don't match.)\n" -++ "\n" -++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" -++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" -++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); -+ dialog.hide(); -+ -+ } else if (evt.getSource() == cancel) { -+ /* Cancel button clicked */ -+ trust_this_session = false; -+ -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+ -+ 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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+class ProxyPasswdDialog implements ActionListener { -+ String guessedHost = null; -+ String guessedPort = null; -+ String guessedUser = null; -+ String guessedPasswd = null; -+ String realm = 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 entry1; -+ TextField entry2; -+ String reply1 = ""; -+ String reply2 = ""; -+ -+ ProxyPasswdDialog (String h, int p, String realm) { -+ guessedHost = h; -+ try { -+ guessedPort = Integer.toString(p); -+ } catch (Exception e) { -+ guessedPort = "8080"; -+ } -+ this.realm = realm; -+ } -+ -+ public void queryUser() { -+ -+ /* create and display the dialog for unverified cert. */ -+ -+ Frame frame = new Frame("Proxy Requires Username and Password"); -+ -+ dialog = new Dialog(frame, true); -+ -+ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); -+ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); -+ entry1 = new TextField(30); -+ entry2 = new TextField(30); -+ entry2.setEchoChar('*'); -+ ok = new Button("OK"); -+ ok.addActionListener(this); -+ -+ dialog.setLayout(new BorderLayout()); -+ dialog.add("North", label); -+ dialog.add("Center", entry1); -+ dialog.add("South", entry2); -+ dialog.add("East", ok); -+ dialog.pack(); -+ dialog.resize(dialog.preferredSize()); -+ -+ dialog.show(); /* block here til OK or Cancel pressed. */ -+ return; -+ } -+ -+ public String getAuth() { -+ return reply1 + ":" + reply2; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == ok) { -+ reply1 = entry1.getText(); -+ reply2 = entry2.getText(); -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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 and/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 += "If the VNC Server connection times out while you are reading this\n"; -+m += "dialog, then restart the connection and try again.\n"; -+m += "\n"; -+m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; -+m += "\n"; -+ -+ TextArea textarea = new TextArea(m, 22, 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. */ -+ System.out.println("done show()"); -+ return; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == yes) { -+ showCertDialog = false; -+ //dialog.dispose(); -+ dialog.hide(); -+ } else if (evt.getSource() == no) { -+ showCertDialog = true; -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ System.out.println("done actionPerformed()"); -+ } -+} -+ -+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 ""; -+ } -+ } -+} -+ -+class Base64Coder { -+ -+ // Mapping table from 6-bit nibbles to Base64 characters. -+ private static char[] map1 = new char[64]; -+ static { -+ int i=0; -+ for (char c='A'; c<='Z'; c++) map1[i++] = c; -+ for (char c='a'; c<='z'; c++) map1[i++] = c; -+ for (char c='0'; c<='9'; c++) map1[i++] = c; -+ map1[i++] = '+'; map1[i++] = '/'; } -+ -+ // Mapping table from Base64 characters to 6-bit nibbles. -+ private static byte[] map2 = new byte[128]; -+ static { -+ for (int i=0; iin. -+ * @return A character array with the Base64 encoded data. -+ */ -+ public static char[] encode (byte[] in, int iLen) { -+ int oDataLen = (iLen*4+2)/3; // output length without padding -+ int oLen = ((iLen+2)/3)*4; // output length including padding -+ char[] out = new char[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++] & 0xff; -+ int i1 = ip < iLen ? in[ip++] & 0xff : 0; -+ int i2 = ip < iLen ? in[ip++] & 0xff : 0; -+ int o0 = i0 >>> 2; -+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); -+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); -+ int o3 = i2 & 0x3F; -+ out[op++] = map1[o0]; -+ out[op++] = map1[o1]; -+ out[op] = op < oDataLen ? map1[o2] : '='; op++; -+ out[op] = op < oDataLen ? map1[o3] : '='; op++; } -+ return out; } -+ -+ /** -+ * Decodes a string from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return A String containing the decoded data. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static String decodeString (String s) { -+ return new String(decode(s)); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (String s) { -+ return decode(s.toCharArray()); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * No blanks or line breaks are allowed within the Base64 encoded data. -+ * @param in a character array containing the Base64 encoded data. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (char[] in) { -+ int iLen = in.length; -+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); -+ while (iLen > 0 && in[iLen-1] == '=') iLen--; -+ int oLen = (iLen*3) / 4; -+ byte[] out = new byte[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++]; -+ int i1 = in[ip++]; -+ int i2 = ip < iLen ? in[ip++] : 'A'; -+ int i3 = ip < iLen ? in[ip++] : 'A'; -+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int b0 = map2[i0]; -+ int b1 = map2[i1]; -+ int b2 = map2[i2]; -+ int b3 = map2[i3]; -+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int o0 = ( b0 <<2) | (b1>>>4); -+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); -+ int o2 = ((b2 & 3)<<6) | b3; -+ out[op++] = (byte)o0; -+ if (op 0) - { - viewer.options.oldEightBitColors = viewer.options.eightBitColors; -@@ -237,6 +265,9 @@ - } - else - { -+// begin runge/x11vnc -+ viewer.options.oldEightBitColors = viewer.options.eightBitColors; -+// end runge/x11vnc - rfb.writeSetPixelFormat( - 32, - 24, -@@ -376,12 +407,14 @@ - // Start/stop session recording if necessary. - viewer.checkRecordingStatus(); - -- rfb.writeFramebufferUpdateRequest( -- 0, -- 0, -- rfb.framebufferWidth, -- rfb.framebufferHeight, -- false); -+ if (!viewer.graftFtp) { -+ rfb.writeFramebufferUpdateRequest( -+ 0, -+ 0, -+ rfb.framebufferWidth, -+ rfb.framebufferHeight, -+ false); -+ } - - // - // main dispatch loop -@@ -390,6 +423,9 @@ - while (true) { - // Read message type from the server. - int msgType = rfb.readServerMessageType(); -+ if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) { -+ System.out.println("msgType:" + msgType); -+ } - - // Process the message depending on its type. - switch (msgType) { -@@ -1332,6 +1368,9 @@ - public void mouseDragged(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } -+ public void mouseWheelMoved(MouseWheelEvent evt) { -+ processLocalMouseWheelEvent(evt); -+ } - - public void processLocalKeyEvent(KeyEvent evt) { - if (viewer.rfb != null && rfb.inNormalProtocol) { -@@ -1367,6 +1406,19 @@ - evt.consume(); - } - -+ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { -+ if (viewer.rfb != null && rfb.inNormalProtocol) { -+ synchronized(rfb) { -+ try { -+ rfb.writeWheelEvent(evt); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ } -+ rfb.notify(); -+ } -+ } -+ } -+ - public void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (moved) { -@@ -1532,9 +1584,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 +1622,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 2010-03-27 18:00:28.000000000 -0400 -@@ -41,6 +41,7 @@ - import java.io.*; - import java.net.*; - import javax.swing.*; -+import java.util.Date; - - public class VncViewer extends java.applet.Applet - implements java.lang.Runnable, WindowListener { -@@ -80,11 +81,11 @@ - GridBagLayout gridbag; - ButtonPanel buttonPanel; - AuthPanel authenticator; -- VncCanvas vc; -+ VncCanvas vc = null; - OptionsFrame options; - ClipboardFrame clipboard; - RecordingFrame rec; -- FTPFrame ftp; // KMC: FTP Frame declaration -+ FTPFrame ftp = null; // KMC: FTP Frame declaration - - // Control session recording. - Object recordingSync; -@@ -96,7 +97,7 @@ - - // Variables read from parameter values. - String host; -- int port; -+ int port, vncserverport; - String passwordParam; - String encPasswordParam; - boolean showControls; -@@ -115,28 +116,75 @@ - int i; - // mslogon support 2 end - -+// begin runge/x11vnc -+boolean disableSSL; -+boolean GET; -+String CONNECT; -+String urlPrefix; -+String httpsPort; -+String oneTimeKey; -+String serverCert; -+String ftpDropDown; -+String proxyHost; -+String proxyPort; -+boolean forceProxy; -+boolean ignoreProxy; -+boolean trustAllVncCerts; -+boolean trustUrlVncCert; -+boolean debugCerts; -+boolean debugKeyboard; -+boolean mapF5_to_atsign; -+boolean forbid_Ctrl_Alt; -+ -+boolean ignoreMSLogonCheck; -+boolean delayAuthPanel; -+boolean ftpOnly; -+boolean graftFtp; -+boolean dsmActive; -+ -+boolean gotAuth; -+int authGot; -+// end runge/x11vnc -+ -+ - // - // init() - // - -+public void ftp_init() { -+ boolean show = false; -+ if (ftp != null) { -+ show = true; -+ } -+ ftp = null; -+ -+ ftp = new FTPFrame(this); // KMC: FTPFrame creation -+ -+ if (show) { -+ ftp.doOpen(); -+ rfb.readServerDriveList(); -+ } -+} -+ - public void init() { - - readParameters(); - - if (inSeparateFrame) { -- vncFrame = new Frame("Ultr@VNC"); -- if (!inAnApplet) { -- vncFrame.add("Center", this); -- } -- vncContainer = vncFrame; -+ vncFrame = new Frame("Ultr@VNC"); -+ if (!inAnApplet) { -+ vncFrame.add("Center", this); -+ } -+ vncContainer = vncFrame; - } else { -- vncContainer = this; -+ vncContainer = this; - } - - recordingSync = new Object(); - - options = new OptionsFrame(this); - clipboard = new ClipboardFrame(this); -+ - // authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate() - if (RecordingFrame.checkSecurity()) - rec = new RecordingFrame(this); -@@ -147,10 +195,11 @@ - cursorUpdatesDef = null; - eightBitColorsDef = null; - -- if (inSeparateFrame) -+ if (inSeparateFrame && vncFrame != null) - vncFrame.addWindowListener(this); - -- ftp = new FTPFrame(this); // KMC: FTPFrame creation -+ ftp_init(); -+ - rfbThread = new Thread(this); - rfbThread.start(); - } -@@ -186,6 +235,30 @@ - gbc.weightx = 1.0; - gbc.weighty = 1.0; - -+ if (ftpOnly) { -+ if (showControls) { -+ buttonPanel.enableButtons(); -+ } -+ ActionListener taskPerformer = new ActionListener() { -+ public void actionPerformed(ActionEvent evt) { -+ vncFrame.setVisible(false); -+ ftp.setSavedLocations(); -+ if (ftp.isVisible()) { -+ ftp.doClose(); -+ } else { -+ ftp.doOpen(); -+ } -+ rfb.readServerDriveList(); -+ } -+ }; -+ Timer t = new Timer(300, taskPerformer); -+ t.setRepeats(false); -+ t.start(); -+ -+ vc.processNormalProtocol(); -+ return; -+ } -+ - // Add ScrollPanel to applet mode - - // Create a panel which itself is resizeable and can hold -@@ -286,6 +359,24 @@ - - void connectAndAuthenticate() throws Exception { - -+ if (graftFtp) { -+ rfb = new RfbProto(host, port, this); -+ rfb.desktopName = "ftponly"; -+ rfb.framebufferWidth = 12; -+ rfb.framebufferHeight = 12; -+ rfb.bitsPerPixel = 32; -+ rfb.depth = 24; -+ rfb.trueColour = true; -+ rfb.redMax = 255; -+ rfb.greenMax = 255; -+ rfb.blueMax = 255; -+ rfb.redShift = 16; -+ rfb.greenShift = 8; -+ rfb.blueShift = 0; -+ rfb.inNormalProtocol = true; -+ return; -+ } -+ - // If "ENCPASSWORD" parameter is set, decrypt the password into - // the passwordParam string. - -@@ -336,7 +427,22 @@ - // - - -- prologueDetectAuthProtocol() ; -+// begin runge/x11vnc -+ gotAuth = false; -+ if (delayAuthPanel) { -+ if (tryAuthenticate(null, null)) { -+ if (inSeparateFrame) { -+ vncFrame.pack(); -+ vncFrame.show(); -+ } -+ return; -+ } -+ } -+// prologueDetectAuthProtocol() ; -+ if (ignoreMSLogonCheck == false) { -+ prologueDetectAuthProtocol() ; -+ } -+// end runge/x11vnc - - authenticator = new AuthPanel(mslogon); - -@@ -371,6 +477,7 @@ - //mslogon support end - } - -+ int tries = 0; - while (true) { - // Wait for user entering a password, or a username and a password - synchronized(authenticator) { -@@ -390,6 +497,13 @@ - break; - //mslogon support end - -+// begin runge/x11vnc -+ gotAuth = false; -+ if (++tries > 2) { -+ throw new Exception("Incorrect password entered " + tries + " times."); -+ } -+// end runge/x11vnc -+ - // Retry on authentication failure. - authenticator.retry(); - } -@@ -405,9 +519,11 @@ - - void prologueDetectAuthProtocol() throws Exception { - -- rfb = new RfbProto(host, port, this); -+ if (!gotAuth) { -+ rfb = new RfbProto(host, port, this); - -- rfb.readVersionMsg(); -+ rfb.readVersionMsg(); -+ } - - System.out.println("RFB server supports protocol version " + - rfb.serverMajor + "." + rfb.serverMinor); -@@ -431,16 +547,36 @@ - - boolean tryAuthenticate(String us, String pw) throws Exception { - -- rfb = new RfbProto(host, port, this); -+ int authScheme; - -- rfb.readVersionMsg(); -+ if (!gotAuth) { -+ rfb = new RfbProto(host, port, this); - -- System.out.println("RFB server supports protocol version " + -- rfb.serverMajor + "." + rfb.serverMinor); -+ rfb.readVersionMsg(); - -- rfb.writeVersionMsg(); -+ System.out.println("RFB server supports protocol version: " + -+ rfb.serverMajor + "." + rfb.serverMinor); - -- int authScheme = rfb.readAuthScheme(); -+ rfb.writeVersionMsg(); -+ -+ authScheme = rfb.readAuthScheme(); -+ -+ gotAuth = true; -+ authGot = authScheme; -+ } else { -+ authScheme = authGot; -+ } -+// begin runge/x11vnc -+ if (delayAuthPanel && pw == null) { -+ if (authScheme == RfbProto.NoAuth) { -+ System.out.println("No authentication needed"); -+ return true; -+ } else { -+ return false; -+ } -+ } -+System.out.println("as: " + authScheme); -+// end runge/x11vnc - - switch (authScheme) { - -@@ -629,6 +765,10 @@ - - void doProtocolInitialisation() throws IOException { - -+ if (graftFtp) { -+ return; -+ } -+ - rfb.writeClientInit(); - - rfb.readServerInit(); -@@ -774,9 +914,28 @@ - fatalError("HOST parameter not specified"); - } - } -+ Date d = new Date(); -+ System.out.println("-\nSSL VNC Java Applet starting. " + d); - -- String str = readParameter("PORT", true); -- port = Integer.parseInt(str); -+ port = 0; -+ String str = readParameter("PORT", false); -+ if (str != null) { -+ port = Integer.parseInt(str); -+ } -+ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). -+ vncserverport = 0; -+ str = readParameter("VNCSERVERPORT", false); -+ if (str != null) { -+ vncserverport = Integer.parseInt(str); -+ } -+ if (port == 0 && vncserverport == 0) { -+ fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); -+ } -+ if (port == 0) { -+ // Nevertheless, fall back to vncserverport if we have to. -+ System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); -+ port = vncserverport; -+ } - - if (inAnApplet) { - str = readParameter("Open New Window", false); -@@ -804,6 +963,158 @@ - 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 + "'"); -+ -+ ftpDropDown = readParameter("ftpDropDown", false); -+ if (ftpDropDown != null) { -+ ftpDropDown = ftpDropDown.replaceAll("%2F", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("%2f", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("_2F_", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("%20", " "); -+ System.out.println("ftpDropDown: '" + ftpDropDown + "'"); -+ } -+ -+ -+ oneTimeKey = readParameter("oneTimeKey", false); -+ if (oneTimeKey != null) { -+ System.out.println("oneTimeKey is set."); -+ } -+ -+ serverCert = readParameter("serverCert", false); -+ if (serverCert != null) { -+ System.out.println("serverCert is set."); -+ } -+ -+ forceProxy = false; -+ proxyHost = null; -+ proxyPort = null; -+ str = readParameter("forceProxy", false); -+ if (str != null) { -+ if (str.equalsIgnoreCase("Yes")) { -+ forceProxy = true; -+ } else if (str.equalsIgnoreCase("No")) { -+ forceProxy = false; -+ } else { -+ forceProxy = true; -+ String[] pieces = str.split(" "); -+ proxyHost = new String(pieces[0]); -+ if (pieces.length >= 2) { -+ proxyPort = new String(pieces[1]); -+ } else { -+ proxyPort = new String("8080"); -+ } -+ } -+ } -+ str = readParameter("proxyHost", false); -+ if (str != null) { -+ proxyHost = new String(str); -+ } -+ str = readParameter("proxyPort", false); -+ if (str != null) { -+ proxyPort = new String(str); -+ } -+ if (proxyHost != null && proxyPort == null) { -+ proxyPort = new String("8080"); -+ } -+ -+ 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; -+ } -+ debugCerts = false; -+ str = readParameter("debugCerts", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugCerts = true; -+ } -+ debugKeyboard = false; -+ str = readParameter("debugKeyboard", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugKeyboard = true; -+ } -+ mapF5_to_atsign = false; -+ str = readParameter("mapF5_to_atsign", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ mapF5_to_atsign = true; -+ } -+ forbid_Ctrl_Alt = false; -+ str = readParameter("forbid_Ctrl_Alt", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ forbid_Ctrl_Alt = true; -+ } -+ ignoreMSLogonCheck = false; -+ str = readParameter("ignoreMSLogonCheck", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ ignoreMSLogonCheck = true; -+ } -+ ftpOnly = false; -+ str = readParameter("ftpOnly", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ ftpOnly = true; -+ } -+ graftFtp = false; -+ str = readParameter("graftFtp", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ graftFtp = true; -+ } -+ dsmActive = false; -+ str = readParameter("dsmActive", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ dsmActive = true; -+ } -+ delayAuthPanel = false; -+ str = readParameter("delayAuthPanel", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ delayAuthPanel = true; -+ } -+// end runge/x11vnc - } - - public String readParameter(String name, boolean required) { diff --git a/configure.ac b/configure.ac index ab65308..94ec6bc 100644 --- a/configure.ac +++ b/configure.ac @@ -916,8 +916,8 @@ AC_CONFIG_FILES([Makefile examples/Makefile examples/android/Makefile vncterm/Makefile - classes/Makefile - classes/ssl/Makefile + webclients/Makefile + webclients/ssl/Makefile libvncclient/Makefile client_examples/Makefile test/Makefile diff --git a/examples/example.c b/examples/example.c index 93fdf28..fc156c0 100644 --- a/examples/example.c +++ b/examples/example.c @@ -288,7 +288,7 @@ int main(int argc,char** argv) rfbScreen->ptrAddEvent = doptr; rfbScreen->kbdAddEvent = dokey; rfbScreen->newClientHook = newclient; - rfbScreen->httpDir = "../classes"; + rfbScreen->httpDir = "../webclients"; rfbScreen->httpEnableProxyConnect = TRUE; initBuffer((unsigned char*)rfbScreen->frameBuffer); diff --git a/examples/pnmshow.c b/examples/pnmshow.c index 6ced92a..dbb66ab 100644 --- a/examples/pnmshow.c +++ b/examples/pnmshow.c @@ -75,7 +75,7 @@ int main(int argc,char** argv) rfbScreen->kbdAddEvent = HandleKey; /* enable http */ - rfbScreen->httpDir = "../classes"; + rfbScreen->httpDir = "../webclients"; /* allocate picture and read it */ rfbScreen->frameBuffer = (char*)malloc(paddedWidth*bytesPerPixel*height); diff --git a/examples/pnmshow24.c b/examples/pnmshow24.c index 81389d7..0c772ea 100644 --- a/examples/pnmshow24.c +++ b/examples/pnmshow24.c @@ -70,7 +70,7 @@ int main(int argc,char** argv) rfbScreen->kbdAddEvent = HandleKey; /* enable http */ - rfbScreen->httpDir = "../classes"; + rfbScreen->httpDir = "../webclients"; /* allocate picture and read it */ rfbScreen->frameBuffer = (char*)malloc(paddedWidth*3*height); diff --git a/rfb/rfb.h b/rfb/rfb.h index 004383d..3317e54 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -1097,7 +1097,7 @@ rfbBool rfbUpdateClient(rfbClientPtr cl); To also start an HTTP server (running on port 5800+display_number), you have to set rfbScreenInfo::httpDir to a directory containing vncviewer.jar and - index.vnc (like the included "classes" directory). + index.vnc (like the included "webclients" directory). @section making_it_interactive Making it interactive diff --git a/webclients/Makefile.am b/webclients/Makefile.am new file mode 100644 index 0000000..c5497a8 --- /dev/null +++ b/webclients/Makefile.am @@ -0,0 +1,5 @@ +EXTRA_DIST=VncViewer.jar index.vnc javaviewer.pseudo_proxy.patch + +SUBDIRS = ssl +DIST_SUBDIRS = ssl + diff --git a/webclients/VncViewer.jar b/webclients/VncViewer.jar new file mode 100644 index 0000000..602fdb9 Binary files /dev/null and b/webclients/VncViewer.jar differ diff --git a/webclients/index.vnc b/webclients/index.vnc new file mode 100644 index 0000000..34ad25d --- /dev/null +++ b/webclients/index.vnc @@ -0,0 +1,37 @@ + + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +
+
+ +If the above Java applet does not work, you can also try the new JavaScript-only noVNC viewer. You will need a HTML5-capable browser though. + +
+ +
+ +
+
+
+LibVNCServer/LibVNCClient Homepage + diff --git a/webclients/javaviewer.pseudo_proxy.patch b/webclients/javaviewer.pseudo_proxy.patch new file mode 100644 index 0000000..4d2f36e --- /dev/null +++ b/webclients/javaviewer.pseudo_proxy.patch @@ -0,0 +1,141 @@ +diff -ru vnc_javasrc/OptionsFrame.java proxy_vnc_javasrc/OptionsFrame.java +--- vnc_javasrc/OptionsFrame.java Fri Jul 5 08:17:23 2002 ++++ proxy_vnc_javasrc/OptionsFrame.java Thu Aug 22 23:24:44 2002 +@@ -70,6 +70,12 @@ + + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; ++ ++ Label proxyHostLabel; ++ TextField proxyHostEdit; ++ Label proxyPortLabel; ++ TextField proxyPortEdit; ++ + Button closeButton; + VncViewer viewer; + +@@ -93,6 +99,9 @@ + boolean shareDesktop; + boolean viewOnly; + ++ String proxyHost; ++ int proxyPort; ++ + // + // Constructor. Set up the labels and choices from the names and values + // arrays. +@@ -126,6 +135,32 @@ + } + } + ++ // TODO: find a way to set these to defaults from browser ++ proxyPort = viewer.readIntParameter("Use Proxy Port", -1); ++ if(proxyPort>-1) { ++ proxyHost = viewer.readParameter("Use Proxy Host", false); ++ if(proxyHost == null) ++ proxyHost = viewer.host; ++ ++ proxyHostLabel = new Label("Proxy Host"); ++ gbc.gridwidth = 1; ++ gridbag.setConstraints(proxyHostLabel,gbc); ++ add(proxyHostLabel); ++ proxyHostEdit = new TextField(); ++ gbc.gridwidth = GridBagConstraints.REMAINDER; ++ gridbag.setConstraints(proxyHostEdit,gbc); ++ add(proxyHostEdit); ++ ++ proxyPortLabel = new Label("Proxy Port"); ++ gbc.gridwidth = 1; ++ gridbag.setConstraints(proxyPortLabel,gbc); ++ add(proxyPortLabel); ++ proxyPortEdit = new TextField(); ++ gbc.gridwidth = GridBagConstraints.REMAINDER; ++ gridbag.setConstraints(proxyPortEdit,gbc); ++ add(proxyPortEdit); ++ } ++ + closeButton = new Button("Close"); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gridbag.setConstraints(closeButton, gbc); +@@ -161,6 +196,11 @@ + } + } + ++ if(proxyPort>-1) { ++ proxyPortEdit.setText(Integer.toString(proxyPort)); ++ proxyHostEdit.setText(proxyHost); ++ } ++ + // Make the booleans and encodings array correspond to the state of the GUI + + setEncodings(); +@@ -361,8 +401,12 @@ + // + + public void actionPerformed(ActionEvent evt) { +- if (evt.getSource() == closeButton) ++ if (evt.getSource() == closeButton) { + setVisible(false); ++ proxyHost = proxyHostEdit.getText(); ++ proxyPort = Integer.parseInt(proxyPortEdit.getText()); ++ System.err.println("proxy is " + proxyHost + ":" + proxyPort); ++ } + } + + // +diff -ru vnc_javasrc/RfbProto.java proxy_vnc_javasrc/RfbProto.java +--- vnc_javasrc/RfbProto.java Sun Aug 4 18:39:35 2002 ++++ proxy_vnc_javasrc/RfbProto.java Thu Aug 22 22:53:53 2002 +@@ -119,12 +119,51 @@ + viewer = v; + host = h; + port = p; +- sock = new Socket(host, port); ++ if(viewer.options.proxyPort>-1) ++ sock = new Socket(viewer.options.proxyHost, viewer.options.proxyPort); ++ else ++ sock = new Socket(host, port); + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); ++ if(viewer.options.proxyPort>-1) ++ negotiateProxy(host,port); + } + ++ // this is inefficient as hell, but only used once per connection ++ String readLine() { ++ byte[] ba = new byte[1]; ++ String s = new String(""); ++ ++ ba[0]=0; ++ try { ++ while(ba[0] != 0xa) { ++ ba[0] = (byte)is.readUnsignedByte(); ++ s += new String(ba); ++ } ++ } catch(Exception e) { ++ e.printStackTrace(); ++ } ++ return s; ++ } ++ ++ void negotiateProxy(String realHost,int realPort) throws IOException { ++ String line; ++ ++ // this would be the correct way, but we want to trick strict proxies. ++ // line = "CONNECT " + realHost + ":" + realPort + " HTTP/1.1\r\nHost: " + realHost + ":" + realPort + "\r\n\r\n"; ++ line = "GET " + realHost + ":" + realPort + "/proxied.connection HTTP/1.0\r\nPragma: No-Cache\r\nProxy-Connection: Keep-Alive\r\n\r\n"; ++ os.write(line.getBytes()); ++ ++ line = readLine(); ++ System.err.println("Proxy said: " + line); ++ if(!(line.substring(0,7)+line.substring(8,12)).equalsIgnoreCase("HTTP/1. 200")) { ++ IOException e = new IOException(line); ++ throw e; ++ } ++ while(!line.equals("\r\n") && !line.equals("\n")) ++ line = readLine(); ++ } + + void close() { + try { diff --git a/webclients/novnc/LICENSE.txt b/webclients/novnc/LICENSE.txt new file mode 100644 index 0000000..755ace3 --- /dev/null +++ b/webclients/novnc/LICENSE.txt @@ -0,0 +1,33 @@ +noVNC is Copyright (C) 2011 Joel Martin + +Some portions of noVNC are copyright to their individual authors. +Please refer to the individual source files and/or to the noVNC commit +history: https://github.com/kanaka/noVNC/commits/master + +noVNC is licensed under the LGPL (GNU Lesser General Public License) +version 3 with the following exceptions (all LGPL-3 compatible): + + include/input.js : LGPL-2 or any later version + + include/base64.js : Dual GPL-2 or LGPL-2.1 + + include/des.js : Various BSD style licenses + + include/web-socket-js/ : New BSD license. Source code at + http://github.com/gimite/web-socket-js + + include/Orbitron* : SIL Open Font License 1.1 + (Copyright 2009 Matt McInerney) + + images/ : Creative Commons Attribution-ShareAlike + http://creativecommons.org/licenses/by-sa/3.0/ + +The license texts are included at: + docs/LICENSE.LGPL-3 and + docs/LICENSE.GPL-3 + docs/LICENSE.OFL-1.1 + +Or alternatively the license texts may be found here: + http://www.gnu.org/licenses/lgpl.html and + http://www.gnu.org/licenses/gpl.html + http://scripts.sil.org/OFL diff --git a/webclients/novnc/README.md b/webclients/novnc/README.md new file mode 100644 index 0000000..4672969 --- /dev/null +++ b/webclients/novnc/README.md @@ -0,0 +1,93 @@ +## noVNC: HTML5 VNC Client + + +### Description + +noVNC is a VNC client implemented using HTML5 technologies, +specifically Canvas and WebSockets (supports 'wss://' encryption). +noVNC is licensed under the +[LGPLv3](http://www.gnu.org/licenses/lgpl.html). + +Special thanks to [Sentry Data Systems](http://www.sentryds.com) for +sponsoring ongoing development of this project (and for employing me). + +There are many companies/projects that have integrated noVNC into +their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), and [SlapOS](http://www.slapos.org). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links. + +Notable commits, announcements and news are posted to +@noVNC + + +### Screenshots + +Running in Chrome before and after connecting: + +  + +See more screenshots here. + + +### Browser Requirements + +* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS + Safari, Opera 11+, Internet Explorer 9+, etc. + +* HTML5 WebSockets: For browsers that do not have builtin + WebSockets support, the project includes + web-socket-js, + a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in + WebSocket support. + +* Fast Javascript Engine: noVNC avoids using new Javascript + functionality so it will run on older browsers, but decode and + rendering happen in Javascript, so a slow Javascript engine will + mean noVNC is painfully slow. + +* I maintain a more detailed browser compatibility list here. + + +### Server Requirements + +Unless you are using a VNC server with support for WebSockets +connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver) +currently), you need to use a WebSockets to TCP socket proxy. There is +a python proxy included ('websockify'). One advantage of using the +proxy is that it has builtin support for SSL/TLS encryption (i.e. +"wss://"). + +There a few reasons why a proxy is required: + + 1. WebSockets is not a pure socket protocol. There is an initial HTTP + like handshake to allow easy hand-off by web servers and allow + some origin policy exchange. Also, each WebSockets frame begins + with 0 ('\x00') and ends with 255 ('\xff'). + + 2. Javascript itself does not have the ability to handle pure byte + arrays. The python proxy encodes the data as base64 so that the + Javascript client can decode the data as an integer array. + + +### Quick Start + +* Use the launch script to start a mini-webserver and the WebSockets + proxy (websockify). The `--vnc` option is used to specify the location of + a running VNC server: + + `./utils/launch.sh --vnc localhost:5901` + +* Point your browser to the cut-and-paste URL that is output by the + launch script. Enter a password if the VNC server has one + configured. Hit the Connect button and enjoy! + + +### Other Pages + +* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL + certificate, starting a VNC server, advanced websockify usage, etc. + +* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects. + +* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems. + + diff --git a/webclients/novnc/favicon.ico b/webclients/novnc/favicon.ico new file mode 120000 index 0000000..45399c8 --- /dev/null +++ b/webclients/novnc/favicon.ico @@ -0,0 +1 @@ +images/favicon.ico \ No newline at end of file diff --git a/webclients/novnc/images/clipboard.png b/webclients/novnc/images/clipboard.png new file mode 100644 index 0000000..24df33c Binary files /dev/null and b/webclients/novnc/images/clipboard.png differ diff --git a/webclients/novnc/images/connect.png b/webclients/novnc/images/connect.png new file mode 100644 index 0000000..79e71ad Binary files /dev/null and b/webclients/novnc/images/connect.png differ diff --git a/webclients/novnc/images/ctrlaltdel.png b/webclients/novnc/images/ctrlaltdel.png new file mode 100644 index 0000000..31922e5 Binary files /dev/null and b/webclients/novnc/images/ctrlaltdel.png differ diff --git a/webclients/novnc/images/disconnect.png b/webclients/novnc/images/disconnect.png new file mode 100644 index 0000000..8832f5e Binary files /dev/null and b/webclients/novnc/images/disconnect.png differ diff --git a/webclients/novnc/images/drag.png b/webclients/novnc/images/drag.png new file mode 100644 index 0000000..433f896 Binary files /dev/null and b/webclients/novnc/images/drag.png differ diff --git a/webclients/novnc/images/favicon.ico b/webclients/novnc/images/favicon.ico new file mode 100644 index 0000000..c999634 Binary files /dev/null and b/webclients/novnc/images/favicon.ico differ diff --git a/webclients/novnc/images/favicon.png b/webclients/novnc/images/favicon.png new file mode 100644 index 0000000..e2bdb19 Binary files /dev/null and b/webclients/novnc/images/favicon.png differ diff --git a/webclients/novnc/images/keyboard.png b/webclients/novnc/images/keyboard.png new file mode 100644 index 0000000..f797952 Binary files /dev/null and b/webclients/novnc/images/keyboard.png differ diff --git a/webclients/novnc/images/mouse_left.png b/webclients/novnc/images/mouse_left.png new file mode 100644 index 0000000..1de7a48 Binary files /dev/null and b/webclients/novnc/images/mouse_left.png differ diff --git a/webclients/novnc/images/mouse_middle.png b/webclients/novnc/images/mouse_middle.png new file mode 100644 index 0000000..81fbd9b Binary files /dev/null and b/webclients/novnc/images/mouse_middle.png differ diff --git a/webclients/novnc/images/mouse_none.png b/webclients/novnc/images/mouse_none.png new file mode 100644 index 0000000..93dbf57 Binary files /dev/null and b/webclients/novnc/images/mouse_none.png differ diff --git a/webclients/novnc/images/mouse_right.png b/webclients/novnc/images/mouse_right.png new file mode 100644 index 0000000..355b25d Binary files /dev/null and b/webclients/novnc/images/mouse_right.png differ diff --git a/webclients/novnc/images/screen_320x460.png b/webclients/novnc/images/screen_320x460.png new file mode 100644 index 0000000..172ec55 Binary files /dev/null and b/webclients/novnc/images/screen_320x460.png differ diff --git a/webclients/novnc/images/screen_57x57.png b/webclients/novnc/images/screen_57x57.png new file mode 100644 index 0000000..e2085f2 Binary files /dev/null and b/webclients/novnc/images/screen_57x57.png differ diff --git a/webclients/novnc/images/screen_700x700.png b/webclients/novnc/images/screen_700x700.png new file mode 100644 index 0000000..ae67768 Binary files /dev/null and b/webclients/novnc/images/screen_700x700.png differ diff --git a/webclients/novnc/images/settings.png b/webclients/novnc/images/settings.png new file mode 100644 index 0000000..a43f5e1 Binary files /dev/null and b/webclients/novnc/images/settings.png differ diff --git a/webclients/novnc/include/Orbitron700.ttf b/webclients/novnc/include/Orbitron700.ttf new file mode 100644 index 0000000..e28729d Binary files /dev/null and b/webclients/novnc/include/Orbitron700.ttf differ diff --git a/webclients/novnc/include/Orbitron700.woff b/webclients/novnc/include/Orbitron700.woff new file mode 100644 index 0000000..61db630 Binary files /dev/null and b/webclients/novnc/include/Orbitron700.woff differ diff --git a/webclients/novnc/include/base.css b/webclients/novnc/include/base.css new file mode 100644 index 0000000..0a62a1b --- /dev/null +++ b/webclients/novnc/include/base.css @@ -0,0 +1,380 @@ +body { + margin:0; + padding:0; + font-family: Helvetica; + /*Background image with light grey curve.*/ + background-color:#494949; + background-repeat:no-repeat; + background-position:right bottom; + height:100%; +} + +html { + height:100%; +} + +#noVNC_controls ul { + list-style: none; + margin: 0px; + padding: 0px; +} +#noVNC_controls li { + padding-bottom:8px; +} + +#noVNC_host { + width:150px; +} +#noVNC_port { + width: 80px; +} +#noVNC_password { + width: 150px; +} +#noVNC_encrypt { +} +#noVNC_connectTimeout { + width: 30px; +} +#noVNC_path { + width: 100px; +} +#noVNC_connect_button { + width: 110px; + float:right; +} + + +#noVNC_view_drag_button { + display: none; +} +#sendCtrlAltDelButton { + display: none; +} +#noVNC_mobile_buttons { + display: none; +} + +.noVNC-buttons-left { + float: left; + padding-left:10px; + padding-top:4px; +} + +.noVNC-buttons-right { + float:right; + right: 0px; + padding-right:10px; + padding-top:4px; +} + +#noVNC_status_bar { + margin-top: 0px; + padding: 0px; +} + +#noVNC_status_bar div { + font-size: 12px; + padding-top: 4px; + width:100%; +} + +#noVNC_status { + height:20px; + text-align: center; +} +#noVNC_settings_menu { + margin: 3px; + text-align: left; +} +#noVNC_settings_menu ul { + list-style: none; + margin: 0px; + padding: 0px; +} + +#noVNC_apply { + float:right; +} + +.noVNC_status_normal { + background: #eee; +} +.noVNC_status_error { + background: #f44; +} +.noVNC_status_warn { + background: #ff4; +} + +/* Do not set width/height for VNC_screen or VNC_canvas or incorrect + * scaling will occur. Canvas resizes to remote VNC settings */ +#noVNC_screen_pad { + margin: 0px; + padding: 0px; + height: 44px; +} +#noVNC_screen { + text-align: center; + display: table; + width:100%; + height:100%; + background-color:#313131; + border-bottom-right-radius: 800px 600px; + /*border-top-left-radius: 800px 600px;*/ +} + +#noVNC_container, #noVNC_canvas { + margin: 0px; + padding: 0px; +} + +#noVNC_canvas { + left: 0px; +} + +#VNC_clipboard_clear_button { + float:right; +} +#VNC_clipboard_text { + font-size: 11px; +} + +#noVNC_clipboard_clear_button { + float:right; +} + +/*Bubble contents divs*/ +#noVNC_settings { + display:none; + margin-top:77px; + right:20px; + position:fixed; +} + +#noVNC_controls { + margin-top:77px; + right:12px; + position:fixed; +} +#noVNC_controls.top:after { + right:15px; +} + +#noVNC_clipboard { + display:none; + margin-top:77px; + right:30px; + position:fixed; +} +#noVNC_clipboard.top:after { + right:85px; +} + +#keyboardinput { + width:1px; + height:1px; + background-color:#fff; + color:#fff; + border:0; + position: relative; + left: -40px; + z-index: -1; +} + +.noVNC_status_warn { + background-color:yellow; +} + +/* + * Advanced Styling + */ + +/* Control bar */ +#noVNC-control-bar { + position:fixed; + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ + + display:block; + height:44px; + left:0; + top:0; + width:100%; + z-index:200; +} + +.noVNC_status_button { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #869dbc; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + background: #b2bdcd; /* Old browsers */ + background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + +.noVNC_status_button_selected { + padding: 4px 4px; + vertical-align: middle; + border:1px solid #4366a9; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + background: #779ced; /* Old browsers */ + background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */ + /*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/ +} + + +/*Settings Bubble*/ +.triangle-right { + position:relative; + padding:15px; + margin:1em 0 3em; + color:#fff; + background:#fff; /* default background for browsers without gradient support */ + /* css3 */ + /*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698)); + background:-moz-linear-gradient(#2e88c4, #075698); + background:-o-linear-gradient(#2e88c4, #075698); + background:linear-gradient(#2e88c4, #075698);*/ + -webkit-border-radius:10px; + -moz-border-radius:10px; + border-radius:10px; + color:#000; + border:2px solid #E0E0E0; +} + +.triangle-right.top:after { + border-color: transparent #E0E0E0; + border-width: 20px 20px 0 0; + bottom: auto; + left: auto; + right: 50px; + top: -20px; +} + +.triangle-right:after { + content:""; + position:absolute; + bottom:-20px; /* value = - border-top-width - border-bottom-width */ + left:50px; /* controls horizontal position */ + border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */ + border-style:solid; + border-color:#E0E0E0 transparent; + /* reduce the damage in FF3.0 */ + display:block; + width:0; +} + +.triangle-right.top:after { + top:-40px; /* value = - border-top-width - border-bottom-width */ + right:50px; /* controls horizontal position */ + bottom:auto; + left:auto; + border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */ + border-color:transparent #E0E0E0; +} + +/*Default noVNC logo.*/ +/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ +@font-face { + font-family: 'Orbitron'; + font-style: normal; + font-weight: 700; + src: local('?'), url('Orbitron700.woff') format('woff'), + url('Orbitron700.ttf') format('truetype'); +} + +#noVNC_logo { + margin-top: 170px; + margin-left: 10px; + color:yellow; + text-align:left; + font-family: 'Orbitron', 'OrbitronTTF', sans-serif; + line-height:90%; + text-shadow: + 5px 5px 0 #000, + -1px -1px 0 #000, + 1px -1px 0 #000, + -1px 1px 0 #000, + 1px 1px 0 #000; +} + + +#noVNC_logo span{ + color:green; +} + +/* ---------------------------------------- + * Media sizing + * ---------------------------------------- + */ + + +.noVNC_status_button { + font-size: 12px; +} + +#noVNC_clipboard_text { + width: 500px; +} + +#noVNC_logo { + font-size: 180px; +} + +@media screen and (min-width: 481px) and (max-width: 640px) { + .noVNC_status_button { + font-size: 10px; + } + #noVNC_clipboard_text { + width: 410px; + } + #noVNC_logo { + font-size: 150px; + } +} + +@media screen and (min-width: 321px) and (max-width: 480px) { + .noVNC_status_button { + font-size: 10px; + } + #noVNC_clipboard_text { + width: 250px; + } + #noVNC_logo { + font-size: 110px; + } +} + +@media screen and (max-width: 320px) { + .noVNC_status_button { + font-size: 9px; + } + #noVNC_clipboard_text { + width: 220px; + } + #noVNC_logo { + font-size: 90px; + } +} diff --git a/webclients/novnc/include/base64.js b/webclients/novnc/include/base64.js new file mode 100644 index 0000000..c68b33a --- /dev/null +++ b/webclients/novnc/include/base64.js @@ -0,0 +1,147 @@ +/* + * Modified from: + * http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla XML-RPC Client component. + * + * The Initial Developer of the Original Code is + * Digital Creations 2, Inc. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Martijn Pieters (original author) + * Samuel Sieb + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/*jslint white: false, bitwise: false, plusplus: false */ +/*global console */ + +var Base64 = { + +/* Convert data (an array of integers) to a Base64 string. */ +toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', +base64Pad : '=', + +encode: function (data) { + "use strict"; + var result = '', + chrTable = Base64.toBase64Table.split(''), + pad = Base64.base64Pad, + length = data.length, + i; + // Convert every three bytes to 4 ascii characters. + for (i = 0; i < (length - 2); i += 3) { + result += chrTable[data[i] >> 2]; + result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; + result += chrTable[data[i+2] & 0x3f]; + } + + // Convert the remaining 1 or 2 bytes, pad out to 4 characters. + if (length%3) { + i = length - (length%3); + result += chrTable[data[i] >> 2]; + if ((length%3) === 2) { + result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += chrTable[(data[i+1] & 0x0f) << 2]; + result += pad; + } else { + result += chrTable[(data[i] & 0x03) << 4]; + result += pad + pad; + } + } + + return result; +}, + +/* Convert Base64 data to a string */ +toBinaryTable : [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +], + +decode: function (data, offset) { + "use strict"; + offset = typeof(offset) !== 'undefined' ? offset : 0; + var binTable = Base64.toBinaryTable, + pad = Base64.base64Pad, + result, result_length, idx, i, c, padding, + leftbits = 0, // number of bits decoded, but yet to be appended + leftdata = 0, // bits decoded, but yet to be appended + data_length = data.indexOf('=') - offset; + + if (data_length < 0) { data_length = data.length - offset; } + + /* Every four characters is 3 resulting numbers */ + result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5); + result = new Array(result_length); + + // Convert one by one. + for (idx = 0, i = offset; i < data.length; i++) { + c = binTable[data.charCodeAt(i) & 0x7f]; + padding = (data.charAt(i) === pad); + // Skip illegal characters and whitespace + if (c === -1) { + console.error("Illegal character '" + data.charCodeAt(i) + "'"); + continue; + } + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) { + result[idx++] = (leftdata >> leftbits) & 0xff; + } + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) { + throw {name: 'Base64-Error', + message: 'Corrupted base64 string'}; + } + + return result; +} + +}; /* End of Base64 namespace */ diff --git a/webclients/novnc/include/black.css b/webclients/novnc/include/black.css new file mode 100644 index 0000000..8f80f66 --- /dev/null +++ b/webclients/novnc/include/black.css @@ -0,0 +1,45 @@ +#keyboardinput { + background-color:#000; +} + +#noVNC-control-bar { + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.triangle-right { + border:2px solid #fff; + background:#000; + color:#fff; +} + +.noVNC_status_button { + font-size: 12px; + vertical-align: middle; + border:1px solid #4c4c4c; + + background: #4c4c4c; /* Old browsers */ + background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */ +} + +.noVNC_status_button_selected { + background: #9dd53a; /* Old browsers */ + background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */ +} diff --git a/webclients/novnc/include/blue.css b/webclients/novnc/include/blue.css new file mode 100644 index 0000000..a8baf70 --- /dev/null +++ b/webclients/novnc/include/blue.css @@ -0,0 +1,27 @@ + +#noVNC-control-bar { + background-color:#04073d; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + color-stop(0.54, rgb(10,15,79)), + color-stop(0.5, rgb(4,7,61)) + ); + background-image: -moz-linear-gradient( + center bottom, + rgb(10,15,79) 54%, + rgb(4,7,61) 50% + ); +} + +.triangle-right { + border:2px solid #fff; + background:#04073d; + color:#fff; +} + +#keyboardinput { + background-color:#04073d; +} + diff --git a/webclients/novnc/include/des.js b/webclients/novnc/include/des.js new file mode 100644 index 0000000..1f95285 --- /dev/null +++ b/webclients/novnc/include/des.js @@ -0,0 +1,273 @@ +/* + * Ported from Flashlight VNC ActionScript implementation: + * http://www.wizhelp.com/flashlight-vnc/ + * + * Full attribution follows: + * + * ------------------------------------------------------------------------- + * + * This DES class has been extracted from package Acme.Crypto for use in VNC. + * The unnecessary odd parity code has been removed. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * 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. + * + + * DesCipher - the DES encryption method + * + * The meat of this code is by Dave Zimmerman , and is: + * + * Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and + * without fee is hereby granted, provided that this copyright notice is kept + * intact. + * + * WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY + * OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + * + * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE + * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE + * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE + * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP + * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR + * HIGH RISK ACTIVITIES. + * + * + * The rest is: + * + * Copyright (C) 1996 by Jef Poskanzer . All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Visit the ACME Labs Java page for up-to-date versions of this and other + * fine Java utilities: http://www.acme.com/java/ + */ + +"use strict"; +/*jslint white: false, bitwise: false, plusplus: false */ + +function DES(passwd) { + +// Tables, permutations, S-boxes, etc. +var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, + 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, + 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], + totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], + z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, + keys = []; + +a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; +SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, + z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, + a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, + c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; +a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; +SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, + a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, + z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, + z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; +a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; +SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, + b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, + c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, + b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; +a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; +SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, + z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, + b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, + c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; +a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; +SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, + a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, + z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, + c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; +a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; +SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, + z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, + b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, + a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; +a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; +SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, + b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, + b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, + z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; +a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; +SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, + c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, + a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, + z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; + +// Set the key. +function setKeys(keyBlock) { + var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], + raw0, raw1, rawi, KnLi; + + for (j = 0, l = 56; j < 56; ++j, l-=8) { + l += l<-5 ? 65 : l<-3 ? 31 : l<-1 ? 63 : l===27 ? 35 : 0; // PC1 + m = l & 0x7; + pc1m[j] = ((keyBlock[l >>> 3] & (1<>> 10; + keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; + ++KnLi; + keys[KnLi] = (raw0 & 0x0003f000) << 12; + keys[KnLi] |= (raw0 & 0x0000003f) << 16; + keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; + keys[KnLi] |= (raw1 & 0x0000003f); + ++KnLi; + } +} + +// Encrypt 8 bytes of text +function enc8(text) { + var i = 0, b = text.slice(), fval, keysi = 0, + l, r, x; // left, right, accumulator + + // Squash 8 bytes to 2 ints + l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; + r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; + + x = ((l >>> 4) ^ r) & 0x0f0f0f0f; + r ^= x; + l ^= (x << 4); + x = ((l >>> 16) ^ r) & 0x0000ffff; + r ^= x; + l ^= (x << 16); + x = ((r >>> 2) ^ l) & 0x33333333; + l ^= x; + r ^= (x << 2); + x = ((r >>> 8) ^ l) & 0x00ff00ff; + l ^= x; + r ^= (x << 8); + r = (r << 1) | ((r >>> 31) & 1); + x = (l ^ r) & 0xaaaaaaaa; + l ^= x; + r ^= x; + l = (l << 1) | ((l >>> 31) & 1); + + for (i = 0; i < 8; ++i) { + x = (r << 28) | (r >>> 4); + x ^= keys[keysi++]; + fval = SP7[x & 0x3f]; + fval |= SP5[(x >>> 8) & 0x3f]; + fval |= SP3[(x >>> 16) & 0x3f]; + fval |= SP1[(x >>> 24) & 0x3f]; + x = r ^ keys[keysi++]; + fval |= SP8[x & 0x3f]; + fval |= SP6[(x >>> 8) & 0x3f]; + fval |= SP4[(x >>> 16) & 0x3f]; + fval |= SP2[(x >>> 24) & 0x3f]; + l ^= fval; + x = (l << 28) | (l >>> 4); + x ^= keys[keysi++]; + fval = SP7[x & 0x3f]; + fval |= SP5[(x >>> 8) & 0x3f]; + fval |= SP3[(x >>> 16) & 0x3f]; + fval |= SP1[(x >>> 24) & 0x3f]; + x = l ^ keys[keysi++]; + fval |= SP8[x & 0x0000003f]; + fval |= SP6[(x >>> 8) & 0x3f]; + fval |= SP4[(x >>> 16) & 0x3f]; + fval |= SP2[(x >>> 24) & 0x3f]; + r ^= fval; + } + + r = (r << 31) | (r >>> 1); + x = (l ^ r) & 0xaaaaaaaa; + l ^= x; + r ^= x; + l = (l << 31) | (l >>> 1); + x = ((l >>> 8) ^ r) & 0x00ff00ff; + r ^= x; + l ^= (x << 8); + x = ((l >>> 2) ^ r) & 0x33333333; + r ^= x; + l ^= (x << 2); + x = ((r >>> 16) ^ l) & 0x0000ffff; + l ^= x; + r ^= (x << 16); + x = ((r >>> 4) ^ l) & 0x0f0f0f0f; + l ^= x; + r ^= (x << 4); + + // Spread ints to bytes + x = [r, l]; + for (i = 0; i < 8; i++) { + b[i] = (x[i>>>2] >>> (8*(3 - (i%4)))) % 256; + if (b[i] < 0) { b[i] += 256; } // unsigned + } + return b; +} + +// Encrypt 16 bytes of text using passwd as key +function encrypt(t) { + return enc8(t.slice(0,8)).concat(enc8(t.slice(8,16))); +} + +setKeys(passwd); // Setup keys +return {'encrypt': encrypt}; // Public interface + +} // function DES diff --git a/webclients/novnc/include/display.js b/webclients/novnc/include/display.js new file mode 100644 index 0000000..2cf262d --- /dev/null +++ b/webclients/novnc/include/display.js @@ -0,0 +1,671 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint browser: true, white: false, bitwise: false */ +/*global Util, Base64, changeCursor */ + +function Display(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + // Private Display namespace variables + c_ctx = null, + c_forceCanvas = false, + + // Predefine function variables (jslint) + imageDataGet, rgbxImageData, cmapImageData, + setFillColor, rescale, + + // The full frame buffer (logical canvas) size + fb_width = 0, + fb_height = 0, + // The visible "physical canvas" viewport + viewport = {'x': 0, 'y': 0, 'w' : 0, 'h' : 0 }, + cleanRect = {'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1}, + + c_prevStyle = "", + tile = null, + tile16x16 = null, + tile_x = 0, + tile_y = 0; + + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', null, 'Canvas element for rendering'], + ['context', 'ro', 'raw', null, 'Canvas 2D context for rendering (read-only)'], + ['logo', 'rw', 'raw', null, 'Logo to display when cleared: {"width": width, "height": height, "data": data}'], + ['true_color', 'rw', 'bool', true, 'Use true-color pixel data'], + ['colourMap', 'rw', 'arr', [], 'Colour map array (when not true-color)'], + ['scale', 'rw', 'float', 1.0, 'Display area scale factor 0.0 - 1.0'], + ['viewport', 'rw', 'bool', false, 'Use a viewport set with viewportChange()'], + ['width', 'rw', 'int', null, 'Display area width'], + ['height', 'rw', 'int', null, 'Display area height'], + + ['render_mode', 'ro', 'str', '', 'Canvas rendering mode (read-only)'], + + ['prefer_js', 'rw', 'str', null, 'Prefer Javascript over canvas methods'], + ['cursor_uri', 'rw', 'raw', null, 'Can we render cursor using data URI'] + ]); + +// Override some specific getters/setters +that.get_context = function () { return c_ctx; }; + +that.set_scale = function(scale) { rescale(scale); }; + +that.set_width = function (val) { that.resize(val, fb_height); }; +that.get_width = function() { return fb_width; }; + +that.set_height = function (val) { that.resize(fb_width, val); }; +that.get_height = function() { return fb_height; }; + + + +// +// Private functions +// + +// Create the public API interface +function constructor() { + Util.Debug(">> Display.constructor"); + + var c, func, i, curDat, curSave, + has_imageData = false, UE = Util.Engine; + + if (! conf.target) { throw("target must be set"); } + + if (typeof conf.target === 'string') { + throw("target must be a DOM element"); + } + + c = conf.target; + + if (! c.getContext) { throw("no getContext method"); } + + if (! c_ctx) { c_ctx = c.getContext('2d'); } + + Util.Debug("User Agent: " + navigator.userAgent); + if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); } + if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); } + if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); } + if (UE.presto) { Util.Debug("Browser: presto " + UE.presto); } + + that.clear(); + + // Check canvas features + if ('createImageData' in c_ctx) { + conf.render_mode = "canvas rendering"; + } else { + throw("Canvas does not support createImageData"); + } + if (conf.prefer_js === null) { + Util.Info("Prefering javascript operations"); + conf.prefer_js = true; + } + + // Initialize cached tile imageData + tile16x16 = c_ctx.createImageData(16, 16); + + /* + * Determine browser support for setting the cursor via data URI + * scheme + */ + curDat = []; + for (i=0; i < 8 * 8 * 4; i += 1) { + curDat.push(255); + } + try { + curSave = c.style.cursor; + changeCursor(conf.target, curDat, curDat, 2, 2, 8, 8); + if (c.style.cursor) { + if (conf.cursor_uri === null) { + conf.cursor_uri = true; + } + Util.Info("Data URI scheme cursor supported"); + } else { + if (conf.cursor_uri === null) { + conf.cursor_uri = false; + } + Util.Warn("Data URI scheme cursor not supported"); + } + c.style.cursor = curSave; + } catch (exc2) { + Util.Error("Data URI scheme cursor test exception: " + exc2); + conf.cursor_uri = false; + } + + Util.Debug("<< Display.constructor"); + return that ; +} + +rescale = function(factor) { + var c, tp, x, y, + properties = ['transform', 'WebkitTransform', 'MozTransform', null]; + c = conf.target; + tp = properties.shift(); + while (tp) { + if (typeof c.style[tp] !== 'undefined') { + break; + } + tp = properties.shift(); + } + + if (tp === null) { + Util.Debug("No scaling support"); + return; + } + + + if (typeof(factor) === "undefined") { + factor = conf.scale; + } else if (factor > 1.0) { + factor = 1.0; + } else if (factor < 0.1) { + factor = 0.1; + } + + if (conf.scale === factor) { + //Util.Debug("Display already scaled to '" + factor + "'"); + return; + } + + conf.scale = factor; + x = c.width - c.width * factor; + y = c.height - c.height * factor; + c.style[tp] = "scale(" + conf.scale + ") translate(-" + x + "px, -" + y + "px)"; +}; + +setFillColor = function(color) { + var rgb, newStyle; + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + newStyle = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")"; + if (newStyle !== c_prevStyle) { + c_ctx.fillStyle = newStyle; + c_prevStyle = newStyle; + } +}; + + +// +// Public API interface functions +// + +// Shift and/or resize the visible viewport +that.viewportChange = function(deltaX, deltaY, width, height) { + var c = conf.target, v = viewport, cr = cleanRect, + saveImg = null, saveStyle, x1, y1, vx2, vy2, w, h; + + if (!conf.viewport) { + Util.Debug("Setting viewport to full display region"); + deltaX = -v.w; // Clamped later if out of bounds + deltaY = -v.h; // Clamped later if out of bounds + width = fb_width; + height = fb_height; + } + + if (typeof(deltaX) === "undefined") { deltaX = 0; } + if (typeof(deltaY) === "undefined") { deltaY = 0; } + if (typeof(width) === "undefined") { width = v.w; } + if (typeof(height) === "undefined") { height = v.h; } + + // Size change + + if (width > fb_width) { width = fb_width; } + if (height > fb_height) { height = fb_height; } + + if ((v.w !== width) || (v.h !== height)) { + // Change width + if ((width < v.w) && (cr.x2 > v.x + width -1)) { + cr.x2 = v.x + width - 1; + } + v.w = width; + + // Change height + if ((height < v.h) && (cr.y2 > v.y + height -1)) { + cr.y2 = v.y + height - 1; + } + v.h = height; + + + if (v.w > 0 && v.h > 0 && c.width > 0 && c.height > 0) { + saveImg = c_ctx.getImageData(0, 0, + (c.width < v.w) ? c.width : v.w, + (c.height < v.h) ? c.height : v.h); + } + + c.width = v.w; + c.height = v.h; + + if (saveImg) { + c_ctx.putImageData(saveImg, 0, 0); + } + } + + vx2 = v.x + v.w - 1; + vy2 = v.y + v.h - 1; + + + // Position change + + if ((deltaX < 0) && ((v.x + deltaX) < 0)) { + deltaX = - v.x; + } + if ((vx2 + deltaX) >= fb_width) { + deltaX -= ((vx2 + deltaX) - fb_width + 1); + } + + if ((v.y + deltaY) < 0) { + deltaY = - v.y; + } + if ((vy2 + deltaY) >= fb_height) { + deltaY -= ((vy2 + deltaY) - fb_height + 1); + } + + if ((deltaX === 0) && (deltaY === 0)) { + //Util.Debug("skipping viewport change"); + return; + } + Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); + + v.x += deltaX; + vx2 += deltaX; + v.y += deltaY; + vy2 += deltaY; + + // Update the clean rectangle + if (v.x > cr.x1) { + cr.x1 = v.x; + } + if (vx2 < cr.x2) { + cr.x2 = vx2; + } + if (v.y > cr.y1) { + cr.y1 = v.y; + } + if (vy2 < cr.y2) { + cr.y2 = vy2; + } + + if (deltaX < 0) { + // Shift viewport left, redraw left section + x1 = 0; + w = - deltaX; + } else { + // Shift viewport right, redraw right section + x1 = v.w - deltaX; + w = deltaX; + } + if (deltaY < 0) { + // Shift viewport up, redraw top section + y1 = 0; + h = - deltaY; + } else { + // Shift viewport down, redraw bottom section + y1 = v.h - deltaY; + h = deltaY; + } + + // Copy the valid part of the viewport to the shifted location + saveStyle = c_ctx.fillStyle; + c_ctx.fillStyle = "rgb(255,255,255)"; + if (deltaX !== 0) { + //that.copyImage(0, 0, -deltaX, 0, v.w, v.h); + //that.fillRect(x1, 0, w, v.h, [255,255,255]); + c_ctx.drawImage(c, 0, 0, v.w, v.h, -deltaX, 0, v.w, v.h); + c_ctx.fillRect(x1, 0, w, v.h); + } + if (deltaY !== 0) { + //that.copyImage(0, 0, 0, -deltaY, v.w, v.h); + //that.fillRect(0, y1, v.w, h, [255,255,255]); + c_ctx.drawImage(c, 0, 0, v.w, v.h, 0, -deltaY, v.w, v.h); + c_ctx.fillRect(0, y1, v.w, h); + } + c_ctx.fillStyle = saveStyle; +}; + + +// Return a map of clean and dirty areas of the viewport and reset the +// tracking of clean and dirty areas. +// +// Returns: {'cleanBox': {'x': x, 'y': y, 'w': w, 'h': h}, +// 'dirtyBoxes': [{'x': x, 'y': y, 'w': w, 'h': h}, ...]} +that.getCleanDirtyReset = function() { + var v = viewport, c = cleanRect, cleanBox, dirtyBoxes = [], + vx2 = v.x + v.w - 1, vy2 = v.y + v.h - 1; + + + // Copy the cleanRect + cleanBox = {'x': c.x1, 'y': c.y1, + 'w': c.x2 - c.x1 + 1, 'h': c.y2 - c.y1 + 1}; + + if ((c.x1 >= c.x2) || (c.y1 >= c.y2)) { + // Whole viewport is dirty + dirtyBoxes.push({'x': v.x, 'y': v.y, 'w': v.w, 'h': v.h}); + } else { + // Redraw dirty regions + if (v.x < c.x1) { + // left side dirty region + dirtyBoxes.push({'x': v.x, 'y': v.y, + 'w': c.x1 - v.x + 1, 'h': v.h}); + } + if (vx2 > c.x2) { + // right side dirty region + dirtyBoxes.push({'x': c.x2 + 1, 'y': v.y, + 'w': vx2 - c.x2, 'h': v.h}); + } + if (v.y < c.y1) { + // top/middle dirty region + dirtyBoxes.push({'x': c.x1, 'y': v.y, + 'w': c.x2 - c.x1 + 1, 'h': c.y1 - v.y}); + } + if (vy2 > c.y2) { + // bottom/middle dirty region + dirtyBoxes.push({'x': c.x1, 'y': c.y2 + 1, + 'w': c.x2 - c.x1 + 1, 'h': vy2 - c.y2}); + } + } + + // Reset the cleanRect to the whole viewport + cleanRect = {'x1': v.x, 'y1': v.y, + 'x2': v.x + v.w - 1, 'y2': v.y + v.h - 1}; + + return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes}; +}; + +// Translate viewport coordinates to absolute coordinates +that.absX = function(x) { + return x + viewport.x; +} +that.absY = function(y) { + return y + viewport.y; +} + + +that.resize = function(width, height) { + c_prevStyle = ""; + + fb_width = width; + fb_height = height; + + rescale(conf.scale); + that.viewportChange(); +}; + +that.clear = function() { + + if (conf.logo) { + that.resize(conf.logo.width, conf.logo.height); + that.blitStringImage(conf.logo.data, 0, 0); + } else { + that.resize(640, 20); + c_ctx.clearRect(0, 0, viewport.w, viewport.h); + } + + // No benefit over default ("source-over") in Chrome and firefox + //c_ctx.globalCompositeOperation = "copy"; +}; + +that.fillRect = function(x, y, width, height, color) { + setFillColor(color); + c_ctx.fillRect(x - viewport.x, y - viewport.y, width, height); +}; + +that.copyImage = function(old_x, old_y, new_x, new_y, w, h) { + var x1 = old_x - viewport.x, y1 = old_y - viewport.y, + x2 = new_x - viewport.x, y2 = new_y - viewport.y; + c_ctx.drawImage(conf.target, x1, y1, w, h, x2, y2, w, h); +}; + + +// Start updating a tile +that.startTile = function(x, y, width, height, color) { + var data, rgb, red, green, blue, i; + tile_x = x; + tile_y = y; + if ((width === 16) && (height === 16)) { + tile = tile16x16; + } else { + tile = c_ctx.createImageData(width, height); + } + data = tile.data; + if (conf.prefer_js) { + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + red = rgb[0]; + green = rgb[1]; + blue = rgb[2]; + for (i = 0; i < (width * height * 4); i+=4) { + data[i ] = red; + data[i + 1] = green; + data[i + 2] = blue; + data[i + 3] = 255; + } + } else { + that.fillRect(x, y, width, height, color); + } +}; + +// Update sub-rectangle of the current tile +that.subTile = function(x, y, w, h, color) { + var data, p, rgb, red, green, blue, width, j, i, xend, yend; + if (conf.prefer_js) { + data = tile.data; + width = tile.width; + if (conf.true_color) { + rgb = color; + } else { + rgb = conf.colourMap[color[0]]; + } + red = rgb[0]; + green = rgb[1]; + blue = rgb[2]; + xend = x + w; + yend = y + h; + for (j = y; j < yend; j += 1) { + for (i = x; i < xend; i += 1) { + p = (i + (j * width) ) * 4; + data[p ] = red; + data[p + 1] = green; + data[p + 2] = blue; + data[p + 3] = 255; + } + } + } else { + that.fillRect(tile_x + x, tile_y + y, w, h, color); + } +}; + +// Draw the current tile to the screen +that.finishTile = function() { + if (conf.prefer_js) { + c_ctx.putImageData(tile, tile_x - viewport.x, tile_y - viewport.y) + } + // else: No-op, if not prefer_js then already done by setSubTile +}; + +rgbxImageData = function(x, y, width, height, arr, offset) { + var img, i, j, data, v = viewport; + /* + if ((x - v.x >= v.w) || (y - v.y >= v.h) || + (x - v.x + width < 0) || (y - v.y + height < 0)) { + // Skipping because outside of viewport + return; + } + */ + img = c_ctx.createImageData(width, height); + data = img.data; + for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) { + data[i ] = arr[j ]; + data[i + 1] = arr[j + 1]; + data[i + 2] = arr[j + 2]; + data[i + 3] = 255; // Set Alpha + } + c_ctx.putImageData(img, x - v.x, y - v.y); +}; + +cmapImageData = function(x, y, width, height, arr, offset) { + var img, i, j, data, rgb, cmap; + img = c_ctx.createImageData(width, height); + data = img.data; + cmap = conf.colourMap; + for (i=0, j=offset; i < (width * height * 4); i+=4, j+=1) { + rgb = cmap[arr[j]]; + data[i ] = rgb[0]; + data[i + 1] = rgb[1]; + data[i + 2] = rgb[2]; + data[i + 3] = 255; // Set Alpha + } + c_ctx.putImageData(img, x - viewport.x, y - viewport.y); +}; + +that.blitImage = function(x, y, width, height, arr, offset) { + if (conf.true_color) { + rgbxImageData(x, y, width, height, arr, offset); + } else { + cmapImageData(x, y, width, height, arr, offset); + } +}; + +that.blitStringImage = function(str, x, y) { + var img = new Image(); + img.onload = function () { + c_ctx.drawImage(img, x - viewport.x, y - viewport.y); + }; + img.src = str; +}; + +that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { + if (conf.cursor_uri === false) { + Util.Warn("changeCursor called but no cursor data URI support"); + return; + } + + if (conf.true_color) { + changeCursor(conf.target, pixels, mask, hotx, hoty, w, h); + } else { + changeCursor(conf.target, pixels, mask, hotx, hoty, w, h, conf.colourMap); + } +}; + +that.defaultCursor = function() { + conf.target.style.cursor = "default"; +}; + +return constructor(); // Return the public API interface + +} // End of Display() + + +/* Set CSS cursor property using data URI encoded cursor file */ +function changeCursor(target, pixels, mask, hotx, hoty, w, h, cmap) { + "use strict"; + var cur = [], rgb, IHDRsz, RGBsz, ANDsz, XORsz, url, idx, alpha, x, y; + //Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h); + + // Push multi-byte little-endian values + cur.push16le = function (num) { + this.push((num ) & 0xFF, + (num >> 8) & 0xFF ); + }; + cur.push32le = function (num) { + this.push((num ) & 0xFF, + (num >> 8) & 0xFF, + (num >> 16) & 0xFF, + (num >> 24) & 0xFF ); + }; + + IHDRsz = 40; + RGBsz = w * h * 4; + XORsz = Math.ceil( (w * h) / 8.0 ); + ANDsz = Math.ceil( (w * h) / 8.0 ); + + // Main header + cur.push16le(0); // 0: Reserved + cur.push16le(2); // 2: .CUR type + cur.push16le(1); // 4: Number of images, 1 for non-animated ico + + // Cursor #1 header (ICONDIRENTRY) + cur.push(w); // 6: width + cur.push(h); // 7: height + cur.push(0); // 8: colors, 0 -> true-color + cur.push(0); // 9: reserved + cur.push16le(hotx); // 10: hotspot x coordinate + cur.push16le(hoty); // 12: hotspot y coordinate + cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz); + // 14: cursor data byte size + cur.push32le(22); // 18: offset of cursor data in the file + + + // Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO) + cur.push32le(IHDRsz); // 22: Infoheader size + cur.push32le(w); // 26: Cursor width + cur.push32le(h*2); // 30: XOR+AND height + cur.push16le(1); // 34: number of planes + cur.push16le(32); // 36: bits per pixel + cur.push32le(0); // 38: Type of compression + + cur.push32le(XORsz + ANDsz); // 43: Size of Image + // Gimp leaves this as 0 + + cur.push32le(0); // 46: reserved + cur.push32le(0); // 50: reserved + cur.push32le(0); // 54: reserved + cur.push32le(0); // 58: reserved + + // 62: color data (RGBQUAD icColors[]) + for (y = h-1; y >= 0; y -= 1) { + for (x = 0; x < w; x += 1) { + idx = y * Math.ceil(w / 8) + Math.floor(x/8); + alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0; + + if (cmap) { + idx = (w * y) + x; + rgb = cmap[pixels[idx]]; + cur.push(rgb[2]); // blue + cur.push(rgb[1]); // green + cur.push(rgb[0]); // red + cur.push(alpha); // alpha + } else { + idx = ((w * y) + x) * 4; + cur.push(pixels[idx + 2]); // blue + cur.push(pixels[idx + 1]); // green + cur.push(pixels[idx ]); // red + cur.push(alpha); // alpha + } + } + } + + // XOR/bitmask data (BYTE icXOR[]) + // (ignored, just needs to be right size) + for (y = 0; y < h; y += 1) { + for (x = 0; x < Math.ceil(w / 8); x += 1) { + cur.push(0x00); + } + } + + // AND/bitmask data (BYTE icAND[]) + // (ignored, just needs to be right size) + for (y = 0; y < h; y += 1) { + for (x = 0; x < Math.ceil(w / 8); x += 1) { + cur.push(0x00); + } + } + + url = "data:image/x-icon;base64," + Base64.encode(cur); + target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default"; + //Util.Debug("<< changeCursor, cur.length: " + cur.length); +} diff --git a/webclients/novnc/include/input.js b/webclients/novnc/include/input.js new file mode 100644 index 0000000..3124d08 --- /dev/null +++ b/webclients/novnc/include/input.js @@ -0,0 +1,1884 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-2 or any later version (see LICENSE.txt) + */ + +/*jslint browser: true, white: false, bitwise: false */ +/*global window, Util */ + + +// +// Keyboard event handler +// + +function Keyboard(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + keyDownList = []; // List of depressed keys + // (even if they are happy) + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', document, 'DOM element that captures keyboard input'], + ['focused', 'rw', 'bool', true, 'Capture and send key events'], + + ['onKeyPress', 'rw', 'func', null, 'Handler for key press/release'] + ]); + + +// +// Private functions +// + +// From the event keyCode return the keysym value for keys that need +// to be suppressed otherwise they may trigger unintended browser +// actions +function getKeysymSpecial(evt) { + var keysym = null; + + switch ( evt.keyCode ) { + // These generate a keyDown and keyPress in Firefox and Opera + case 8 : keysym = 0xFF08; break; // BACKSPACE + case 13 : keysym = 0xFF0D; break; // ENTER + + // This generates a keyDown and keyPress in Opera + case 9 : keysym = 0xFF09; break; // TAB + default : break; + } + + if (evt.type === 'keydown') { + switch ( evt.keyCode ) { + case 27 : keysym = 0xFF1B; break; // ESCAPE + case 46 : keysym = 0xFFFF; break; // DELETE + + case 36 : keysym = 0xFF50; break; // HOME + case 35 : keysym = 0xFF57; break; // END + case 33 : keysym = 0xFF55; break; // PAGE_UP + case 34 : keysym = 0xFF56; break; // PAGE_DOWN + case 45 : keysym = 0xFF63; break; // INSERT + // '-' during keyPress + case 37 : keysym = 0xFF51; break; // LEFT + case 38 : keysym = 0xFF52; break; // UP + case 39 : keysym = 0xFF53; break; // RIGHT + case 40 : keysym = 0xFF54; break; // DOWN + case 16 : keysym = 0xFFE1; break; // SHIFT + case 17 : keysym = 0xFFE3; break; // CONTROL + //case 18 : keysym = 0xFFE7; break; // Left Meta (Mac Option) + case 18 : keysym = 0xFFE9; break; // Left ALT (Mac Command) + + case 112 : keysym = 0xFFBE; break; // F1 + case 113 : keysym = 0xFFBF; break; // F2 + case 114 : keysym = 0xFFC0; break; // F3 + case 115 : keysym = 0xFFC1; break; // F4 + case 116 : keysym = 0xFFC2; break; // F5 + case 117 : keysym = 0xFFC3; break; // F6 + case 118 : keysym = 0xFFC4; break; // F7 + case 119 : keysym = 0xFFC5; break; // F8 + case 120 : keysym = 0xFFC6; break; // F9 + case 121 : keysym = 0xFFC7; break; // F10 + case 122 : keysym = 0xFFC8; break; // F11 + case 123 : keysym = 0xFFC9; break; // F12 + + default : break; + } + } + + if ((!keysym) && (evt.ctrlKey || evt.altKey)) { + if ((typeof(evt.which) !== "undefined") && (evt.which > 0)) { + keysym = evt.which; + } else { + // IE9 always + // Firefox and Opera when ctrl/alt + special + Util.Warn("which not set, using keyCode"); + keysym = evt.keyCode; + } + + /* Remap symbols */ + switch (keysym) { + case 186 : keysym = 59; break; // ; (IE) + case 187 : keysym = 61; break; // = (IE) + case 188 : keysym = 44; break; // , (Mozilla, IE) + case 109 : // - (Mozilla, Opera) + if (Util.Engine.gecko || Util.Engine.presto) { + keysym = 45; } + break; + case 189 : keysym = 45; break; // - (IE) + case 190 : keysym = 46; break; // . (Mozilla, IE) + case 191 : keysym = 47; break; // / (Mozilla, IE) + case 192 : keysym = 96; break; // ` (Mozilla, IE) + case 219 : keysym = 91; break; // [ (Mozilla, IE) + case 220 : keysym = 92; break; // \ (Mozilla, IE) + case 221 : keysym = 93; break; // ] (Mozilla, IE) + case 222 : keysym = 39; break; // ' (Mozilla, IE) + } + + /* Remap shifted and unshifted keys */ + if (!!evt.shiftKey) { + switch (keysym) { + case 48 : keysym = 41 ; break; // ) (shifted 0) + case 49 : keysym = 33 ; break; // ! (shifted 1) + case 50 : keysym = 64 ; break; // @ (shifted 2) + case 51 : keysym = 35 ; break; // # (shifted 3) + case 52 : keysym = 36 ; break; // $ (shifted 4) + case 53 : keysym = 37 ; break; // % (shifted 5) + case 54 : keysym = 94 ; break; // ^ (shifted 6) + case 55 : keysym = 38 ; break; // & (shifted 7) + case 56 : keysym = 42 ; break; // * (shifted 8) + case 57 : keysym = 40 ; break; // ( (shifted 9) + + case 59 : keysym = 58 ; break; // : (shifted `) + case 61 : keysym = 43 ; break; // + (shifted ;) + case 44 : keysym = 60 ; break; // < (shifted ,) + case 45 : keysym = 95 ; break; // _ (shifted -) + case 46 : keysym = 62 ; break; // > (shifted .) + case 47 : keysym = 63 ; break; // ? (shifted /) + case 96 : keysym = 126; break; // ~ (shifted `) + case 91 : keysym = 123; break; // { (shifted [) + case 92 : keysym = 124; break; // | (shifted \) + case 93 : keysym = 125; break; // } (shifted ]) + case 39 : keysym = 34 ; break; // " (shifted ') + } + } else if ((keysym >= 65) && (keysym <=90)) { + /* Remap unshifted A-Z */ + keysym += 32; + } else if (evt.keyLocation === 3) { + // numpad keys + switch (keysym) { + case 96 : keysym = 48; break; // 0 + case 97 : keysym = 49; break; // 1 + case 98 : keysym = 50; break; // 2 + case 99 : keysym = 51; break; // 3 + case 100: keysym = 52; break; // 4 + case 101: keysym = 53; break; // 5 + case 102: keysym = 54; break; // 6 + case 103: keysym = 55; break; // 7 + case 104: keysym = 56; break; // 8 + case 105: keysym = 57; break; // 9 + case 109: keysym = 45; break; // - + case 110: keysym = 46; break; // . + case 111: keysym = 47; break; // / + } + } + } + + return keysym; +} + +/* Translate DOM keyPress event to keysym value */ +function getKeysym(evt) { + var keysym, msg; + + if (typeof(evt.which) !== "undefined") { + // WebKit, Firefox, Opera + keysym = evt.which; + } else { + // IE9 + Util.Warn("which not set, using keyCode"); + keysym = evt.keyCode; + } + + if ((keysym > 255) && (keysym < 0xFF00)) { + msg = "Mapping character code " + keysym; + // Map Unicode outside Latin 1 to X11 keysyms + keysym = unicodeTable[keysym]; + if (typeof(keysym) === 'undefined') { + keysym = 0; + } + Util.Debug(msg + " to " + keysym); + } + + return keysym; +} + +function show_keyDownList(kind) { + var c; + var msg = "keyDownList (" + kind + "):\n"; + for (c = 0; c < keyDownList.length; c++) { + msg = msg + " " + c + " - keyCode: " + keyDownList[c].keyCode + + " - which: " + keyDownList[c].which + "\n"; + } + Util.Debug(msg); +} + +function copyKeyEvent(evt) { + var members = ['type', 'keyCode', 'charCode', 'which', + 'altKey', 'ctrlKey', 'shiftKey', + 'keyLocation', 'keyIdentifier'], i, obj = {}; + for (i = 0; i < members.length; i++) { + if (typeof(evt[members[i]]) !== "undefined") { + obj[members[i]] = evt[members[i]]; + } + } + return obj; +} + +function pushKeyEvent(fevt) { + keyDownList.push(fevt); +} + +function getKeyEvent(keyCode, pop) { + var i, fevt = null; + for (i = keyDownList.length-1; i >= 0; i--) { + if (keyDownList[i].keyCode === keyCode) { + if ((typeof(pop) !== "undefined") && (pop)) { + fevt = keyDownList.splice(i, 1)[0]; + } else { + fevt = keyDownList[i]; + } + break; + } + } + return fevt; +} + +function ignoreKeyEvent(evt) { + // Blarg. Some keys have a different keyCode on keyDown vs keyUp + if (evt.keyCode === 229) { + // French AZERTY keyboard dead key. + // Lame thing is that the respective keyUp is 219 so we can't + // properly ignore the keyUp event + return true; + } + return false; +} + + +// +// Key Event Handling: +// +// There are several challenges when dealing with key events: +// - The meaning and use of keyCode, charCode and which depends on +// both the browser and the event type (keyDown/Up vs keyPress). +// - We cannot automatically determine the keyboard layout +// - The keyDown and keyUp events have a keyCode value that has not +// been translated by modifier keys. +// - The keyPress event has a translated (for layout and modifiers) +// character code but the attribute containing it differs. keyCode +// contains the translated value in WebKit (Chrome/Safari), Opera +// 11 and IE9. charCode contains the value in WebKit and Firefox. +// The which attribute contains the value on WebKit, Firefox and +// Opera 11. +// - The keyDown/Up keyCode value indicates (sort of) the physical +// key was pressed but only for standard US layout. On a US +// keyboard, the '-' and '_' characters are on the same key and +// generate a keyCode value of 189. But on an AZERTY keyboard even +// though they are different physical keys they both still +// generate a keyCode of 189! +// - To prevent a key event from propagating to the browser and +// causing unwanted default actions (such as closing a tab, +// opening a menu, shifting focus, etc) we must suppress this +// event in both keyDown and keyPress because not all key strokes +// generate on a keyPress event. Also, in WebKit and IE9 +// suppressing the keyDown prevents a keyPress but other browsers +// still generated a keyPress even if keyDown is suppressed. +// +// For safe key events, we wait until the keyPress event before +// reporting a key down event. For unsafe key events, we report a key +// down event when the keyDown event fires and we suppress any further +// actions (including keyPress). +// +// In order to report a key up event that matches what we reported +// for the key down event, we keep a list of keys that are currently +// down. When the keyDown event happens, we add the key event to the +// list. If it is a safe key event, then we update the which attribute +// in the most recent item on the list when we received a keyPress +// event (keyPress should immediately follow keyDown). When we +// received a keyUp event we search for the event on the list with +// a matching keyCode and we report the character code using the value +// in the 'which' attribute that was stored with that key. +// + +function onKeyDown(e) { + if (! conf.focused) { + return true; + } + var fevt = null, evt = (e ? e : window.event), + keysym = null, suppress = false; + //Util.Debug("onKeyDown kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + fevt = copyKeyEvent(evt); + + keysym = getKeysymSpecial(evt); + // Save keysym decoding for use in keyUp + fevt.keysym = keysym; + if (keysym) { + // If it is a key or key combination that might trigger + // browser behaviors or it has no corresponding keyPress + // event, then send it immediately + if (conf.onKeyPress && !ignoreKeyEvent(evt)) { + Util.Debug("onKeyPress down, keysym: " + keysym + + " (onKeyDown key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 1, evt); + } + suppress = true; + } + + if (! ignoreKeyEvent(evt)) { + // Add it to the list of depressed keys + pushKeyEvent(fevt); + //show_keyDownList('down'); + } + + if (suppress) { + // Suppress bubbling/default actions + Util.stopEvent(e); + return false; + } else { + // Allow the event to bubble and become a keyPress event which + // will have the character code translated + return true; + } +} + +function onKeyPress(e) { + if (! conf.focused) { + return true; + } + var evt = (e ? e : window.event), + kdlen = keyDownList.length, keysym = null; + //Util.Debug("onKeyPress kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + if (((evt.which !== "undefined") && (evt.which === 0)) || + (getKeysymSpecial(evt))) { + // Firefox and Opera generate a keyPress event even if keyDown + // is suppressed. But the keys we want to suppress will have + // either: + // - the which attribute set to 0 + // - getKeysymSpecial() will identify it + Util.Debug("Ignoring special key in keyPress"); + Util.stopEvent(e); + return false; + } + + keysym = getKeysym(evt); + + // Modify the the which attribute in the depressed keys list so + // that the keyUp event will be able to have the character code + // translation available. + if (kdlen > 0) { + keyDownList[kdlen-1].keysym = keysym; + } else { + Util.Warn("keyDownList empty when keyPress triggered"); + } + + //show_keyDownList('press'); + + // Send the translated keysym + if (conf.onKeyPress && (keysym > 0)) { + Util.Debug("onKeyPress down, keysym: " + keysym + + " (onKeyPress key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 1, evt); + } + + // Stop keypress events just in case + Util.stopEvent(e); + return false; +} + +function onKeyUp(e) { + if (! conf.focused) { + return true; + } + var fevt = null, evt = (e ? e : window.event), keysym; + //Util.Debug("onKeyUp kC:" + evt.keyCode + " cC:" + evt.charCode + " w:" + evt.which); + + fevt = getKeyEvent(evt.keyCode, true); + + if (fevt) { + keysym = fevt.keysym; + } else { + Util.Warn("Key event (keyCode = " + evt.keyCode + + ") not found on keyDownList"); + keysym = 0; + } + + //show_keyDownList('up'); + + if (conf.onKeyPress && (keysym > 0)) { + //Util.Debug("keyPress up, keysym: " + keysym + + // " (key: " + evt.keyCode + ", which: " + evt.which + ")"); + Util.Debug("onKeyPress up, keysym: " + keysym + + " (onKeyPress key: " + evt.keyCode + + ", which: " + evt.which + ")"); + conf.onKeyPress(keysym, 0, evt); + } + Util.stopEvent(e); + return false; +} + +// +// Public API interface functions +// + +that.grab = function() { + //Util.Debug(">> Keyboard.grab"); + var c = conf.target; + + Util.addEvent(c, 'keydown', onKeyDown); + Util.addEvent(c, 'keyup', onKeyUp); + Util.addEvent(c, 'keypress', onKeyPress); + + //Util.Debug("<< Keyboard.grab"); +}; + +that.ungrab = function() { + //Util.Debug(">> Keyboard.ungrab"); + var c = conf.target; + + Util.removeEvent(c, 'keydown', onKeyDown); + Util.removeEvent(c, 'keyup', onKeyUp); + Util.removeEvent(c, 'keypress', onKeyPress); + + //Util.Debug(">> Keyboard.ungrab"); +}; + +return that; // Return the public API interface + +} // End of Keyboard() + + +// +// Mouse event handler +// + +function Mouse(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}; // Configuration attributes + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'ro', 'dom', document, 'DOM element that captures mouse input'], + ['focused', 'rw', 'bool', true, 'Capture and send mouse clicks/movement'], + ['scale', 'rw', 'float', 1.0, 'Viewport scale factor 0.0 - 1.0'], + + ['onMouseButton', 'rw', 'func', null, 'Handler for mouse button click/release'], + ['onMouseMove', 'rw', 'func', null, 'Handler for mouse movement'], + ['touchButton', 'rw', 'int', 1, 'Button mask (1, 2, 4) for touch devices (0 means ignore clicks)'] + ]); + + +// +// Private functions +// + +function onMouseButton(e, down) { + var evt, pos, bmask; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + if (e.touches || e.changedTouches) { + // Touch device + bmask = conf.touchButton; + // If bmask is set + } else if (evt.which) { + /* everything except IE */ + bmask = 1 << evt.button; + } else { + /* IE including 9 */ + bmask = (evt.button & 0x1) + // Left + (evt.button & 0x2) * 2 + // Right + (evt.button & 0x4) / 2; // Middle + } + //Util.Debug("mouse " + pos.x + "," + pos.y + " down: " + down + + // " bmask: " + bmask + "(evt.button: " + evt.button + ")"); + if (bmask > 0 && conf.onMouseButton) { + Util.Debug("onMouseButton " + (down ? "down" : "up") + + ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); + conf.onMouseButton(pos.x, pos.y, down, bmask); + } + Util.stopEvent(e); + return false; +} + +function onMouseDown(e) { + onMouseButton(e, 1); +} + +function onMouseUp(e) { + onMouseButton(e, 0); +} + +function onMouseWheel(e) { + var evt, pos, bmask, wheelData; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + wheelData = evt.detail ? evt.detail * -1 : evt.wheelDelta / 40; + if (wheelData > 0) { + bmask = 1 << 3; + } else { + bmask = 1 << 4; + } + //Util.Debug('mouse scroll by ' + wheelData + ':' + pos.x + "," + pos.y); + if (conf.onMouseButton) { + conf.onMouseButton(pos.x, pos.y, 1, bmask); + conf.onMouseButton(pos.x, pos.y, 0, bmask); + } + Util.stopEvent(e); + return false; +} + +function onMouseMove(e) { + var evt, pos; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + //Util.Debug('mouse ' + evt.which + '/' + evt.button + ' up:' + pos.x + "," + pos.y); + if (conf.onMouseMove) { + conf.onMouseMove(pos.x, pos.y); + } + Util.stopEvent(e); + return false; +} + +function onMouseDisable(e) { + var evt, pos; + if (! conf.focused) { + return true; + } + evt = (e ? e : window.event); + pos = Util.getEventPosition(e, conf.target, conf.scale); + /* Stop propagation if inside canvas area */ + if ((pos.x >= 0) && (pos.y >= 0) && + (pos.x < conf.target.offsetWidth) && + (pos.y < conf.target.offsetHeight)) { + //Util.Debug("mouse event disabled"); + Util.stopEvent(e); + return false; + } + //Util.Debug("mouse event not disabled"); + return true; +} + +// +// Public API interface functions +// + +that.grab = function() { + //Util.Debug(">> Mouse.grab"); + var c = conf.target; + + if ('ontouchstart' in document.documentElement) { + Util.addEvent(c, 'touchstart', onMouseDown); + Util.addEvent(c, 'touchend', onMouseUp); + Util.addEvent(c, 'touchmove', onMouseMove); + } else { + Util.addEvent(c, 'mousedown', onMouseDown); + Util.addEvent(c, 'mouseup', onMouseUp); + Util.addEvent(c, 'mousemove', onMouseMove); + Util.addEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', + onMouseWheel); + } + + /* Work around right and middle click browser behaviors */ + Util.addEvent(document, 'click', onMouseDisable); + Util.addEvent(document.body, 'contextmenu', onMouseDisable); + + //Util.Debug("<< Mouse.grab"); +}; + +that.ungrab = function() { + //Util.Debug(">> Mouse.ungrab"); + var c = conf.target; + + if ('ontouchstart' in document.documentElement) { + Util.removeEvent(c, 'touchstart', onMouseDown); + Util.removeEvent(c, 'touchend', onMouseUp); + Util.removeEvent(c, 'touchmove', onMouseMove); + } else { + Util.removeEvent(c, 'mousedown', onMouseDown); + Util.removeEvent(c, 'mouseup', onMouseUp); + Util.removeEvent(c, 'mousemove', onMouseMove); + Util.removeEvent(c, (Util.Engine.gecko) ? 'DOMMouseScroll' : 'mousewheel', + onMouseWheel); + } + + /* Work around right and middle click browser behaviors */ + Util.removeEvent(document, 'click', onMouseDisable); + Util.removeEvent(document.body, 'contextmenu', onMouseDisable); + + //Util.Debug(">> Mouse.ungrab"); +}; + +return that; // Return the public API interface + +} // End of Mouse() + + +/* + * Browser keypress to X11 keysym for Unicode characters > U+00FF + */ +unicodeTable = { + 0x0104 : 0x01a1, + 0x02D8 : 0x01a2, + 0x0141 : 0x01a3, + 0x013D : 0x01a5, + 0x015A : 0x01a6, + 0x0160 : 0x01a9, + 0x015E : 0x01aa, + 0x0164 : 0x01ab, + 0x0179 : 0x01ac, + 0x017D : 0x01ae, + 0x017B : 0x01af, + 0x0105 : 0x01b1, + 0x02DB : 0x01b2, + 0x0142 : 0x01b3, + 0x013E : 0x01b5, + 0x015B : 0x01b6, + 0x02C7 : 0x01b7, + 0x0161 : 0x01b9, + 0x015F : 0x01ba, + 0x0165 : 0x01bb, + 0x017A : 0x01bc, + 0x02DD : 0x01bd, + 0x017E : 0x01be, + 0x017C : 0x01bf, + 0x0154 : 0x01c0, + 0x0102 : 0x01c3, + 0x0139 : 0x01c5, + 0x0106 : 0x01c6, + 0x010C : 0x01c8, + 0x0118 : 0x01ca, + 0x011A : 0x01cc, + 0x010E : 0x01cf, + 0x0110 : 0x01d0, + 0x0143 : 0x01d1, + 0x0147 : 0x01d2, + 0x0150 : 0x01d5, + 0x0158 : 0x01d8, + 0x016E : 0x01d9, + 0x0170 : 0x01db, + 0x0162 : 0x01de, + 0x0155 : 0x01e0, + 0x0103 : 0x01e3, + 0x013A : 0x01e5, + 0x0107 : 0x01e6, + 0x010D : 0x01e8, + 0x0119 : 0x01ea, + 0x011B : 0x01ec, + 0x010F : 0x01ef, + 0x0111 : 0x01f0, + 0x0144 : 0x01f1, + 0x0148 : 0x01f2, + 0x0151 : 0x01f5, + 0x0171 : 0x01fb, + 0x0159 : 0x01f8, + 0x016F : 0x01f9, + 0x0163 : 0x01fe, + 0x02D9 : 0x01ff, + 0x0126 : 0x02a1, + 0x0124 : 0x02a6, + 0x0130 : 0x02a9, + 0x011E : 0x02ab, + 0x0134 : 0x02ac, + 0x0127 : 0x02b1, + 0x0125 : 0x02b6, + 0x0131 : 0x02b9, + 0x011F : 0x02bb, + 0x0135 : 0x02bc, + 0x010A : 0x02c5, + 0x0108 : 0x02c6, + 0x0120 : 0x02d5, + 0x011C : 0x02d8, + 0x016C : 0x02dd, + 0x015C : 0x02de, + 0x010B : 0x02e5, + 0x0109 : 0x02e6, + 0x0121 : 0x02f5, + 0x011D : 0x02f8, + 0x016D : 0x02fd, + 0x015D : 0x02fe, + 0x0138 : 0x03a2, + 0x0156 : 0x03a3, + 0x0128 : 0x03a5, + 0x013B : 0x03a6, + 0x0112 : 0x03aa, + 0x0122 : 0x03ab, + 0x0166 : 0x03ac, + 0x0157 : 0x03b3, + 0x0129 : 0x03b5, + 0x013C : 0x03b6, + 0x0113 : 0x03ba, + 0x0123 : 0x03bb, + 0x0167 : 0x03bc, + 0x014A : 0x03bd, + 0x014B : 0x03bf, + 0x0100 : 0x03c0, + 0x012E : 0x03c7, + 0x0116 : 0x03cc, + 0x012A : 0x03cf, + 0x0145 : 0x03d1, + 0x014C : 0x03d2, + 0x0136 : 0x03d3, + 0x0172 : 0x03d9, + 0x0168 : 0x03dd, + 0x016A : 0x03de, + 0x0101 : 0x03e0, + 0x012F : 0x03e7, + 0x0117 : 0x03ec, + 0x012B : 0x03ef, + 0x0146 : 0x03f1, + 0x014D : 0x03f2, + 0x0137 : 0x03f3, + 0x0173 : 0x03f9, + 0x0169 : 0x03fd, + 0x016B : 0x03fe, + 0x1E02 : 0x1001e02, + 0x1E03 : 0x1001e03, + 0x1E0A : 0x1001e0a, + 0x1E80 : 0x1001e80, + 0x1E82 : 0x1001e82, + 0x1E0B : 0x1001e0b, + 0x1EF2 : 0x1001ef2, + 0x1E1E : 0x1001e1e, + 0x1E1F : 0x1001e1f, + 0x1E40 : 0x1001e40, + 0x1E41 : 0x1001e41, + 0x1E56 : 0x1001e56, + 0x1E81 : 0x1001e81, + 0x1E57 : 0x1001e57, + 0x1E83 : 0x1001e83, + 0x1E60 : 0x1001e60, + 0x1EF3 : 0x1001ef3, + 0x1E84 : 0x1001e84, + 0x1E85 : 0x1001e85, + 0x1E61 : 0x1001e61, + 0x0174 : 0x1000174, + 0x1E6A : 0x1001e6a, + 0x0176 : 0x1000176, + 0x0175 : 0x1000175, + 0x1E6B : 0x1001e6b, + 0x0177 : 0x1000177, + 0x0152 : 0x13bc, + 0x0153 : 0x13bd, + 0x0178 : 0x13be, + 0x203E : 0x047e, + 0x3002 : 0x04a1, + 0x300C : 0x04a2, + 0x300D : 0x04a3, + 0x3001 : 0x04a4, + 0x30FB : 0x04a5, + 0x30F2 : 0x04a6, + 0x30A1 : 0x04a7, + 0x30A3 : 0x04a8, + 0x30A5 : 0x04a9, + 0x30A7 : 0x04aa, + 0x30A9 : 0x04ab, + 0x30E3 : 0x04ac, + 0x30E5 : 0x04ad, + 0x30E7 : 0x04ae, + 0x30C3 : 0x04af, + 0x30FC : 0x04b0, + 0x30A2 : 0x04b1, + 0x30A4 : 0x04b2, + 0x30A6 : 0x04b3, + 0x30A8 : 0x04b4, + 0x30AA : 0x04b5, + 0x30AB : 0x04b6, + 0x30AD : 0x04b7, + 0x30AF : 0x04b8, + 0x30B1 : 0x04b9, + 0x30B3 : 0x04ba, + 0x30B5 : 0x04bb, + 0x30B7 : 0x04bc, + 0x30B9 : 0x04bd, + 0x30BB : 0x04be, + 0x30BD : 0x04bf, + 0x30BF : 0x04c0, + 0x30C1 : 0x04c1, + 0x30C4 : 0x04c2, + 0x30C6 : 0x04c3, + 0x30C8 : 0x04c4, + 0x30CA : 0x04c5, + 0x30CB : 0x04c6, + 0x30CC : 0x04c7, + 0x30CD : 0x04c8, + 0x30CE : 0x04c9, + 0x30CF : 0x04ca, + 0x30D2 : 0x04cb, + 0x30D5 : 0x04cc, + 0x30D8 : 0x04cd, + 0x30DB : 0x04ce, + 0x30DE : 0x04cf, + 0x30DF : 0x04d0, + 0x30E0 : 0x04d1, + 0x30E1 : 0x04d2, + 0x30E2 : 0x04d3, + 0x30E4 : 0x04d4, + 0x30E6 : 0x04d5, + 0x30E8 : 0x04d6, + 0x30E9 : 0x04d7, + 0x30EA : 0x04d8, + 0x30EB : 0x04d9, + 0x30EC : 0x04da, + 0x30ED : 0x04db, + 0x30EF : 0x04dc, + 0x30F3 : 0x04dd, + 0x309B : 0x04de, + 0x309C : 0x04df, + 0x06F0 : 0x10006f0, + 0x06F1 : 0x10006f1, + 0x06F2 : 0x10006f2, + 0x06F3 : 0x10006f3, + 0x06F4 : 0x10006f4, + 0x06F5 : 0x10006f5, + 0x06F6 : 0x10006f6, + 0x06F7 : 0x10006f7, + 0x06F8 : 0x10006f8, + 0x06F9 : 0x10006f9, + 0x066A : 0x100066a, + 0x0670 : 0x1000670, + 0x0679 : 0x1000679, + 0x067E : 0x100067e, + 0x0686 : 0x1000686, + 0x0688 : 0x1000688, + 0x0691 : 0x1000691, + 0x060C : 0x05ac, + 0x06D4 : 0x10006d4, + 0x0660 : 0x1000660, + 0x0661 : 0x1000661, + 0x0662 : 0x1000662, + 0x0663 : 0x1000663, + 0x0664 : 0x1000664, + 0x0665 : 0x1000665, + 0x0666 : 0x1000666, + 0x0667 : 0x1000667, + 0x0668 : 0x1000668, + 0x0669 : 0x1000669, + 0x061B : 0x05bb, + 0x061F : 0x05bf, + 0x0621 : 0x05c1, + 0x0622 : 0x05c2, + 0x0623 : 0x05c3, + 0x0624 : 0x05c4, + 0x0625 : 0x05c5, + 0x0626 : 0x05c6, + 0x0627 : 0x05c7, + 0x0628 : 0x05c8, + 0x0629 : 0x05c9, + 0x062A : 0x05ca, + 0x062B : 0x05cb, + 0x062C : 0x05cc, + 0x062D : 0x05cd, + 0x062E : 0x05ce, + 0x062F : 0x05cf, + 0x0630 : 0x05d0, + 0x0631 : 0x05d1, + 0x0632 : 0x05d2, + 0x0633 : 0x05d3, + 0x0634 : 0x05d4, + 0x0635 : 0x05d5, + 0x0636 : 0x05d6, + 0x0637 : 0x05d7, + 0x0638 : 0x05d8, + 0x0639 : 0x05d9, + 0x063A : 0x05da, + 0x0640 : 0x05e0, + 0x0641 : 0x05e1, + 0x0642 : 0x05e2, + 0x0643 : 0x05e3, + 0x0644 : 0x05e4, + 0x0645 : 0x05e5, + 0x0646 : 0x05e6, + 0x0647 : 0x05e7, + 0x0648 : 0x05e8, + 0x0649 : 0x05e9, + 0x064A : 0x05ea, + 0x064B : 0x05eb, + 0x064C : 0x05ec, + 0x064D : 0x05ed, + 0x064E : 0x05ee, + 0x064F : 0x05ef, + 0x0650 : 0x05f0, + 0x0651 : 0x05f1, + 0x0652 : 0x05f2, + 0x0653 : 0x1000653, + 0x0654 : 0x1000654, + 0x0655 : 0x1000655, + 0x0698 : 0x1000698, + 0x06A4 : 0x10006a4, + 0x06A9 : 0x10006a9, + 0x06AF : 0x10006af, + 0x06BA : 0x10006ba, + 0x06BE : 0x10006be, + 0x06CC : 0x10006cc, + 0x06D2 : 0x10006d2, + 0x06C1 : 0x10006c1, + 0x0492 : 0x1000492, + 0x0493 : 0x1000493, + 0x0496 : 0x1000496, + 0x0497 : 0x1000497, + 0x049A : 0x100049a, + 0x049B : 0x100049b, + 0x049C : 0x100049c, + 0x049D : 0x100049d, + 0x04A2 : 0x10004a2, + 0x04A3 : 0x10004a3, + 0x04AE : 0x10004ae, + 0x04AF : 0x10004af, + 0x04B0 : 0x10004b0, + 0x04B1 : 0x10004b1, + 0x04B2 : 0x10004b2, + 0x04B3 : 0x10004b3, + 0x04B6 : 0x10004b6, + 0x04B7 : 0x10004b7, + 0x04B8 : 0x10004b8, + 0x04B9 : 0x10004b9, + 0x04BA : 0x10004ba, + 0x04BB : 0x10004bb, + 0x04D8 : 0x10004d8, + 0x04D9 : 0x10004d9, + 0x04E2 : 0x10004e2, + 0x04E3 : 0x10004e3, + 0x04E8 : 0x10004e8, + 0x04E9 : 0x10004e9, + 0x04EE : 0x10004ee, + 0x04EF : 0x10004ef, + 0x0452 : 0x06a1, + 0x0453 : 0x06a2, + 0x0451 : 0x06a3, + 0x0454 : 0x06a4, + 0x0455 : 0x06a5, + 0x0456 : 0x06a6, + 0x0457 : 0x06a7, + 0x0458 : 0x06a8, + 0x0459 : 0x06a9, + 0x045A : 0x06aa, + 0x045B : 0x06ab, + 0x045C : 0x06ac, + 0x0491 : 0x06ad, + 0x045E : 0x06ae, + 0x045F : 0x06af, + 0x2116 : 0x06b0, + 0x0402 : 0x06b1, + 0x0403 : 0x06b2, + 0x0401 : 0x06b3, + 0x0404 : 0x06b4, + 0x0405 : 0x06b5, + 0x0406 : 0x06b6, + 0x0407 : 0x06b7, + 0x0408 : 0x06b8, + 0x0409 : 0x06b9, + 0x040A : 0x06ba, + 0x040B : 0x06bb, + 0x040C : 0x06bc, + 0x0490 : 0x06bd, + 0x040E : 0x06be, + 0x040F : 0x06bf, + 0x044E : 0x06c0, + 0x0430 : 0x06c1, + 0x0431 : 0x06c2, + 0x0446 : 0x06c3, + 0x0434 : 0x06c4, + 0x0435 : 0x06c5, + 0x0444 : 0x06c6, + 0x0433 : 0x06c7, + 0x0445 : 0x06c8, + 0x0438 : 0x06c9, + 0x0439 : 0x06ca, + 0x043A : 0x06cb, + 0x043B : 0x06cc, + 0x043C : 0x06cd, + 0x043D : 0x06ce, + 0x043E : 0x06cf, + 0x043F : 0x06d0, + 0x044F : 0x06d1, + 0x0440 : 0x06d2, + 0x0441 : 0x06d3, + 0x0442 : 0x06d4, + 0x0443 : 0x06d5, + 0x0436 : 0x06d6, + 0x0432 : 0x06d7, + 0x044C : 0x06d8, + 0x044B : 0x06d9, + 0x0437 : 0x06da, + 0x0448 : 0x06db, + 0x044D : 0x06dc, + 0x0449 : 0x06dd, + 0x0447 : 0x06de, + 0x044A : 0x06df, + 0x042E : 0x06e0, + 0x0410 : 0x06e1, + 0x0411 : 0x06e2, + 0x0426 : 0x06e3, + 0x0414 : 0x06e4, + 0x0415 : 0x06e5, + 0x0424 : 0x06e6, + 0x0413 : 0x06e7, + 0x0425 : 0x06e8, + 0x0418 : 0x06e9, + 0x0419 : 0x06ea, + 0x041A : 0x06eb, + 0x041B : 0x06ec, + 0x041C : 0x06ed, + 0x041D : 0x06ee, + 0x041E : 0x06ef, + 0x041F : 0x06f0, + 0x042F : 0x06f1, + 0x0420 : 0x06f2, + 0x0421 : 0x06f3, + 0x0422 : 0x06f4, + 0x0423 : 0x06f5, + 0x0416 : 0x06f6, + 0x0412 : 0x06f7, + 0x042C : 0x06f8, + 0x042B : 0x06f9, + 0x0417 : 0x06fa, + 0x0428 : 0x06fb, + 0x042D : 0x06fc, + 0x0429 : 0x06fd, + 0x0427 : 0x06fe, + 0x042A : 0x06ff, + 0x0386 : 0x07a1, + 0x0388 : 0x07a2, + 0x0389 : 0x07a3, + 0x038A : 0x07a4, + 0x03AA : 0x07a5, + 0x038C : 0x07a7, + 0x038E : 0x07a8, + 0x03AB : 0x07a9, + 0x038F : 0x07ab, + 0x0385 : 0x07ae, + 0x2015 : 0x07af, + 0x03AC : 0x07b1, + 0x03AD : 0x07b2, + 0x03AE : 0x07b3, + 0x03AF : 0x07b4, + 0x03CA : 0x07b5, + 0x0390 : 0x07b6, + 0x03CC : 0x07b7, + 0x03CD : 0x07b8, + 0x03CB : 0x07b9, + 0x03B0 : 0x07ba, + 0x03CE : 0x07bb, + 0x0391 : 0x07c1, + 0x0392 : 0x07c2, + 0x0393 : 0x07c3, + 0x0394 : 0x07c4, + 0x0395 : 0x07c5, + 0x0396 : 0x07c6, + 0x0397 : 0x07c7, + 0x0398 : 0x07c8, + 0x0399 : 0x07c9, + 0x039A : 0x07ca, + 0x039B : 0x07cb, + 0x039C : 0x07cc, + 0x039D : 0x07cd, + 0x039E : 0x07ce, + 0x039F : 0x07cf, + 0x03A0 : 0x07d0, + 0x03A1 : 0x07d1, + 0x03A3 : 0x07d2, + 0x03A4 : 0x07d4, + 0x03A5 : 0x07d5, + 0x03A6 : 0x07d6, + 0x03A7 : 0x07d7, + 0x03A8 : 0x07d8, + 0x03A9 : 0x07d9, + 0x03B1 : 0x07e1, + 0x03B2 : 0x07e2, + 0x03B3 : 0x07e3, + 0x03B4 : 0x07e4, + 0x03B5 : 0x07e5, + 0x03B6 : 0x07e6, + 0x03B7 : 0x07e7, + 0x03B8 : 0x07e8, + 0x03B9 : 0x07e9, + 0x03BA : 0x07ea, + 0x03BB : 0x07eb, + 0x03BC : 0x07ec, + 0x03BD : 0x07ed, + 0x03BE : 0x07ee, + 0x03BF : 0x07ef, + 0x03C0 : 0x07f0, + 0x03C1 : 0x07f1, + 0x03C3 : 0x07f2, + 0x03C2 : 0x07f3, + 0x03C4 : 0x07f4, + 0x03C5 : 0x07f5, + 0x03C6 : 0x07f6, + 0x03C7 : 0x07f7, + 0x03C8 : 0x07f8, + 0x03C9 : 0x07f9, + 0x23B7 : 0x08a1, + 0x2320 : 0x08a4, + 0x2321 : 0x08a5, + 0x23A1 : 0x08a7, + 0x23A3 : 0x08a8, + 0x23A4 : 0x08a9, + 0x23A6 : 0x08aa, + 0x239B : 0x08ab, + 0x239D : 0x08ac, + 0x239E : 0x08ad, + 0x23A0 : 0x08ae, + 0x23A8 : 0x08af, + 0x23AC : 0x08b0, + 0x2264 : 0x08bc, + 0x2260 : 0x08bd, + 0x2265 : 0x08be, + 0x222B : 0x08bf, + 0x2234 : 0x08c0, + 0x221D : 0x08c1, + 0x221E : 0x08c2, + 0x2207 : 0x08c5, + 0x223C : 0x08c8, + 0x2243 : 0x08c9, + 0x21D4 : 0x08cd, + 0x21D2 : 0x08ce, + 0x2261 : 0x08cf, + 0x221A : 0x08d6, + 0x2282 : 0x08da, + 0x2283 : 0x08db, + 0x2229 : 0x08dc, + 0x222A : 0x08dd, + 0x2227 : 0x08de, + 0x2228 : 0x08df, + 0x2202 : 0x08ef, + 0x0192 : 0x08f6, + 0x2190 : 0x08fb, + 0x2191 : 0x08fc, + 0x2192 : 0x08fd, + 0x2193 : 0x08fe, + 0x25C6 : 0x09e0, + 0x2592 : 0x09e1, + 0x2409 : 0x09e2, + 0x240C : 0x09e3, + 0x240D : 0x09e4, + 0x240A : 0x09e5, + 0x2424 : 0x09e8, + 0x240B : 0x09e9, + 0x2518 : 0x09ea, + 0x2510 : 0x09eb, + 0x250C : 0x09ec, + 0x2514 : 0x09ed, + 0x253C : 0x09ee, + 0x23BA : 0x09ef, + 0x23BB : 0x09f0, + 0x2500 : 0x09f1, + 0x23BC : 0x09f2, + 0x23BD : 0x09f3, + 0x251C : 0x09f4, + 0x2524 : 0x09f5, + 0x2534 : 0x09f6, + 0x252C : 0x09f7, + 0x2502 : 0x09f8, + 0x2003 : 0x0aa1, + 0x2002 : 0x0aa2, + 0x2004 : 0x0aa3, + 0x2005 : 0x0aa4, + 0x2007 : 0x0aa5, + 0x2008 : 0x0aa6, + 0x2009 : 0x0aa7, + 0x200A : 0x0aa8, + 0x2014 : 0x0aa9, + 0x2013 : 0x0aaa, + 0x2026 : 0x0aae, + 0x2025 : 0x0aaf, + 0x2153 : 0x0ab0, + 0x2154 : 0x0ab1, + 0x2155 : 0x0ab2, + 0x2156 : 0x0ab3, + 0x2157 : 0x0ab4, + 0x2158 : 0x0ab5, + 0x2159 : 0x0ab6, + 0x215A : 0x0ab7, + 0x2105 : 0x0ab8, + 0x2012 : 0x0abb, + 0x215B : 0x0ac3, + 0x215C : 0x0ac4, + 0x215D : 0x0ac5, + 0x215E : 0x0ac6, + 0x2122 : 0x0ac9, + 0x2018 : 0x0ad0, + 0x2019 : 0x0ad1, + 0x201C : 0x0ad2, + 0x201D : 0x0ad3, + 0x211E : 0x0ad4, + 0x2032 : 0x0ad6, + 0x2033 : 0x0ad7, + 0x271D : 0x0ad9, + 0x2663 : 0x0aec, + 0x2666 : 0x0aed, + 0x2665 : 0x0aee, + 0x2720 : 0x0af0, + 0x2020 : 0x0af1, + 0x2021 : 0x0af2, + 0x2713 : 0x0af3, + 0x2717 : 0x0af4, + 0x266F : 0x0af5, + 0x266D : 0x0af6, + 0x2642 : 0x0af7, + 0x2640 : 0x0af8, + 0x260E : 0x0af9, + 0x2315 : 0x0afa, + 0x2117 : 0x0afb, + 0x2038 : 0x0afc, + 0x201A : 0x0afd, + 0x201E : 0x0afe, + 0x22A4 : 0x0bc2, + 0x230A : 0x0bc4, + 0x2218 : 0x0bca, + 0x2395 : 0x0bcc, + 0x22A5 : 0x0bce, + 0x25CB : 0x0bcf, + 0x2308 : 0x0bd3, + 0x22A3 : 0x0bdc, + 0x22A2 : 0x0bfc, + 0x2017 : 0x0cdf, + 0x05D0 : 0x0ce0, + 0x05D1 : 0x0ce1, + 0x05D2 : 0x0ce2, + 0x05D3 : 0x0ce3, + 0x05D4 : 0x0ce4, + 0x05D5 : 0x0ce5, + 0x05D6 : 0x0ce6, + 0x05D7 : 0x0ce7, + 0x05D8 : 0x0ce8, + 0x05D9 : 0x0ce9, + 0x05DA : 0x0cea, + 0x05DB : 0x0ceb, + 0x05DC : 0x0cec, + 0x05DD : 0x0ced, + 0x05DE : 0x0cee, + 0x05DF : 0x0cef, + 0x05E0 : 0x0cf0, + 0x05E1 : 0x0cf1, + 0x05E2 : 0x0cf2, + 0x05E3 : 0x0cf3, + 0x05E4 : 0x0cf4, + 0x05E5 : 0x0cf5, + 0x05E6 : 0x0cf6, + 0x05E7 : 0x0cf7, + 0x05E8 : 0x0cf8, + 0x05E9 : 0x0cf9, + 0x05EA : 0x0cfa, + 0x0E01 : 0x0da1, + 0x0E02 : 0x0da2, + 0x0E03 : 0x0da3, + 0x0E04 : 0x0da4, + 0x0E05 : 0x0da5, + 0x0E06 : 0x0da6, + 0x0E07 : 0x0da7, + 0x0E08 : 0x0da8, + 0x0E09 : 0x0da9, + 0x0E0A : 0x0daa, + 0x0E0B : 0x0dab, + 0x0E0C : 0x0dac, + 0x0E0D : 0x0dad, + 0x0E0E : 0x0dae, + 0x0E0F : 0x0daf, + 0x0E10 : 0x0db0, + 0x0E11 : 0x0db1, + 0x0E12 : 0x0db2, + 0x0E13 : 0x0db3, + 0x0E14 : 0x0db4, + 0x0E15 : 0x0db5, + 0x0E16 : 0x0db6, + 0x0E17 : 0x0db7, + 0x0E18 : 0x0db8, + 0x0E19 : 0x0db9, + 0x0E1A : 0x0dba, + 0x0E1B : 0x0dbb, + 0x0E1C : 0x0dbc, + 0x0E1D : 0x0dbd, + 0x0E1E : 0x0dbe, + 0x0E1F : 0x0dbf, + 0x0E20 : 0x0dc0, + 0x0E21 : 0x0dc1, + 0x0E22 : 0x0dc2, + 0x0E23 : 0x0dc3, + 0x0E24 : 0x0dc4, + 0x0E25 : 0x0dc5, + 0x0E26 : 0x0dc6, + 0x0E27 : 0x0dc7, + 0x0E28 : 0x0dc8, + 0x0E29 : 0x0dc9, + 0x0E2A : 0x0dca, + 0x0E2B : 0x0dcb, + 0x0E2C : 0x0dcc, + 0x0E2D : 0x0dcd, + 0x0E2E : 0x0dce, + 0x0E2F : 0x0dcf, + 0x0E30 : 0x0dd0, + 0x0E31 : 0x0dd1, + 0x0E32 : 0x0dd2, + 0x0E33 : 0x0dd3, + 0x0E34 : 0x0dd4, + 0x0E35 : 0x0dd5, + 0x0E36 : 0x0dd6, + 0x0E37 : 0x0dd7, + 0x0E38 : 0x0dd8, + 0x0E39 : 0x0dd9, + 0x0E3A : 0x0dda, + 0x0E3F : 0x0ddf, + 0x0E40 : 0x0de0, + 0x0E41 : 0x0de1, + 0x0E42 : 0x0de2, + 0x0E43 : 0x0de3, + 0x0E44 : 0x0de4, + 0x0E45 : 0x0de5, + 0x0E46 : 0x0de6, + 0x0E47 : 0x0de7, + 0x0E48 : 0x0de8, + 0x0E49 : 0x0de9, + 0x0E4A : 0x0dea, + 0x0E4B : 0x0deb, + 0x0E4C : 0x0dec, + 0x0E4D : 0x0ded, + 0x0E50 : 0x0df0, + 0x0E51 : 0x0df1, + 0x0E52 : 0x0df2, + 0x0E53 : 0x0df3, + 0x0E54 : 0x0df4, + 0x0E55 : 0x0df5, + 0x0E56 : 0x0df6, + 0x0E57 : 0x0df7, + 0x0E58 : 0x0df8, + 0x0E59 : 0x0df9, + 0x0587 : 0x1000587, + 0x0589 : 0x1000589, + 0x055D : 0x100055d, + 0x058A : 0x100058a, + 0x055C : 0x100055c, + 0x055B : 0x100055b, + 0x055E : 0x100055e, + 0x0531 : 0x1000531, + 0x0561 : 0x1000561, + 0x0532 : 0x1000532, + 0x0562 : 0x1000562, + 0x0533 : 0x1000533, + 0x0563 : 0x1000563, + 0x0534 : 0x1000534, + 0x0564 : 0x1000564, + 0x0535 : 0x1000535, + 0x0565 : 0x1000565, + 0x0536 : 0x1000536, + 0x0566 : 0x1000566, + 0x0537 : 0x1000537, + 0x0567 : 0x1000567, + 0x0538 : 0x1000538, + 0x0568 : 0x1000568, + 0x0539 : 0x1000539, + 0x0569 : 0x1000569, + 0x053A : 0x100053a, + 0x056A : 0x100056a, + 0x053B : 0x100053b, + 0x056B : 0x100056b, + 0x053C : 0x100053c, + 0x056C : 0x100056c, + 0x053D : 0x100053d, + 0x056D : 0x100056d, + 0x053E : 0x100053e, + 0x056E : 0x100056e, + 0x053F : 0x100053f, + 0x056F : 0x100056f, + 0x0540 : 0x1000540, + 0x0570 : 0x1000570, + 0x0541 : 0x1000541, + 0x0571 : 0x1000571, + 0x0542 : 0x1000542, + 0x0572 : 0x1000572, + 0x0543 : 0x1000543, + 0x0573 : 0x1000573, + 0x0544 : 0x1000544, + 0x0574 : 0x1000574, + 0x0545 : 0x1000545, + 0x0575 : 0x1000575, + 0x0546 : 0x1000546, + 0x0576 : 0x1000576, + 0x0547 : 0x1000547, + 0x0577 : 0x1000577, + 0x0548 : 0x1000548, + 0x0578 : 0x1000578, + 0x0549 : 0x1000549, + 0x0579 : 0x1000579, + 0x054A : 0x100054a, + 0x057A : 0x100057a, + 0x054B : 0x100054b, + 0x057B : 0x100057b, + 0x054C : 0x100054c, + 0x057C : 0x100057c, + 0x054D : 0x100054d, + 0x057D : 0x100057d, + 0x054E : 0x100054e, + 0x057E : 0x100057e, + 0x054F : 0x100054f, + 0x057F : 0x100057f, + 0x0550 : 0x1000550, + 0x0580 : 0x1000580, + 0x0551 : 0x1000551, + 0x0581 : 0x1000581, + 0x0552 : 0x1000552, + 0x0582 : 0x1000582, + 0x0553 : 0x1000553, + 0x0583 : 0x1000583, + 0x0554 : 0x1000554, + 0x0584 : 0x1000584, + 0x0555 : 0x1000555, + 0x0585 : 0x1000585, + 0x0556 : 0x1000556, + 0x0586 : 0x1000586, + 0x055A : 0x100055a, + 0x10D0 : 0x10010d0, + 0x10D1 : 0x10010d1, + 0x10D2 : 0x10010d2, + 0x10D3 : 0x10010d3, + 0x10D4 : 0x10010d4, + 0x10D5 : 0x10010d5, + 0x10D6 : 0x10010d6, + 0x10D7 : 0x10010d7, + 0x10D8 : 0x10010d8, + 0x10D9 : 0x10010d9, + 0x10DA : 0x10010da, + 0x10DB : 0x10010db, + 0x10DC : 0x10010dc, + 0x10DD : 0x10010dd, + 0x10DE : 0x10010de, + 0x10DF : 0x10010df, + 0x10E0 : 0x10010e0, + 0x10E1 : 0x10010e1, + 0x10E2 : 0x10010e2, + 0x10E3 : 0x10010e3, + 0x10E4 : 0x10010e4, + 0x10E5 : 0x10010e5, + 0x10E6 : 0x10010e6, + 0x10E7 : 0x10010e7, + 0x10E8 : 0x10010e8, + 0x10E9 : 0x10010e9, + 0x10EA : 0x10010ea, + 0x10EB : 0x10010eb, + 0x10EC : 0x10010ec, + 0x10ED : 0x10010ed, + 0x10EE : 0x10010ee, + 0x10EF : 0x10010ef, + 0x10F0 : 0x10010f0, + 0x10F1 : 0x10010f1, + 0x10F2 : 0x10010f2, + 0x10F3 : 0x10010f3, + 0x10F4 : 0x10010f4, + 0x10F5 : 0x10010f5, + 0x10F6 : 0x10010f6, + 0x1E8A : 0x1001e8a, + 0x012C : 0x100012c, + 0x01B5 : 0x10001b5, + 0x01E6 : 0x10001e6, + 0x01D2 : 0x10001d1, + 0x019F : 0x100019f, + 0x1E8B : 0x1001e8b, + 0x012D : 0x100012d, + 0x01B6 : 0x10001b6, + 0x01E7 : 0x10001e7, + 0x01D2 : 0x10001d2, + 0x0275 : 0x1000275, + 0x018F : 0x100018f, + 0x0259 : 0x1000259, + 0x1E36 : 0x1001e36, + 0x1E37 : 0x1001e37, + 0x1EA0 : 0x1001ea0, + 0x1EA1 : 0x1001ea1, + 0x1EA2 : 0x1001ea2, + 0x1EA3 : 0x1001ea3, + 0x1EA4 : 0x1001ea4, + 0x1EA5 : 0x1001ea5, + 0x1EA6 : 0x1001ea6, + 0x1EA7 : 0x1001ea7, + 0x1EA8 : 0x1001ea8, + 0x1EA9 : 0x1001ea9, + 0x1EAA : 0x1001eaa, + 0x1EAB : 0x1001eab, + 0x1EAC : 0x1001eac, + 0x1EAD : 0x1001ead, + 0x1EAE : 0x1001eae, + 0x1EAF : 0x1001eaf, + 0x1EB0 : 0x1001eb0, + 0x1EB1 : 0x1001eb1, + 0x1EB2 : 0x1001eb2, + 0x1EB3 : 0x1001eb3, + 0x1EB4 : 0x1001eb4, + 0x1EB5 : 0x1001eb5, + 0x1EB6 : 0x1001eb6, + 0x1EB7 : 0x1001eb7, + 0x1EB8 : 0x1001eb8, + 0x1EB9 : 0x1001eb9, + 0x1EBA : 0x1001eba, + 0x1EBB : 0x1001ebb, + 0x1EBC : 0x1001ebc, + 0x1EBD : 0x1001ebd, + 0x1EBE : 0x1001ebe, + 0x1EBF : 0x1001ebf, + 0x1EC0 : 0x1001ec0, + 0x1EC1 : 0x1001ec1, + 0x1EC2 : 0x1001ec2, + 0x1EC3 : 0x1001ec3, + 0x1EC4 : 0x1001ec4, + 0x1EC5 : 0x1001ec5, + 0x1EC6 : 0x1001ec6, + 0x1EC7 : 0x1001ec7, + 0x1EC8 : 0x1001ec8, + 0x1EC9 : 0x1001ec9, + 0x1ECA : 0x1001eca, + 0x1ECB : 0x1001ecb, + 0x1ECC : 0x1001ecc, + 0x1ECD : 0x1001ecd, + 0x1ECE : 0x1001ece, + 0x1ECF : 0x1001ecf, + 0x1ED0 : 0x1001ed0, + 0x1ED1 : 0x1001ed1, + 0x1ED2 : 0x1001ed2, + 0x1ED3 : 0x1001ed3, + 0x1ED4 : 0x1001ed4, + 0x1ED5 : 0x1001ed5, + 0x1ED6 : 0x1001ed6, + 0x1ED7 : 0x1001ed7, + 0x1ED8 : 0x1001ed8, + 0x1ED9 : 0x1001ed9, + 0x1EDA : 0x1001eda, + 0x1EDB : 0x1001edb, + 0x1EDC : 0x1001edc, + 0x1EDD : 0x1001edd, + 0x1EDE : 0x1001ede, + 0x1EDF : 0x1001edf, + 0x1EE0 : 0x1001ee0, + 0x1EE1 : 0x1001ee1, + 0x1EE2 : 0x1001ee2, + 0x1EE3 : 0x1001ee3, + 0x1EE4 : 0x1001ee4, + 0x1EE5 : 0x1001ee5, + 0x1EE6 : 0x1001ee6, + 0x1EE7 : 0x1001ee7, + 0x1EE8 : 0x1001ee8, + 0x1EE9 : 0x1001ee9, + 0x1EEA : 0x1001eea, + 0x1EEB : 0x1001eeb, + 0x1EEC : 0x1001eec, + 0x1EED : 0x1001eed, + 0x1EEE : 0x1001eee, + 0x1EEF : 0x1001eef, + 0x1EF0 : 0x1001ef0, + 0x1EF1 : 0x1001ef1, + 0x1EF4 : 0x1001ef4, + 0x1EF5 : 0x1001ef5, + 0x1EF6 : 0x1001ef6, + 0x1EF7 : 0x1001ef7, + 0x1EF8 : 0x1001ef8, + 0x1EF9 : 0x1001ef9, + 0x01A0 : 0x10001a0, + 0x01A1 : 0x10001a1, + 0x01AF : 0x10001af, + 0x01B0 : 0x10001b0, + 0x20A0 : 0x10020a0, + 0x20A1 : 0x10020a1, + 0x20A2 : 0x10020a2, + 0x20A3 : 0x10020a3, + 0x20A4 : 0x10020a4, + 0x20A5 : 0x10020a5, + 0x20A6 : 0x10020a6, + 0x20A7 : 0x10020a7, + 0x20A8 : 0x10020a8, + 0x20A9 : 0x10020a9, + 0x20AA : 0x10020aa, + 0x20AB : 0x10020ab, + 0x20AC : 0x20ac, + 0x2070 : 0x1002070, + 0x2074 : 0x1002074, + 0x2075 : 0x1002075, + 0x2076 : 0x1002076, + 0x2077 : 0x1002077, + 0x2078 : 0x1002078, + 0x2079 : 0x1002079, + 0x2080 : 0x1002080, + 0x2081 : 0x1002081, + 0x2082 : 0x1002082, + 0x2083 : 0x1002083, + 0x2084 : 0x1002084, + 0x2085 : 0x1002085, + 0x2086 : 0x1002086, + 0x2087 : 0x1002087, + 0x2088 : 0x1002088, + 0x2089 : 0x1002089, + 0x2202 : 0x1002202, + 0x2205 : 0x1002205, + 0x2208 : 0x1002208, + 0x2209 : 0x1002209, + 0x220B : 0x100220B, + 0x221A : 0x100221A, + 0x221B : 0x100221B, + 0x221C : 0x100221C, + 0x222C : 0x100222C, + 0x222D : 0x100222D, + 0x2235 : 0x1002235, + 0x2245 : 0x1002248, + 0x2247 : 0x1002247, + 0x2262 : 0x1002262, + 0x2263 : 0x1002263, + 0x2800 : 0x1002800, + 0x2801 : 0x1002801, + 0x2802 : 0x1002802, + 0x2803 : 0x1002803, + 0x2804 : 0x1002804, + 0x2805 : 0x1002805, + 0x2806 : 0x1002806, + 0x2807 : 0x1002807, + 0x2808 : 0x1002808, + 0x2809 : 0x1002809, + 0x280a : 0x100280a, + 0x280b : 0x100280b, + 0x280c : 0x100280c, + 0x280d : 0x100280d, + 0x280e : 0x100280e, + 0x280f : 0x100280f, + 0x2810 : 0x1002810, + 0x2811 : 0x1002811, + 0x2812 : 0x1002812, + 0x2813 : 0x1002813, + 0x2814 : 0x1002814, + 0x2815 : 0x1002815, + 0x2816 : 0x1002816, + 0x2817 : 0x1002817, + 0x2818 : 0x1002818, + 0x2819 : 0x1002819, + 0x281a : 0x100281a, + 0x281b : 0x100281b, + 0x281c : 0x100281c, + 0x281d : 0x100281d, + 0x281e : 0x100281e, + 0x281f : 0x100281f, + 0x2820 : 0x1002820, + 0x2821 : 0x1002821, + 0x2822 : 0x1002822, + 0x2823 : 0x1002823, + 0x2824 : 0x1002824, + 0x2825 : 0x1002825, + 0x2826 : 0x1002826, + 0x2827 : 0x1002827, + 0x2828 : 0x1002828, + 0x2829 : 0x1002829, + 0x282a : 0x100282a, + 0x282b : 0x100282b, + 0x282c : 0x100282c, + 0x282d : 0x100282d, + 0x282e : 0x100282e, + 0x282f : 0x100282f, + 0x2830 : 0x1002830, + 0x2831 : 0x1002831, + 0x2832 : 0x1002832, + 0x2833 : 0x1002833, + 0x2834 : 0x1002834, + 0x2835 : 0x1002835, + 0x2836 : 0x1002836, + 0x2837 : 0x1002837, + 0x2838 : 0x1002838, + 0x2839 : 0x1002839, + 0x283a : 0x100283a, + 0x283b : 0x100283b, + 0x283c : 0x100283c, + 0x283d : 0x100283d, + 0x283e : 0x100283e, + 0x283f : 0x100283f, + 0x2840 : 0x1002840, + 0x2841 : 0x1002841, + 0x2842 : 0x1002842, + 0x2843 : 0x1002843, + 0x2844 : 0x1002844, + 0x2845 : 0x1002845, + 0x2846 : 0x1002846, + 0x2847 : 0x1002847, + 0x2848 : 0x1002848, + 0x2849 : 0x1002849, + 0x284a : 0x100284a, + 0x284b : 0x100284b, + 0x284c : 0x100284c, + 0x284d : 0x100284d, + 0x284e : 0x100284e, + 0x284f : 0x100284f, + 0x2850 : 0x1002850, + 0x2851 : 0x1002851, + 0x2852 : 0x1002852, + 0x2853 : 0x1002853, + 0x2854 : 0x1002854, + 0x2855 : 0x1002855, + 0x2856 : 0x1002856, + 0x2857 : 0x1002857, + 0x2858 : 0x1002858, + 0x2859 : 0x1002859, + 0x285a : 0x100285a, + 0x285b : 0x100285b, + 0x285c : 0x100285c, + 0x285d : 0x100285d, + 0x285e : 0x100285e, + 0x285f : 0x100285f, + 0x2860 : 0x1002860, + 0x2861 : 0x1002861, + 0x2862 : 0x1002862, + 0x2863 : 0x1002863, + 0x2864 : 0x1002864, + 0x2865 : 0x1002865, + 0x2866 : 0x1002866, + 0x2867 : 0x1002867, + 0x2868 : 0x1002868, + 0x2869 : 0x1002869, + 0x286a : 0x100286a, + 0x286b : 0x100286b, + 0x286c : 0x100286c, + 0x286d : 0x100286d, + 0x286e : 0x100286e, + 0x286f : 0x100286f, + 0x2870 : 0x1002870, + 0x2871 : 0x1002871, + 0x2872 : 0x1002872, + 0x2873 : 0x1002873, + 0x2874 : 0x1002874, + 0x2875 : 0x1002875, + 0x2876 : 0x1002876, + 0x2877 : 0x1002877, + 0x2878 : 0x1002878, + 0x2879 : 0x1002879, + 0x287a : 0x100287a, + 0x287b : 0x100287b, + 0x287c : 0x100287c, + 0x287d : 0x100287d, + 0x287e : 0x100287e, + 0x287f : 0x100287f, + 0x2880 : 0x1002880, + 0x2881 : 0x1002881, + 0x2882 : 0x1002882, + 0x2883 : 0x1002883, + 0x2884 : 0x1002884, + 0x2885 : 0x1002885, + 0x2886 : 0x1002886, + 0x2887 : 0x1002887, + 0x2888 : 0x1002888, + 0x2889 : 0x1002889, + 0x288a : 0x100288a, + 0x288b : 0x100288b, + 0x288c : 0x100288c, + 0x288d : 0x100288d, + 0x288e : 0x100288e, + 0x288f : 0x100288f, + 0x2890 : 0x1002890, + 0x2891 : 0x1002891, + 0x2892 : 0x1002892, + 0x2893 : 0x1002893, + 0x2894 : 0x1002894, + 0x2895 : 0x1002895, + 0x2896 : 0x1002896, + 0x2897 : 0x1002897, + 0x2898 : 0x1002898, + 0x2899 : 0x1002899, + 0x289a : 0x100289a, + 0x289b : 0x100289b, + 0x289c : 0x100289c, + 0x289d : 0x100289d, + 0x289e : 0x100289e, + 0x289f : 0x100289f, + 0x28a0 : 0x10028a0, + 0x28a1 : 0x10028a1, + 0x28a2 : 0x10028a2, + 0x28a3 : 0x10028a3, + 0x28a4 : 0x10028a4, + 0x28a5 : 0x10028a5, + 0x28a6 : 0x10028a6, + 0x28a7 : 0x10028a7, + 0x28a8 : 0x10028a8, + 0x28a9 : 0x10028a9, + 0x28aa : 0x10028aa, + 0x28ab : 0x10028ab, + 0x28ac : 0x10028ac, + 0x28ad : 0x10028ad, + 0x28ae : 0x10028ae, + 0x28af : 0x10028af, + 0x28b0 : 0x10028b0, + 0x28b1 : 0x10028b1, + 0x28b2 : 0x10028b2, + 0x28b3 : 0x10028b3, + 0x28b4 : 0x10028b4, + 0x28b5 : 0x10028b5, + 0x28b6 : 0x10028b6, + 0x28b7 : 0x10028b7, + 0x28b8 : 0x10028b8, + 0x28b9 : 0x10028b9, + 0x28ba : 0x10028ba, + 0x28bb : 0x10028bb, + 0x28bc : 0x10028bc, + 0x28bd : 0x10028bd, + 0x28be : 0x10028be, + 0x28bf : 0x10028bf, + 0x28c0 : 0x10028c0, + 0x28c1 : 0x10028c1, + 0x28c2 : 0x10028c2, + 0x28c3 : 0x10028c3, + 0x28c4 : 0x10028c4, + 0x28c5 : 0x10028c5, + 0x28c6 : 0x10028c6, + 0x28c7 : 0x10028c7, + 0x28c8 : 0x10028c8, + 0x28c9 : 0x10028c9, + 0x28ca : 0x10028ca, + 0x28cb : 0x10028cb, + 0x28cc : 0x10028cc, + 0x28cd : 0x10028cd, + 0x28ce : 0x10028ce, + 0x28cf : 0x10028cf, + 0x28d0 : 0x10028d0, + 0x28d1 : 0x10028d1, + 0x28d2 : 0x10028d2, + 0x28d3 : 0x10028d3, + 0x28d4 : 0x10028d4, + 0x28d5 : 0x10028d5, + 0x28d6 : 0x10028d6, + 0x28d7 : 0x10028d7, + 0x28d8 : 0x10028d8, + 0x28d9 : 0x10028d9, + 0x28da : 0x10028da, + 0x28db : 0x10028db, + 0x28dc : 0x10028dc, + 0x28dd : 0x10028dd, + 0x28de : 0x10028de, + 0x28df : 0x10028df, + 0x28e0 : 0x10028e0, + 0x28e1 : 0x10028e1, + 0x28e2 : 0x10028e2, + 0x28e3 : 0x10028e3, + 0x28e4 : 0x10028e4, + 0x28e5 : 0x10028e5, + 0x28e6 : 0x10028e6, + 0x28e7 : 0x10028e7, + 0x28e8 : 0x10028e8, + 0x28e9 : 0x10028e9, + 0x28ea : 0x10028ea, + 0x28eb : 0x10028eb, + 0x28ec : 0x10028ec, + 0x28ed : 0x10028ed, + 0x28ee : 0x10028ee, + 0x28ef : 0x10028ef, + 0x28f0 : 0x10028f0, + 0x28f1 : 0x10028f1, + 0x28f2 : 0x10028f2, + 0x28f3 : 0x10028f3, + 0x28f4 : 0x10028f4, + 0x28f5 : 0x10028f5, + 0x28f6 : 0x10028f6, + 0x28f7 : 0x10028f7, + 0x28f8 : 0x10028f8, + 0x28f9 : 0x10028f9, + 0x28fa : 0x10028fa, + 0x28fb : 0x10028fb, + 0x28fc : 0x10028fc, + 0x28fd : 0x10028fd, + 0x28fe : 0x10028fe, + 0x28ff : 0x10028ff +}; \ No newline at end of file diff --git a/webclients/novnc/include/logo.js b/webclients/novnc/include/logo.js new file mode 100644 index 0000000..befa598 --- /dev/null +++ b/webclients/novnc/include/logo.js @@ -0,0 +1 @@ +noVNC_logo = {"width": 640, "height": 435, "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAGzCAYAAAC/y6a9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAStAAAErQBBHTWggAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAACAASURBVHic7N13fBvlwQfw3522ZMm2vPdIGCFkA4GyoYyGsCmjk+7dQksHL2/H2/dtC4W2tLTlfelu2VA2lEILFCgQIHEGJCQkdjzkLdmWZGvfvX8oOkmJEy/pNO73/Xz44DtLzz2RT7qfnnXC8uXLZUxDlqfdnUYQhIP+bjbPn+5xhypzrmUf6rGzOc5cjzVduXN9/nTPyfRrMt/jzOcY05U5n3L2f95s/34LPW4m/p6FbLp/73xe+5nKnWuZs/07ZOOcnusx5nucbJU727LneuxslDmdTBxn/2NmusyEuZS7kHMxG/XP5Gf3TOVmQzY+u/PhPMnkMcSsH5WIiIiI8goDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHGMAASERERaQwDIBEREZHG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"}; diff --git a/webclients/novnc/include/playback.js b/webclients/novnc/include/playback.js new file mode 100644 index 0000000..22a00a3 --- /dev/null +++ b/webclients/novnc/include/playback.js @@ -0,0 +1,90 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.LGPL-3) + */ + +"use strict"; +/*jslint browser: true, white: false */ +/*global Util, VNC_frame_data, finish */ + +var rfb, mode, test_state, frame_idx, frame_length, + iteration, iterations, istart_time, + + // Pre-declarations for jslint + send_array, next_iteration, queue_next_packet, do_packet; + +// Override send_array +send_array = function (arr) { + // Stub out send_array +}; + +next_iteration = function () { + if (iteration === 0) { + frame_length = VNC_frame_data.length; + test_state = 'running'; + } else { + rfb.disconnect(); + } + + if (test_state !== 'running') { return; } + + iteration += 1; + if (iteration > iterations) { + finish(); + return; + } + + frame_idx = 0; + istart_time = (new Date()).getTime(); + rfb.connect('test', 0, "bogus"); + + queue_next_packet(); + +}; + +queue_next_packet = function () { + var frame, foffset, toffset, delay; + if (test_state !== 'running') { return; } + + frame = VNC_frame_data[frame_idx]; + while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) { + //Util.Debug("Send frame " + frame_idx); + frame_idx += 1; + frame = VNC_frame_data[frame_idx]; + } + + if (frame === 'EOF') { + Util.Debug("Finished, found EOF"); + next_iteration(); + return; + } + if (frame_idx >= frame_length) { + Util.Debug("Finished, no more frames"); + next_iteration(); + return; + } + + if (mode === 'realtime') { + foffset = frame.slice(1, frame.indexOf('{', 1)); + toffset = (new Date()).getTime() - istart_time; + delay = foffset - toffset; + if (delay < 1) { + delay = 1; + } + + setTimeout(do_packet, delay); + } else { + setTimeout(do_packet, 1); + } +}; + +do_packet = function () { + //Util.Debug("Processing frame: " + frame_idx); + var frame = VNC_frame_data[frame_idx]; + rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)}); + frame_idx += 1; + + queue_next_packet(); +}; + diff --git a/webclients/novnc/include/rfb.js b/webclients/novnc/include/rfb.js new file mode 100644 index 0000000..b7aa3f6 --- /dev/null +++ b/webclients/novnc/include/rfb.js @@ -0,0 +1,1613 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint white: false, browser: true, bitwise: false, plusplus: false */ +/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */ + + +function RFB(defaults) { +"use strict"; + +var that = {}, // Public API methods + conf = {}, // Configuration attributes + + // Pre-declare private functions used before definitions (jslint) + init_vars, updateState, fail, handle_message, + init_msg, normal_msg, framebufferUpdate, print_stats, + + pixelFormat, clientEncodings, fbUpdateRequest, fbUpdateRequests, + keyEvent, pointerEvent, clientCutText, + + extract_data_uri, scan_tight_imgQ, + keyPress, mouseButton, mouseMove, + + checkEvents, // Overridable for testing + + + // + // Private RFB namespace variables + // + rfb_host = '', + rfb_port = 5900, + rfb_password = '', + rfb_path = '', + + rfb_state = 'disconnected', + rfb_version = 0, + rfb_max_version= 3.8, + rfb_auth_scheme= '', + + + // In preference order + encodings = [ + ['COPYRECT', 0x01 ], + ['TIGHT_PNG', -260 ], + ['HEXTILE', 0x05 ], + ['RRE', 0x02 ], + ['RAW', 0x00 ], + ['DesktopSize', -223 ], + ['Cursor', -239 ], + + // Psuedo-encoding settings + ['JPEG_quality_lo', -32 ], + //['JPEG_quality_hi', -23 ], + ['compress_lo', -255 ] + //['compress_hi', -247 ] + ], + + encHandlers = {}, + encNames = {}, + encStats = {}, // [rectCnt, rectCntTot] + + ws = null, // Websock object + display = null, // Display object + keyboard = null, // Keyboard input handler object + mouse = null, // Mouse input handler object + sendTimer = null, // Send Queue check timer + connTimer = null, // connection timer + disconnTimer = null, // disconnection timer + msgTimer = null, // queued handle_message timer + + // Frame buffer update state + FBU = { + rects : 0, + subrects : 0, // RRE + lines : 0, // RAW + tiles : 0, // HEXTILE + bytes : 0, + x : 0, + y : 0, + width : 0, + height : 0, + encoding : 0, + subencoding : -1, + background : null, + imgQ : [] // TIGHT_PNG image queue + }, + + fb_Bpp = 4, + fb_depth = 3, + fb_width = 0, + fb_height = 0, + fb_name = "", + + scan_imgQ_rate = 40, // 25 times per second or so + last_req_time = 0, + rre_chunk_sz = 100, + + timing = { + last_fbu : 0, + fbu_total : 0, + fbu_total_cnt : 0, + full_fbu_total : 0, + full_fbu_cnt : 0, + + fbu_rt_start : 0, + fbu_rt_total : 0, + fbu_rt_cnt : 0 + }, + + test_mode = false, + + def_con_timeout = Websock_native ? 2 : 5, + + /* Mouse state */ + mouse_buttonMask = 0, + mouse_arr = [], + viewportDragging = false, + viewportDragPos = {}; + +// Configuration attributes +Util.conf_defaults(conf, that, defaults, [ + ['target', 'wo', 'dom', null, 'VNC display rendering Canvas object'], + ['focusContainer', 'wo', 'dom', document, 'DOM element that captures keyboard input'], + + ['encrypt', 'rw', 'bool', false, 'Use TLS/SSL/wss encryption'], + ['true_color', 'rw', 'bool', true, 'Request true color pixel data'], + ['local_cursor', 'rw', 'bool', false, 'Request locally rendered cursor'], + ['shared', 'rw', 'bool', true, 'Request shared mode'], + + ['connectTimeout', 'rw', 'int', def_con_timeout, 'Time (s) to wait for connection'], + ['disconnectTimeout', 'rw', 'int', 3, 'Time (s) to wait for disconnection'], + + ['viewportDrag', 'rw', 'bool', false, 'Move the viewport on mouse drags'], + + ['check_rate', 'rw', 'int', 217, 'Timing (ms) of send/receive check'], + ['fbu_req_rate', 'rw', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'], + + // Callback functions + ['onUpdateState', 'rw', 'func', function() { }, + 'onUpdateState(rfb, state, oldstate, statusMsg): RFB state update/change '], + ['onPasswordRequired', 'rw', 'func', function() { }, + 'onPasswordRequired(rfb): VNC password is required '], + ['onClipboard', 'rw', 'func', function() { }, + 'onClipboard(rfb, text): RFB clipboard contents received'], + ['onBell', 'rw', 'func', function() { }, + 'onBell(rfb): RFB Bell message received '], + ['onFBUReceive', 'rw', 'func', function() { }, + 'onFBUReceive(rfb, fbu): RFB FBU received but not yet processed '], + ['onFBUComplete', 'rw', 'func', function() { }, + 'onFBUComplete(rfb, fbu): RFB FBU received and processed '], + + // These callback names are deprecated + ['updateState', 'rw', 'func', function() { }, + 'obsolete, use onUpdateState'], + ['clipboardReceive', 'rw', 'func', function() { }, + 'obsolete, use onClipboard'] + ]); + + +// Override/add some specific configuration getters/setters +that.set_local_cursor = function(cursor) { + if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) { + conf.local_cursor = false; + } else { + if (display.get_cursor_uri()) { + conf.local_cursor = true; + } else { + Util.Warn("Browser does not support local cursor"); + } + } +}; + +// These are fake configuration getters +that.get_display = function() { return display; }; + +that.get_keyboard = function() { return keyboard; }; + +that.get_mouse = function() { return mouse; }; + + + +// +// Setup routines +// + +// Create the public API interface and initialize values that stay +// constant across connect/disconnect +function constructor() { + var i, rmode; + Util.Debug(">> RFB.constructor"); + + // Create lookup tables based encoding number + for (i=0; i < encodings.length; i+=1) { + encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]]; + encNames[encodings[i][1]] = encodings[i][0]; + encStats[encodings[i][1]] = [0, 0]; + } + // Initialize display, mouse, keyboard, and websock + try { + display = new Display({'target': conf.target}); + } catch (exc) { + Util.Error("Display exception: " + exc); + updateState('fatal', "No working Display"); + } + keyboard = new Keyboard({'target': conf.focusContainer, + 'onKeyPress': keyPress}); + mouse = new Mouse({'target': conf.target, + 'onMouseButton': mouseButton, + 'onMouseMove': mouseMove}); + + rmode = display.get_render_mode(); + + ws = new Websock(); + ws.on('message', handle_message); + ws.on('open', function() { + if (rfb_state === "connect") { + updateState('ProtocolVersion', "Starting VNC handshake"); + } else { + fail("Got unexpected WebSockets connection"); + } + }); + ws.on('close', function() { + if (rfb_state === 'disconnect') { + updateState('disconnected', 'VNC disconnected'); + } else if (rfb_state === 'ProtocolVersion') { + fail('Failed to connect to server'); + } else if (rfb_state in {'failed':1, 'disconnected':1}) { + Util.Error("Received onclose while disconnected"); + } else { + fail('Server disconnected'); + } + }); + ws.on('error', function(e) { + fail("WebSock error: " + e); + }); + + + init_vars(); + + /* Check web-socket-js if no builtin WebSocket support */ + if (Websock_native) { + Util.Info("Using native WebSockets"); + updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode); + } else { + Util.Warn("Using web-socket-js bridge. Flash version: " + + Util.Flash.version); + if ((! Util.Flash) || + (Util.Flash.version < 9)) { + updateState('fatal', "WebSockets or Adobe Flash<\/a> is required"); + } else if (document.location.href.substr(0, 7) === "file://") { + updateState('fatal', + "'file://' URL is incompatible with Adobe Flash"); + } else { + updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode); + } + } + + Util.Debug("<< RFB.constructor"); + return that; // Return the public API interface +} + +function connect() { + Util.Debug(">> RFB.connect"); + + var uri = ""; + if (conf.encrypt) { + uri = "wss://"; + } else { + uri = "ws://"; + } + uri += rfb_host + ":" + rfb_port + "/" + rfb_path; + Util.Info("connecting to " + uri); + ws.open(uri); + + Util.Debug("<< RFB.connect"); +} + +// Initialize variables that are reset before each connection +init_vars = function() { + var i; + + /* Reset state */ + ws.init(); + + FBU.rects = 0; + FBU.subrects = 0; // RRE and HEXTILE + FBU.lines = 0; // RAW + FBU.tiles = 0; // HEXTILE + FBU.imgQ = []; // TIGHT_PNG image queue + mouse_buttonMask = 0; + mouse_arr = []; + + // Clear the per connection encoding stats + for (i=0; i < encodings.length; i+=1) { + encStats[encodings[i][1]][0] = 0; + } +}; + +// Print statistics +print_stats = function() { + var i, s; + Util.Info("Encoding stats for this connection:"); + for (i=0; i < encodings.length; i+=1) { + s = encStats[encodings[i][1]]; + if ((s[0] + s[1]) > 0) { + Util.Info(" " + encodings[i][0] + ": " + + s[0] + " rects"); + } + } + Util.Info("Encoding stats since page load:"); + for (i=0; i < encodings.length; i+=1) { + s = encStats[encodings[i][1]]; + if ((s[0] + s[1]) > 0) { + Util.Info(" " + encodings[i][0] + ": " + + s[1] + " rects"); + } + } +}; + +// +// Utility routines +// + + +/* + * Page states: + * loaded - page load, equivalent to disconnected + * disconnected - idle state + * connect - starting to connect (to ProtocolVersion) + * normal - connected + * disconnect - starting to disconnect + * failed - abnormal disconnect + * fatal - failed to load page, or fatal error + * + * RFB protocol initialization states: + * ProtocolVersion + * Security + * Authentication + * password - waiting for password, not part of RFB + * SecurityResult + * ClientInitialization - not triggered by server message + * ServerInitialization (to normal) + */ +updateState = function(state, statusMsg) { + var func, cmsg, oldstate = rfb_state; + + if (state === oldstate) { + /* Already here, ignore */ + Util.Debug("Already in state '" + state + "', ignoring."); + return; + } + + /* + * These are disconnected states. A previous connect may + * asynchronously cause a connection so make sure we are closed. + */ + if (state in {'disconnected':1, 'loaded':1, 'connect':1, + 'disconnect':1, 'failed':1, 'fatal':1}) { + if (sendTimer) { + clearInterval(sendTimer); + sendTimer = null; + } + + if (msgTimer) { + clearInterval(msgTimer); + msgTimer = null; + } + + if (display && display.get_context()) { + keyboard.ungrab(); + mouse.ungrab(); + display.defaultCursor(); + if ((Util.get_logging() !== 'debug') || + (state === 'loaded')) { + // Show noVNC logo on load and when disconnected if + // debug is off + display.clear(); + } + } + + ws.close(); + } + + if (oldstate === 'fatal') { + Util.Error("Fatal error, cannot continue"); + } + + if ((state === 'failed') || (state === 'fatal')) { + func = Util.Error; + } else { + func = Util.Warn; + } + + if ((oldstate === 'failed') && (state === 'disconnected')) { + // Do disconnect action, but stay in failed state. + rfb_state = 'failed'; + } else { + rfb_state = state; + } + + cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : ""; + func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg); + + if (connTimer && (rfb_state !== 'connect')) { + Util.Debug("Clearing connect timer"); + clearInterval(connTimer); + connTimer = null; + } + + if (disconnTimer && (rfb_state !== 'disconnect')) { + Util.Debug("Clearing disconnect timer"); + clearInterval(disconnTimer); + disconnTimer = null; + } + + switch (state) { + case 'normal': + if ((oldstate === 'disconnected') || (oldstate === 'failed')) { + Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'"); + } + + break; + + + case 'connect': + + connTimer = setTimeout(function () { + fail("Connect timeout"); + }, conf.connectTimeout * 1000); + + init_vars(); + connect(); + + // WebSocket.onopen transitions to 'ProtocolVersion' + break; + + + case 'disconnect': + + if (! test_mode) { + disconnTimer = setTimeout(function () { + fail("Disconnect timeout"); + }, conf.disconnectTimeout * 1000); + } + + print_stats(); + + // WebSocket.onclose transitions to 'disconnected' + break; + + + case 'failed': + if (oldstate === 'disconnected') { + Util.Error("Invalid transition from 'disconnected' to 'failed'"); + } + if (oldstate === 'normal') { + Util.Error("Error while connected."); + } + if (oldstate === 'init') { + Util.Error("Error while initializing."); + } + + // Make sure we transition to disconnected + setTimeout(function() { updateState('disconnected'); }, 50); + + break; + + + default: + // No state change action to take + + } + + if ((oldstate === 'failed') && (state === 'disconnected')) { + // Leave the failed message + conf.updateState(that, state, oldstate); // Obsolete + conf.onUpdateState(that, state, oldstate); + } else { + conf.updateState(that, state, oldstate, statusMsg); // Obsolete + conf.onUpdateState(that, state, oldstate, statusMsg); + } +}; + +fail = function(msg) { + updateState('failed', msg); + return false; +}; + +handle_message = function() { + //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen()); + //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + if (ws.rQlen() === 0) { + Util.Warn("handle_message called on empty receive queue"); + return; + } + switch (rfb_state) { + case 'disconnected': + case 'failed': + Util.Error("Got data while disconnected"); + break; + case 'normal': + if (normal_msg() && ws.rQlen() > 0) { + // true means we can continue processing + // Give other events a chance to run + if (msgTimer === null) { + Util.Debug("More data to process, creating timer"); + msgTimer = setTimeout(function () { + msgTimer = null; + handle_message(); + }, 10); + } else { + Util.Debug("More data to process, existing timer"); + } + } + break; + default: + init_msg(); + break; + } +}; + + +function genDES(password, challenge) { + var i, passwd = []; + for (i=0; i < password.length; i += 1) { + passwd.push(password.charCodeAt(i)); + } + return (new DES(passwd)).encrypt(challenge); +} + +function flushClient() { + if (mouse_arr.length > 0) { + //send(mouse_arr.concat(fbUpdateRequests())); + ws.send(mouse_arr); + setTimeout(function() { + ws.send(fbUpdateRequests()); + }, 50); + + mouse_arr = []; + return true; + } else { + return false; + } +} + +// overridable for testing +checkEvents = function() { + var now; + if (rfb_state === 'normal' && !viewportDragging) { + if (! flushClient()) { + now = new Date().getTime(); + if (now > last_req_time + conf.fbu_req_rate) { + last_req_time = now; + ws.send(fbUpdateRequests()); + } + } + } + setTimeout(checkEvents, conf.check_rate); +}; + +keyPress = function(keysym, down) { + var arr; + arr = keyEvent(keysym, down); + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +mouseButton = function(x, y, down, bmask) { + if (down) { + mouse_buttonMask |= bmask; + } else { + mouse_buttonMask ^= bmask; + } + + if (conf.viewportDrag) { + if (down && !viewportDragging) { + viewportDragging = true; + viewportDragPos = {'x': x, 'y': y}; + + // Skip sending mouse events + return; + } else { + viewportDragging = false; + } + } + + mouse_arr = mouse_arr.concat( + pointerEvent(display.absX(x), display.absY(y)) ); + flushClient(); +}; + +mouseMove = function(x, y) { + //Util.Debug('>> mouseMove ' + x + "," + y); + var deltaX, deltaY; + + if (viewportDragging) { + //deltaX = x - viewportDragPos.x; // drag viewport + deltaX = viewportDragPos.x - x; // drag frame buffer + //deltaY = y - viewportDragPos.y; // drag viewport + deltaY = viewportDragPos.y - y; // drag frame buffer + viewportDragPos = {'x': x, 'y': y}; + + display.viewportChange(deltaX, deltaY); + + // Skip sending mouse events + return; + } + + mouse_arr = mouse_arr.concat( + pointerEvent(display.absX(x), display.absY(y)) ); +}; + + +// +// Server message handlers +// + +// RFB/VNC initialisation message handler +init_msg = function() { + //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']"); + + var strlen, reason, length, sversion, cversion, + i, types, num_types, challenge, response, bpp, depth, + big_endian, red_max, green_max, blue_max, red_shift, + green_shift, blue_shift, true_color, name_length; + + //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0)); + switch (rfb_state) { + + case 'ProtocolVersion' : + if (ws.rQlen() < 12) { + return fail("Incomplete protocol version"); + } + sversion = ws.rQshiftStr(12).substr(4,7); + Util.Info("Server ProtocolVersion: " + sversion); + switch (sversion) { + case "003.003": rfb_version = 3.3; break; + case "003.006": rfb_version = 3.3; break; // UltraVNC + case "003.007": rfb_version = 3.7; break; + case "003.008": rfb_version = 3.8; break; + default: + return fail("Invalid server version " + sversion); + } + if (rfb_version > rfb_max_version) { + rfb_version = rfb_max_version; + } + + if (! test_mode) { + sendTimer = setInterval(function() { + // Send updates either at a rate of one update + // every 50ms, or whatever slower rate the network + // can handle. + ws.flush(); + }, 50); + } + + cversion = "00" + parseInt(rfb_version,10) + + ".00" + ((rfb_version * 10) % 10); + ws.send_string("RFB " + cversion + "\n"); + updateState('Security', "Sent ProtocolVersion: " + cversion); + break; + + case 'Security' : + if (rfb_version >= 3.7) { + // Server sends supported list, client decides + num_types = ws.rQshift8(); + if (ws.rQwait("security type", num_types, 1)) { return false; } + if (num_types === 0) { + strlen = ws.rQshift32(); + reason = ws.rQshiftStr(strlen); + return fail("Security failure: " + reason); + } + rfb_auth_scheme = 0; + types = ws.rQshiftBytes(num_types); + Util.Debug("Server security types: " + types); + for (i=0; i < types.length; i+=1) { + if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) { + rfb_auth_scheme = types[i]; + } + } + if (rfb_auth_scheme === 0) { + return fail("Unsupported security types: " + types); + } + + ws.send([rfb_auth_scheme]); + } else { + // Server decides + if (ws.rQwait("security scheme", 4)) { return false; } + rfb_auth_scheme = ws.rQshift32(); + } + updateState('Authentication', + "Authenticating using scheme: " + rfb_auth_scheme); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + // Triggered by fallthough, not by server message + case 'Authentication' : + //Util.Debug("Security auth scheme: " + rfb_auth_scheme); + switch (rfb_auth_scheme) { + case 0: // connection failed + if (ws.rQwait("auth reason", 4)) { return false; } + strlen = ws.rQshift32(); + reason = ws.rQshiftStr(strlen); + return fail("Auth failure: " + reason); + case 1: // no authentication + if (rfb_version >= 3.8) { + updateState('SecurityResult'); + return; + } + // Fall through to ClientInitialisation + break; + case 2: // VNC authentication + if (rfb_password.length === 0) { + // Notify via both callbacks since it is kind of + // a RFB state change and a UI interface issue. + updateState('password', "Password Required"); + conf.onPasswordRequired(that); + return; + } + if (ws.rQwait("auth challenge", 16)) { return false; } + challenge = ws.rQshiftBytes(16); + //Util.Debug("Password: " + rfb_password); + //Util.Debug("Challenge: " + challenge + + // " (" + challenge.length + ")"); + response = genDES(rfb_password, challenge); + //Util.Debug("Response: " + response + + // " (" + response.length + ")"); + + //Util.Debug("Sending DES encrypted auth response"); + ws.send(response); + updateState('SecurityResult'); + return; + default: + fail("Unsupported auth scheme: " + rfb_auth_scheme); + return; + } + updateState('ClientInitialisation', "No auth required"); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + case 'SecurityResult' : + if (ws.rQwait("VNC auth response ", 4)) { return false; } + switch (ws.rQshift32()) { + case 0: // OK + // Fall through to ClientInitialisation + break; + case 1: // failed + if (rfb_version >= 3.8) { + length = ws.rQshift32(); + if (ws.rQwait("SecurityResult reason", length, 8)) { + return false; + } + reason = ws.rQshiftStr(length); + fail(reason); + } else { + fail("Authentication failed"); + } + return; + case 2: // too-many + return fail("Too many auth attempts"); + } + updateState('ClientInitialisation', "Authentication OK"); + init_msg(); // Recursive fallthrough (workaround JSLint complaint) + break; + + // Triggered by fallthough, not by server message + case 'ClientInitialisation' : + ws.send([conf.shared ? 1 : 0]); // ClientInitialisation + updateState('ServerInitialisation', "Authentication OK"); + break; + + case 'ServerInitialisation' : + if (ws.rQwait("server initialization", 24)) { return false; } + + /* Screen size */ + fb_width = ws.rQshift16(); + fb_height = ws.rQshift16(); + + /* PIXEL_FORMAT */ + bpp = ws.rQshift8(); + depth = ws.rQshift8(); + big_endian = ws.rQshift8(); + true_color = ws.rQshift8(); + + red_max = ws.rQshift16(); + green_max = ws.rQshift16(); + blue_max = ws.rQshift16(); + red_shift = ws.rQshift8(); + green_shift = ws.rQshift8(); + blue_shift = ws.rQshift8(); + ws.rQshiftStr(3); // padding + + Util.Info("Screen: " + fb_width + "x" + fb_height + + ", bpp: " + bpp + ", depth: " + depth + + ", big_endian: " + big_endian + + ", true_color: " + true_color + + ", red_max: " + red_max + + ", green_max: " + green_max + + ", blue_max: " + blue_max + + ", red_shift: " + red_shift + + ", green_shift: " + green_shift + + ", blue_shift: " + blue_shift); + + /* Connection name/title */ + name_length = ws.rQshift32(); + fb_name = ws.rQshiftStr(name_length); + + display.set_true_color(conf.true_color); + display.resize(fb_width, fb_height); + keyboard.grab(); + mouse.grab(); + + if (conf.true_color) { + fb_Bpp = 4; + fb_depth = 3; + } else { + fb_Bpp = 1; + fb_depth = 1; + } + + response = pixelFormat(); + response = response.concat(clientEncodings()); + response = response.concat(fbUpdateRequests()); + timing.fbu_rt_start = (new Date()).getTime(); + ws.send(response); + + /* Start pushing/polling */ + setTimeout(checkEvents, conf.check_rate); + setTimeout(scan_tight_imgQ, scan_imgQ_rate); + + if (conf.encrypt) { + updateState('normal', "Connected (encrypted) to: " + fb_name); + } else { + updateState('normal', "Connected (unencrypted) to: " + fb_name); + } + break; + } + //Util.Debug("<< init_msg"); +}; + + +/* Normal RFB/VNC server message handler */ +normal_msg = function() { + //Util.Debug(">> normal_msg"); + + var ret = true, msg_type, length, text, + c, first_colour, num_colours, red, green, blue; + + if (FBU.rects > 0) { + msg_type = 0; + } else { + msg_type = ws.rQshift8(); + } + switch (msg_type) { + case 0: // FramebufferUpdate + ret = framebufferUpdate(); // false means need more data + break; + case 1: // SetColourMapEntries + Util.Debug("SetColourMapEntries"); + ws.rQshift8(); // Padding + first_colour = ws.rQshift16(); // First colour + num_colours = ws.rQshift16(); + for (c=0; c < num_colours; c+=1) { + red = ws.rQshift16(); + //Util.Debug("red before: " + red); + red = parseInt(red / 256, 10); + //Util.Debug("red after: " + red); + green = parseInt(ws.rQshift16() / 256, 10); + blue = parseInt(ws.rQshift16() / 256, 10); + display.set_colourMap([red, green, blue], first_colour + c); + } + Util.Debug("colourMap: " + display.get_colourMap()); + Util.Info("Registered " + num_colours + " colourMap entries"); + //Util.Debug("colourMap: " + display.get_colourMap()); + break; + case 2: // Bell + Util.Debug("Bell"); + conf.onBell(that); + break; + case 3: // ServerCutText + Util.Debug("ServerCutText"); + if (ws.rQwait("ServerCutText header", 7, 1)) { return false; } + ws.rQshiftBytes(3); // Padding + length = ws.rQshift32(); + if (ws.rQwait("ServerCutText", length, 8)) { return false; } + + text = ws.rQshiftStr(length); + conf.clipboardReceive(that, text); // Obsolete + conf.onClipboard(that, text); + break; + default: + fail("Disconnected: illegal server message type " + msg_type); + Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); + break; + } + //Util.Debug("<< normal_msg"); + return ret; +}; + +framebufferUpdate = function() { + var now, hdr, fbu_rt_diff, ret = true; + + if (FBU.rects === 0) { + //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20)); + if (ws.rQwait("FBU header", 3)) { + ws.rQunshift8(0); // FBU msg_type + return false; + } + ws.rQshift8(); // padding + FBU.rects = ws.rQshift16(); + //Util.Debug("FramebufferUpdate, rects:" + FBU.rects); + FBU.bytes = 0; + timing.cur_fbu = 0; + if (timing.fbu_rt_start > 0) { + now = (new Date()).getTime(); + Util.Info("First FBU latency: " + (now - timing.fbu_rt_start)); + } + } + + while (FBU.rects > 0) { + if (rfb_state !== "normal") { + return false; + } + if (ws.rQwait("FBU", FBU.bytes)) { return false; } + if (FBU.bytes === 0) { + if (ws.rQwait("rect header", 12)) { return false; } + /* New FramebufferUpdate */ + + hdr = ws.rQshiftBytes(12); + FBU.x = (hdr[0] << 8) + hdr[1]; + FBU.y = (hdr[2] << 8) + hdr[3]; + FBU.width = (hdr[4] << 8) + hdr[5]; + FBU.height = (hdr[6] << 8) + hdr[7]; + FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) + + (hdr[10] << 8) + hdr[11], 10); + + conf.onFBUReceive(that, + {'x': FBU.x, 'y': FBU.y, + 'width': FBU.width, 'height': FBU.height, + 'encoding': FBU.encoding, + 'encodingName': encNames[FBU.encoding]}); + + if (encNames[FBU.encoding]) { + // Debug: + /* + var msg = "FramebufferUpdate rects:" + FBU.rects; + msg += " x: " + FBU.x + " y: " + FBU.y; + msg += " width: " + FBU.width + " height: " + FBU.height; + msg += " encoding:" + FBU.encoding; + msg += "(" + encNames[FBU.encoding] + ")"; + msg += ", ws.rQlen(): " + ws.rQlen(); + Util.Debug(msg); + */ + } else { + fail("Disconnected: unsupported encoding " + + FBU.encoding); + return false; + } + } + + timing.last_fbu = (new Date()).getTime(); + + ret = encHandlers[FBU.encoding](); + + now = (new Date()).getTime(); + timing.cur_fbu += (now - timing.last_fbu); + + if (ret) { + encStats[FBU.encoding][0] += 1; + encStats[FBU.encoding][1] += 1; + } + + if (FBU.rects === 0) { + if (((FBU.width === fb_width) && + (FBU.height === fb_height)) || + (timing.fbu_rt_start > 0)) { + timing.full_fbu_total += timing.cur_fbu; + timing.full_fbu_cnt += 1; + Util.Info("Timing of full FBU, cur: " + + timing.cur_fbu + ", total: " + + timing.full_fbu_total + ", cnt: " + + timing.full_fbu_cnt + ", avg: " + + (timing.full_fbu_total / + timing.full_fbu_cnt)); + } + if (timing.fbu_rt_start > 0) { + fbu_rt_diff = now - timing.fbu_rt_start; + timing.fbu_rt_total += fbu_rt_diff; + timing.fbu_rt_cnt += 1; + Util.Info("full FBU round-trip, cur: " + + fbu_rt_diff + ", total: " + + timing.fbu_rt_total + ", cnt: " + + timing.fbu_rt_cnt + ", avg: " + + (timing.fbu_rt_total / + timing.fbu_rt_cnt)); + timing.fbu_rt_start = 0; + } + } + if (! ret) { + return ret; // false ret means need more data + } + } + + conf.onFBUComplete(that, + {'x': FBU.x, 'y': FBU.y, + 'width': FBU.width, 'height': FBU.height, + 'encoding': FBU.encoding, + 'encodingName': encNames[FBU.encoding]}); + + return true; // We finished this FBU +}; + +// +// FramebufferUpdate encodings +// + +encHandlers.RAW = function display_raw() { + //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)"); + + var cur_y, cur_height; + + if (FBU.lines === 0) { + FBU.lines = FBU.height; + } + FBU.bytes = FBU.width * fb_Bpp; // At least a line + if (ws.rQwait("RAW", FBU.bytes)) { return false; } + cur_y = FBU.y + (FBU.height - FBU.lines); + cur_height = Math.min(FBU.lines, + Math.floor(ws.rQlen()/(FBU.width * fb_Bpp))); + display.blitImage(FBU.x, cur_y, FBU.width, cur_height, + ws.get_rQ(), ws.get_rQi()); + ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp); + FBU.lines -= cur_height; + + if (FBU.lines > 0) { + FBU.bytes = FBU.width * fb_Bpp; // At least another line + } else { + FBU.rects -= 1; + FBU.bytes = 0; + } + //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)"); + return true; +}; + +encHandlers.COPYRECT = function display_copy_rect() { + //Util.Debug(">> display_copy_rect"); + + var old_x, old_y; + + if (ws.rQwait("COPYRECT", 4)) { return false; } + old_x = ws.rQshift16(); + old_y = ws.rQshift16(); + display.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height); + FBU.rects -= 1; + FBU.bytes = 0; + return true; +}; + +encHandlers.RRE = function display_rre() { + //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)"); + var color, x, y, width, height, chunk; + + if (FBU.subrects === 0) { + if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; } + FBU.subrects = ws.rQshift32(); + color = ws.rQshiftBytes(fb_Bpp); // Background + display.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); + } + while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) { + color = ws.rQshiftBytes(fb_Bpp); + x = ws.rQshift16(); + y = ws.rQshift16(); + width = ws.rQshift16(); + height = ws.rQshift16(); + display.fillRect(FBU.x + x, FBU.y + y, width, height, color); + FBU.subrects -= 1; + } + //Util.Debug(" display_rre: rects: " + FBU.rects + + // ", FBU.subrects: " + FBU.subrects); + + if (FBU.subrects > 0) { + chunk = Math.min(rre_chunk_sz, FBU.subrects); + FBU.bytes = (fb_Bpp + 8) * chunk; + } else { + FBU.rects -= 1; + FBU.bytes = 0; + } + //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes); + return true; +}; + +encHandlers.HEXTILE = function display_hextile() { + //Util.Debug(">> display_hextile"); + var subencoding, subrects, color, cur_tile, + tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh, + rQ = ws.get_rQ(), rQi = ws.get_rQi(); + + if (FBU.tiles === 0) { + FBU.tiles_x = Math.ceil(FBU.width/16); + FBU.tiles_y = Math.ceil(FBU.height/16); + FBU.total_tiles = FBU.tiles_x * FBU.tiles_y; + FBU.tiles = FBU.total_tiles; + } + + /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */ + while (FBU.tiles > 0) { + FBU.bytes = 1; + if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; } + subencoding = rQ[rQi]; // Peek + if (subencoding > 30) { // Raw + fail("Disconnected: illegal hextile subencoding " + subencoding); + //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30)); + return false; + } + subrects = 0; + cur_tile = FBU.total_tiles - FBU.tiles; + tile_x = cur_tile % FBU.tiles_x; + tile_y = Math.floor(cur_tile / FBU.tiles_x); + x = FBU.x + tile_x * 16; + y = FBU.y + tile_y * 16; + w = Math.min(16, (FBU.x + FBU.width) - x); + h = Math.min(16, (FBU.y + FBU.height) - y); + + /* Figure out how much we are expecting */ + if (subencoding & 0x01) { // Raw + //Util.Debug(" Raw subencoding"); + FBU.bytes += w * h * fb_Bpp; + } else { + if (subencoding & 0x02) { // Background + FBU.bytes += fb_Bpp; + } + if (subencoding & 0x04) { // Foreground + FBU.bytes += fb_Bpp; + } + if (subencoding & 0x08) { // AnySubrects + FBU.bytes += 1; // Since we aren't shifting it off + if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; } + subrects = rQ[rQi + FBU.bytes-1]; // Peek + if (subencoding & 0x10) { // SubrectsColoured + FBU.bytes += subrects * (fb_Bpp + 2); + } else { + FBU.bytes += subrects * 2; + } + } + } + + /* + Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) + + " (" + tile_x + "," + tile_y + ")" + + " [" + x + "," + y + "]@" + w + "x" + h + + ", subenc:" + subencoding + + "(last: " + FBU.lastsubencoding + "), subrects:" + + subrects + + ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes + + " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) + + " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10)); + */ + if (ws.rQwait("hextile", FBU.bytes)) { return false; } + + /* We know the encoding and have a whole tile */ + FBU.subencoding = rQ[rQi]; + rQi += 1; + if (FBU.subencoding === 0) { + if (FBU.lastsubencoding & 0x01) { + /* Weird: ignore blanks after RAW */ + Util.Debug(" Ignoring blank after RAW"); + } else { + display.fillRect(x, y, w, h, FBU.background); + } + } else if (FBU.subencoding & 0x01) { // Raw + display.blitImage(x, y, w, h, rQ, rQi); + rQi += FBU.bytes - 1; + } else { + if (FBU.subencoding & 0x02) { // Background + FBU.background = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } + if (FBU.subencoding & 0x04) { // Foreground + FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } + + display.startTile(x, y, w, h, FBU.background); + if (FBU.subencoding & 0x08) { // AnySubrects + subrects = rQ[rQi]; + rQi += 1; + for (s = 0; s < subrects; s += 1) { + if (FBU.subencoding & 0x10) { // SubrectsColoured + color = rQ.slice(rQi, rQi + fb_Bpp); + rQi += fb_Bpp; + } else { + color = FBU.foreground; + } + xy = rQ[rQi]; + rQi += 1; + sx = (xy >> 4); + sy = (xy & 0x0f); + + wh = rQ[rQi]; + rQi += 1; + sw = (wh >> 4) + 1; + sh = (wh & 0x0f) + 1; + + display.subTile(sx, sy, sw, sh, color); + } + } + display.finishTile(); + } + ws.set_rQi(rQi); + FBU.lastsubencoding = FBU.subencoding; + FBU.bytes = 0; + FBU.tiles -= 1; + } + + if (FBU.tiles === 0) { + FBU.rects -= 1; + } + + //Util.Debug("<< display_hextile"); + return true; +}; + + +encHandlers.TIGHT_PNG = function display_tight_png() { + //Util.Debug(">> display_tight_png"); + var ctl, cmode, clength, getCLength, color, img; + //Util.Debug(" FBU.rects: " + FBU.rects); + //Util.Debug(" starting ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + + FBU.bytes = 1; // compression-control byte + if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; } + + // Get 'compact length' header and data size + getCLength = function (arr) { + var header = 1, data = 0; + data += arr[0] & 0x7f; + if (arr[0] & 0x80) { + header += 1; + data += (arr[1] & 0x7f) << 7; + if (arr[1] & 0x80) { + header += 1; + data += arr[2] << 14; + } + } + return [header, data]; + }; + + ctl = ws.rQpeek8(); + switch (ctl >> 4) { + case 0x08: cmode = "fill"; break; + case 0x09: cmode = "jpeg"; break; + case 0x0A: cmode = "png"; break; + default: throw("Illegal basic compression received, ctl: " + ctl); + } + switch (cmode) { + // fill uses fb_depth because TPIXELs drop the padding byte + case "fill": FBU.bytes += fb_depth; break; // TPIXEL + case "jpeg": FBU.bytes += 3; break; // max clength + case "png": FBU.bytes += 3; break; // max clength + } + + if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } + + //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + //Util.Debug(" cmode: " + cmode); + + // Determine FBU.bytes + switch (cmode) { + case "fill": + ws.rQshift8(); // shift off ctl + color = ws.rQshiftBytes(fb_depth); + FBU.imgQ.push({ + 'type': 'fill', + 'img': {'complete': true}, + 'x': FBU.x, + 'y': FBU.y, + 'width': FBU.width, + 'height': FBU.height, + 'color': color}); + break; + case "jpeg": + case "png": + clength = getCLength(ws.rQslice(1, 4)); + FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data + if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; } + + // We have everything, render it + //Util.Debug(" png, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]); + ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length + img = new Image(); + //img.onload = scan_tight_imgQ; + FBU.imgQ.push({ + 'type': 'img', + 'img': img, + 'x': FBU.x, + 'y': FBU.y}); + img.src = "data:image/" + cmode + + extract_data_uri(ws.rQshiftBytes(clength[1])); + img = null; + break; + } + FBU.bytes = 0; + FBU.rects -= 1; + //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")"); + //Util.Debug("<< display_tight_png"); + return true; +}; + +extract_data_uri = function(arr) { + //var i, stra = []; + //for (i=0; i< arr.length; i += 1) { + // stra.push(String.fromCharCode(arr[i])); + //} + //return "," + escape(stra.join('')); + return ";base64," + Base64.encode(arr); +}; + +scan_tight_imgQ = function() { + var data, imgQ, ctx; + ctx = display.get_context(); + if (rfb_state === 'normal') { + imgQ = FBU.imgQ; + while ((imgQ.length > 0) && (imgQ[0].img.complete)) { + data = imgQ.shift(); + if (data['type'] === 'fill') { + display.fillRect(data.x, data.y, data.width, data.height, data.color); + } else { + ctx.drawImage(data.img, data.x, data.y); + } + } + setTimeout(scan_tight_imgQ, scan_imgQ_rate); + } +}; + +encHandlers.DesktopSize = function set_desktopsize() { + Util.Debug(">> set_desktopsize"); + fb_width = FBU.width; + fb_height = FBU.height; + display.resize(fb_width, fb_height); + timing.fbu_rt_start = (new Date()).getTime(); + // Send a new non-incremental request + ws.send(fbUpdateRequests()); + + FBU.bytes = 0; + FBU.rects -= 1; + + Util.Debug("<< set_desktopsize"); + return true; +}; + +encHandlers.Cursor = function set_cursor() { + var x, y, w, h, pixelslength, masklength; + //Util.Debug(">> set_cursor"); + x = FBU.x; // hotspot-x + y = FBU.y; // hotspot-y + w = FBU.width; + h = FBU.height; + + pixelslength = w * h * fb_Bpp; + masklength = Math.floor((w + 7) / 8) * h; + + FBU.bytes = pixelslength + masklength; + if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; } + + //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h); + + display.changeCursor(ws.rQshiftBytes(pixelslength), + ws.rQshiftBytes(masklength), + x, y, w, h); + + FBU.bytes = 0; + FBU.rects -= 1; + + //Util.Debug("<< set_cursor"); + return true; +}; + +encHandlers.JPEG_quality_lo = function set_jpeg_quality() { + Util.Error("Server sent jpeg_quality pseudo-encoding"); +}; + +encHandlers.compress_lo = function set_compress_level() { + Util.Error("Server sent compress level pseudo-encoding"); +}; + +/* + * Client message routines + */ + +pixelFormat = function() { + //Util.Debug(">> pixelFormat"); + var arr; + arr = [0]; // msg-type + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + + arr.push8(fb_Bpp * 8); // bits-per-pixel + arr.push8(fb_depth * 8); // depth + arr.push8(0); // little-endian + arr.push8(conf.true_color ? 1 : 0); // true-color + + arr.push16(255); // red-max + arr.push16(255); // green-max + arr.push16(255); // blue-max + arr.push8(0); // red-shift + arr.push8(8); // green-shift + arr.push8(16); // blue-shift + + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + //Util.Debug("<< pixelFormat"); + return arr; +}; + +clientEncodings = function() { + //Util.Debug(">> clientEncodings"); + var arr, i, encList = []; + + for (i=0; i> fbUpdateRequest"); + if (typeof(x) === "undefined") { x = 0; } + if (typeof(y) === "undefined") { y = 0; } + if (typeof(xw) === "undefined") { xw = fb_width; } + if (typeof(yw) === "undefined") { yw = fb_height; } + var arr; + arr = [3]; // msg-type + arr.push8(incremental); + arr.push16(x); + arr.push16(y); + arr.push16(xw); + arr.push16(yw); + //Util.Debug("<< fbUpdateRequest"); + return arr; +}; + +// Based on clean/dirty areas, generate requests to send +fbUpdateRequests = function() { + var cleanDirty = display.getCleanDirtyReset(), + arr = [], i, cb, db; + + cb = cleanDirty.cleanBox; + if (cb.w > 0 && cb.h > 0) { + // Request incremental for clean box + arr = arr.concat(fbUpdateRequest(1, cb.x, cb.y, cb.w, cb.h)); + } + for (i = 0; i < cleanDirty.dirtyBoxes.length; i++) { + db = cleanDirty.dirtyBoxes[i]; + // Force all (non-incremental for dirty box + arr = arr.concat(fbUpdateRequest(0, db.x, db.y, db.w, db.h)); + } + return arr; +}; + + + +keyEvent = function(keysym, down) { + //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down); + var arr; + arr = [4]; // msg-type + arr.push8(down); + arr.push16(0); + arr.push32(keysym); + //Util.Debug("<< keyEvent"); + return arr; +}; + +pointerEvent = function(x, y) { + //Util.Debug(">> pointerEvent, x,y: " + x + "," + y + + // " , mask: " + mouse_buttonMask); + var arr; + arr = [5]; // msg-type + arr.push8(mouse_buttonMask); + arr.push16(x); + arr.push16(y); + //Util.Debug("<< pointerEvent"); + return arr; +}; + +clientCutText = function(text) { + //Util.Debug(">> clientCutText"); + var arr, i, n; + arr = [6]; // msg-type + arr.push8(0); // padding + arr.push8(0); // padding + arr.push8(0); // padding + arr.push32(text.length); + n = text.length; + for (i=0; i < n; i+=1) { + arr.push(text.charCodeAt(i)); + } + //Util.Debug("<< clientCutText:" + arr); + return arr; +}; + + + +// +// Public API interface functions +// + +that.connect = function(host, port, password, path) { + //Util.Debug(">> connect"); + + rfb_host = host; + rfb_port = port; + rfb_password = (password !== undefined) ? password : ""; + rfb_path = (path !== undefined) ? path : ""; + + if ((!rfb_host) || (!rfb_port)) { + return fail("Must set host and port"); + } + + updateState('connect'); + //Util.Debug("<< connect"); + +}; + +that.disconnect = function() { + //Util.Debug(">> disconnect"); + updateState('disconnect', 'Disconnecting'); + //Util.Debug("<< disconnect"); +}; + +that.sendPassword = function(passwd) { + rfb_password = passwd; + rfb_state = "Authentication"; + setTimeout(init_msg, 1); +}; + +that.sendCtrlAltDel = function() { + if (rfb_state !== "normal") { return false; } + Util.Info("Sending Ctrl-Alt-Del"); + var arr = []; + arr = arr.concat(keyEvent(0xFFE3, 1)); // Control + arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt + arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete + arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete + arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt + arr = arr.concat(keyEvent(0xFFE3, 0)); // Control + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +// Send a key press. If 'down' is not specified then send a down key +// followed by an up key. +that.sendKey = function(code, down) { + if (rfb_state !== "normal") { return false; } + var arr = []; + if (typeof down !== 'undefined') { + Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code); + arr = arr.concat(keyEvent(code, down ? 1 : 0)); + } else { + Util.Info("Sending key code (down + up): " + code); + arr = arr.concat(keyEvent(code, 1)); + arr = arr.concat(keyEvent(code, 0)); + } + arr = arr.concat(fbUpdateRequests()); + ws.send(arr); +}; + +that.clipboardPasteFrom = function(text) { + if (rfb_state !== "normal") { return; } + //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "..."); + ws.send(clientCutText(text)); + //Util.Debug("<< clipboardPasteFrom"); +}; + +// Override internal functions for testing +that.testMode = function(override_send) { + test_mode = true; + that.recv_message = ws.testMode(override_send); + + checkEvents = function () { /* Stub Out */ }; + that.connect = function(host, port, password) { + rfb_host = host; + rfb_port = port; + rfb_password = password; + updateState('ProtocolVersion', "Starting VNC handshake"); + }; +}; + + +return constructor(); // Return the public API interface + +} // End of RFB() diff --git a/webclients/novnc/include/ui.js b/webclients/novnc/include/ui.js new file mode 100644 index 0000000..74a0005 --- /dev/null +++ b/webclients/novnc/include/ui.js @@ -0,0 +1,629 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +"use strict"; +/*jslint white: false, browser: true */ +/*global window, $D, Util, WebUtil, RFB, Display */ + +var UI = { + +rfb_state : 'loaded', +settingsOpen : false, +connSettingsOpen : true, +clipboardOpen: false, +keyboardVisible: false, + +// Render default UI and initialize settings menu +load: function() { + var html = '', i, sheet, sheets, llevels; + + // Stylesheet selection dropdown + sheet = WebUtil.selectStylesheet(); + sheets = WebUtil.getStylesheets(); + for (i = 0; i < sheets.length; i += 1) { + UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title); + } + + // Logging selection dropdown + llevels = ['error', 'warn', 'info', 'debug']; + for (i = 0; i < llevels.length; i += 1) { + UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]); + } + + // Settings with immediate effects + UI.initSetting('logging', 'warn'); + WebUtil.init_logging(UI.getSetting('logging')); + + UI.initSetting('stylesheet', 'default'); + WebUtil.selectStylesheet(null); + // call twice to get around webkit bug + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + + /* Populate the controls if defaults are provided in the URL */ + UI.initSetting('host', ''); + UI.initSetting('port', ''); + UI.initSetting('password', ''); + UI.initSetting('encrypt', false); + UI.initSetting('true_color', true); + UI.initSetting('cursor', false); + UI.initSetting('shared', true); + UI.initSetting('connectTimeout', 2); + UI.initSetting('path', ''); + + UI.rfb = RFB({'target': $D('noVNC_canvas'), + 'onUpdateState': UI.updateState, + 'onClipboard': UI.clipReceive}); + UI.updateVisualState(); + + // Unfocus clipboard when over the VNC area + //$D('VNC_screen').onmousemove = function () { + // var keyboard = UI.rfb.get_keyboard(); + // if ((! keyboard) || (! keyboard.get_focused())) { + // $D('VNC_clipboard_text').blur(); + // } + // }; + + // Show mouse selector buttons on touch screen devices + if ('ontouchstart' in document.documentElement) { + // Show mobile buttons + $D('noVNC_mobile_buttons').style.display = "inline"; + UI.setMouseButton(); + // Remove the address bar + setTimeout(function() { window.scrollTo(0, 1); }, 100); + UI.forceSetting('clip', true); + $D('noVNC_clip').disabled = true; + } else { + UI.initSetting('clip', false); + } + + //iOS Safari does not support CSS position:fixed. + //This detects iOS devices and enables javascript workaround. + if ((navigator.userAgent.match(/iPhone/i)) || + (navigator.userAgent.match(/iPod/i)) || + (navigator.userAgent.match(/iPad/i))) { + //UI.setOnscroll(); + //UI.setResize(); + } + + $D('noVNC_host').focus(); + + UI.setViewClip(); + Util.addEvent(window, 'resize', UI.setViewClip); + + Util.addEvent(window, 'beforeunload', function () { + if (UI.rfb_state === 'normal') { + return "You are currently connected."; + } + } ); + +}, + +// Read form control compatible setting from cookie +getSetting: function(name) { + var val, ctrl = $D('noVNC_' + name); + val = WebUtil.readCookie(name); + if (ctrl.type === 'checkbox') { + if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) { + val = false; + } else { + val = true; + } + } + return val; +}, + +// Update cookie and form control setting. If value is not set, then +// updates from control to current cookie setting. +updateSetting: function(name, value) { + + var i, ctrl = $D('noVNC_' + name); + // Save the cookie for this session + if (typeof value !== 'undefined') { + WebUtil.createCookie(name, value); + } + + // Update the settings control + value = UI.getSetting(name); + + if (ctrl.type === 'checkbox') { + ctrl.checked = value; + + } else if (typeof ctrl.options !== 'undefined') { + for (i = 0; i < ctrl.options.length; i += 1) { + if (ctrl.options[i].value === value) { + ctrl.selectedIndex = i; + break; + } + } + } else { + /*Weird IE9 error leads to 'null' appearring + in textboxes instead of ''.*/ + if (value === null) { + value = ""; + } + ctrl.value = value; + } +}, + +// Save control setting to cookie +saveSetting: function(name) { + var val, ctrl = $D('noVNC_' + name); + if (ctrl.type === 'checkbox') { + val = ctrl.checked; + } else if (typeof ctrl.options !== 'undefined') { + val = ctrl.options[ctrl.selectedIndex].value; + } else { + val = ctrl.value; + } + WebUtil.createCookie(name, val); + //Util.Debug("Setting saved '" + name + "=" + val + "'"); + return val; +}, + +// Initial page load read/initialization of settings +initSetting: function(name, defVal) { + var val; + + // Check Query string followed by cookie + val = WebUtil.getQueryVar(name); + if (val === null) { + val = WebUtil.readCookie(name, defVal); + } + UI.updateSetting(name, val); + //Util.Debug("Setting '" + name + "' initialized to '" + val + "'"); + return val; +}, + +// Force a setting to be a certain value +forceSetting: function(name, val) { + UI.updateSetting(name, val); + return val; +}, + + +// Show the clipboard panel +toggleClipboardPanel: function() { + //Close settings if open + if (UI.settingsOpen == true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } + //Close connection settings if open + if (UI.connSettingsOpen == true) { + UI.toggleConnectPanel(); + } + //Toggle Clipboard Panel + if (UI.clipboardOpen == true) { + $D('noVNC_clipboard').style.display = "none"; + $D('clipboardButton').className = "noVNC_status_button"; + UI.clipboardOpen = false; + } else { + $D('noVNC_clipboard').style.display = "block"; + $D('clipboardButton').className = "noVNC_status_button_selected"; + UI.clipboardOpen = true; + } +}, + +// Show the connection settings panel/menu +toggleConnectPanel: function() { + //Close connection settings if open + if (UI.settingsOpen == true) { + UI.settingsApply(); + UI.closeSettingsMenu(); + $D('connectButton').className = "noVNC_status_button"; + } + if (UI.clipboardOpen == true) { + UI.toggleClipboardPanel(); + } + + //Toggle Connection Panel + if (UI.connSettingsOpen == true) { + $D('noVNC_controls').style.display = "none"; + $D('connectButton').className = "noVNC_status_button"; + UI.connSettingsOpen = false; + } else { + $D('noVNC_controls').style.display = "block"; + $D('connectButton').className = "noVNC_status_button_selected"; + UI.connSettingsOpen = true; + $D('noVNC_host').focus(); + } +}, + +// Toggle the settings menu: +// On open, settings are refreshed from saved cookies. +// On close, settings are applied +toggleSettingsPanel: function() { + if (UI.settingsOpen) { + UI.settingsApply(); + UI.closeSettingsMenu(); + } else { + UI.updateSetting('encrypt'); + UI.updateSetting('true_color'); + if (UI.rfb.get_display().get_cursor_uri()) { + UI.updateSetting('cursor'); + } else { + UI.updateSetting('cursor', false); + $D('noVNC_cursor').disabled = true; + } + UI.updateSetting('clip'); + UI.updateSetting('shared'); + UI.updateSetting('connectTimeout'); + UI.updateSetting('path'); + UI.updateSetting('stylesheet'); + UI.updateSetting('logging'); + + UI.openSettingsMenu(); + } +}, + +// Open menu +openSettingsMenu: function() { + if (UI.clipboardOpen == true) { + UI.toggleClipboardPanel(); + } + //Close connection settings if open + if (UI.connSettingsOpen == true) { + UI.toggleConnectPanel(); + } + $D('noVNC_settings').style.display = "block"; + $D('settingsButton').className = "noVNC_status_button_selected"; + UI.settingsOpen = true; +}, + +// Close menu (without applying settings) +closeSettingsMenu: function() { + $D('noVNC_settings').style.display = "none"; + $D('settingsButton').className = "noVNC_status_button"; + UI.settingsOpen = false; +}, + +// Save/apply settings when 'Apply' button is pressed +settingsApply: function() { + //Util.Debug(">> settingsApply"); + UI.saveSetting('encrypt'); + UI.saveSetting('true_color'); + if (UI.rfb.get_display().get_cursor_uri()) { + UI.saveSetting('cursor'); + } + UI.saveSetting('clip'); + UI.saveSetting('shared'); + UI.saveSetting('connectTimeout'); + UI.saveSetting('path'); + UI.saveSetting('stylesheet'); + UI.saveSetting('logging'); + + // Settings with immediate (non-connected related) effect + WebUtil.selectStylesheet(UI.getSetting('stylesheet')); + WebUtil.init_logging(UI.getSetting('logging')); + UI.setViewClip(); + UI.setViewDrag(UI.rfb.get_viewportDrag()); + //Util.Debug("<< settingsApply"); +}, + + + +setPassword: function() { + UI.rfb.sendPassword($D('noVNC_password').value); + //Reset connect button. + $D('noVNC_connect_button').value = "Connect"; + $D('noVNC_connect_button').onclick = UI.Connect; + //Hide connection panel. + UI.toggleConnectPanel(); + return false; +}, + +sendCtrlAltDel: function() { + UI.rfb.sendCtrlAltDel(); +}, + +setMouseButton: function(num) { + var b, blist = [0, 1,2,4], button; + + if (typeof num === 'undefined') { + // Disable mouse buttons + num = -1; + } + if (UI.rfb) { + UI.rfb.get_mouse().set_touchButton(num); + } + + for (b = 0; b < blist.length; b++) { + button = $D('noVNC_mouse_button' + blist[b]); + if (blist[b] === num) { + button.style.display = ""; + } else { + button.style.display = "none"; + /* + button.style.backgroundColor = "black"; + button.style.color = "lightgray"; + button.style.backgroundColor = ""; + button.style.color = ""; + */ + } + } +}, + +updateState: function(rfb, state, oldstate, msg) { + var s, sb, c, d, cad, vd, klass; + UI.rfb_state = state; + s = $D('noVNC_status'); + sb = $D('noVNC_status_bar'); + switch (state) { + case 'failed': + case 'fatal': + klass = "noVNC_status_error"; + break; + case 'normal': + klass = "noVNC_status_normal"; + break; + case 'disconnected': + $D('noVNC_logo').style.display = "block"; + case 'loaded': + klass = "noVNC_status_normal"; + break; + case 'password': + UI.toggleConnectPanel(); + + $D('noVNC_connect_button').value = "Send Password"; + $D('noVNC_connect_button').onclick = UI.setPassword; + $D('noVNC_password').focus(); + + klass = "noVNC_status_warn"; + break; + default: + klass = "noVNC_status_warn"; + break; + } + + if (typeof(msg) !== 'undefined') { + s.setAttribute("class", klass); + sb.setAttribute("class", klass); + s.innerHTML = msg; + } + + UI.updateVisualState(); +}, + +// Disable/enable controls depending on connection state +updateVisualState: function() { + var connected = UI.rfb_state === 'normal' ? true : false; + + //Util.Debug(">> updateVisualState"); + $D('noVNC_encrypt').disabled = connected; + $D('noVNC_true_color').disabled = connected; + if (UI.rfb && UI.rfb.get_display() && + UI.rfb.get_display().get_cursor_uri()) { + $D('noVNC_cursor').disabled = connected; + } else { + UI.updateSetting('cursor', false); + $D('noVNC_cursor').disabled = true; + } + $D('noVNC_shared').disabled = connected; + $D('noVNC_connectTimeout').disabled = connected; + $D('noVNC_path').disabled = connected; + + if (connected) { + UI.setViewClip(); + UI.setMouseButton(1); + $D('showKeyboard').style.display = "inline"; + $D('sendCtrlAltDelButton').style.display = "inline"; + } else { + UI.setMouseButton(); + $D('showKeyboard').style.display = "none"; + $D('sendCtrlAltDelButton').style.display = "none"; + } + // State change disables viewport dragging. + // It is enabled (toggled) by direct click on the button + UI.setViewDrag(false); + + switch (UI.rfb_state) { + case 'fatal': + case 'failed': + case 'loaded': + case 'disconnected': + $D('connectButton').style.display = ""; + $D('disconnectButton').style.display = "none"; + break; + default: + $D('connectButton').style.display = "none"; + $D('disconnectButton').style.display = ""; + break; + } + + //Util.Debug("<< updateVisualState"); +}, + + +clipReceive: function(rfb, text) { + Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); + $D('noVNC_clipboard_text').value = text; + Util.Debug("<< UI.clipReceive"); +}, + + +connect: function() { + var host, port, password, path; + + UI.closeSettingsMenu(); + UI.toggleConnectPanel(); + + host = $D('noVNC_host').value; + port = $D('noVNC_port').value; + password = $D('noVNC_password').value; + path = $D('noVNC_path').value; + if ((!host) || (!port)) { + throw("Must set host and port"); + } + + UI.rfb.set_encrypt(UI.getSetting('encrypt')); + UI.rfb.set_true_color(UI.getSetting('true_color')); + UI.rfb.set_local_cursor(UI.getSetting('cursor')); + UI.rfb.set_shared(UI.getSetting('shared')); + UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout')); + + UI.rfb.connect(host, port, password, path); + //Close dialog. + setTimeout(UI.setBarPosition, 100); + $D('noVNC_logo').style.display = "none"; +}, + +disconnect: function() { + UI.closeSettingsMenu(); + UI.rfb.disconnect(); + + $D('noVNC_logo').style.display = "block"; + UI.connSettingsOpen = false; + UI.toggleConnectPanel(); +}, + +displayBlur: function() { + UI.rfb.get_keyboard().set_focused(false); + UI.rfb.get_mouse().set_focused(false); +}, + +displayFocus: function() { + UI.rfb.get_keyboard().set_focused(true); + UI.rfb.get_mouse().set_focused(true); +}, + +clipClear: function() { + $D('noVNC_clipboard_text').value = ""; + UI.rfb.clipboardPasteFrom(""); +}, + +clipSend: function() { + var text = $D('noVNC_clipboard_text').value; + Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "..."); + UI.rfb.clipboardPasteFrom(text); + Util.Debug("<< UI.clipSend"); +}, + + +// Enable/disable and configure viewport clipping +setViewClip: function(clip) { + var display, cur_clip, pos, new_w, new_h; + + if (UI.rfb) { + display = UI.rfb.get_display(); + } else { + return; + } + + cur_clip = display.get_viewport(); + + if (typeof(clip) !== 'boolean') { + // Use current setting + clip = UI.getSetting('clip'); + } + + if (clip && !cur_clip) { + // Turn clipping on + UI.updateSetting('clip', true); + } else if (!clip && cur_clip) { + // Turn clipping off + UI.updateSetting('clip', false); + display.set_viewport(false); + $D('noVNC_canvas').style.position = 'static'; + display.viewportChange(); + } + if (UI.getSetting('clip')) { + // If clipping, update clipping settings + $D('noVNC_canvas').style.position = 'absolute'; + pos = Util.getPosition($D('noVNC_canvas')); + new_w = window.innerWidth - pos.x; + new_h = window.innerHeight - pos.y; + display.set_viewport(true); + display.viewportChange(0, 0, new_w, new_h); + } +}, + +// Toggle/set/unset the viewport drag/move button +setViewDrag: function(drag) { + var vmb = $D('noVNC_view_drag_button'); + if (!UI.rfb) { return; } + + if (UI.rfb_state === 'normal' && + UI.rfb.get_display().get_viewport()) { + vmb.style.display = "inline"; + } else { + vmb.style.display = "none"; + } + + if (typeof(drag) === "undefined") { + // If not specified, then toggle + drag = !UI.rfb.get_viewportDrag(); + } + if (drag) { + vmb.className = "noVNC_status_button_selected"; + UI.rfb.set_viewportDrag(true); + } else { + vmb.className = "noVNC_status_button"; + UI.rfb.set_viewportDrag(false); + } +}, + +// On touch devices, show the OS keyboard +showKeyboard: function() { + if(UI.keyboardVisible == false) { + $D('keyboardinput').focus(); + UI.keyboardVisible = true; + $D('showKeyboard').className = "noVNC_status_button_selected"; + } else if(UI.keyboardVisible == true) { + $D('keyboardinput').blur(); + $D('showKeyboard').className = "noVNC_status_button"; + UI.keyboardVisible = false; + } +}, + +keyInputBlur: function() { + $D('showKeyboard').className = "noVNC_status_button"; + //Weird bug in iOS if you change keyboardVisible + //here it does not actually occur so next time + //you click keyboard icon it doesnt work. + setTimeout("UI.setKeyboard()",100) +}, + +setKeyboard: function() { + UI.keyboardVisible = false; +}, + +// iOS < Version 5 does not support position fixed. Javascript workaround: +setOnscroll: function() { + window.onscroll = function() { + UI.setBarPosition(); + }; +}, + +setResize: function () { + window.onResize = function() { + UI.setBarPosition(); + }; +}, + +//Helper to add options to dropdown. +addOption: function(selectbox,text,value ) +{ + var optn = document.createElement("OPTION"); + optn.text = text; + optn.value = value; + selectbox.options.add(optn); +}, + +setBarPosition: function() { + $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px'; + $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px'; + + var vncwidth = $D('noVNC_screen').style.offsetWidth; + $D('noVNC-control-bar').style.width = vncwidth + 'px'; +} + +}; + + + + diff --git a/webclients/novnc/include/util.js b/webclients/novnc/include/util.js new file mode 100644 index 0000000..0a9e0e0 --- /dev/null +++ b/webclients/novnc/include/util.js @@ -0,0 +1,276 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +"use strict"; +/*jslint bitwise: false, white: false */ +/*global window, console, document, navigator, ActiveXObject */ + +// Globals defined here +var Util = {}; + + +/* + * Make arrays quack + */ + +Array.prototype.push8 = function (num) { + this.push(num & 0xFF); +}; + +Array.prototype.push16 = function (num) { + this.push((num >> 8) & 0xFF, + (num ) & 0xFF ); +}; +Array.prototype.push32 = function (num) { + this.push((num >> 24) & 0xFF, + (num >> 16) & 0xFF, + (num >> 8) & 0xFF, + (num ) & 0xFF ); +}; + +/* + * ------------------------------------------------------ + * Namespaced in Util + * ------------------------------------------------------ + */ + +/* + * Logging/debug routines + */ + +Util._log_level = 'warn'; +Util.init_logging = function (level) { + if (typeof level === 'undefined') { + level = Util._log_level; + } else { + Util._log_level = level; + } + if (typeof window.console === "undefined") { + if (typeof window.opera !== "undefined") { + window.console = { + 'log' : window.opera.postError, + 'warn' : window.opera.postError, + 'error': window.opera.postError }; + } else { + window.console = { + 'log' : function(m) {}, + 'warn' : function(m) {}, + 'error': function(m) {}}; + } + } + + Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {}; + switch (level) { + case 'debug': Util.Debug = function (msg) { console.log(msg); }; + case 'info': Util.Info = function (msg) { console.log(msg); }; + case 'warn': Util.Warn = function (msg) { console.warn(msg); }; + case 'error': Util.Error = function (msg) { console.error(msg); }; + case 'none': + break; + default: + throw("invalid logging type '" + level + "'"); + } +}; +Util.get_logging = function () { + return Util._log_level; +}; +// Initialize logging level +Util.init_logging(); + + +// Set configuration default for Crockford style function namespaces +Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) { + var getter, setter; + + // Default getter function + getter = function (idx) { + if ((type in {'arr':1, 'array':1}) && + (typeof idx !== 'undefined')) { + return cfg[v][idx]; + } else { + return cfg[v]; + } + }; + + // Default setter function + setter = function (val, idx) { + if (type in {'boolean':1, 'bool':1}) { + if ((!val) || (val in {'0':1, 'no':1, 'false':1})) { + val = false; + } else { + val = true; + } + } else if (type in {'integer':1, 'int':1}) { + val = parseInt(val, 10); + } else if (type === 'func') { + if (!val) { + val = function () {}; + } + } + if (typeof idx !== 'undefined') { + cfg[v][idx] = val; + } else { + cfg[v] = val; + } + }; + + // Set the description + api[v + '_description'] = desc; + + // Set the getter function + if (typeof api['get_' + v] === 'undefined') { + api['get_' + v] = getter; + } + + // Set the setter function with extra sanity checks + if (typeof api['set_' + v] === 'undefined') { + api['set_' + v] = function (val, idx) { + if (mode in {'RO':1, 'ro':1}) { + throw(v + " is read-only"); + } else if ((mode in {'WO':1, 'wo':1}) && + (typeof cfg[v] !== 'undefined')) { + throw(v + " can only be set once"); + } + setter(val, idx); + }; + } + + // Set the default value + if (typeof defaults[v] !== 'undefined') { + defval = defaults[v]; + } else if ((type in {'arr':1, 'array':1}) && + (! (defval instanceof Array))) { + defval = []; + } + // Coerce existing setting to the right type + //Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]); + setter(defval); +}; + +// Set group of configuration defaults +Util.conf_defaults = function(cfg, api, defaults, arr) { + var i; + for (i = 0; i < arr.length; i++) { + Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1], + arr[i][2], arr[i][3], arr[i][4]); + } +} + + +/* + * Cross-browser routines + */ + +// Get DOM element position on page +Util.getPosition = function (obj) { + var x = 0, y = 0; + if (obj.offsetParent) { + do { + x += obj.offsetLeft; + y += obj.offsetTop; + obj = obj.offsetParent; + } while (obj); + } + return {'x': x, 'y': y}; +}; + +// Get mouse event position in DOM element +Util.getEventPosition = function (e, obj, scale) { + var evt, docX, docY, pos; + //if (!e) evt = window.event; + evt = (e ? e : window.event); + evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt); + if (evt.pageX || evt.pageY) { + docX = evt.pageX; + docY = evt.pageY; + } else if (evt.clientX || evt.clientY) { + docX = evt.clientX + document.body.scrollLeft + + document.documentElement.scrollLeft; + docY = evt.clientY + document.body.scrollTop + + document.documentElement.scrollTop; + } + pos = Util.getPosition(obj); + if (typeof scale === "undefined") { + scale = 1; + } + return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale}; +}; + + +// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events +Util.addEvent = function (obj, evType, fn){ + if (obj.attachEvent){ + var r = obj.attachEvent("on"+evType, fn); + return r; + } else if (obj.addEventListener){ + obj.addEventListener(evType, fn, false); + return true; + } else { + throw("Handler could not be attached"); + } +}; + +Util.removeEvent = function(obj, evType, fn){ + if (obj.detachEvent){ + var r = obj.detachEvent("on"+evType, fn); + return r; + } else if (obj.removeEventListener){ + obj.removeEventListener(evType, fn, false); + return true; + } else { + throw("Handler could not be removed"); + } +}; + +Util.stopEvent = function(e) { + if (e.stopPropagation) { e.stopPropagation(); } + else { e.cancelBubble = true; } + + if (e.preventDefault) { e.preventDefault(); } + else { e.returnValue = false; } +}; + + +// Set browser engine versions. Based on mootools. +Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)}; + +Util.Engine = { + 'presto': (function() { + return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()), + 'trident': (function() { + return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()), + 'webkit': (function() { + try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()), + //'webkit': (function() { + // return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()), + 'gecko': (function() { + return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }()) +}; +if (Util.Engine.webkit) { + // Extract actual webkit version if available + Util.Engine.webkit = (function(v) { + var re = new RegExp('WebKit/([0-9\.]*) '); + v = (navigator.userAgent.match(re) || ['', v])[1]; + return parseFloat(v, 10); + })(Util.Engine.webkit); +} + +Util.Flash = (function(){ + var v, version; + try { + v = navigator.plugins['Shockwave Flash'].description; + } catch(err1) { + try { + v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version'); + } catch(err2) { + v = '0 r0'; + } + } + version = v.match(/\d+/g); + return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0}; +}()); diff --git a/webclients/novnc/include/vnc.js b/webclients/novnc/include/vnc.js new file mode 100644 index 0000000..f938be7 --- /dev/null +++ b/webclients/novnc/include/vnc.js @@ -0,0 +1,42 @@ +/* + * noVNC: HTML5 VNC client + * Copyright (C) 2011 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + * + * See README.md for usage and integration instructions. + */ + +/*jslint evil: true */ +/*global window, document, INCLUDE_URI */ + +/* + * Load supporting scripts + */ +function get_INCLUDE_URI() { + return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/"; +} + +(function () { + "use strict"; + + var extra = "", start, end; + + start = " + + + + + +
+ +
+ +
+ + + + + + +
+
+ + +
+ + + + + +
+ + +
+ +
+ +
+ + +
+ +
    +
  • Encrypt
  • +
  • True Color
  • +
  • Local Cursor
  • +
  • Clip to window
  • +
  • Shared Mode
  • +
  • Connect Timeout (s)
  • +
  • Path
  • +
    + +
  • +
  • + + +
  • +
  • +
    +
  • +
+
+
+ + +
+
    +
  • +
  • +
  • +
  • +
+
+ +
+ + +
+
+ +
+
Loading
+
+ +

no
VNC

+ + +
+ + Canvas not supported. + +
+ +
+ + + + diff --git a/webclients/novnc/vnc_auto.html b/webclients/novnc/vnc_auto.html new file mode 100644 index 0000000..a500b79 --- /dev/null +++ b/webclients/novnc/vnc_auto.html @@ -0,0 +1,116 @@ + + + + + noVNC + + + + + + + +
+
+ + + +
Loading
+ +
+
+ + Canvas not supported. + +
+ + + + + + diff --git a/webclients/ssl/Makefile.am b/webclients/ssl/Makefile.am new file mode 100644 index 0000000..fd1c201 --- /dev/null +++ b/webclients/ssl/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc + diff --git a/webclients/ssl/README b/webclients/ssl/README new file mode 100644 index 0000000..b244cf1 --- /dev/null +++ b/webclients/ssl/README @@ -0,0 +1,338 @@ +This directory contains a patched Java applet VNC viewer that is SSL +enabled. + +The patches in the *.patch files are relative to the source tarball: + + tightvnc-1.3dev7_javasrc.tar.gz + +currently (4/06) available here: + + http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download + +It also includes some simple patches to: + + - fix richcursor colors + + - make the Java Applet cursor (not the cursor drawn to the canvas + framebuffer) invisible when it is inside the canvas. + + - allow Tab (and some other) keystrokes to be sent to the vnc + server instead of doing widget traversal. + + +This SSL applet should work with any VNC viewer that has an SSL tunnel in +front of it. It has been tested on x11vnc and using the stunnel tunnel +to other VNC servers. + +By default this Vnc Viewer will only do SSL. To do unencrypted traffic +see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc). + +Proxies: they are a general problem with java socket applets (a socket +connection does not go through the proxy). See the info in the proxy.vnc +file for a workaround. It uses SignedVncViewer.jar which is simply +a signed version of VncViewer.jar. The basic idea is the user clicks +"Yes" to trust the applet and then it can connect directly to the proxy +and issue a CONNECT request. + +This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun +Java plugin. It may not work on older releases or different vendor VM's. +Send full Java Console output for failures. + +--------------------------------------------------------------- +Tips: + +When doing single-port proxy connections (e.g. both VNC and HTTPS +thru port 5900) it helps to move through the 'do you trust this site' +dialogs quickly. x11vnc has to wait to see if the traffic is VNC or +HTTP and this can cause timeouts if you don't move thru them quickly. + +You may have to restart your browser completely if it gets into a +weird state. For one case we saw the JVM requesting VncViewer.class +even when no such file exists. + + +--------------------------------------------------------------- +Extras: + +ss_vncviewer (not Java): + + Wrapper script for native VNC viewer to connect to x11vnc in + SSL mode. Script launches stunnel(8) and then connects to it + via localhost which in turn is then redirected to x11vnc via an + SSL tunnel. stunnel(8) must be installed and available in PATH. + + +Running Java SSL VncViewer from the command line: + + From this directory: + + java -cp ./VncViewer.jar VncViewer HOST PORT + + substitute and with the actual values. + You can add any other parameters, e.g.: ignoreProxy yes + +--------------------------------------------------------------- +UltraVNC: + +The UltraVNC java viewer has also been patched to support SSL. Various +bugs in the UltraVNC java viewer were also fixed. This viewer can be +useful because is support UltraVNC filetransfer, and so it works on +Unix, etc. + +UltraViewerSSL.jar +SignedUltraViewerSSL.jar +ultra.vnc +ultraproxy.vnc +ultravnc-102-JavaViewer-ssl-etc.patch + +--------------------------------------------------------------- +Applet Parameters: + +Some additional applet parameters can be set via the URL, e.g. + + http://host:5800/?param=value + http://host:5800/ultra.vnc?param=value + https://host:5900/ultra.vnc?param=value + +etc. If running java from command line as show above, it comes +in as java ... VncViewer param value ... + +There is a limitation with libvncserver that param and value can +only be alphanumeric, underscore, "+" (for space), or "." + +We have added some applet parameters to the stock VNC java +viewers. Here are the applet parameters: + +Both TightVNC and UltraVNC Java viewers: + + HOST + string, default: none. + The Hostname to connect to. + + PORT + number, default: 0 + The VNC server port to connect to. + + Open New Window + yes/no, default: no + Run applet in separate frame. + + Show Controls + yes/no, default: yes + Show Controls button panel. + + Show Offline Desktop + yes/no, default: no + Do we continue showing desktop on remote disconnect? + + Defer screen updates + number, default: 20 + Milliseconds delay + + Defer cursor updates + number, default: 10 + Milliseconds delay + + Defer update requests + number, default: 50 + Milliseconds delay + + PASSWORD + string, default: none + VNC session password in plain text. + + ENCPASSWORD + string, default: none + VNC session password in encrypted in DES with KNOWN FIXED + key. It is a hex string. This is like the ~/.vnc/passwd format. + + + The following are added by x11vnc and/or ssvnc project + + VNCSERVERPORT + number, default: 0 + Like PORT, but if there is a firewall this is the Actual VNC + server port. PORT might be a redir port on the firewall. + + DisableSSL + yes/no, default: no + Do unencrypted connection, no SSL. + + httpsPort + number, default: none + When checking for proxy, use this at the url port number. + + CONNECT + string, default: none + Sets to host:port for the CONNECT line to a Web proxy. + The Web proxy should connect us to it. + + GET + yes/no, default: no + Set to do a special HTTP GET (/request.https.vnc.connection) + to the vnc server that will cause it to switch to VNC instead. + This is to speedup/make more robust, the single port HTTPS and VNC + mode of x11vnc (e.g. both services thru port 5900, etc) + + urlPrefix + string, default: none + set to a string that will be prefixed to all URL's when contacting + the VNC server. Idea is a special proxy will use this to indicate + internal hostname, etc. + + oneTimeKey + string, default: none + set a special hex "key" to correspond to an SSL X.509 cert+key. + See the 'onetimekey' helper script. Can also be PROMPT to prompt + the user to paste the hex key string in. + + This provides a Client-Side cert+key that the client will use to + authenticate itself by SSL To the VNC Server. + + This is to try to work around the problem that the Java applet + cannot keep an SSL keystore on disk, etc. E.g. if they log + into an HTTPS website via password they are authenticated and + encrypted, then the website can safely put oneTimeKey=... on the + URL. The Vncviewer authenticates the VNC server with this key. + + Note that there is currently a problem in that if x11vnc requires + Client Certificates the user cannot download the index.vnc HTML + and VncViewer.jar from the same x11vnc. Those need to come from + a different x11vnc or from a web server. + + Note that the HTTPS website can also put the VNC Password + (e.g. a temporary/one-time one) in the parameter PASSWORD. + The Java Applet will automatically supply this VNC password + instead of prompting. + + serverCert + string, default: none + set a special hex "cert" to correspond to an SSL X.509 cert + See the 'onetimekey -certonly' helper script. + + This provides a Server-Side cert that the client will authenticate + the VNC Server against by SSL. + + This is to try to work around the problem that the Java applet + cannot keep an SSL keystore on disk, etc. E.g. if they log + into an HTTPS website via password they are authenticated and + encrypted, then the website can safely put serverCert=... on the + URL. + + Of course the VNC Server is sending this string to the Java + Applet, so this is only reasonable security if the VNC Viewer + already trusts the HTTPS retrieval of the URL + serverCert param + that it gets. This should be done over HTTPS not HTTP. + + proxyHost + string, default: none + Do not try to guess the proxy's hostname, use the value in + proxyHost. Does not imply forceProxy (below.) + + proxyPort + string, default: none + Do not try to guess the proxy's port number, use the value in + proxyPort. Does not imply forceProxy (below.) + + forceProxy + yes/no, default: no + Assume there is a proxy and force its use. + + If a string other than "yes" or "no" is given, it implies "yes" + and uses the string for proxyHost and proxyPort (see above). + In this case the string must be of the form "hostname+port". + Note that it is "+" and not ":" before the port number. + + ignoreProxy + yes/no, default: no + Don't check for a proxy, assume there is none. + + trustAllVncCerts + yes/no, default: no + Automatically trust any cert received from the VNC server + (obviously this could be dangerous and lead to man in the + middle attack). Do not ask the user to verify any of these + certs from the VNC server. + + trustUrlVncCert + yes/no, default: no + Automatically trust any cert that the web browsers has accepted. + E.g. the user said "Yes" or "Continue" to a web browser dialog + regarding a certificate. If we get the same cert (chain) from + the VNC server we trust it without prompting the user. + + debugCerts + yes/no, default: no + Print out every cert in the Server, TrustUrl, TrustAll chains. + + +TightVNC Java viewer only: + + Offer Relogin + yes/no, default: yes + "Offer Relogin" set to "No" disables "Login again" + + SocketFactory + string, default: none + set Java Socket class factory. + +UltraVNC Java viewer only: + + None. + + The following are added by x11vnc and/or ssvnc project + + ftpDropDown + string, default: none + Sets the file transfer "drives" dropdown to the "." separated + list. Use "+" for space. The default is + + My+Documents.Desktop.Home + + for 3 entries in the dropdown in addition to the "drives" + (e.g. C:\) These items should be expanded properly by the VNC + Server. x11vnc will prepend $HOME to them, which is normally + what one wants. To include a "/" use "_2F_". Another example: + + Home.Desktop.bin_2F_linux + + If an item is prefixed with "TOP_" then the item is inserted at + the top of the drop down rather than being appended to the end. + E.g. to try to initially load the user homedir instead of /: + + TOP_Home.My+Documents.Desktop + + If ftpDropDown is set to the empty string, "", then no special + locations, [Desktop] etc., are placed in the drop down. Only the + ultravnc "drives" will appear. + + ftpOnly + yes/no, default: no + The VNC viewer only shows the filetransfer panel, no desktop + is displayed. + + graftFtp + yes/no, default: no + As ftpOnly, the VNC viewer only shows the filetransfer panel, + no desktop is displayed, however it is "grafted" onto an existing + SSVNC unix vncviewer. The special SSVNC vncviewer merges the two + channels. + + dsmActive + yes/no, default: no + Special usage mode with the SSVNC unix vncviewer. The UltraVNC + DSM encryption is active. Foolishly, UltraVNC DSM encryption + *MODIFIES* the VNC protocol when active (it is not a pure tunnel). + This option indicates to modify the VNC protocol to make this work. + Usually only used with graftFtp and SSVNC unix vncviewer. + + delayAuthPanel + yes/no, default: no + This is another special usage mode with the SSVNC unix vncviewer. + A login panel is delayed (not shown at startup.) Could be useful + for non SSVNC usage too. + + ignoreMSLogonCheck + yes/no, default: no + Similar to delayAuthPanel, do not put up a popup asking for + Windows username, etc. diff --git a/webclients/ssl/SignedUltraViewerSSL.jar b/webclients/ssl/SignedUltraViewerSSL.jar new file mode 100644 index 0000000..6c18737 Binary files /dev/null and b/webclients/ssl/SignedUltraViewerSSL.jar differ diff --git a/webclients/ssl/SignedVncViewer.jar b/webclients/ssl/SignedVncViewer.jar new file mode 100644 index 0000000..95c0b0b Binary files /dev/null and b/webclients/ssl/SignedVncViewer.jar differ diff --git a/webclients/ssl/UltraViewerSSL.jar b/webclients/ssl/UltraViewerSSL.jar new file mode 100644 index 0000000..45259fd Binary files /dev/null and b/webclients/ssl/UltraViewerSSL.jar differ diff --git a/webclients/ssl/VncViewer.jar b/webclients/ssl/VncViewer.jar new file mode 100644 index 0000000..9453c6f Binary files /dev/null and b/webclients/ssl/VncViewer.jar differ diff --git a/webclients/ssl/index.vnc b/webclients/ssl/index.vnc new file mode 100644 index 0000000..ec520dc --- /dev/null +++ b/webclients/ssl/index.vnc @@ -0,0 +1,26 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + +$PARAMS + +
+
x11vnc site + diff --git a/webclients/ssl/onetimekey b/webclients/ssl/onetimekey new file mode 100755 index 0000000..bf57c8f --- /dev/null +++ b/webclients/ssl/onetimekey @@ -0,0 +1,65 @@ +#!/bin/sh +# +# usage: onetimekey path/to/mycert.pem +# onetimekey -certonly path/to/mycert.pem +# +# Takes an openssl cert+key pem file and turns into a long string +# for the x11vnc SSL VNC Java Viewer. +# +# The Java applet URL parameter can be oneTimeKey= where str is +# the output of this program, or can be oneTimeKey=PROMPT in which +# case the applet will ask you to paste in the string. +# +# The problem trying to be solved here is it is difficult to get +# the Java applet to have or use a keystore with the key saved +# in it. Also, as the name implies, an HTTPS server can create +# a one time key to send to the applet (the user has already +# logged in via password to the HTTPS server). +# +# Note oneTimeKey is to provide a CLIENT Certificate for the viewer +# to authenticate itself to the VNC Server. +# +# There is also the serverCert= Applet parameter. This is +# a cert to authenticate the VNC server against. To create that +# string with this tool specify -certonly as the first argument. + +certonly="" +if [ "X$1" = "X-certonly" ]; then + shift + certonly=1 +fi + +in=$1 +der=/tmp/1time$$.der +touch $der +chmod 600 $der + +openssl pkcs8 -topk8 -nocrypt -in "$in" -out "$der" -outform der + +pbinhex=/tmp/pbinhex.$$ +cat > $pbinhex < + + + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/ssl/ss_vncviewer b/webclients/ssl/ss_vncviewer new file mode 100755 index 0000000..7e793ff --- /dev/null +++ b/webclients/ssl/ss_vncviewer @@ -0,0 +1,3676 @@ +#!/bin/sh +# +# ss_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel +# or an SSH tunnel. +# +# Copyright (c) 2006-2009 by Karl J. Runge +# +# ss_vncviewer 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. +# +# ss_vncviewer 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 ss_vncviewer; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA +# or see . +# +# +# You must have stunnel(8) installed on the system and in your PATH +# (however, see the -ssh option below, in which case you will need ssh(1) +# installed) Note: stunnel is usually installed in an "sbin" subdirectory. +# +# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." +# already running as the VNC server on the remote machine. +# (or use stunnel on the server side for any other VNC server) +# +# +# Usage: ss_vncviewer [cert-args] host:display +# +# e.g.: ss_vncviewer snoopy:0 +# ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" +# +# [cert-args] can be: +# +# -verify /path/to/cacert.pem +# -mycert /path/to/mycert.pem +# -crl /path/to/my_crl.pem (or directory) +# -proxy host:port +# +# -verify specifies a CA cert PEM file (or a self-signed one) for +# authenticating the VNC server. +# +# -mycert specifies this client's cert+key PEM file for the VNC server to +# authenticate this client. +# +# -proxy try host:port as a Web proxy to use the CONNECT method +# to reach the VNC server (e.g. your firewall requires a proxy). +# +# For the "double proxy" case use -proxy host1:port1,host2:port2 +# (the first CONNECT is done through host1:port1 to host2:port2 +# and then a 2nd CONNECT to the destination VNC server.) +# +# Use socks://host:port, socks4://host:port, or socks5://host,port +# to force usage of a SOCKS proxy. Also repeater://host:port and +# sslrepeater://host:port. +# +# -showcert Only fetch the certificate using the 'openssl s_client' +# command (openssl(1) must in installed). On ssvnc 1.0.27 and +# later the bundled command 'ultravnc_dsm_helper' is used. +# +# See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on +# SSL certificates with VNC. +# +# A few other args (not related to SSL and certs): +# +# -2nd Run the vncviewer a 2nd time if the first connections fails. +# +# -ssh Use ssh instead of stunnel SSL. ssh(1) must be installed and you +# must be able to log into the remote machine via ssh. +# +# In this case "host:display" may be of the form "user@host:display" +# where "user@host" is used for the ssh login (see ssh(1) manpage). +# +# If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port" +# "user@gwhost" or "user@gwhost:port". "gwhost" is an incoming ssh +# gateway machine (the VNC server is not running there), an ssh -L +# redir is used to "host" in "host:display" from "gwhost". Any "user@" +# part must be in the -proxy string (not in "host:display"). +# +# Under -proxy use "gwhost:port" if connecting to any ssh port +# other than the default (22). (even for the non-gateway case, +# -proxy must be used to specify a non-standard ssh port) +# +# A "double ssh" can be specified via a -proxy string with the two +# hosts separated by a comma: +# +# [user1@]host1[:port1],[user2@]host2[:port2] +# +# in which case a ssh to host1 and thru it via a -L redir a 2nd +# ssh is established to host2. +# +# Examples: +# +# ss_vncviewer -ssh bob@bobs-home.net:0 +# ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0 +# +# ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0 +# ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0 +# +# ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0 +# +# -sshcmd cmd Run "cmd" via ssh instead of the default "sleep 15" +# e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900' +# +# -sshargs "args" pass "args" to the ssh process, e.g. -L/-R port redirs. +# +# -sshssl Tunnel the SSL connection thru a SSH connection. The tunnel as +# under -ssh is set up and the SSL connection goes thru it. Use +# this if you want to have and end-to-end SSL connection but must +# go thru a SSH gateway host (e.g. not the vnc server). Or use +# this if you need to tunnel additional services via -R and -L +# (see -sshargs above). +# +# ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0 +# +# -listen (or -reverse) set up a reverse connection. +# +# -alpha turn on cursor alphablending hack if you are using the +# enhanced tightvnc vncviewer. +# +# -grab turn on XGrabServer hack if you are using the enhanced tightvnc +# vncviewer (e.g. for fullscreen mode in some windowmanagers like +# fvwm that do not otherwise work in fullscreen mode) +# +# +# set VNCVIEWERCMD to whatever vncviewer command you want to use. +# +VNCIPCMD=${VNCVIEWERCMD:-vncip} +VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer} +if [ "X$SSVNC_TURBOVNC" != "X" ]; then + if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then + : + else + if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then + VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc" + fi + fi +fi +# +# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc. +# + +# turn on verbose debugging output +if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then + set -xv +fi + +PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH + +localhost="localhost" +if uname | grep Darwin >/dev/null; then + localhost="127.0.0.1" +fi + +# work out which stunnel to use (debian installs as stunnel4) +stunnel_set_here="" +if [ "X$STUNNEL" = "X" ]; then + check_stunnel=1 + if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then + if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then + type stunnel > /dev/null 2>&1 + if [ $? = 0 ]; then + # found ours + STUNNEL=stunnel + check_stunnel=0 + fi + fi + fi + if [ "X$check_stunnel" = "X1" ]; then + type stunnel4 > /dev/null 2>&1 + if [ $? = 0 ]; then + STUNNEL=stunnel4 + else + STUNNEL=stunnel + fi + fi + stunnel_set_here=1 +fi + +help() { + tail -n +2 "$0" | sed -e '/^$/ q' +} + +secondtry="" +gotalpha="" +use_ssh="" +use_sshssl="" +direct_connect="" +ssh_sleep=15 + +# sleep longer in -listen mode: +if echo "$*" | grep '.*-listen' > /dev/null; then + ssh_sleep=1800 +fi + + +ssh_cmd="" +# env override of ssh_cmd: +if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then + ssh_cmd="$SS_VNCVIEWER_SSH_CMD" +fi + +ssh_args="" +showcert="" +reverse="" + +ciphers="" +anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH" +anondh_set="" +stunnel_debug="6" +if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then + stunnel_debug="7" +fi + +if [ "X$1" = "X-viewerflavor" ]; then + # special case, try to guess which viewer: + # + if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + echo "unknown" + exit 0 + fi + if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then + echo "cotvnc" + exit 0 + fi + if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then + echo "ultravnc" + exit 0 + fi + # OK, run it for help output... + str=`$VNCVIEWERCMD -h 2>&1 | head -n 5` + if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then + echo "tightvnc" + elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then + echo "realvnc3" + elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then + echo "realvnc4" + elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then + echo "realvnc4" + else + echo "unknown" + fi + exit 0 +fi +if [ "X$1" = "X-viewerhelp" ]; then + $VNCVIEWERCMD -h 2>&1 + exit 0 +fi + +# grab our cmdline options: +while [ "X$1" != "X" ] +do + case $1 in + "-verify") shift; verify="$1" + ;; + "-mycert") shift; mycert="$1" + ;; + "-crl") shift; crl="$1" + ;; + "-proxy") shift; proxy="$1" + ;; + "-ssh") use_ssh=1 + ;; + "-sshssl") use_ssh=1 + use_sshssl=1 + ;; + "-sshcmd") shift; ssh_cmd="$1" + ;; + "-sshargs") shift; ssh_args="$1" + ;; + "-anondh") ciphers="ciphers=$anondh" + ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1 + export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH + anondh_set=1 + ;; + "-ciphers") shift; ciphers="ciphers=$1" + ;; + "-alpha") gotalpha=1 + ;; + "-showcert") showcert=1 + ;; + "-listen") reverse=1 + ;; + "-reverse") reverse=1 + ;; + "-2nd") secondtry=1 + ;; + "-grab") VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER + ;; + "-x11cursor") VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR + ;; + "-rawlocal") VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL + ;; + "-scale") shift; SSVNC_SCALE="$1"; export SSVNC_SCALE + ;; + "-onelisten") SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + ;; + "-sendclipboard") VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD + ;; + "-sendalways") VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS + ;; + "-recvtext") shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT + ;; + "-escape") shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE + ;; + "-ssvnc_encodings") shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS + ;; + "-ssvnc_extra_opts") shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS + ;; + "-rfbversion") shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION + ;; + "-nobell") VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL + ;; + "-popupfix") VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX + ;; + "-realvnc4") VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4 + ;; + "-h"*) help; exit 0 + ;; + "--h"*) help; exit 0 + ;; + *) break + ;; + esac + shift +done + +# maxconn is something we added to stunnel, this disables it: +if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +elif [ "X$reverse" != "X" ]; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +else + # new way (our patches). other than the above, we set these: + if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then + STUNNEL_ONCE=1; export STUNNEL_ONCE + fi + if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then + STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS + fi +fi +# always set this one: +if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then + STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG +fi + +# this is the -t ssh option (gives better keyboard response thru SSH tunnel) +targ="-t" +if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then + targ="" +fi + +# set the alpha blending env. hack: +if [ "X$gotalpha" = "X1" ]; then + VNCVIEWER_ALPHABLEND=1 + export VNCVIEWER_ALPHABLEND +else + NO_ALPHABLEND=1 + export NO_ALPHABLEND +fi + +if [ "X$reverse" != "X" ]; then + ssh_sleep=1800 + if [ "X$proxy" != "X" ]; then + # check proxy usage under reverse connection: + if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then + echo "" + if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then + : + else + echo "*Warning*: SSL -listen and a Web proxy does not make sense." + sleep 2 + fi + elif echo "$proxy" | grep "," > /dev/null; then + : + else + echo "" + echo "*Warning*: -listen and a single proxy/gateway does not make sense." + sleep 2 + fi + + # we now try to PPROXY_LOOP_THYSELF, set this var to disable that. + #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + fi +fi +if [ "X$ssh_cmd" = "X" ]; then + # if no remote ssh cmd, sleep a bit: + ssh_cmd="sleep $ssh_sleep" +fi + +# this should be a host:display: +# +orig="$1" +shift + +dL="-L" +if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then + dL="-h" +fi + +have_uvnc_dsm_helper_showcert="" +if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then + if type ultravnc_dsm_helper >/dev/null 2>&1; then + if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then + have_uvnc_dsm_helper_showcert=1 + fi + fi +fi +have_uvnc_dsm_helper_ipv6="" +if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if type ultravnc_dsm_helper >/dev/null 2>&1; then + if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then + have_uvnc_dsm_helper_ipv6=1 + fi + fi +fi + +rchk() { + # a kludge to set $RANDOM if we are not bash: + if [ "X$BASH_VERSION" = "X" ]; then + RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'` + fi +} +rchk + +# a portable, but not absolutely safe, tmp file creator +mytmp() { + tf=$1 + if type mktemp > /dev/null 2>&1; then + # if we have mktemp(1), use it: + tf2="$tf.XXXXXX" + tf2=`mktemp "$tf2"` + if [ "X$tf2" != "X" -a -f "$tf2" ]; then + if [ "X$DEBUG_MKTEMP" != "X" ]; then + echo "mytmp-mktemp: $tf2" 1>&2 + fi + echo "$tf2" + return + fi + fi + # fallback to multiple cmds: + rm -rf "$tf" || exit 1 + if [ -d "$tf" ]; then + echo "tmp file $tf still exists as a directory." + exit 1 + elif [ $dL "$tf" ]; then + echo "tmp file $tf still exists as a symlink." + exit 1 + elif [ -f "$tf" ]; then + echo "tmp file $tf still exists." + exit 1 + fi + touch "$tf" || exit 1 + chmod 600 "$tf" || exit 1 + rchk + if [ "X$DEBUG_MKTEMP" != "X" ]; then + echo "mytmp-touch: $tf" 1>&2 + fi + echo "$tf" +} + +# set up special case of ultravnc single click III mode: +if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then + pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'` + pstr1=`echo "$pstr" | sed -e 's/+.*$//'` + pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'` + SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER + orig=$pstr1 + echo + echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''" + proxy="" +fi +if echo "$proxy" | egrep "vencrypt://" > /dev/null; then + vtmp="/tmp/ss_handshake${RANDOM}.$$.txt" + vtmp=`mytmp "$vtmp"` + SSVNC_PREDIGESTED_HANDSHAKE="$vtmp" + export SSVNC_PREDIGESTED_HANDSHAKE + if [ "X$SSVNC_USE_OURS" = "X" ]; then + NEED_VENCRYPT_VIEWER_BRIDGE=1 + fi +fi +if [ "X$SSVNC_USE_OURS" = "X" ]; then + VNCVIEWERCMD_EXTRA_OPTS="" +fi + + +# check -ssh and -mycert/-verify conflict: +if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then + if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then + echo "-mycert and -verify cannot be used in -ssh mode" + exit 1 + fi +fi + +# direct mode Vnc:// means show no warnings. +# direct mode vnc:// will show warnings. +if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then + SSVNC_NO_ENC_WARN=1 + export SSVNC_NO_ENC_WARN + orig=`echo "$orig" | sed -e 's/^...:/vnc:/'` +fi + +# interprest the pseudo URL proto:// strings: +if echo "$orig" | grep '^vnc://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc://,,'` + verify="" + mycert="" + crl="" + use_ssh="" + use_sshssl="" + direct_connect=1 +elif echo "$orig" | grep '^vncs://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncs://,,'` +elif echo "$orig" | grep '^vncssl://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncssl://,,'` +elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'` +elif echo "$orig" | grep '^vncssh://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncssh://,,'` + use_ssh=1 +elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'` + use_ssh=1 +fi + +if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + verify="" + mycert="" + crl="" + use_ssh="" + use_sshssl="" + direct_connect=1 + if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then + SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM + fi +fi + +# rsh mode is an internal/secret thing only I use. +rsh="" +if echo "$orig" | grep '^rsh://' > /dev/null; then + use_ssh=1 + rsh=1 + orig=`echo "$orig" | sed -e 's,rsh://,,'` +elif echo "$orig" | grep '^rsh:' > /dev/null; then + use_ssh=1 + rsh=1 + orig=`echo "$orig" | sed -e 's,rsh:,,'` +fi + +# play around with host:display port: +if echo "$orig" | grep ':[0-9][0-9]*$' > /dev/null; then + : +else + # add or assume :0 if no ':' + if [ "X$reverse" = "X" ]; then + orig="$orig:0" + elif [ "X$orig" = "X" ]; then + orig=":0" + fi +fi + +# extract host and disp number: + +# try to see if it is ipv6 address: +ipv6=0 +if echo "$orig" | grep '\[' > /dev/null; then + # ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900 + host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'` + disp=`echo "$orig" | sed -e 's/^.*\]://'` + ipv6=1 +elif echo "$orig" | grep ':..*:' > /dev/null; then + # ipv6 fe80::219:dbff:fee5:3f92%eth1:5900 + host=`echo "$orig" | sed -e 's/:[^:]*$//'` + disp=`echo "$orig" | sed -e 's/^.*://'` + ipv6=1 +else + # regular host:port + host=`echo "$orig" | awk -F: '{print $1}'` + disp=`echo "$orig" | awk -F: '{print $2}'` +fi + +if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then + STUNNEL_LISTEN=$host + echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN" +fi + +if [ "X$host" = "X" ]; then + host=$localhost +fi + +if [ "X$SSVNC_IPV6" = "X0" ]; then + # disable checking for it. + ipv6=0 +#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then +# ipv6=0 +elif [ "X$ipv6" = "X1" ]; then + : +elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then + : +else + # regular hostname, can't be sure... + gout="" + if type getent > /dev/null 2>/dev/null; then + gout=`getent hosts "$host" 2>/dev/null` + fi + if echo "$gout" | grep ':.*:' > /dev/null; then + if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then + : + else + echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1` + ipv6=1 + fi + fi + if [ "X$ipv6" = "X0" ]; then + hout="" + if type host > /dev/null 2>/dev/null; then + host "$host" >/dev/null 2>&1 + host "$host" >/dev/null 2>&1 + hout=`host "$host" 2>/dev/null` + fi + if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then + if echo "$hout" | grep -i 'has address' > /dev/null; then + : + else + echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1` + ipv6=1 + fi + fi + fi + if [ "X$ipv6" = "X0" ]; then + dout="" + if type dig > /dev/null 2>/dev/null; then + dout=`dig -t any "$host" 2>/dev/null` + fi + if echo "$dout" | grep -i "^$host" | grep '[ ]AAAA[ ]' > /dev/null; then + if echo "$dout" | grep -i "^$host" | grep '[ ]A[ ]' > /dev/null; then + : + else + echo "ipv6: "`echo "$dout" | grep -i '[ ]AAAA[ ]' | head -n 1` + ipv6=1 + fi + fi + fi + if [ "X$ipv6" = "X0" ]; then + sout=`env LOOKUP="$host" \ + perl -e ' eval {use Socket}; exit 0 if $@; + eval {use Socket6}; exit 0 if $@; + @res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM); + $ipv4 = 0; + $ipv6 = 0; + $ip6 = ""; + while (scalar(@res) >= 5) { + ($family, $socktype, $proto, $saddr, $canon, @res) = @res; + $ipv4 = 1 if $family == AF_INET; + $ipv6 = 1 if $family == AF_INET6; + if ($family == AF_INET6 && $ip6 eq "") { + my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV); + $ip6 = $host; + } + } + if (! $ipv4 && $ipv6) { + print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n"; + } + exit 0; + ' 2>/dev/null` + if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then + echo "$sout" + ipv6=1 + fi + fi +fi +if [ "X$ipv6" = "X1" ]; then + echo "ipv6: addr=$host disp=$disp" +fi +if [ "X$disp" = "X" ]; then + port="" # probably -listen mode. +elif [ $disp -lt 0 ]; then + # negative means use |n| without question: + port=`expr 0 - $disp` +elif [ $disp -lt 200 ]; then + # less than 200 means 5900+n + if [ "X$reverse" = "X" ]; then + port=`expr $disp + 5900` + else + port=`expr $disp + 5500` + fi +else + # otherwise use the number directly, e.g. 443, 2345 + port=$disp +fi + +if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then + if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then + if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then + : + elif [ "X$SSVNC_USE_OURS" = "X1" ]; then + # requires 1.0.27 and later ssvncviewer binary + : + else + proxy="ipv6://$host:$port" + echo "direct connect: set proxy=$proxy" + fi + fi +fi + +# (possibly) tell the vncviewer to only listen on lo: +if [ "X$reverse" != "X" ]; then + if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then + VNCVIEWER_LISTEN_LOCALHOST=1 + export VNCVIEWER_LISTEN_LOCALHOST + fi +fi + +# try to find an open listening port via netstat(1): +inuse="" +if uname | grep Linux > /dev/null; then + inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'` +elif uname | grep SunOS > /dev/null; then + inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'` +elif uname | egrep -i 'bsd|darwin' > /dev/null; then + inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'` +# add others... +fi + +# this is a crude attempt for unique ports tags, etc. +date_sec=`date +%S` + +# these are special cases of no vnc, e.g. sleep or xmessage. +# these are for using ssvnc as a general port redirector. +if echo "$VNCVIEWERCMD" | grep '^sleep[ ][ ]*[0-9][0-9]*' > /dev/null; then + if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then + p=`echo "$VNCVIEWERCMD" | awk '{print $3}'` + if [ "X$p" != "X" ]; then + SS_VNCVIEWER_LISTEN_PORT=$p + fi + fi + p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'` + VNCVIEWERCMD="eval sleep $p2; echo Local " +elif echo "$VNCVIEWERCMD" | grep '^xmessage[ ][ ]*[0-9][0-9]*' > /dev/null; then + if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then + p=`echo "$VNCVIEWERCMD" | awk '{print $2}'` + SS_VNCVIEWER_LISTEN_PORT=$p + fi +fi + +# utility to find a free port to listen on. +findfree() { + try0=$1 + try=$try0 + use0="" + + if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then + echo "$SS_VNCVIEWER_LISTEN_PORT" + return + fi + if [ $try -ge 6000 ]; then + fmax=`expr $try + 1000` + else + fmax=6000 + fi + + while [ $try -lt $fmax ] + do + if [ "X$inuse" = "X" ]; then + break + fi + if echo "$inuse" | grep -w $try > /dev/null; then + : + else + use0=$try + break + fi + try=`expr $try + 1` + done + if [ "X$use0" = "X" ]; then + use0=`expr $date_sec + $try0` + fi + + echo $use0 +} + +# utility for exiting; kills some helper processes, +# removes files, etc. +final() { + echo "" + if [ "X$tmp_cfg" != "X" ]; then + rm -f $tmp_cfg + fi + if [ "X$SS_VNCVIEWER_RM" != "X" ]; then + rm -f $SS_VNCVIEWER_RM 2>/dev/null + fi + if [ "X$tcert" != "X" ]; then + rm -f $tcert + fi + if [ "X$pssh" != "X" ]; then + echo "Terminating background ssh process" + echo kill -TERM "$pssh" + kill -TERM "$pssh" 2>/dev/null + sleep 1 + kill -KILL "$pssh" 2>/dev/null + pssh="" + fi + if [ "X$stunnel_pid" != "X" ]; then + echo "Terminating background stunnel process" + echo kill -TERM "$stunnel_pid" + kill -TERM "$stunnel_pid" 2>/dev/null + sleep 1 + kill -KILL "$stunnel_pid" 2>/dev/null + stunnel_pid="" + fi + if [ "X$dsm_pid" != "X" ]; then + echo "Terminating background ultravnc_dsm_helper process" + echo kill -TERM "$dsm_pid" + kill -TERM "$dsm_pid" 2>/dev/null + sleep 1 + kill -KILL "$dsm_pid" 2>/dev/null + stunnel_pid="" + fi + if [ "X$tail_pid" != "X" ]; then + kill -TERM $tail_pid + fi + if [ "X$tail_pid2" != "X" ]; then + kill -TERM $tail_pid2 + fi +} + +if [ "X$reverse" = "X" ]; then + # normal connections try 5930-5999: + if [ "X$showcert" = "X" ]; then + use=`findfree 5930` + else + # move away from normal place for (possibly many) -showcert + pstart=`date +%S` + pstart=`expr 6130 + $pstart + $pstart` + use=`findfree $pstart` + fi + if [ $use -ge 5900 ]; then + N=`expr $use - 5900` + else + N=$use + fi +else + # reverse connections: + p2=`expr $port + 30` + use=`findfree $p2` + if [ $use -ge 5500 ]; then + N=`expr $use - 5500` + else + N=$use + fi +fi + +# this is for my special use of ss_vncip -> vncip viewer. +if echo "$0" | grep vncip > /dev/null; then + VNCVIEWERCMD="$VNCIPCMD" +fi + +if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + : +elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then + VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS" +fi + +# trick for the undocumented rsh://host:port method. +rsh_setup() { + if echo "$ssh_host" | grep '@' > /dev/null; then + ul=`echo "$ssh_host" | awk -F@ '{print $1}'` + ul="-l $ul" + ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'` + else + ul="" + fi + ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'` +} + +# trick for the undocumented rsh://host:port method. +rsh_viewer() { + trap "final" 0 2 15 + if [ "X$PORT" = "X" ]; then + exit 1 + elif [ $PORT -ge 5900 ]; then + vdpy=`expr $PORT - 5900` + else + vdpy=":$PORT" + fi + stty sane + echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy + echo "" + $VNCVIEWERCMD "$@" $ssh_host:$vdpy + if [ $? != 0 ]; then + sleep 2 + $VNCVIEWERCMD "$@" $ssh_host:$vdpy + fi +} + +check_perl() { + if type "$1" > /dev/null 2>&1; then + : + elif [ ! -x "$1" ]; then + echo "" + echo "*******************************************************" + echo "** Problem finding the Perl command '$1': **" + echo "" + type "perl" + echo "" + echo "** Perhaps you need to install the Perl package. **" + echo "*******************************************************" + echo "" + sleep 5 + fi +} + +# this is the PPROXY tool. used only here for now... +pcode() { + tf=$1 + PPROXY_PROXY=$proxy; export PPROXY_PROXY + PPROXY_DEST="$host:$port"; export PPROXY_DEST + check_perl /usr/bin/perl + + cod='#!/usr/bin/perl + +# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for +# client connections. +# Also acts as a VeNCrypt bridge (by redirecting to stunnel.) + +use IO::Socket::INET; + +my $have_inet6 = ""; +eval "use IO::Socket::INET6;"; +$have_inet6 = 1 if $@ eq ""; + +#my $have_sock6 = ""; +#eval "use Socket; use Socket6;"; +#$have_sock6 = 1 if $@ eq ""; + +if (exists $ENV{PPROXY_LOOP_THYSELF}) { + # used for reverse vnc, run a repeating outer loop. + print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n"; + my $rm = $ENV{PPROXY_REMOVE}; + my $lp = $ENV{PPROXY_LOOP_THYSELF}; + delete $ENV{PPROXY_REMOVE}; + delete $ENV{PPROXY_LOOP_THYSELF}; + $ENV{PPROXY_LOOP_THYSELF_MASTER} = $$; + my $pid = $$; + my $dbg = 0; + my $c = 0; + use POSIX ":sys_wait_h"; + while (1) { + $pid = fork(); + last if ! defined $pid; + if ($pid eq "0") { + last; + } + $c++; + print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n"; + while (1) { + waitpid(-1, WNOHANG); + fsleep(0.25); + if (! kill 0, $pid) { + print STDERR "PPROXY_LOOP: child=$pid gone.\n"; + last; + } + print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg; + if (! -f $lp) { + print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n"; + kill TERM, $pid; + fsleep(0.1); + wait; + last; + } + print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg; + } + last if ! -f $lp; + fsleep(0.25); + } + if ($pid ne "0") { + unlink($0) if $rm; + exit 0; + } +} + +if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) { + print STDERR "PPROXY_PID: $$\n"; + sleep $ENV{PPROXY_SLEEP}; +} + +foreach my $var (qw( + PPROXY_DEST + PPROXY_KILLPID + PPROXY_LISTEN + PPROXY_PROXY + PPROXY_REMOVE + PPROXY_REPEATER + PPROXY_REVERSE + PPROXY_SLEEP + PPROXY_SOCKS + PPROXY_VENCRYPT + PPROXY_VENCRYPT_VIEWER_BRIDGE + )) { + if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) { + print STDERR "$var: $ENV{$var}\n"; + } +} + +if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) { + if ($ENV{PPROXY_SOCKS} eq "5") { + $ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}"; + } else { + $ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}"; + } +} + +my $rfbSecTypeAnonTls = 18; +my $rfbSecTypeVencrypt = 19; + +my $rfbVencryptPlain = 256; +my $rfbVencryptTlsNone = 257; +my $rfbVencryptTlsVnc = 258; +my $rfbVencryptTlsPlain = 259; +my $rfbVencryptX509None = 260; +my $rfbVencryptX509Vnc = 261; +my $rfbVencryptX509Plain = 262; + +my $handshake_file = ""; +if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE}) { + $handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE}; +} + +my $have_gettimeofday = 0; +eval "use Time::HiRes;"; +if ($@ eq "") { + $have_gettimeofday = 1; +} +sub gettime { + my $t = "0.0"; + if ($have_gettimeofday) { + $t = Time::HiRes::gettimeofday(); + } + return $t; +} + +my $listen_handle = ""; +my $sock = ""; +my $parent = $$; + +my $initial_data = ""; + +if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { + my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}); + do_vencrypt_viewer_bridge($from, $to); + exit 0; +} + +my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3); +my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", ""); + +($first, $mode_1st) = url_parse($first); + +my ($proxy_host, $proxy_port) = ($first, ""); +if ($proxy_host =~ /^(.*):(\d+)$/) { + $proxy_host = $1; + $proxy_port = $2; +} +my $connect = $ENV{PPROXY_DEST}; + +if ($second ne "") { + ($second, $mode_2nd) = url_parse($second); +} + +if ($third ne "") { + ($third, $mode_3rd) = url_parse($third); +} + + +print STDERR "\n"; +print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n"; +print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n"; +print STDERR "proxy_host: $proxy_host\n"; +print STDERR "proxy_port: $proxy_port\n"; +print STDERR "proxy_connect: $connect\n"; +print STDERR "pproxy_params: $ENV{PPROXY_PROXY}\n"; +print STDERR "pproxy_listen: $ENV{PPROXY_LISTEN}\n"; +print STDERR "pproxy_reverse: $ENV{PPROXY_REVERSE}\n"; +print STDERR "io_socket_inet6: $have_inet6\n"; +print STDERR "\n"; +if (! $have_inet6) { + print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n"; +} + +if (1) { + print STDERR "pproxy 1st: $first\t- $mode_1st\n"; + print STDERR "pproxy 2nd: $second\t- $mode_2nd\n"; + print STDERR "pproxy 3rd: $third\t- $mode_3rd\n"; + print STDERR "\n"; +} + +sub pdie { + my $msg = shift; + kill_proxy_pids(); + die "$msg"; +} + +if ($ENV{PPROXY_REVERSE} ne "") { + my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, ""); + if ($rhost =~ /^(.*):(\d+)$/) { + $rhost = $1; + $rport = $2; + } + $rport = 5900 unless $rport; + my $emsg = ""; + $listen_handle = IO::Socket::INET->new( + PeerAddr => $rhost, + PeerPort => $rport, + Proto => "tcp" + ); + $emsg = $!; + if (! $listen_handle && $have_inet6) { + eval {$listen_handle = IO::Socket::INET6->new( + PeerAddr => $rhost, + PeerPort => $rport, + Proto => "tcp" + );}; + $emsg .= " / $!"; + } + if (! $listen_handle) { + pdie "pproxy: $emsg -- PPROXY_REVERSE\n"; + } + print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n"; + +} elsif ($ENV{PPROXY_LISTEN} ne "") { + my $listen_sock = ""; + my $maxtry = 12; + my $sleep = 5; + my $p2 = ""; + my $emsg = ""; + for (my $i=0; $i < $maxtry; $i++) { + my ($if, $p) = ("", $ENV{PPROXY_LISTEN}); + if ($p =~ /^(.*):(\d+)$/) { + $if = $1; + $p = $2; + } + $p2 = "*:$p"; + if ($if eq "") { + $if = "localhost"; + } + print STDERR "pproxy interface: $if\n"; + + $emsg = ""; + if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) { + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => "::", + LocalPort => $p, + Proto => "tcp" + );}; + $p2 = ":::$p"; + } elsif ($if =~ /^INADDR_ANY/) { + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalPort => $p, + Proto => "tcp" + ); + } elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) { + $p2 = "::1:$p"; + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => "::1", + LocalPort => $p, + Proto => "tcp" + );}; + $p2 = "::1:$p"; + } else { + $p2 = "$if:$p"; + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalAddr => $if, + LocalPort => $p, + Proto => "tcp" + ); + $emsg = $!; + + if (! $listen_sock && $have_inet6) { + print STDERR "PPROXY_LISTEN: retry with INET6\n"; + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => $if, + LocalPort => $p, + Proto => "tcp" + );}; + $emsg .= " / $!"; + } + } + if (! $listen_sock) { + if ($i < $maxtry - 1) { + warn "pproxy: $emsg $!\n"; + warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n"; + sleep $sleep; + } + } else { + last; + } + } + if (! $listen_sock) { + pdie "pproxy: $emsg -- PPROXY_LISTEN\n"; + } + print STDERR "pproxy: listening on $p2\n"; + my $ip; + ($listen_handle, $ip) = $listen_sock->accept(); + my $err = $!; + close $listen_sock; + if (! $listen_handle) { + pdie "pproxy: $err\n"; + } + + if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { + my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; + if ($sml ne "" && $sml ne "0") { + setpgrp(0, 0); + if (fork()) { + close $viewer_sock; + wait; + exit 0; + } + if (fork()) { + close $viewer_sock; + exit 0; + } + setpgrp(0, 0); + $parent = $$; + } + } +} + +$sock = IO::Socket::INET->new( + PeerAddr => $proxy_host, + PeerPort => $proxy_port, + Proto => "tcp" +); + +my $err = ""; + +if (! $sock && $have_inet6) { + $err = $!; + + print STDERR "pproxy: $!\n"; + + eval {$sock = IO::Socket::INET6->new( + PeerAddr => $proxy_host, + PeerPort => $proxy_port, + Proto => "tcp" + );}; + $err .= " / $!"; +} + +if (! $sock && ($proxy_host =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i || $proxy_host =~ /^::ffff:([\da-f]+:[\da-f]+)$/i)) { + print STDERR "pproxy: $!\n"; + my $ipv4_addr = $1; + if ($ipv4_addr =~ /:/) { + my ($a, $b) = split(/:/, $ipv4_addr); + $a = hex($a); + $b = hex($b); + $ipv4_addr = sprintf("%d.", ($a & 0xff00) >> 8); + $ipv4_addr .= sprintf("%d.", ($a & 0x00ff)); + $ipv4_addr .= sprintf("%d.", ($b & 0xff00) >> 8); + $ipv4_addr .= sprintf("%d", ($b & 0x00ff)); + } + + print STDERR "pproxy: re-trying with ipv4 addr: $ipv4_addr\n"; + + eval {$sock = IO::Socket::INET->new( + PeerAddr => $ipv4_addr, + PeerPort => $proxy_port, + Proto => "tcp" + );}; + $err .= " / $!"; +} + +if (! $sock) { + unlink($0) if $ENV{PPROXY_REMOVE}; + pdie "pproxy: $err\n"; +} + +unlink($0) if $ENV{PPROXY_REMOVE}; + +if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) { + print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n"; + my $tmp_swap = $sock; + $sock = $listen_handle; + $listen_handle = $tmp_swap; +} + +$cur_proxy = $first; +setmode($mode_1st); + +if ($second ne "") { + connection($second, 1); + + setmode($mode_2nd); + $cur_proxy = $second; + + if ($third ne "") { + connection($third, 2); + setmode($mode_3rd); + $cur_proxy = $third; + connection($connect, 3); + } else { + connection($connect, 2); + } +} else { + connection($connect, 1); +} + +sub kill_proxy_pids() { + if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { + return; + } + if ($ENV{PPROXY_KILLPID}) { + foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) { + if ($p =~ /^(\+|-)/) { + $p = $parent + $p; + } + print STDERR "kill TERM, $p (PPROXY_KILLPID)\n"; + kill "TERM", $p; + } + } +} + +sub xfer { + my($in, $out) = @_; + $RIN = $WIN = $EIN = ""; + $ROUT = ""; + vec($RIN, fileno($in), 1) = 1; + vec($WIN, fileno($in), 1) = 1; + $EIN = $RIN | $WIN; + + while (1) { + my $nf = 0; + while (! $nf) { + $nf = select($ROUT=$RIN, undef, undef, undef); + } + my $len = sysread($in, $buf, 8192); + if (! defined($len)) { + next if $! =~ /^Interrupted/; + print STDERR "pproxy[$$]: $!\n"; + last; + } elsif ($len == 0) { + print STDERR "pproxy[$$]: Input is EOF.\n"; + last; + } + my $offset = 0; + my $quit = 0; + while ($len) { + my $written = syswrite($out, $buf, $len, $offset); + if (! defined $written) { + print STDERR "pproxy[$$]: Output is EOF. $!\n"; + $quit = 1; + last; + } + $len -= $written; + $offset += $written; + } + last if $quit; + } + close($out); + close($in); + print STDERR "pproxy[$$]: finished xfer.\n"; +} + +sub handler { + print STDERR "pproxy[$$]: got SIGTERM.\n"; + close $listen_handle if $listen_handle; + close $sock if $sock; + exit; +} + +sub xfer_both { + $child = fork; + + if (! defined $child) { + kill_proxy_pids(); + exit 1; + } + + $SIG{TERM} = "handler"; + + if ($child) { + if ($listen_handle) { + print STDERR "pproxy parent[$$] listen_handle -> socket\n"; + xfer($listen_handle, $sock); + } else { + print STDERR "pproxy parent[$$] STDIN -> socket\n"; + xfer(STDIN, $sock); + } + select(undef, undef, undef, 0.25); + if (kill 0, $child) { + select(undef, undef, undef, 0.9); + if (kill 0, $child) { + print STDERR "pproxy[$$]: kill TERM child $child\n"; + kill "TERM", $child; + } else { + print STDERR "pproxy[$$]: child $child gone.\n"; + } + } + } else { + select(undef, undef, undef, 0.05); + if ($listen_handle) { + print STDERR "pproxy child [$$] socket -> listen_handle\n"; + if ($initial_data ne "") { + my $len = length $initial_data; + print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; + syswrite($listen_handle, $initial_data, $len); + } else { + print STDERR "\n"; + } + xfer($sock, $listen_handle); + } else { + print STDERR "pproxy child [$$] socket -> STDOUT\n"; + if ($initial_data ne "") { + my $len = length $initial_data; + print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; + syswrite(STDOUT, $initial_data, $len); + } else { + print STDERR "\n"; + } + xfer($sock, STDOUT); + } + select(undef, undef, undef, 0.25); + if (kill 0, $parent) { + select(undef, undef, undef, 0.8); + if (kill 0, $parent) { + print STDERR "pproxy[$$]: kill TERM parent $parent\n"; + kill "TERM", $parent; + } else { + print STDERR "pproxy[$$]: parent $parent gone.\n"; + } + } + } + + kill_proxy_pids(); +} + +xfer_both(); + +exit; + +sub fsleep { + select(undef, undef, undef, shift); +} + +sub url_parse { + my $hostport = shift; + my $mode = "http"; + if ($hostport =~ m,^socks4?://(\S*)$,i) { + $mode = "socks4"; + $hostport = $1; + } elsif ($hostport =~ m,^socks5://(\S*)$,i) { + $mode = "socks5"; + $hostport = $1; + } elsif ($hostport =~ m,^https?://(\S*)$,i) { + $mode = "http"; + $hostport = $1; + } elsif ($hostport =~ m,^ipv6://(\S*)$,i) { + $mode = "ipv6"; + $hostport = $1; + } elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) { + # ultravnc repeater proxy. + $hostport = $1; + $mode = "repeater:$2"; + if ($hostport !~ /:\d+$/) { + $hostport .= ":5900"; + } + } elsif ($hostport =~ m,^vencrypt://(\S*)$,i) { + # vencrypt handshake. + $hostport = $1; + my $m = "connect"; + if ($hostpost =~ /^(\S+)\+(\S+)$/) { + $hostport = $1; + $mode = $2; + } + $mode = "vencrypt:$m"; + if ($hostport !~ /:\d+$/) { + $hostport .= ":5900"; + } + } + return ($hostport, $mode); +} + +sub setmode { + my $mode = shift; + $ENV{PPROXY_REPEATER} = ""; + $ENV{PPROXY_VENCRYPT} = ""; + if ($mode =~ /^socks/) { + if ($mode =~ /^socks5/) { + $ENV{PPROXY_SOCKS} = 5; + } else { + $ENV{PPROXY_SOCKS} = 1; + } + } elsif ($mode =~ /^ipv6/i) { + $ENV{PPROXY_SOCKS} = 0; + } elsif ($mode =~ /^repeater:(.*)/) { + $ENV{PPROXY_REPEATER} = $1; + $ENV{PPROXY_SOCKS} = ""; + } elsif ($mode =~ /^vencrypt:(.*)/) { + $ENV{PPROXY_VENCRYPT} = $1; + $ENV{PPROXY_SOCKS} = ""; + } else { + $ENV{PPROXY_SOCKS} = ""; + } +} + +sub connection { + my ($CONNECT, $w) = @_; + + my $con = ""; + my $msg = ""; + + if ($ENV{PPROXY_SOCKS} eq "5") { + # SOCKS5 + my ($h, $p) = ($CONNECT, ""); + if ($h =~ /^(.*):(\d+)$/) { + $h = $1; + $p = $2; + } + $con .= pack("C", 0x05); + $con .= pack("C", 0x01); + $con .= pack("C", 0x00); + + $msg = "SOCKS5 via $cur_proxy to $h:$p\n\n"; + print STDERR "proxy_request$w: $msg"; + + syswrite($sock, $con, length($con)); + + my ($n1, $n2, $n3, $n4, $n5, $n6); + my ($r1, $r2, $r3, $r4, $r5, $r6); + my ($s1, $s2, $s3, $s4, $s5, $s6); + + $n1 = sysread($sock, $r1, 1); + $n2 = sysread($sock, $r2, 1); + + $s1 = unpack("C", $r1); + $s2 = unpack("C", $r2); + if ($s1 != 0x05 || $s2 != 0x00) { + print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n"; + close $sock; + exit(1); + } + + $con = ""; + $con .= pack("C", 0x05); + $con .= pack("C", 0x01); + $con .= pack("C", 0x00); + $con .= pack("C", 0x03); + $con .= pack("C", length($h)); + $con .= $h; + $con .= pack("C", $p >> 8); + $con .= pack("C", $p & 0xff); + + syswrite($sock, $con, length($con)); + + $n1 = sysread($sock, $r1, 1); + $n2 = sysread($sock, $r2, 1); + $n3 = sysread($sock, $r3, 1); + $n4 = sysread($sock, $r4, 1); + $s1 = unpack("C", $r1); + $s2 = unpack("C", $r2); + $s3 = unpack("C", $r3); + $s4 = unpack("C", $r4); + + if ($s4 == 0x1) { + sysread($sock, $r5, 4 + 2); + } elsif ($s4 == 0x3) { + sysread($sock, $r5, 1); + $s5 = unpack("C", $r5); + sysread($sock, $r6, $s5 + 2); + } elsif ($s4 == 0x4) { + sysread($sock, $r5, 16 + 2); + } + + if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) { + print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n"; + close $sock; + exit(1); + } + + } elsif ($ENV{PPROXY_SOCKS} eq "1") { + # SOCKS4 SOCKS4a + my ($h, $p) = ($CONNECT, ""); + if ($h =~ /^(.*):(\d+)$/) { + $h = $1; + $p = $2; + } + $con .= pack("C", 0x04); + $con .= pack("C", 0x01); + $con .= pack("n", $p); + + my $SOCKS_4a = 0; + if ($h eq "localhost" || $h eq "127.0.0.1") { + $con .= pack("C", 127); + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 1); + } elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + $con .= pack("C", $1); + $con .= pack("C", $2); + $con .= pack("C", $3); + $con .= pack("C", $4); + } else { + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 3); + $SOCKS_4a = 1; + } + + $con .= "nobody"; + $con .= pack("C", 0); + + $msg = "SOCKS4 via $cur_proxy to $h:$p\n\n"; + if ($SOCKS_4a) { + $con .= $h; + $con .= pack("C", 0); + $msg =~ s/SOCKS4/SOCKS4a/; + } + print STDERR "proxy_request$w: $msg"; + syswrite($sock, $con, length($con)); + + my $ok = 1; + for (my $i = 0; $i < 8; $i++) { + my $c; + sysread($sock, $c, 1); + my $s = unpack("C", $c); + if ($i == 0) { + $ok = 0 if $s != 0x0; + } elsif ($i == 1) { + $ok = 0 if $s != 0x5a; + } + } + if (! $ok) { + print STDERR "SOCKS4 failed.\n"; + close $sock; + exit(1); + } + } elsif ($ENV{PPROXY_SOCKS} eq "0") { + # hack for ipv6 "proxy", nothing to do, assume INET6 call worked. + ; + } elsif ($ENV{PPROXY_REPEATER} ne "") { + my $rep = $ENV{PPROXY_REPEATER}; + print STDERR "repeater: $rep\n"; + $rep .= pack("x") x 250; + syswrite($sock, $rep, 250); + + my $rfb = ""; + + my $ok = 1; + for (my $i = 0; $i < 12; $i++) { + my $c; + last if $ENV{PPROXY_GENERIC_REPEATER}; + sysread($sock, $c, 1); + print STDERR $c; + $rfb .= $c; + } + if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) { + $initial_data = $rfb; + $rfb =~ s/\n//g; + print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n"; + } + } elsif ($ENV{PPROXY_VENCRYPT} ne "") { + my $vencrypt = $ENV{PPROXY_VENCRYPT}; + vencrypt_dialog($vencrypt); + + } else { + # Web Proxy: + $con = "CONNECT $CONNECT HTTP/1.1\r\n"; + $con .= "Host: $CONNECT\r\n"; + $con .= "Connection: close\r\n\r\n"; + $msg = $con; + + print STDERR "proxy_request$w: via $cur_proxy:\n$msg"; + syswrite($sock, $con, length($con)); + + my $rep = ""; + my $n = 0; + while ($rep !~ /\r\n\r\n/ && $n < 30000) { + my $c; + sysread($sock, $c, 1); + print STDERR $c; + $rep .= $c; + $n++; + } + if ($rep !~ m,HTTP/.* 200,) { + print STDERR "HTTP CONNECT failed.\n"; + close $sock; + exit(1); + } + } +} + +sub vdie { + append_handshake("done\n"); + close $sock; + kill_proxy_pids(); + exit(1); +} + +sub anontls_handshake { + my ($vmode, $db) = @_; + + print STDERR "\nPPROXY: Doing ANONTLS Handshake\n"; + + my $psec = pack("C", $rfbSecTypeAnonTls); + syswrite($sock, $psec, 1); + + append_handshake("done\n"); +} + +sub vencrypt_handshake { + + my ($vmode, $db) = @_; + + print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n"; + + my $psec = pack("C", $rfbSecTypeVencrypt); + + if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { + my $fake = $ENV{SSVNC_TEST_SEC_TYPE}; + print STDERR "PPROXY: sending sec-type: $fake\n"; + $psec = pack("C", $fake); + } + + syswrite($sock, $psec, 1); + + my $vmajor; + my $vminor; + sysread($sock, $vmajor, 1); + sysread($sock, $vminor, 1); + + vdie if $vmajor eq "" || $vminor eq ""; + + $vmajor = unpack("C", $vmajor); + $vminor = unpack("C", $vminor); + print STDERR "server vencrypt version $vmajor.$vminor\n" if $db; + + if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { + print STDERR "PPROXY: continuing on in test mode.\n"; + } else { + vdie if $vmajor ne 0; + vdie if $vminor < 2; + } + + $vmajor = pack("C", 0); + $vminor = pack("C", 2); + append_handshake("subversion=0.2\n"); + + syswrite($sock, $vmajor, 1); + syswrite($sock, $vminor, 1); + + my $result; + sysread($sock, $result, 1); + print STDERR "result empty\n" if $db && $result eq ""; + + vdie if $result eq ""; + $result = unpack("C", $result); + print STDERR "result=$result\n" if $db; + + vdie if $result ne 0; + + my $nsubtypes; + sysread($sock, $nsubtypes, 1); + + vdie if $nsubtypes eq ""; + $nsubtypes = unpack("C", $nsubtypes); + print STDERR "nsubtypes=$nsubtypes\n" if $db; + + my %subtypes; + + for (my $i = 0; $i < $nsubtypes; $i++) { + my $subtype = ""; + sysread($sock, $subtype, 4); + vdie if length($subtype) != 4; + + # XXX fix 64bit. + $subtype = unpack("N", $subtype); + print STDERR "subtype: $subtype\n" if $db; + $subtypes{$subtype} = 1; + append_handshake("sst$i=$subtype\n"); + } + + my $subtype = 0; + if (exists $subtypes{$rfbVencryptX509None}) { + $subtype = $rfbVencryptX509None; + print STDERR "selected rfbVencryptX509None\n" if $db; + } elsif (exists $subtypes{$rfbVencryptX509Vnc}) { + $subtype = $rfbVencryptX509Vnc; + print STDERR "selected rfbVencryptX509Vnc\n" if $db; + } elsif (exists $subtypes{$rfbVencryptX509Plain}) { + $subtype = $rfbVencryptX509Plain; + print STDERR "selected rfbVencryptX509Plain\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsNone}) { + $subtype = $rfbVencryptTlsNone; + print STDERR "selected rfbVencryptTlsNone\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsVnc}) { + $subtype = $rfbVencryptTlsVnc; + print STDERR "selected rfbVencryptTlsVnc\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsPlain}) { + $subtype = $rfbVencryptTlsPlain; + print STDERR "selected rfbVencryptTlsPlain\n" if $db; + } + + if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { + my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE}; + print STDERR "PPROXY: sending sec-subtype: $fake\n"; + $subtype = $fake; + } + + append_handshake("subtype=$subtype\n"); + + my $pst = pack("N", $subtype); + syswrite($sock, $pst, 4); + + if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { + print STDERR "PPROXY: continuing on in test mode.\n"; + } else { + vdie if $subtype == 0; + } + + my $ok; + sysread($sock, $ok, 1); + $ok = unpack("C", $ok); + print STDERR "ok=$ok\n" if $db; + + append_handshake("done\n"); + + vdie if $ok == 0; +} + +sub vencrypt_dialog { + my $vmode = shift; + my $db = 0; + + $db = 1 if exists $ENV{SS_DEBUG}; + $db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG}; + + append_handshake("mode=$vmode\n"); + + my $server_rfb = ""; + #syswrite($sock, $rep, 250); + for (my $i = 0; $i < 12; $i++) { + my $c; + sysread($sock, $c, 1); + $server_rfb .= $c; + print STDERR $c; + } + print STDERR "server_rfb: $server_rfb\n" if $db; + append_handshake("server=$server_rfb"); + + my $minor = ""; + if ($server_rfb =~ /^RFB 003\.(\d+)/) { + $minor = $1; + } else { + vdie; + } + my $viewer_rfb = "RFB 003.008\n"; + if ($minor < 7) { + vdie; + } elsif ($minor == 7) { + $viewer_rfb = "RFB 003.007\n"; + } + my $nsec; + my $t1 = gettime(); + my $t0 = gettime(); + + syswrite($sock, $viewer_rfb, 12); + sysread($sock, $nsec, 1); + + $t1 = gettime(); + $t1 = sprintf("%.6f", $t1 - $t0); + + append_handshake("viewer=$viewer_rfb"); + append_handshake("latency=$t1\n"); + + vdie if $nsec eq ""; + + $nsec = unpack("C", $nsec); + + print STDERR "nsec: $nsec\n" if $db; + vdie if $nsec eq 0 || $nsec > 100; + + my %sectypes = (); + + for (my $i = 0; $i < $nsec; $i++) { + my $sec; + sysread($sock, $sec, 1); + vdie if $sec eq ""; + $sec = unpack("C", $sec); + print STDERR "sec: $sec\n" if $db; + $sectypes{$sec} = 1; + } + + if (exists $sectypes{$rfbSecTypeVencrypt}) { + print STDERR "found rfbSecTypeVencrypt\n" if $db; + append_handshake("sectype=$rfbSecTypeVencrypt\n"); + vencrypt_handshake($vmode, $db); + } elsif (exists $sectypes{$rfbSecTypeAnonTls}) { + print STDERR "found rfbSecTypeAnonTls\n" if $db; + append_handshake("sectype=$rfbSecTypeAnonTls\n"); + anontls_handshake($vmode, $db); + } else { + print STDERR "No supported sec-type found\n" if $db; + vdie; + } +} + +sub append_handshake { + my $str = shift; + if ($handshake_file) { + if (open(HSF, ">>$handshake_file")) { + print HSF $str; + close HSF; + } + } +} + +sub do_vencrypt_viewer_bridge { + my ($listen, $connect) = @_; + print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n"; + my $db = 0; + my $backwards = 0; + if ($listen < 0) { + $backwards = 1; + $listen = -$listen; + } + if ($handshake_file eq "") { + die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n"; + } + my $listen_sock; + my $maxtry = 12; + my $sleep = 5; + for (my $i=0; $i < $maxtry; $i++) { + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalAddr => "127.0.0.1", + LocalPort => $listen, + Proto => "tcp" + ); + if (! $listen_sock) { + if ($i < $maxtry - 1) { + warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; + warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n"; + sleep $sleep; + } + } else { + last; + } + } + if (! $listen_sock) { + die "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n"; + my ($viewer_sock, $ip) = $listen_sock->accept(); + my $err = $!; + close $listen_sock; + if (! $viewer_sock) { + die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; + } + if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { + my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; + if ($sml ne "" && $sml ne "0") { + setpgrp(0, 0); + if (fork()) { + close $viewer_sock; + wait; + exit 0; + } + if (fork()) { + close $viewer_sock; + exit 0; + } + setpgrp(0, 0); + $parent = $$; + } + } + print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db; + + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n"; + my $server_sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1", + PeerPort => $connect, + Proto => "tcp" + ); + print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db; + if (! $server_sock) { + my $err = $!; + die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; + } + + if ($backwards) { + print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n"; + my $t = $viewer_sock; + $viewer_sock = $server_sock; + $server_sock = $t; + } + + my %hs = (); + my $dt = 0.2; + my $slept = 0.0; + while ($slept < 20.0) { + select(undef, undef, undef, $dt); + $slept += $dt; + if (-f $handshake_file && open(HSF, "<$handshake_file")) { + my $done = 0; + %hs = (); + my $str = ""; + while () { + print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG}; + $str .= "vencrypt_viewer_bridge[$$]: $_"; + chomp; + if ($_ eq "done") { + $done = 1; + } else { + my ($k, $v) = split(/=/, $_, 2); + if ($k ne "" && $v ne "") { + $hs{$k} = $v; + } + } + } + close HSF; + if ($done) { + print STDERR "\n" . $str; + last; + } + } + } + if (! exists $hs{server}) { + $hs{server} = "RFB 003.008"; + } + if (! exists $hs{sectype}) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n"; + } + syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1); + my $viewer_rfb = ""; + for (my $i = 0; $i < 12; $i++) { + my $c; + sysread($viewer_sock, $c, 1); + $viewer_rfb .= $c; + print STDERR $c; + } + my $viewer_major = 3; + my $viewer_minor = 8; + if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) { + $viewer_major = $1; + $viewer_minor = $2; + } + my $u0 = pack("C", 0); + my $u1 = pack("C", 1); + my $u2 = pack("C", 2); + if ($hs{sectype} == $rfbSecTypeAnonTls) { + unlink($handshake_file); + print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n"; + if ($viewer_major > 3 || $viewer_minor >= 7) { + ; # setup ok, proceed to xfer. + } else { + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; + my $n; + sysread($server_sock, $n, 1); + $n = unpack("C", $n); + if ($n == 0) { + die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n"; + } + my %types; + for (my $i = 0; $i < $n; $i++) { + my $t; + sysread($server_sock, $t, 1); + $t = unpack("C", $t); + $types{$t} = 1; + } + my $use = 1; # None + if (exists $types{1}) { + $use = 1; # None + } elsif (exists $types{2}) { + $use = 2; # VncAuth + } else { + die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n"; + } + + # send 4 bytes sectype to viewer: + # (note this should be MSB, network byte order...) + my $up = pack("C", $use); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $up, 1); + # and tell server the one we selected: + syswrite($server_sock, $up, 1); + if ($use == 1) { + # even None has security result, so read it here and discard it. + my $sr = ""; + sysread($server_sock, $sr, 4); + } + } + } elsif ($hs{sectype} == $rfbSecTypeVencrypt) { + print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n"; + if (! exists $hs{subtype}) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n"; + } + my $fake_type = "None"; + my $plain = 0; + my $sub_type = $hs{subtype}; + if ($sub_type == $rfbVencryptTlsNone) { + $fake_type = "None"; + } elsif ($sub_type == $rfbVencryptTlsVnc) { + $fake_type = "VncAuth"; + } elsif ($sub_type == $rfbVencryptTlsPlain) { + $fake_type = "None"; + $plain = 1; + } elsif ($sub_type == $rfbVencryptX509None) { + $fake_type = "None"; + } elsif ($sub_type == $rfbVencryptX509Vnc) { + $fake_type = "VncAuth"; + } elsif ($sub_type == $rfbVencryptX509Plain) { + $fake_type = "None"; + $plain = 1; + } + if ($plain) { + if (!open(W, ">$handshake_file")) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n"; + } + print W <<"END"; + + proc print_out {} { + global user pass env + + if [info exists env(SSVNC_UP_DEBUG)] { + toplevel .b + button .b.b -text "user=\$user pass=\$pass" -command {destroy .b} + pack .b.b + update + tkwait window .b + } + + if [info exists env(SSVNC_UP_FILE)] { + set fh "" + catch {set fh [open \$env(SSVNC_UP_FILE) w]} + if {\$fh != ""} { + puts \$fh user=\$user\\npass=\$pass + flush \$fh + close \$fh + return + } + } + puts stdout user=\$user\\npass=\$pass + flush stdout + } + + proc center_win {w} { + update + set W [winfo screenwidth \$w] + set W [expr \$W + 1] + wm geometry \$w +\$W+0 + update + set x [expr [winfo screenwidth \$w]/2 - [winfo width \$w]/2] + set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2] + + wm geometry \$w +\$x+\$y + wm deiconify \$w + update + } + + wm withdraw . + + global env + set up {} + if [info exists env(SSVNC_UNIXPW)] { + set rm 0 + set up \$env(SSVNC_UNIXPW) + if [regexp {^rm:} \$up] { + set rm 1 + regsub {^rm:} \$up {} up + } + if [file exists \$up] { + set fh "" + set f \$up + catch {set fh [open \$up r]} + if {\$fh != ""} { + gets \$fh u + gets \$fh p + close \$fh + set up "\$u@\$p" + } + if {\$rm} { + catch {file delete \$f} + } + } + } elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] { + set up \$env(SSVNC_VENCRYPT_USERPASS) + } + #puts stderr up=\$up + if {\$up != ""} { + if [regexp {@} \$up] { + global user pass + set user \$up + set pass \$up + regsub {@.*\$} \$user "" user + regsub {^[^@]*@} \$pass "" pass + print_out + exit + } + } + + wm title . {VeNCrypt Viewer Bridge User/Pass} + + set user {} + set pass {} + + label .l -text {SSVNC VeNCrypt Viewer Bridge} + + frame .f0 + frame .f0.fL + label .f0.fL.la -text {Username: } + label .f0.fL.lb -text {Password: } + + pack .f0.fL.la .f0.fL.lb -side top + + frame .f0.fR + entry .f0.fR.ea -width 24 -textvariable user + entry .f0.fR.eb -width 24 -textvariable pass -show * + + pack .f0.fR.ea .f0.fR.eb -side top -fill x + + pack .f0.fL -side left + pack .f0.fR -side right -expand 1 -fill x + + button .no -text Cancel -command {destroy .} + button .ok -text Done -command {print_out; destroy .} + + center_win . + pack .l .f0 .no .ok -side top -fill x + update + wm deiconify . + + bind .f0.fR.ea {focus .f0.fR.eb} + bind .f0.fR.eb {print_out; destroy .} + focus .f0.fR.ea + + wm resizable . 1 0 + wm minsize . [winfo reqwidth .] [winfo reqheight .] +END + close W; + + #system("cat $handshake_file"); + my $w = "wish"; + if ($ENV{WISH}) { + $w = $ENV{WISH}; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt VencryptPlain user and passwd.\n"; + my $res = ""; + if (`uname` =~ /Darwin/) { + my $mtmp = `mktemp /tmp/hsup.XXXXXX`; + chomp $mtmp; + system("env SSVNC_UP_FILE=$mtmp $w $handshake_file"); + $res = `cat $mtmp`; + unlink $mtmp; + } else { + $res = `$w $handshake_file`; + } + my $user = ""; + my $pass = ""; + if ($res =~ /user=(\S*)/) { + $user = $1; + } + if ($res =~ /pass=(\S*)/) { + $pass = $1; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n"; + my $ulen = pack("C", length($user)); + my $plen = pack("C", length($pass)); + # (note this should be MSB, network byte order...) + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $ulen, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $plen, 1); + syswrite($server_sock, $user, length($user)); + syswrite($server_sock, $pass, length($pass)); + } + unlink($handshake_file); + + my $ft = 0; + if ($fake_type eq "None") { + $ft = 1; + } elsif ($fake_type eq "VncAuth") { + $ft = 2; + } else { + die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n"; + } + my $fp = pack("C", $ft); + if ($viewer_major > 3 || $viewer_minor >= 7) { + syswrite($viewer_sock, $u1, 1); + syswrite($viewer_sock, $fp, 1); + my $cr; + sysread($viewer_sock, $cr, 1); + $cr = unpack("C", $cr); + if ($cr != $ft) { + die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n"; + } + } else { + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; + # send 4 bytes sect type to viewer: + # (note this should be MSB, network byte order...) + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $fp, 1); + if ($ft == 1) { + # even None has security result, so read it here and discard it. + my $sr = ""; + sysread($server_sock, $sr, 4); + } + } + } + + $listen_handle = $viewer_sock; + $sock = $server_sock; + + xfer_both(); +} +' + # ' + # xpg_echo will expand \n \r, etc. + # try to unset and then test for it. + if type shopt > /dev/null 2>&1; then + shopt -u xpg_echo >/dev/null 2>&1 + fi + v='print STDOUT "abc\n";' + echo "$v" > $tf + chmod 700 $tf + + lc=`wc -l $tf | awk '{print $1}'` + if [ "X$lc" = "X1" ]; then + echo "$cod" > $tf + else + printf "%s" "$cod" > $tf + echo "" >> $tf + fi + # prime perl + perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1 +} + +# make_tcert is no longer invoked via the ssvnc gui (Listen mode). +# make_tcert is for testing only now via -mycert BUILTIN +make_tcert() { + tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$" + tcert=`mytmp "$tcert"` + cat > $tcert < Advanced -> Private SSH KnownHosts file' (or set" + echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts" + echo "** Warning: file. That file holds the 'localhost' cert for this specific" + echo "** Warning: connection. This yields a both secure and convenient solution." + echo "" +} + +space_expand() { + str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'` + echo "$str" +} + +# handle ssh case: +# +if [ "X$use_ssh" = "X1" ]; then + # + # USING SSH + # + ssh_port="22" + ssh_host="$host" + vnc_host="$localhost" + ssh_UKHF="" + localhost_extra="" + # let user override ssh via $SSH + ssh=${SSH:-"ssh -x"} + + sshword=`echo "$ssh" | awk '{print $1}'` + if [ "X$sshword" != "X" ]; then + if [ -x "$sshword" ]; then + : + elif type "$sshword" > /dev/null 2>&1; then + : + else + echo "" + echo "*********************************************************" + echo "** Problem finding the SSH command '$sshword': **" + echo "" + type "$sshword" + echo "" + echo "** Perhaps you need to install the SSH client package. **" + echo "*********************************************************" + echo "" + sleep 5 + fi + fi + + ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes" + if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then + ssh_NHAFL="" + fi + if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then + ssh_NHAFL="" + + ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE" + ssh_args="$ssh_args $ssh_UKHF" + if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then + touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 + fi + chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 + fi + did_ssh_NHAFL="" + + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then + SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD" + fi + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then + echo "" + echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD" + fi + + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then + plvar=LD_PRELOAD + if uname | grep Darwin >/dev/null; then + plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES" + fi + ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh" + else + SSVNC_LIM_ACCEPT_PRELOAD="" + fi + + ssh_vencrypt_proxy="" + # We handle vencrypt for SSH+SSL mode. + if echo "$proxy" | grep 'vencrypt://' > /dev/null; then + proxynew="" + for part in `echo "$proxy" | tr ',' ' '` + do + if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then + ssh_vencrypt_proxy=$part + else + if [ "X$proxynew" = "X" ]; then + proxynew="$part" + else + proxynew="$proxynew,$part" + fi + fi + done + proxy=$proxynew + fi + Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy + + # note that user must supply http:// for web proxy in SSH and SSH+SSL. + # No xxxx:// implies ssh server+port. + # + if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then + # Handle Web or SOCKS proxy(ies) for the initial connect. + Kecho host=$host + Kecho port=$port + pproxy="" + sproxy1="" + sproxy_rest="" + for part in `echo "$proxy" | tr ',' ' '` + do + Kecho proxy_part=$part + if [ "X$part" = "X" ]; then + continue + elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then + pproxy="$pproxy,$part" + else + if [ "X$sproxy1" = "X" ]; then + sproxy1="$part" + else + sproxy_rest="$sproxy_rest,$part" + fi + fi + done + pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'` + sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'` + + Kecho pproxy=$pproxy + Kecho sproxy1=$sproxy1 + Kecho sproxy_rest=$sproxy_rest + + sproxy1_host="" + sproxy1_port="" + sproxy1_user="" + + if [ "X$sproxy1" != "X" ]; then + # XXX fix ipv6 ip adder here and below. + sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'` + sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` + sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` + if [ "X$sproxy1_host" = "X" ]; then + sproxy1_host=$sproxy1_user + sproxy1_user="" + else + sproxy1_user="${sproxy1_user}@" + fi + sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'` + if [ "X$sproxy1_port" = "X" ]; then + sproxy1_port="22" + fi + else + sproxy1_host=`echo "$host" | awk -F: '{print $1}'` + sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` + sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` + if [ "X$sproxy1_host" = "X" ]; then + sproxy1_host=$sproxy1_user + sproxy1_user="" + else + sproxy1_user="${sproxy1_user}@" + fi + sproxy1_port=`echo "$host" | awk -F: '{print $2}'` + if [ "X$sproxy1_port" = "X" ]; then + sproxy1_port="22" + fi + fi + + Kecho sproxy1_host=$sproxy1_host + Kecho sproxy1_port=$sproxy1_port + Kecho sproxy1_user=$sproxy1_user + + ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl" + ptmp=`mytmp "$ptmp"` + PPROXY_REMOVE=1; export PPROXY_REMOVE + proxy=$pproxy + port_save=$port + host_save=$host + if [ "X$sproxy1_host" != "X" ]; then + host=$sproxy1_host + fi + if [ "X$sproxy1_port" != "X" ]; then + port=$sproxy1_port + fi + host=`echo "$host" | sed -e 's/^.*@//'` + port=`echo "$port" | sed -e 's/^.*://'` + pcode "$ptmp" + port=$port_save + host=$host_save + + nd=`findfree 6600` + PPROXY_LISTEN=$nd; export PPROXY_LISTEN + # XXX no reverse forever PPROXY_LOOP_THYSELF ... + $ptmp & + sleep 1 + if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then + NHAFL_warning + ssh_args="$ssh_args $ssh_NHAFL" + did_ssh_NHAFL=1 + fi + sleep 1 + if [ "X$sproxy1" = "X" ]; then + u="" + if echo "$host" | grep '@' > /dev/null; then + u=`echo "$host" | sed -e 's/@.*$/@/'` + fi + + proxy="${u}$localhost:$nd" + else + proxy="${sproxy1_user}$localhost:$nd" + fi + localhost_extra=".2" + if [ "X$sproxy_rest" != "X" ]; then + proxy="$proxy,$sproxy_rest" + fi + Kecho proxy=$proxy + fi + + if echo "$proxy" | grep "," > /dev/null; then + + proxy1=`echo "$proxy" | awk -F, '{print $1}'` + proxy2=`echo "$proxy" | awk -F, '{print $2}'` + + # user1@gw1.com:port1,user2@ws2:port2 + ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'` + ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'` + if [ "X$ssh_port1" != "X" ]; then + ssh_port1="-p $ssh_port1" + fi + ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'` + ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'` + ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'` + if [ "X$ssh_host2" = "X" ]; then + ssh_host2=$ssh_user2 + ssh_user2="" + else + ssh_user2="${ssh_user2}@" + fi + ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'` + if [ "X$ssh_port2" = "X" ]; then + ssh_port2="22" + fi + proxport=`findfree 3500` + if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then + NHAFL_warning + did_ssh_NHAFL=1 + sleep 1 + fi + echo + echo "Running 1st ssh proxy:" + ukhf="" + if [ "X$ssh_UKHF" != "X" ]; then + ukhf="$ssh_UKHF$localhost_extra" + fi + if echo "$ssh_host1" | grep '%' > /dev/null; then + uath=`space_expand "$ssh_host1"` + else + uath="$ssh_host1" + fi + echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\"" + echo "" + $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30" + ssh_args="$ssh_args $ssh_NHAFL" + sleep 1 + stty sane + proxy="${ssh_user2}$localhost:$proxport" + fi + + if [ "X$proxy" != "X" ]; then + ssh_port=`echo "$proxy" | awk -F: '{print $2}'` + if [ "X$ssh_port" = "X" ]; then + ssh_port="22" + fi + ssh_host=`echo "$proxy" | awk -F: '{print $1}'` + vnc_host="$host" + fi + + echo "" + echo "Running ssh:" + sz=`echo "$ssh_cmd" | wc -c` + if [ "$sz" -gt 300 ]; then + info="..." + else + info="$ssh_cmd" + fi + + C="" + if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then + C="-C" + fi + + getport="" + teeport="" + if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then + getport=1 + if echo "$ssh_cmd" | egrep "P= " > /dev/null; then + teeport=1 + fi + + PORT="" + ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ ]*//' -e 's/P=//'` + SSVNC_NO_ENC_WARN=1 + if [ "X$use_sshssl" = "X" ]; then + direct_connect=1 + fi + fi + if [ "X$getport" != "X" ]; then + ssh_redir="-D ${use}" + elif [ "X$reverse" = "X" ]; then + ssh_redir="-L ${use}:${vnc_host}:${port}" + else + ssh_redir="-R ${port}:${vnc_host}:${use}" + fi + pmark=`sh -c 'echo $$'` + + # the -t option actually speeds up typing response via VNC!! + if [ "X$ssh_port" = "X22" ]; then + ssh_port="" + else + ssh_port="-p $ssh_port" + fi + + if echo "$ssh_host" | grep '%' > /dev/null; then + uath=`space_expand "$ssh_host"` + else + uath="$ssh_host" + fi + if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then + echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd" + exit $? + + elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then + echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" + rc=$? + + elif [ "X$getport" != "X" ]; then + tport=/tmp/ss_vncviewer_tport${RANDOM}.$$ + tport=`mytmp "$tport"` + tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$ + tport2=`mytmp "$tport2"` + + if [ "X$rsh" != "X1" ]; then + if echo "$ssh_cmd" | grep "sudo " > /dev/null; then + echo "" + echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one" + echo "will require no password..." + echo "" + targ="-t" + $ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty" + echo "" + fi + echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2 + if [ "X$teeport" = "X1" ]; then + tail -f $tport 1>&2 & + tail_pid=$! + tail -f $tport2 1>&2 & + tail_pid2=$! + fi + rc=$? + else + rsh_setup + echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" + echo "" + rsh $ul "$ssh_host" "$ssh_cmd" > $tport & + sleep 1 + rc=0 + fi + + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + + stty sane + i=0 + if type perl > /dev/null 2>&1; then + imax=50 + sleepit="perl -e 'select(undef, undef, undef, 0.20)'" + else + imax=10 + sleepit="sleep 1" + fi + while [ $i -lt $imax ]; do + #echo $sleepit + eval $sleepit + PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'` + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + break + fi + vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'` + if [ "X$vnss" != "X" ]; then + PORT=`echo "$vnss" | awk -F: '{print $2}'` + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + if [ $PORT -lt 100 ]; then + PORT=`expr $PORT + 5900` + fi + fi + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1` + echo "vncserver string: $vnss" 1>&2 + break + fi + fi + i=`expr $i + 1` + done + + echo "found: PORT='$PORT'" 1>&2 + lh6="" + if [ "X$SSVNC_PORT_IPV6" != "X" ]; then + lh6=1 + elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then + lh6=1 + fi + if [ "X$lh6" = "X1" ]; then + echo "set SOCKS5 localhost to ::1" 1>&2 + fi + rm -f $tport $tport2 + if [ "X$rsh" = "X1" ]; then + rsh_viewer "$@" + exit $? + fi + PPROXY_SOCKS=5 + if [ "X$SSVNC_SOCKS5" != "X" ]; then + PPROXY_SOCKS=5 + elif [ "X$SSVNC_SOCKS4" != "X" ]; then + PPROXY_SOCKS=1 + fi + export PPROXY_SOCKS + if [ "X$lh6" = "X" ]; then + host="$localhost" + else + host="::1" + fi + port="$PORT" + proxy="$localhost:$use" + + else + if [ "X$rsh" != "X1" ]; then + echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" + rc=$? + else + rsh_setup + echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" + echo "" + rsh $ul "$ssh_host" "$ssh_cmd" & + sleep 1 + PORT=$port + rsh_viewer "$@" + exit $? + fi + fi + + if [ "$rc" != "0" ]; then + echo "" + echo "ssh to \"$uath\" failed." + exit 1 + fi + stty sane + + c=0 + pssh="" + while [ $c -lt 40 ] + do + p=`expr $pmark + $c` + pout=`ps -p "$p" 2>/dev/null | grep -v '^[ ]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'` + if echo "$pout" | grep "ssh" > /dev/null; then + if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then + : + elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then + : + else + pssh=$p + break + fi + fi + c=`expr $c + 1` + done + if [ "X$getport" != "X" ]; then + : + elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then + sleep 2 + elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then + #echo T sleep 1 + sleep 1 + elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then + #echo T sleep 2 + sleep 2 + else + # let any command get started a bit. + #echo T sleep 5 + sleep 5 + fi + echo "" + #reset + stty sane + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + echo "ssh_pid='$pssh'"; echo + if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then + if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo + fi + echo "Running viewer:" + + trap "final" 0 2 15 + if [ "X$reverse" = "X" ]; then + echo "$VNCVIEWERCMD" "$@" $localhost:$N + echo "" + $VNCVIEWERCMD "$@" $localhost:$N + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" $localhost:$N + fi + fi + else + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + N2=$N + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + N2=`echo "$N2" | sed -e 's/://g'` + if [ $N2 -le 200 ]; then + N2=`expr $N2 + 5500` + fi + fi + echo "$VNCVIEWERCMD" "$@" -listen $N2 + echo "" + $VNCVIEWERCMD "$@" -listen $N2 + fi + + exit $? + else + use2=`findfree 5960` + host0=$host + port0=$port + host=$localhost + port=$use + use=$use2 + N=`expr $use - 5900` + if [ "X$getport" != "X" ]; then + host="$host0" + port="$port0" + else + proxy="" + fi + if [ "X$ssh_vencrypt_proxy" != "X" ]; then + ssh_vencrypt_proxy="vencrypt://$host:$port" + if [ "X$proxy" = "X" ]; then + proxy=$ssh_vencrypt_proxy + else + proxy="$proxy,$ssh_vencrypt_proxy" + fi + Kecho "proxy_now=$proxy" + unset PPROXY_LISTEN + fi + fi +fi + +if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then + if type $STUNNEL > /dev/null 2>&1; then + : + else + echo "" + echo "***************************************************************" + echo "** Problem finding the Stunnel command '$STUNNEL': **" + echo "" + type $STUNNEL + echo "" + echo "** Perhaps you need to install the stunnel/stunnel4 package. **" + echo "***************************************************************" + echo "" + sleep 5 + fi +fi + +# create the stunnel config file: +if [ "X$verify" != "X" ]; then + if [ -d $verify ]; then + verify="CApath = $verify" + else + verify="CAfile = $verify" + fi + verify="$verify +verify = 2" +fi +if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then + verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'` +fi +if [ "X$mycert" != "X" ]; then + cert="cert = $mycert" +fi +if [ "X$crl" != "X" ]; then + if [ -d $crl ]; then + crl="CRLpath = $crl" + else + crl="CRLfile = $crl" + fi +fi + +if [ "X$showcert" = "X1" ]; then + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then + : + elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then + proxy="ipv6://$host:$port" + fi +fi + +if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then + proxy=reverse_direct +fi + +ptmp="" +if [ "X$proxy" != "X" ]; then + ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl" + ptmp=`mytmp "$ptmp"` + PPROXY_REMOVE=1; export PPROXY_REMOVE + pcode "$ptmp" + if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then + if uname | egrep 'Darwin|SunOS' >/dev/null; then + vout=`echo "$proxy" | grep -i vencrypt` + if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then + # need to exec for reverse vencrypt + connect="exec = $ptmp" + else + # on mac and solaris we need to listen on socket instead of stdio: + nd=`findfree 6700` + PPROXY_LISTEN=$nd + export PPROXY_LISTEN + if [ "X$reverse" = "X" ]; then + $ptmp & + fi + sleep 2 + host="$localhost" + port="$nd" + connect="connect = $localhost:$nd" + fi + else + # otherwise on unix we can exec it: + connect="exec = $ptmp" + fi + else + connect="exec = $ptmp" + fi +else + connect="connect = $host:$port" +fi + +# handle showcert case: +# +if [ "X$showcert" = "X1" ]; then + if [ "X$proxy" != "X" ]; then + PPROXY_LISTEN=$use + export PPROXY_LISTEN + if [ "X$SS_DEBUG" != "X" ]; then + $ptmp & + else + $ptmp 2>/dev/null & + fi + sleep 1 + more_sleep=1 + if uname | grep Linux > /dev/null; then + if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then + more_sleep="" + fi + elif uname | grep SunOS > /dev/null; then + if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then + more_sleep="" + fi + elif uname | egrep -i 'bsd|darwin' > /dev/null; then + if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then + more_sleep="" + fi + fi + if [ "X$more_sleep" = "X1" ]; then + sleep 1 + fi + host="$localhost" + port="$use" + fi + cipher_args="" + if [ "X$ciphers" != "X" ]; then + cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'` + fi + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + : + elif type openssl > /dev/null 2>&1; then + : + else + echo "" + echo "********************************************************" + echo "** Problem finding the OpenSSL command 'openssl': **" + echo "" + type openssl 2>&1 + echo "" + echo "** Perhaps you need to install the 'openssl' package. **" + echo "********************************************************" + echo "" + fi + #echo "openssl s_client $cipher_args -connect $host:$port" + if [ "X$reverse" = "X" ]; then + if type host > /dev/null 2>/dev/null; then + host $host >/dev/null 2>&1 + host $host >/dev/null 2>&1 + fi + timeout=15 + if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then + timeout=$SSVNC_FETCH_TIMEOUT + fi + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + if type pkill >/dev/null 2>&1; then + (sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 & + fi + ultravnc_dsm_helper showcert $host:$port 2>&1 + else + if type pkill >/dev/null 2>&1; then + (sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 & + fi + openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null + fi + rc=$? + else + tcert="" + if [ "X$mycert" = "X" ]; then + tcert=`make_tcert` + cert_args="-cert $tcert -CAfile $tcert" + else + cert_args="-cert $mycert -CAfile $mycert" + fi + tmp_out=/tmp/showcert_out${RANDOM}.$$ + tmp_out=`mytmp "$tmp_out"` + tmp_err=/tmp/showcert_err${RANDOM}.$$ + tmp_err=`mytmp "$tmp_err"` + + #echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2 + + # assume we have perl: + check_perl perl + + perl -e " + \$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\"); + exit 1 unless \$p; + while (1) { + sleep 1; + if (!open(F, \"<$tmp_out\")) { + kill \$p; + exit 1; + } + while () { + if (/RFB 00/) { + fsleep(0.25); + print O \"RFB 000.000\\n\"; + fsleep(1.00); + kill \$p; + fsleep(0.25); + exit 0; + } + } + close F; + } + sub fsleep { + select(undef, undef, undef, shift); + } + "; + + echo "" + cat $tmp_out + echo "" + echo "----2----" + cat $tmp_err + if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then + rc=0 + else + rc=1 + fi + + rm -f $tmp_out $tmp_err + fi + if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then + rm -f $SSVNC_PREDIGESTED_HANDSHAKE + fi + if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then + exit 0 + else + exit $rc + fi +fi + +# handle direct connect case: +# +if [ "X$direct_connect" != "X" ]; then + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + SSVNC_NO_ENC_WARN=1 + echo "" + echo "Using UltraVNC DSM Plugin key for encryption:" + echo "" + ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo " $ustr PORT HOST:PORT" + echo "" + elif [ "X$getport" = "X" ]; then + echo "" + echo "Running viewer for direct connection:" + if echo X"$@" | grep chatonly > /dev/null; then + : + else + echo "" + echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **" + echo "" + fi + fi + x="" + if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then + if [ "X$getport" = "X" ]; then + sleep 1 + fi + elif type printf > /dev/null 2>&1; then + printf "Are you sure you want to continue? [y]/n " + read x + else + echo -n "Are you sure you want to continue? [y]/n " + read x + fi + if [ "X$x" = "Xn" ]; then + exit 1 + fi + echo "" + if [ "X$ptmp" != "X" ]; then + if [ "X$reverse" = "X" ]; then + PPROXY_LISTEN=$use + export PPROXY_LISTEN + else + if [ "X$proxy" = "Xreverse_direct" ]; then + PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`" + PPROXY_DEST="$localhost:$use" + PPROXY_PROXY="ipv6://$localhost:$use" # not always ipv6.. + export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY + pps=1 + else + PPROXY_REVERSE="$localhost:$use" + export PPROXY_LISTEN + pps=3 + fi + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + pps=2 + fi + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + pps=`expr $pps + $SSVNC_EXTRA_SLEEP` + fi + PPROXY_SLEEP=$pps; export PPROXY_SLEEP; + PPROXY_KILLPID=+1; export PPROXY_KILLPID; + fi + + $ptmp & + + if [ "X$reverse" = "X" ]; then + #sleep 2 + #echo T sleep 1 + sleep 1 + fi + host="$localhost" + disp="$N" + port=`expr $disp + 5900` + fi + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "T sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo + fi + if [ "X$reverse" = "X" ]; then + hostdisp="$host:$disp" + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if [ "X$SSVNC_USE_OURS" = "X1" ]; then + hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port" + else + pf=`findfree 5970` + cmd="$SSVNC_ULTRA_DSM -$pf $host:$port" + pf=`expr $pf - 5900` + hostdisp="$localhost:$pf" + ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "Running:" + echo + echo "$ustr &" + echo + $cmd & + dsm_pid=$! + sleep 2 + fi + fi + hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "$VNCVIEWERCMD" "$@" "$hostdisp2" + trap "final" 0 2 15 + echo "" + $VNCVIEWERCMD "$@" "$hostdisp" + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" "$hostdisp" + fi + fi + else + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + trap "final" 0 2 15 + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then + echo "NOTE: The ultravnc_dsm_helper only runs once. So after the first LISTEN" + echo " ends you must restart the Listening mode. You may also need to" + echo " Press Ctrl-C to stop the viewer and restart for another connection." + echo "" + fi + #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + VNCVIEWER_LISTEN_LOCALHOST=1 + export VNCVIEWER_LISTEN_LOCALHOST + dport=`expr 5500 + $disp` + cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use" + ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "Running:" + echo + echo "$ustr &" + echo + if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then + $cmd & + dsm_pid=$! + else + while [ 1 ]; do $cmd; sleep 1; done & + dsm_pid=$! + fi + sleep 2 + disp=$use + if [ $disp -ge 5500 ]; then + disp=`expr $disp - 5500` + fi + fi + disp2=$disp + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + disp2=`echo "$disp2" | sed -e 's/://g'` + if [ $disp2 -le 200 ]; then + disp2=`expr $disp2 + 5500` + fi + fi + echo "$VNCVIEWERCMD" "$@" -listen $disp2 + echo "" + $VNCVIEWERCMD "$@" -listen $disp2 + if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF + fi + fi + exit $? +fi + +tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$ +tmp_cfg=`mytmp "$tmp_cfg"` + +stunnel_exec="" +if [ "X$SSVNC_USE_OURS" != "X1" ]; then + : +elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then + stunnel_exec="#" +fi + +if [ "X$reverse" = "X" ]; then + + if echo "$proxy" | grep "^repeater://" > /dev/null; then + if [ "X$cert" = "XBUILTIN" ]; then + ttcert=`make_tcert` + cert="cert = $ttcert" + fi + # Note for listen mode, an empty cert will cause stunnel to fail. + # The ssvnc gui will have already taken care of this. + fi + + cat > "$tmp_cfg" < /dev/null; then + hloc="$localhost:" + pv=`findfree 5570` + proxy="vencrypt:$pv:$port" + port=$pv + if [ "X$anondh_set" = "X1" ]; then + # not needed for ANONDH in this mode + #ciphers="ciphers = ADH:@STRENGTH" + : + fi + fi + cat > "$tmp_cfg" < /dev/null 2>&1 + $STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty & + stunnel_pid=$! + echo "" + + # pause here to let the user supply a possible passphrase for the + # mycert key: + if [ "X$mycert" != "X" ]; then + nsl=10 + dsl=0 + if [ ! -f $mycert ]; then + dsl=0 + elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then + dsl=1 + fi + if [ "X$dsl" = "X1" ]; then + echo "" + echo "(** pausing $nsl secs for possible certificate passphrase dialog **)" + echo "" + sleep $nsl + echo "(** done pausing for passphrase **)" + echo "" + fi + fi + #echo T sleep 1 + sleep 1 + rm -f "$tmp_cfg" +fi + + +echo "" +if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP +fi +if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo +fi + +if [ "X$reverse" = "X" ]; then + if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then + port1=`expr 5900 + $N` # stunnel port + port2=`findfree 5970` # bridge port (viewer connects to it.) + N=`expr $port2 - 5900` + env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp & + sleep 1 + fi + echo "Running viewer:" + vnc_hp=$localhost:$N + if [ "X$stunnel_exec" != "X" ]; then + vnc_hp="exec=$STUNNEL $tmp_cfg" + fi + echo "$VNCVIEWERCMD" "$@" "$vnc_hp" + trap "final" 0 2 15 + echo "" + $VNCVIEWERCMD "$@" "$vnc_hp" + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" "$vnc_hp" + fi + fi +else + echo "Running viewer:" + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + trap "final" 0 2 15 + N2=$N + N2_trim=`echo "$N2" | sed -e 's/://g'` + if [ $N2_trim -le 200 ]; then + N2_trim=`expr $N2_trim + 5500` + fi + if [ "X$proxy" != "X" ]; then + if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then + pstunnel=`echo "$proxy" | awk -F: '{print $2}'` + plisten=`echo "$proxy" | awk -F: '{print $3}'` + IF=INADDR_ANY + if [ "X$STUNNEL_LISTEN" != "X" ]; then + IF=$STUNNEL_LISTEN + fi + PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE + PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN + PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY + PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST + STUNNEL_ONCE=1; export STUNNEL_ONCE + STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS + if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then + port1=`expr 5500 + $N2` + port2=`findfree 5580` + N2=`expr $port2 - 5500` + N2_trim=`echo "$N2" | sed -e 's/://g'` + if [ $N2_trim -le 200 ]; then + N2_trim=`expr $N2_trim + 5500` + fi + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF + fi + env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp & + sleep 1 + fi + else + PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE + PPROXY_SLEEP=1; export PPROXY_SLEEP; + fi + PPROXY_KILLPID=+1; export PPROXY_KILLPID; + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + fi + $ptmp & + # Important to have no extra pids generated between here and VNCVIEWERCMD + fi + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + N2=$N2_trim + fi + echo "$VNCVIEWERCMD" "$@" -listen $N2 + echo "" + $VNCVIEWERCMD "$@" -listen $N2 + + if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF + fi + if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF0 + fi +fi + +sleep 1 diff --git a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch new file mode 100644 index 0000000..bc10f3c --- /dev/null +++ b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch @@ -0,0 +1,111 @@ +--- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400 ++++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500 +@@ -28,13 +28,14 @@ + import java.lang.*; + import java.util.zip.*; + ++import java.util.Collections; + + // + // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. + // + + class VncCanvas extends Canvas +- implements KeyListener, MouseListener, MouseMotionListener { ++ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { + + VncViewer viewer; + RfbProto rfb; +@@ -81,6 +82,20 @@ + cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); + cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); + ++ // 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); ++ + colors = new Color[256]; + for (int i = 0; i < 256; i++) + colors[i] = new Color(cm8.getRGB(i)); +@@ -169,6 +184,7 @@ + inputEnabled = true; + addMouseListener(this); + addMouseMotionListener(this); ++ addMouseWheelListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(true); + } +@@ -177,6 +193,7 @@ + inputEnabled = false; + removeMouseListener(this); + removeMouseMotionListener(this); ++ removeMouseWheelListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(false); + } +@@ -1190,6 +1207,9 @@ + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } ++ public void mouseWheelMoved(MouseWheelEvent evt) { ++ processLocalMouseWheelEvent(evt); ++ } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { +@@ -1221,6 +1241,19 @@ + evt.consume(); + } + ++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { ++ if (viewer.rfb != null && rfb.inNormalProtocol) { ++ synchronized(rfb) { ++ try { ++ rfb.writeWheelEvent(evt); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ rfb.notify(); ++ } ++ } ++ } ++ + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { +@@ -1387,9 +1420,9 @@ + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 | +- (pixBuf[i * 4 + 1] & 0xFF) << 16 | +- (pixBuf[i * 4 + 2] & 0xFF) << 8 | +- (pixBuf[i * 4 + 3] & 0xFF); ++ (pixBuf[i * 4 + 2] & 0xFF) << 16 | ++ (pixBuf[i * 4 + 1] & 0xFF) << 8 | ++ (pixBuf[i * 4 + 0] & 0xFF); + } + } else { + result = 0; // Transparent pixel +@@ -1403,9 +1436,9 @@ + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 | +- (pixBuf[i * 4 + 1] & 0xFF) << 16 | +- (pixBuf[i * 4 + 2] & 0xFF) << 8 | +- (pixBuf[i * 4 + 3] & 0xFF); ++ (pixBuf[i * 4 + 2] & 0xFF) << 16 | ++ (pixBuf[i * 4 + 1] & 0xFF) << 8 | ++ (pixBuf[i * 4 + 0] & 0xFF); + } + } else { + result = 0; // Transparent pixel diff --git a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch new file mode 100644 index 0000000..801234a --- /dev/null +++ b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch @@ -0,0 +1,2600 @@ +diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile +--- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 ++++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400 +@@ -4,6 +4,7 @@ + + CP = cp + JC = javac ++JC_ARGS = -target 1.4 -source 1.4 + JAR = jar + ARCHIVE = VncViewer.jar + MANIFEST = MANIFEST.MF +@@ -15,25 +16,29 @@ + DesCipher.class CapabilityInfo.class CapsContainer.class \ + RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \ + SocketFactory.class HTTPConnectSocketFactory.class \ +- HTTPConnectSocket.class ReloginPanel.class ++ HTTPConnectSocket.class ReloginPanel.class \ ++ SSLSocketToMe.class ++ ++SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class + + SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ + OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ + DesCipher.java CapabilityInfo.java CapsContainer.java \ + RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \ + SocketFactory.java HTTPConnectSocketFactory.java \ +- HTTPConnectSocket.java ReloginPanel.java ++ HTTPConnectSocket.java ReloginPanel.java \ ++ SSLSocketToMe.java + + all: $(CLASSES) $(ARCHIVE) + + $(CLASSES): $(SOURCES) +- $(JC) -target 1.1 -O $(SOURCES) ++ $(JC) $(JC_ARGS) -O $(SOURCES) + + $(ARCHIVE): $(CLASSES) $(MANIFEST) +- $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) ++ $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES) + + install: $(CLASSES) $(ARCHIVE) +- $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) ++ $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) + + export:: $(CLASSES) $(ARCHIVE) $(PAGES) + @$(ExportJavaClasses) +diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java +--- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500 ++++ vnc_javasrc/RfbProto.java 2010-11-30 22:05:12.000000000 -0500 +@@ -199,7 +199,21 @@ + host = h; + port = p; + +- if (viewer.socketFactory == null) { ++ 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 if (viewer.socketFactory == null) { + sock = new Socket(host, port); + } else { + try { +@@ -255,7 +269,7 @@ + || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) + { + throw new Exception("Host " + host + " port " + port + +- " is not an RFB server"); ++ " is not an RFB server: " + b); + } + + serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); +@@ -892,6 +906,38 @@ + final static int ALT_MASK = InputEvent.ALT_MASK; + + ++ void writeWheelEvent(MouseWheelEvent evt) throws IOException { ++ ++ eventBufLen = 0; ++ ++ int x = evt.getX(); ++ int y = evt.getY(); ++ ++ if (x < 0) x = 0; ++ if (y < 0) y = 0; ++ ++ int ptrmask; ++ ++ int clicks = evt.getWheelRotation(); ++ System.out.println("writeWheelEvent: clicks: " + clicks); ++ if (clicks > 0) { ++ ptrmask = 16; ++ } else if (clicks < 0) { ++ ptrmask = 8; ++ } else { ++ return; ++ } ++ ++ eventBuf[eventBufLen++] = (byte) PointerEvent; ++ eventBuf[eventBufLen++] = (byte) ptrmask; ++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (x & 0xff); ++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (y & 0xff); ++ ++ os.write(eventBuf, 0, eventBufLen); ++ } ++ + // + // Write a pointer event message. We may need to send modifier key events + // around it to set the correct modifier state. +@@ -992,6 +1038,19 @@ + boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); + + int key; ++ if (viewer.debugKeyboard) { ++ System.out.println("----------------------------------------"); ++ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); ++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); ++ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); ++ System.out.println("evt.getID: " + evt.getID()); ++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); ++ System.out.println("evt.isActionKey: " + evt.isActionKey()); ++ System.out.println("evt.isControlDown: " + evt.isControlDown()); ++ System.out.println("evt.getModifiers: " + evt.getModifiers()); ++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); ++ System.out.println("evt.paramString: " + evt.paramString()); ++ } + if (evt.isActionKey()) { + + // +@@ -1025,6 +1084,13 @@ + return; + } + ++ if(key == 0xffc2 && viewer.mapF5_to_atsign) { ++ if (viewer.debugKeyboard) { ++ System.out.println("Mapping: F5 -> AT "); ++ } ++ key = 0x40; ++ } ++ + } else { + + // +@@ -1036,6 +1102,7 @@ + + key = keyChar; + ++ + if (key < 0x20) { + if (evt.isControlDown()) { + key += 0x60; +@@ -1121,6 +1188,16 @@ + int oldModifiers = 0; + + void writeModifierKeyEvents(int newModifiers) { ++ if(viewer.forbid_Ctrl_Alt) { ++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { ++ int orig = newModifiers; ++ newModifiers &= ~ALT_MASK; ++ newModifiers &= ~CTRL_MASK; ++ if (viewer.debugKeyboard) { ++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); ++ } ++ } ++ } + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + +diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java +--- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 ++++ vnc_javasrc/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 +@@ -0,0 +1,2067 @@ ++/* ++ * 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, 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. ++ * ++ */ ++ ++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; ++ boolean debug_certs = false; ++ ++ /* sockets */ ++ SSLSocket socket = null; ++ SSLSocketFactory factory; ++ ++ /* fallback for Proxy connection */ ++ boolean proxy_in_use = false; ++ boolean proxy_failure = false; ++ public DataInputStream is = null; ++ public OutputStream os = null; ++ ++ /* strings from user WRT proxy: */ ++ String proxy_auth_string = 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 trustsrv_ctx; ++ SSLContext trusturl_ctx; ++ SSLContext trustone_ctx; ++ ++ /* corresponding trust managers */ ++ TrustManager[] trustAllCerts; ++ TrustManager[] trustSrvCert; ++ TrustManager[] trustUrlCert; ++ TrustManager[] trustOneCert; ++ ++ /* client-side SSL auth key (oneTimeKey=...) */ ++ KeyManager[] mykey = null; ++ ++ boolean user_wants_to_see_cert = true; ++ String cert_fail = null; ++ ++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ ++ java.security.cert.Certificate[] trustallCerts = null; ++ java.security.cert.Certificate[] trustsrvCerts = null; ++ java.security.cert.Certificate[] trusturlCerts = null; ++ ++ /* utility to decode hex oneTimeKey=... and serverCert=... */ ++ 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; ++ ++ debug_certs = v.debugCerts; ++ ++ /* we will first try default factory for certification: */ ++ ++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); ++ ++ dbg("SSL startup: " + host + " " + port); ++ ++ ++ /* create trust managers to be used if initial handshake fails: */ ++ ++ trustAllCerts = new TrustManager[] { ++ /* ++ * this one accepts everything. Only used if user ++ * has disabled checking (trustAllVncCerts=yes) ++ * or when we grab the cert to show it to them in ++ * a dialog and ask them to manually verify/accept it. ++ */ ++ 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 and stored in ++ * trusturlCerts. ++ */ ++ 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 (URL)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trusturlCerts' */ ++ if (trusturlCerts == null) { ++ throw new CertificateException( ++ "No Trust url Certs array."); ++ } ++ if (trusturlCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust url Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trusturlCerts.length) { ++ throw new CertificateException( ++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trusturlCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("URL: cert mismatch at i=" + i); ++ dbg("URL: cert mismatch cert" + certs[i]); ++ dbg("URL: cert mismatch url" + trusturlCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("URL: cert info at i=" + i); ++ dbg("URL: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("URL: cert info url" + trusturlCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != URL Cert Chain."); ++ } ++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustSrvCert = new TrustManager[] { ++ /* ++ * this one accepts cert given to us in the serverCert ++ * Applet Parameter we were started with. It is ++ * currently a fatal error if the VNC Server's cert ++ * doesn't match it. ++ */ ++ 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 (SRV)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustsrvCerts' */ ++ if (trustsrvCerts == null) { ++ throw new CertificateException( ++ "No Trust srv Certs array."); ++ } ++ if (trustsrvCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust srv Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustsrvCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustsrvCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("SRV: cert mismatch at i=" + i); ++ dbg("SRV: cert mismatch cert" + certs[i]); ++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("SRV: cert info at i=" + i); ++ dbg("SRV: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("SRV: cert info srv" + trustsrvCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); ++ } ++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustOneCert = new TrustManager[] { ++ /* ++ * this one accepts only the retrieved server ++ * cert by SSLSocket by this applet we stored in ++ * trustallCerts that user has accepted or applet ++ * parameter trustAllVncCerts=yes is set. This is ++ * for when we reconnect after the user has manually ++ * accepted the trustall cert in the dialog (or set ++ * trustAllVncCerts=yes applet param.) ++ */ ++ 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 (ONE)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustallCerts' */ ++ 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 (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustallCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustallCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("ONE: cert mismatch at i=" + i); ++ dbg("ONE: cert mismatch cert" + certs[i]); ++ dbg("ONE: cert mismatch all" + trustallCerts[i]); ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("ONE: cert info at i=" + i); ++ dbg("ONE: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("ONE: cert info all" + trustallCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != TRUSTALL Cert Chain."); ++ } ++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ /* ++ * The above TrustManagers are used: ++ * ++ * 1) to retrieve the server cert in case of failure to ++ * display it to the user in a dialog. ++ * 2) to subsequently connect to the server if user agrees. ++ */ ++ ++ /* ++ * build oneTimeKey cert+key if supplied in applet parameter: ++ */ ++ 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); ++ if (debug_certs) { ++ dbg("one time key " + ff); ++ } ++ ++ 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)); ++ if (debug_certs) { ++ dbg("one time cert" + 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(); ++ } ++ ++ /* ++ * build serverCert cert if supplied in applet parameter: ++ */ ++ if (viewer.serverCert != null) { ++ CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++ byte[] cert = hex2bytes(viewer.serverCert); ++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++ trustsrvCerts = new Certificate[c.toArray().length]; ++ if (c.size() == 1) { ++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++ trustsrvCerts[0] = tmpcert; ++ } else { ++ trustsrvCerts = (Certificate[]) c.toArray(); ++ } ++ } ++ ++ /* the trust loc certs context: */ ++ try { ++ trustloc_ctx = SSLContext.getInstance("SSL"); ++ ++ /* ++ * below is a failed attempt to get jvm's default ++ * trust manager using null (below) makes it so ++ * for HttpsURLConnection the server cannot be ++ * verified (no prompting.) ++ */ ++ if (false) { ++ boolean didit = false; ++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); ++ tmf.init((KeyStore) null); ++ TrustManager [] tml = tmf.getTrustManagers(); ++ for (int i = 0; i < tml.length; i++) { ++ TrustManager tm = tml[i]; ++ if (tm instanceof X509TrustManager) { ++ TrustManager tm1[] = new TrustManager[1]; ++ tm1[0] = tm; ++ trustloc_ctx.init(mykey, tm1, null); ++ didit = true; ++ break; ++ } ++ } ++ if (!didit) { ++ trustloc_ctx.init(mykey, null, null); ++ } ++ } else { ++ /* we have to set trust manager to null */ ++ trustloc_ctx.init(mykey, null, null); ++ } ++ ++ } catch (Exception e) { ++ String msg = "SSL trustloc_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust all certs context: */ ++ 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); ++ } ++ ++ /* the trust url certs context: */ ++ 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); ++ } ++ ++ /* the trust srv certs context: */ ++ try { ++ trustsrv_ctx = SSLContext.getInstance("SSL"); ++ trustsrv_ctx.init(mykey, trustSrvCert, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trustsrv_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust the one cert from server context: */ ++ 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); ++ } ++ } ++ ++ /* ++ * we call this early on to 1) check for a proxy, 2) grab ++ * Browser/JVM accepted HTTPS cert. ++ */ ++ public void check_for_proxy_and_grab_vnc_server_cert() { ++ ++ trusturlCerts = null; ++ proxy_in_use = false; ++ ++ if (viewer.ignoreProxy) { ++ /* applet param says skip it. */ ++ /* the downside is we do not set trusturlCerts for comparison later... */ ++ /* nor do we autodetect x11vnc for GET=1. */ ++ return; ++ } ++ ++ dbg("------------------------------------------------"); ++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); ++ ++ dbg("TRYING HTTPS:"); ++ String ustr = "https://" + host + ":"; ++ if (viewer.httpsPort != null) { ++ ustr += viewer.httpsPort; ++ } else { ++ ustr += port; ++ } ++ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTPS URL connection to host:port */ ++ URL url = new URL(ustr); ++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); ++ ++ if (mykey != null) { ++ /* with oneTimeKey (mykey) we can't use the default SSL context */ ++ if (trustsrvCerts != null) { ++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); ++ } else if (trustloc_ctx != null) { ++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); ++ } ++ } ++ ++ https.setUseCaches(false); ++ https.setRequestMethod("GET"); ++ https.setRequestProperty("Pragma", "No-Cache"); ++ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++ https.setDoInput(true); ++ ++ dbg("trying https.connect()"); ++ https.connect(); ++ ++ dbg("trying https.getServerCertificates()"); ++ 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; ++ dbg("An HTTPS proxy is in use. There may be connection problems."); ++ } ++ ++ dbg("trying https.getContent()"); ++ Object output = https.getContent(); ++ dbg("trying https.disconnect()"); ++ https.disconnect(); ++ if (! viewer.GET) { ++ String header = https.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (1), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ ++ } catch(Exception e) { ++ dbg("HttpsURLConnection: " + e.getMessage()); ++ } ++ ++ if (proxy_in_use) { ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } else if (trusturlCerts != null && !viewer.forceProxy) { ++ /* Allow user to require HTTP check? use forceProxy for now. */ ++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } ++ ++ /* ++ * XXX need to remember scenario where this extra check ++ * gives useful info. User's Browser proxy settings? ++ */ ++ dbg("TRYING HTTP:"); ++ ustr = "http://" + host + ":" + port; ++ ustr += viewer.urlPrefix + "/index.vnc"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ ++ 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); ++ ++ dbg("trying http.connect()"); ++ http.connect(); ++ ++ if (http.usingProxy()) { ++ proxy_in_use = true; ++ dbg("An HTTP proxy is in use. There may be connection problems."); ++ } ++ dbg("trying http.getContent()"); ++ Object output = http.getContent(); ++ dbg("trying http.disconnect()"); ++ http.disconnect(); ++ if (! viewer.GET) { ++ String header = http.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (2), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ } catch(Exception e) { ++ dbg("HttpURLConnection: " + e.getMessage()); ++ } ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ } ++ ++ public Socket connectSock() throws IOException { ++ /* ++ * first try a https connection to detect a proxy, and ++ * grab the VNC server cert at the same time: ++ */ ++ check_for_proxy_and_grab_vnc_server_cert(); ++ ++ boolean srv_cert = false; ++ ++ if (trustsrvCerts != null) { ++ /* applet parameter suppled serverCert */ ++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); ++ factory = trustsrv_ctx.getSocketFactory(); ++ srv_cert = true; ++ } else if (viewer.trustAllVncCerts) { ++ /* trust all certs (no checking) */ ++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); ++ factory = trustall_ctx.getSocketFactory(); ++ } else if (trusturlCerts != null) { ++ /* trust certs the Browser/JVM accepted in check_for_proxy... */ ++ dbg("using trusturl_ctx"); ++ factory = trusturl_ctx.getSocketFactory(); ++ } else { ++ /* trust the local defaults */ ++ 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"); ++ } ++ ++ int timeout = 6; ++ if (timeout > 0) { ++ socket = (SSLSocket) factory.createSocket(); ++ InetSocketAddress inetaddr = new InetSocketAddress(host, port); ++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); ++ socket.connect(inetaddr, timeout * 1000); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } ++ ++ } catch (Exception esock) { ++ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); ++ } ++ } else { ++ /* n.b. socket is left in error state to cause ex. below. */ ++ } ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The Server Connection Verified OK on 1st try."); ++ ++ java.security.cert.Certificate[] currentTrustedCerts; ++ BrowserCertsDialog bcd; ++ ++ SSLSession sess = socket.getSession(); ++ currentTrustedCerts = sess.getPeerCertificates(); ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-1 keeping socket."); ++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy."); ++ } ++ 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 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.serverCert != null && trustsrvCerts != null) { ++ dbg("viewer.serverCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.trustUrlVncCert) { ++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else { ++ /* have a dialog with the user: */ ++ bcd = new BrowserCertsDialog(serv, host + ":" + port); ++ dbg("browser certs dialog begin."); ++ bcd.queryUser(); ++ dbg("browser certs dialog finished."); ++ ++ if (bcd.showCertDialog) { ++ String msg = "user wants to see cert"; ++ dbg(msg); ++ user_wants_to_see_cert = true; ++ if (cert_fail == null) { ++ cert_fail = "user-view"; ++ } ++ throw new SSLHandshakeException(msg); ++ } else { ++ user_wants_to_see_cert = false; ++ dbg("browser certs dialog: user said yes, accept it"); ++ } ++ } ++ ++ } catch (SSLHandshakeException eh) { ++ dbg("SSLHandshakeException: could not automatically verify Server."); ++ dbg("msg: " + eh.getMessage()); ++ ++ ++ /* send a cleanup string just in case: */ ++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; ++ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!"); ++ } ++ ++ /* reload */ ++ ++ socket = null; ++ ++ String reason = null; ++ ++ if (srv_cert) { ++ /* for serverCert usage we make this a fatal error. */ ++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); ++ /* see below in TrustDialog were we describe this case to user anyway */ ++ } ++ ++ /* ++ * Reconnect, trusting any cert, so we can grab ++ * the cert to show it to the user in a dialog ++ * for him to manually accept. This 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); ++ } ++ ++ if (debug_certs) { ++ dbg("trusturlCerts: " + trusturlCerts); ++ dbg("trustsrvCerts: " + trustsrvCerts); ++ } ++ if (trusturlCerts == null && cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); ++ ++ /* grab the cert: */ ++ try { ++ SSLSession sess = socket.getSession(); ++ trustallCerts = sess.getPeerCertificates(); ++ } catch (Exception e) { ++ throw new Exception("Could not get " + ++ "Peer Certificate"); ++ } ++ if (debug_certs) { ++ dbg("trustallCerts: " + trustallCerts); ++ } ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); ++ } else if (! browser_cert_match()) { ++ /* ++ * close socket now, we will reopen after ++ * dialog if user agrees to use the cert. ++ */ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!"); ++ } ++ socket = null; ++ ++ /* dialog with user to accept cert or not: */ ++ ++ TrustDialog td= new TrustDialog(host, port, ++ trustallCerts); ++ ++ if (cert_fail == null) { ++ ; ++ } else if (cert_fail.equals("user-view")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " You Asked to View the Certificate."; ++ } else if (cert_fail.equals("server-cert-mismatch")) { ++ /* this is now fatal error, see above. */ ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Certificate\n" ++ + " specified in the supplied 'serverCert' Applet Parameter."; ++ } else if (cert_fail.equals("cert-mismatch")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Website's\n" ++ + " HTTPS Certificate (that you previously accepted; either\n" ++ + " manually or automatically via Certificate Authority.)"; ++ } else if (cert_fail.equals("missing-certs")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " Not all Certificates could be obtained to check."; ++ } ++ ++ if (! td.queryUser(reason)) { ++ 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()); ++ } ++ ++ /* reload again: */ ++ ++ if (socket != null) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!!"); ++ } ++ socket = null; ++ } ++ ++ /* ++ * Now connect a 3rd time, using the cert ++ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); ++ ++ } catch (Exception ehand3) { ++ dbg("** Could not TrustAll/TrustOne Verify Server #3."); ++ ++ throw new IOException(ehand3.getMessage()); ++ } ++ } ++ ++ /* we have socket (possibly null) at this point, so proceed: */ ++ ++ /* handle x11vnc GET=1, if applicable: */ ++ 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: " + str); ++ OutputStream os = socket.getOutputStream(); ++ String type = "os"; ++ ++ if (type == "os") { ++ os.write(str.getBytes()); ++ os.flush(); ++ System.out.println("used OutputStream"); ++ } else if (type == "bs") { ++ BufferedOutputStream bs = new BufferedOutputStream(os); ++ bs.write(str.getBytes()); ++ bs.flush(); ++ System.out.println("used BufferedOutputStream"); ++ } else if (type == "ds") { ++ DataOutputStream ds = new DataOutputStream(os); ++ ds.write(str.getBytes()); ++ ds.flush(); ++ System.out.println("used DataOutputStream"); ++ } ++ 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."); ++ dbg(""); ++ ++ /* could be null, let caller handle that. */ ++ return (Socket) socket; ++ } ++ ++ boolean browser_cert_match() { ++ String msg = "Browser URL accept previously accepted cert"; ++ ++ if (user_wants_to_see_cert) { ++ return false; ++ } ++ ++ if (viewer.serverCert != null || trustsrvCerts != null) { ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (trustallCerts != null && trusturlCerts != null) { ++ if (trustallCerts.length == trusturlCerts.length) { ++ boolean ok = true; ++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ ++ for (int i = 0; i < trusturlCerts.length; i++) { ++ if (! trustallCerts[i].equals(trusturlCerts[i])) { ++ dbg("BCM: cert mismatch at i=" + i); ++ dbg("BCM: cert mismatch url" + trusturlCerts[i]); ++ dbg("BCM: cert mismatch all" + trustallCerts[i]); ++ ok = false; ++ } ++ } ++ if (ok) { ++ System.out.println(msg); ++ if (cert_fail == null) { ++ cert_fail = "did-not-fail"; ++ } ++ return true; ++ } else { ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ return false; ++ } ++ } ++ } ++ if (cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ return false; ++ } ++ ++ 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; ++ } ++ ++ /* this will do the proxy CONNECT negotiation and hook us up. */ ++ ++ private void proxy_helper(String proxyHost, int proxyPort) { ++ ++ boolean proxy_auth = false; ++ String proxy_auth_basic_realm = ""; ++ String hp = host + ":" + port; ++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); ++ ++ /* we loop here a few times trying for the password case */ ++ for (int k=0; k < 2; k++) { ++ dbg("proxy_in_use psocket: " + k); ++ ++ if (proxySock != null) { ++ try { ++ proxySock.close(); ++ } catch (Exception e) { ++ dbg("proxy socket is grumpy."); ++ } ++ } ++ ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("1-a sadly, returning a null socket"); ++ return; ++ } ++ ++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n"; ++ ++ dbg("requesting via proxy: " + req1); ++ ++ if (proxy_auth) { ++ if (proxy_auth_string == null) { ++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); ++ pp.queryUser(); ++ proxy_auth_string = pp.getAuth(); ++ } ++ //dbg("auth1: " + proxy_auth_string); ++ ++ String auth2 = Base64Coder.encodeString(proxy_auth_string); ++ //dbg("auth2: " + auth2); ++ ++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; ++ //dbg("req1: " + req1); ++ ++ dbg("added Proxy-Authorization: Basic ... to request"); ++ } ++ req1 += "\r\n"; ++ ++ try { ++ proxy_os.write(req1.getBytes()); ++ String reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply.trim()); ++ ++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { ++ proxy_auth = true; ++ proxySock.close(); ++ } else 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; ++ } ++ } ++ } catch(Exception e) { ++ dbg("some proxy socket problem: " + e.getMessage()); ++ } ++ ++ /* read the rest of the HTTP headers */ ++ while (true) { ++ String line = readline(proxy_is); ++ dbg("proxy line: " + line.trim()); ++ if (proxy_auth) { ++ String uc = line.toLowerCase(); ++ if (uc.indexOf("proxy-authenticate:") == 0) { ++ if (uc.indexOf(" basic ") >= 0) { ++ int idx = uc.indexOf(" realm"); ++ if (idx >= 0) { ++ proxy_auth_basic_realm = uc.substring(idx+1); ++ } ++ } ++ } ++ } ++ if (line.equals("\r\n") || line.equals("\n")) { ++ break; ++ } ++ } ++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { ++ /* we only try once for the non-password case: */ ++ break; ++ } ++ } ++ } ++ ++ 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) { ++ /* sandboxed applet might not be able to read it. */ ++ dbg("props failed: " + e.getMessage()); ++ } ++ if (viewer.proxyHost != null) { ++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); ++ proxyHost = viewer.proxyHost; ++ if (viewer.proxyPort != null) { ++ proxyPort = gint(viewer.proxyPort); ++ } else { ++ proxyPort = 8080; ++ } ++ ++ } else if (props != null) { ++ dbg("\n---------------\nAll props:"); ++ props.list(System.out); ++ dbg("\n---------------\n\n"); ++ ++ /* scrape throught properties looking for proxy info: */ ++ ++ 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.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; ++ } ++ } ++ ++ 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 (proxyHost != null && proxyPort > 0) { ++ break; ++ } ++ ++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 ++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { ++ continue; ++ } ++ if (v2.indexOf("http") < 0) { ++ 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) { ++ j = p.indexOf("http"); ++ 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()); ++ } ++ ++ proxy_helper(proxyHost, proxyPort); ++ if (proxySock == null) { ++ return null; ++ } ++ } 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("proxy socket problem-2: " + 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(String reason) { ++ ++ /* 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"); ++ } ++ if (reason != null) { ++ reason += "\n\n"; ++ } ++ ++ text = "\n" +++ "Unable to verify the identity of\n" +++ "\n" +++ " " + host + ":" + port + "\n" +++ "\n" +++ infostr +++ "\n" +++ "as a trusted VNC server.\n" +++ "\n" +++ reason +++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" +++ "is due to one of the following:\n" +++ "\n" +++ " - Your requesting to View the Certificate before accepting.\n" +++ "\n" +++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" +++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" +++ "\n" +++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" +++ " the Apache Web server has a certificate *different* from the VNC server's.\n" +++ "\n" +++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" +++ " obtained by this applet to compare the VNC Server Certificate against.\n" +++ "\n" +++ " - The VNC Server's Certificate does not match the one specified in the\n" +++ " supplied 'serverCert' Java Applet Parameter.\n" +++ "\n" +++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" +++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" +++ "automatically authenticate this VNC Server.\n" +++ "\n" +++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" +++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" +++ "certificate (except for the Apache portal case above where they don't match.)\n" +++ "\n" +++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" +++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" +++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); ++ dialog.hide(); ++ ++ } else if (evt.getSource() == cancel) { ++ /* Cancel button clicked */ ++ trust_this_session = false; ++ ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++ ++ 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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++class ProxyPasswdDialog implements ActionListener { ++ String guessedHost = null; ++ String guessedPort = null; ++ String guessedUser = null; ++ String guessedPasswd = null; ++ String realm = 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 entry1; ++ TextField entry2; ++ String reply1 = ""; ++ String reply2 = ""; ++ ++ ProxyPasswdDialog (String h, int p, String realm) { ++ guessedHost = h; ++ try { ++ guessedPort = Integer.toString(p); ++ } catch (Exception e) { ++ guessedPort = "8080"; ++ } ++ this.realm = realm; ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Proxy Requires Username and Password"); ++ ++ dialog = new Dialog(frame, true); ++ ++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); ++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); ++ entry1 = new TextField(30); ++ entry2 = new TextField(30); ++ entry2.setEchoChar('*'); ++ ok = new Button("OK"); ++ ok.addActionListener(this); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", label); ++ dialog.add("Center", entry1); ++ dialog.add("South", entry2); ++ dialog.add("East", ok); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til OK or Cancel pressed. */ ++ return; ++ } ++ ++ public String getAuth() { ++ return reply1 + ":" + reply2; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == ok) { ++ reply1 = entry1.getText(); ++ reply2 = entry2.getText(); ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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 and/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 += "If the VNC Server connection times out while you are reading this\n"; ++m += "dialog, then restart the connection and try again.\n"; ++m += "\n"; ++m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; ++m += "\n"; ++ ++ TextArea textarea = new TextArea(m, 22, 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. */ ++ System.out.println("done show()"); ++ return; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == yes) { ++ showCertDialog = false; ++ //dialog.dispose(); ++ dialog.hide(); ++ } else if (evt.getSource() == no) { ++ showCertDialog = true; ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ System.out.println("done actionPerformed()"); ++ } ++} ++ ++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 ""; ++ } ++ } ++} ++ ++class Base64Coder { ++ ++ // Mapping table from 6-bit nibbles to Base64 characters. ++ private static char[] map1 = new char[64]; ++ static { ++ int i=0; ++ for (char c='A'; c<='Z'; c++) map1[i++] = c; ++ for (char c='a'; c<='z'; c++) map1[i++] = c; ++ for (char c='0'; c<='9'; c++) map1[i++] = c; ++ map1[i++] = '+'; map1[i++] = '/'; } ++ ++ // Mapping table from Base64 characters to 6-bit nibbles. ++ private static byte[] map2 = new byte[128]; ++ static { ++ for (int i=0; iin. ++ * @return A character array with the Base64 encoded data. ++ */ ++ public static char[] encode (byte[] in, int iLen) { ++ int oDataLen = (iLen*4+2)/3; // output length without padding ++ int oLen = ((iLen+2)/3)*4; // output length including padding ++ char[] out = new char[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++] & 0xff; ++ int i1 = ip < iLen ? in[ip++] & 0xff : 0; ++ int i2 = ip < iLen ? in[ip++] & 0xff : 0; ++ int o0 = i0 >>> 2; ++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); ++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); ++ int o3 = i2 & 0x3F; ++ out[op++] = map1[o0]; ++ out[op++] = map1[o1]; ++ out[op] = op < oDataLen ? map1[o2] : '='; op++; ++ out[op] = op < oDataLen ? map1[o3] : '='; op++; } ++ return out; } ++ ++ /** ++ * Decodes a string from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return A String containing the decoded data. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static String decodeString (String s) { ++ return new String(decode(s)); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (String s) { ++ return decode(s.toCharArray()); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * No blanks or line breaks are allowed within the Base64 encoded data. ++ * @param in a character array containing the Base64 encoded data. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (char[] in) { ++ int iLen = in.length; ++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); ++ while (iLen > 0 && in[iLen-1] == '=') iLen--; ++ int oLen = (iLen*3) / 4; ++ byte[] out = new byte[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++]; ++ int i1 = in[ip++]; ++ int i2 = ip < iLen ? in[ip++] : 'A'; ++ int i3 = ip < iLen ? in[ip++] : 'A'; ++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int b0 = map2[i0]; ++ int b1 = map2[i1]; ++ int b2 = map2[i2]; ++ int b3 = map2[i3]; ++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int o0 = ( b0 <<2) | (b1>>>4); ++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); ++ int o2 = ((b2 & 3)<<6) | b3; ++ out[op++] = (byte)o0; ++ if (op= 2) { ++ proxyPort = new String(pieces[1]); ++ } else { ++ proxyPort = new String("8080"); ++ } ++ } ++ } ++ str = readParameter("proxyHost", false); ++ if (str != null) { ++ proxyHost = new String(str); ++ } ++ str = readParameter("proxyPort", false); ++ if (str != null) { ++ proxyPort = new String(str); ++ } ++ if (proxyHost != null && proxyPort == null) { ++ proxyPort = new String("8080"); ++ } ++ ++ 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; ++ } ++ debugCerts = false; ++ str = readParameter("debugCerts", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugCerts = true; ++ } ++ debugKeyboard = false; ++ str = readParameter("debugKeyboard", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugKeyboard = true; ++ } ++ mapF5_to_atsign = false; ++ str = readParameter("mapF5_to_atsign", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ mapF5_to_atsign = true; ++ } ++ forbid_Ctrl_Alt = false; ++ str = readParameter("forbid_Ctrl_Alt", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ forbid_Ctrl_Alt = true; ++ } + } + + public String readParameter(String name, boolean required) { diff --git a/webclients/ssl/ultra.vnc b/webclients/ssl/ultra.vnc new file mode 100644 index 0000000..3c57445 --- /dev/null +++ b/webclients/ssl/ultra.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/ssl/ultraproxy.vnc b/webclients/ssl/ultraproxy.vnc new file mode 100644 index 0000000..fd842c4 --- /dev/null +++ b/webclients/ssl/ultraproxy.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/ssl/ultrasigned.vnc b/webclients/ssl/ultrasigned.vnc new file mode 100644 index 0000000..a711655 --- /dev/null +++ b/webclients/ssl/ultrasigned.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch new file mode 100644 index 0000000..3309860 --- /dev/null +++ b/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch @@ -0,0 +1,5494 @@ +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-31 15:40:45.000000000 -0400 +@@ -43,30 +43,36 @@ + viewer = v; + + setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); +- disconnectButton = new Button("Disconnect"); ++ if (v.ftpOnly) { ++ disconnectButton = new Button("Quit"); ++ } else { ++ disconnectButton = new Button("Close"); ++ } + disconnectButton.setEnabled(false); + add(disconnectButton); + disconnectButton.addActionListener(this); +- optionsButton = new Button("Options"); +- add(optionsButton); +- optionsButton.addActionListener(this); +- clipboardButton = new Button("Clipboard"); +- clipboardButton.setEnabled(false); +- add(clipboardButton); +- clipboardButton.addActionListener(this); +- if (viewer.rec != null) { +- recordButton = new Button("Record"); +- add(recordButton); +- recordButton.addActionListener(this); +- } +- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); +- ctrlAltDelButton.setEnabled(false); +- add(ctrlAltDelButton); +- ctrlAltDelButton.addActionListener(this); +- refreshButton = new Button("Refresh"); +- refreshButton.setEnabled(false); +- add(refreshButton); +- refreshButton.addActionListener(this); ++ if (!v.ftpOnly) { ++ optionsButton = new Button("Options"); ++ add(optionsButton); ++ optionsButton.addActionListener(this); ++ clipboardButton = new Button("Clipboard"); ++ clipboardButton.setEnabled(false); ++ add(clipboardButton); ++ clipboardButton.addActionListener(this); ++ if (viewer.rec != null) { ++ recordButton = new Button("Record"); ++ add(recordButton); ++ recordButton.addActionListener(this); ++ } ++ ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); ++ ctrlAltDelButton.setEnabled(false); ++ add(ctrlAltDelButton); ++ ctrlAltDelButton.addActionListener(this); ++ refreshButton = new Button("Refresh"); ++ refreshButton.setEnabled(false); ++ add(refreshButton); ++ refreshButton.addActionListener(this); ++ } + ftpButton = new Button("File Transfer"); + ftpButton.setEnabled(false); + add(ftpButton); +@@ -79,9 +85,10 @@ + + public void enableButtons() { + disconnectButton.setEnabled(true); ++ ftpButton.setEnabled(true); ++ if (viewer.ftpOnly) {return;} + clipboardButton.setEnabled(true); + refreshButton.setEnabled(true); +- ftpButton.setEnabled(true); + } + + // +@@ -89,6 +96,9 @@ + // + + public void disableButtonsOnDisconnect() { ++ ftpButton.setEnabled(false); ++ if (viewer.ftpOnly) {return;} ++ + remove(disconnectButton); + disconnectButton = new Button("Hide desktop"); + disconnectButton.setEnabled(true); +@@ -99,7 +109,6 @@ + clipboardButton.setEnabled(false); + ctrlAltDelButton.setEnabled(false); + refreshButton.setEnabled(false); +- ftpButton.setEnabled(false); + + validate(); + } +@@ -110,6 +119,7 @@ + // + + public void enableRemoteAccessControls(boolean enable) { ++ if (viewer.ftpOnly) {return;} + ctrlAltDelButton.setEnabled(enable); + } + +@@ -163,9 +173,19 @@ + } + else if (evt.getSource() == ftpButton) + { +- viewer.ftp.setVisible(!viewer.ftp.isVisible()); ++// begin runge/x11vnc ++ if (viewer.ftpOnly) { ++ viewer.vncFrame.setVisible(false); ++ } ++ viewer.ftp.setSavedLocations(); ++ if (viewer.ftp.isVisible()) { ++ viewer.ftp.doClose(); ++ } else { ++ viewer.ftp.doOpen(); ++ } ++// end runge/x11vnc + 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 2009-01-13 09:48:30.000000000 -0500 +@@ -24,8 +24,17 @@ + import java.io.*; + import java.util.ArrayList; + import java.util.Vector; ++import java.util.Date; + import javax.swing.*; + ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.*; ++ ++// begin runge/x11vnc ++import java.util.Arrays; ++// end runge/x11vnc ++ + + /* + * Created on Feb 25, 2004 +@@ -74,12 +83,31 @@ + public javax.swing.JTextField connectionStatus = null; + public boolean updateDriveList; + private Vector remoteList = null; ++ private Vector remoteListInfo = null; + private Vector localList = null; ++ private Vector localListInfo = null; + private File currentLocalDirectory = null; // Holds the current local Directory + private File currentRemoteDirectory = null; // Holds the current remote Directory + 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; ++ private javax.swing.JButton refreshButton = 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; ++ private int lastRemoteIndex = -1; ++ private int lastLocalIndex = -1; ++ private boolean doingShortcutDir = false; ++ private boolean gotShortcutDir = false; ++ private boolean ignore_events = false; ++// end runge/x11vnc + + // sf@2004 - Separate directories and files for better lisibility + private ArrayList DirsList; +@@ -125,11 +153,61 @@ + + void refreshRemoteLocation() + { ++ ++//System.out.println("refreshRemoteLocation1"); + remoteList.clear(); ++ remoteListInfo.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 +@@ -143,6 +221,9 @@ + int size = str.length(); + String driveType = null; + String[] drive = new String[str.length() / 3]; ++ int idx = 0, C_drive = -1, O_drive = -1; ++ ++System.out.println("ComboBox: Str '" + str + "'"); + + // Loop through the string to create a String[] + for (int i = 0; i < size; i = i + 3) { +@@ -150,26 +231,68 @@ + driveType = str.substring(i + 2, i + 3); + if (driveType.compareTo("f") == 0) + drive[i / 3] += "\\ Floppy"; +- if (driveType.compareTo("l") == 0) ++ if (driveType.compareTo("l") == 0) { + drive[i / 3] += "\\ Local Disk"; ++ if (drive[i/3].substring(0,1).toUpperCase().equals("C")) { ++ C_drive = idx; ++ } else if (O_drive < 0) { ++ O_drive = idx; ++ } ++ } + if (driveType.compareTo("c") == 0) + drive[i / 3] += "\\ CD-ROM"; + if (driveType.compareTo("n") == 0) + drive[i / 3] += "\\ Network"; + + remoteDrivesComboBox.addItem(drive[i / 3]); ++System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'"); ++ idx++; ++ } ++ ++ // runge ++ if (viewer.ftpDropDown != null) { ++ String[] dd = viewer.ftpDropDown.split("\\."); ++ for (int i=0; i < dd.length; i++) { ++ if (!dd[i].equals("")) { ++ String s = dd[i]; ++ if (s.startsWith("TOP_")) { ++ s = s.substring(4); ++ remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0); ++ } else { ++ remoteDrivesComboBox.addItem(" [" + s + "]"); ++ } ++ } ++ } ++ } else { ++ remoteDrivesComboBox.addItem(" [My Documents]"); ++ remoteDrivesComboBox.addItem(" [Desktop]"); ++ remoteDrivesComboBox.addItem(" [Home]"); + } ++ + //sf@ - Select Drive C:as default if possible + boolean bFound = false; +- for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) +- { +- if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) +- { +- remoteDrivesComboBox.setSelectedIndex(i); ++ ++ if (false) { ++ for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) { ++ if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) { ++ remoteDrivesComboBox.setSelectedIndex(i); ++ bFound = true; ++ } ++ } ++ } else { ++ if (C_drive >= 0) { ++ remoteDrivesComboBox.setSelectedIndex(C_drive); ++ bFound = true; ++System.out.println("ComboBox: C_drive index: " + C_drive); ++ } else if (O_drive >= 0) { ++ remoteDrivesComboBox.setSelectedIndex(O_drive); + bFound = true; ++System.out.println("ComboBox: Other_drive index: " + O_drive); + } + } ++ + if (!bFound) remoteDrivesComboBox.setSelectedIndex(0); ++ + updateDriveList = false; + return drive; + } +@@ -185,6 +308,8 @@ + stopButton.setVisible(true); + stopButton.setEnabled(true); + receiveButton.setEnabled(false); ++ viewButton.setEnabled(false); // runge/x11vnc ++ refreshButton.setEnabled(false); + remoteTopButton.setEnabled(false); + sendButton.setEnabled(false); + remoteFileTable.setEnabled(false); +@@ -207,6 +332,8 @@ + stopButton.setVisible(false); + stopButton.setEnabled(false); + receiveButton.setEnabled(true); ++ viewButton.setEnabled(true); // runge/x11vnc ++ refreshButton.setEnabled(true); + remoteTopButton.setEnabled(true); + sendButton.setEnabled(true); + remoteFileTable.setEnabled(true); +@@ -221,10 +348,11 @@ + /* + * Print Directory prints out all the contents of a directory + */ +- void printDirectory(ArrayList a) { ++ void printDirectory(ArrayList a, ArrayList b) { + + for (int i = 0; i < a.size(); i++) { + remoteList.addElement(a.get(i)); ++ remoteListInfo.addElement(b.get(i)); + } + remoteFileTable.setListData(remoteList); + } +@@ -235,10 +363,12 @@ + * @return void + */ + private void initialize() { ++ ignore_events = true; + this.setSize(794, 500); + this.setContentPane(getJContentPane()); ++ ignore_events = false; + updateDriveList = true; +- } ++ } + /** + * This method initializes jContentPane. This is the main content pane + * +@@ -253,6 +383,33 @@ + jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST); + jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST); + jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER); ++ ++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); ++ AbstractAction escapeAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ System.out.println("Escape Pressed"); ++ if (viewer.ftpOnly) { ++ System.out.println("exiting..."); ++ System.exit(0); ++ } else { ++ doClose(); ++ } ++ } ++ }; ++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++ jContentPane.getInputMap().put(stroke, "escapeAction"); ++ jContentPane.getActionMap().put("escapeAction", escapeAction); ++ ++ stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK); ++ AbstractAction resetAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ System.out.println("Ctrl-R Pressed"); ++ doReset(); ++ } ++ }; ++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction"); ++ jContentPane.getInputMap().put(stroke, "resetAction"); ++ jContentPane.getActionMap().put("resetAction", resetAction); + } + return jContentPane; + } +@@ -270,6 +427,7 @@ + topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER); + topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST); + topPanelLocal.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelLocal"); + } + return topPanelLocal; + } +@@ -288,6 +446,7 @@ + topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER); + topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST); + topPanelRemote.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelRemote"); + } + return topPanelRemote; + } +@@ -301,6 +460,7 @@ + if (topPanelCenter == null) { + topPanelCenter = new javax.swing.JPanel(); + topPanelCenter.add(getDummyButton(), null); ++//System.out.println("getTopPanelCenter"); + } + return topPanelCenter; + } +@@ -328,6 +488,7 @@ + topPanel.add(getRemoteTopButton(), null); + topPanel.setBackground(java.awt.Color.lightGray); + */ ++//System.out.println("getTopPanel"); + } + return topPanel; + } +@@ -348,6 +509,7 @@ + statusPanel.add(getJProgressBar(), null); + statusPanel.add(getConnectionStatus(), null); + statusPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getStatusPanel"); + + } + return statusPanel; +@@ -368,6 +530,7 @@ + remotePanel.add(getRemoteScrollPane(), null); + remotePanel.add(getRemoteStatus(), null); + remotePanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getRemotePanel"); + } + return remotePanel; + } +@@ -390,6 +553,7 @@ + localPanel.setComponentOrientation( + java.awt.ComponentOrientation.UNKNOWN); + localPanel.setName("localPanel"); ++//System.out.println("getLocalPanel"); + } + return localPanel; + } +@@ -405,12 +569,15 @@ + buttonPanel = new javax.swing.JPanel(); + buttonPanel.setLayout(null); + buttonPanel.add(getReceiveButton(), null); ++ buttonPanel.add(getRefreshButton(), null); // runge/x11vnc ++ buttonPanel.add(getViewButton(), null); // runge/x11vnc + buttonPanel.add(getNewFolderButton(), null); + buttonPanel.add(getCloseButton(), null); + buttonPanel.add(getDeleteButton(), null); + buttonPanel.add(getSendButton(), null); + buttonPanel.add(getStopButton(), null); + buttonPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getButtonPanel"); + } + return buttonPanel; + } +@@ -422,10 +589,11 @@ + 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); ++//System.out.println("getSendButton"); + + } + return sendButton; +@@ -438,7 +606,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 +621,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 +636,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 +644,39 @@ + return newFolderButton; + } + ++// begin runge/x11vnc ++ /** ++ * This method initializes refreshButton ++ * ++ * @return javax.swing.JButton ++ */ ++ private javax.swing.JButton getRefreshButton() { ++ if (refreshButton == null) { ++ refreshButton = new javax.swing.JButton(); ++ refreshButton.setBounds(15, 170, 107, 25); ++ refreshButton.setText("Refresh"); ++ refreshButton.setName("refreshButton"); ++ refreshButton.addActionListener(this); ++ } ++ return refreshButton; ++ } ++ /** ++ * This method initializes viewButton ++ * ++ * @return javax.swing.JButton ++ */ ++ private javax.swing.JButton getViewButton() { ++ if (viewButton == null) { ++ viewButton = new javax.swing.JButton(); ++ viewButton.setBounds(15, 200, 107, 25); ++ viewButton.setText("View File"); ++ viewButton.setName("viewButton"); ++ viewButton.addActionListener(this); ++ } ++ return viewButton; ++ } ++// end runge/x11vnc ++ + /** + * This method initializes stopButton + * +@@ -486,7 +687,7 @@ + if (stopButton == null) + { + stopButton = new javax.swing.JButton(); +- stopButton.setBounds(20, 200, 97, 25); ++ stopButton.setBounds(15, 230, 107, 25); // runge/x11vnc + stopButton.setText("Stop"); + stopButton.setName("stopButton"); + stopButton.addActionListener(this); +@@ -503,8 +704,12 @@ + private javax.swing.JButton getCloseButton() { + if (closeButton == null) { + closeButton = new javax.swing.JButton(); +- closeButton.setBounds(20, 325, 97, 25); +- closeButton.setText("Close"); ++ closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc ++ if (viewer.ftpOnly) { ++ closeButton.setText("Quit"); ++ } else { ++ closeButton.setText("Close"); ++ } + closeButton.setName("closeButton"); + closeButton.addActionListener(this); + } +@@ -551,6 +756,7 @@ + //Select the second entry (e.g. C:\) + // localDrivesComboBox.setSelectedIndex(1); + localDrivesComboBox.addActionListener(this); ++//System.out.println("getLocalDrivesComboBox"); + } + updateDriveList = false; + return localDrivesComboBox; +@@ -567,6 +773,7 @@ + remoteDrivesComboBox.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + remoteDrivesComboBox.addActionListener(this); ++//System.out.println("getRemoteDrivesComboBox"); + + } + return remoteDrivesComboBox; +@@ -587,6 +794,7 @@ + localMachineLabel.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 11)); + localMachineLabel.setEditable(false); ++//System.out.println("getLocalMachineLabel"); + } + return localMachineLabel; + } +@@ -622,6 +830,7 @@ + localTopButton.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + localTopButton.addActionListener(this); ++//System.out.println("getLocalTopButton"); + } + return localTopButton; + } +@@ -638,6 +847,7 @@ + remoteTopButton.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + remoteTopButton.addActionListener(this); ++//System.out.println("getRemoteTopButton"); + } + return remoteTopButton; + } +@@ -650,9 +860,24 @@ + private javax.swing.JList getLocalFileTable() { + if (localFileTable == null) { + localList = new Vector(0); ++ localListInfo = new Vector(0); + localFileTable = new JList(localList); ++ MouseMotionListener mlisten = new MouseMotionAdapter() { ++ public void mouseMoved(MouseEvent e) { ++ int index = localFileTable.locationToIndex(e.getPoint()); ++ if (index == lastLocalIndex) { ++ return; ++ } else if (index < 0) { ++ return; ++ } ++ lastLocalIndex = index; ++ connectionStatus.setText((String) localListInfo.get(index)); ++ } ++ }; + localFileTable.addMouseListener(this); ++ localFileTable.addMouseMotionListener(mlisten); + localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getLocalFileTable"); + } + return localFileTable; + } +@@ -669,6 +894,7 @@ + localScrollPane.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + localScrollPane.setName("localFileList"); ++//System.out.println("getLocalScrollPane"); + } + return localScrollPane; + } +@@ -680,10 +906,25 @@ + private javax.swing.JList getRemoteFileTable() { + if (remoteFileTable == null) { + remoteList = new Vector(0); ++ remoteListInfo = new Vector(0); + remoteFileTable = new JList(remoteList); ++ MouseMotionListener mlisten = new MouseMotionAdapter() { ++ public void mouseMoved(MouseEvent e) { ++ int index = remoteFileTable.locationToIndex(e.getPoint()); ++ if (index == lastRemoteIndex) { ++ return; ++ } else if (index < 0) { ++ return; ++ } ++ lastRemoteIndex = index; ++ connectionStatus.setText((String) remoteListInfo.get(index)); ++ } ++ }; + remoteFileTable.addMouseListener(this); ++ remoteFileTable.addMouseMotionListener(mlisten); + remoteFileTable.setSelectedValue("C:\\", false); + remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getRemoteFileTable"); + + } + return remoteFileTable; +@@ -698,6 +939,7 @@ + remoteScrollPane = new javax.swing.JScrollPane(); + remoteScrollPane.setViewportView(getRemoteFileTable()); + remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418)); ++//System.out.println("getRemoteScrollPane"); + } + return remoteScrollPane; + } +@@ -716,6 +958,7 @@ + remoteLocation.setBackground(new Color(255,255,238)); + remoteLocation.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getRemoteLocation"); + } + return remoteLocation; + } +@@ -732,6 +975,7 @@ + localLocation.setBackground( new Color(255,255,238)); + localLocation.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getLocalLocation"); + } + return localLocation; + } +@@ -748,6 +992,7 @@ + localStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + localStatus.setEditable(false); ++//System.out.println("getLocalStatus"); + } + return localStatus; + } +@@ -764,6 +1009,7 @@ + remoteStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + remoteStatus.setEditable(false); ++//System.out.println("getRemoteStatus"); + } + return remoteStatus; + } +@@ -777,9 +1023,10 @@ + historyComboBox = new javax.swing.JComboBox(); + historyComboBox.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); +- historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0); ++ historyComboBox.insertItemAt(new String("Pulldown to view history; Press Escape to Close/Quit; Press Ctrl-R to Reset Panel."),0); + historyComboBox.setSelectedIndex(0); + historyComboBox.addActionListener(this); ++//System.out.println("getHistoryComboBox"); + } + return historyComboBox; + } +@@ -791,6 +1038,7 @@ + private javax.swing.JProgressBar getJProgressBar() { + if (jProgressBar == null) { + jProgressBar = new javax.swing.JProgressBar(); ++//System.out.println("getJProgressBar"); + } + return jProgressBar; + } +@@ -806,6 +1054,7 @@ + connectionStatus.setBackground(java.awt.Color.lightGray); + connectionStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getConnectionStatus"); + } + connectionStatus.setEditable(false); + return connectionStatus; +@@ -815,7 +1064,12 @@ + * Implements Action listener. + */ + public void actionPerformed(ActionEvent evt) { +- System.out.println(evt.getSource()); ++// System.out.println(evt.getSource()); ++ ++ if (ignore_events) { ++ System.out.println("ignore_events: " + evt.getSource()); ++ return; ++ } + + if (evt.getSource() == closeButton) + { // Close Button +@@ -829,15 +1083,27 @@ + { + 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); ++ ++ // are these really needed? changeRemoteDrive() does them at the end. ++ if (false) { ++ remoteList.clear(); ++ remoteListInfo.clear(); ++ remoteFileTable.setListData(remoteList); ++ } + } + else if (evt.getSource() == localTopButton) + { +@@ -845,12 +1111,17 @@ + } + else if (evt.getSource() == remoteTopButton) + { ++//System.out.println("remoteTopButton"); // runge/x11vnc + changeRemoteDrive(); + } + else if(evt.getSource() == deleteButton) + { + doDelete(); + } ++ else if(evt.getSource() == refreshButton) ++ { ++ doRefresh(); ++ } + else if(evt.getSource()==newFolderButton) + { + doNewFolder(); +@@ -864,7 +1135,7 @@ + + private void doNewFolder() + { +- String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); ++ String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); + if(selectedTable.equals("remote")) + { + name = remoteLocation.getText()+name; +@@ -880,34 +1151,106 @@ + historyComboBox.setSelectedIndex(0); + } + } +- private void doClose() ++ public void doClose() + { ++ if (viewer.ftpOnly) { ++ viewer.disconnect(); ++ return; ++ } + try { + this.setVisible(false); +- viewer.rfb.writeFramebufferUpdateRequest( +- 0, +- 0, +- viewer.rfb.framebufferWidth, +- viewer.rfb.framebufferHeight, +- true); ++ viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth, ++ viewer.rfb.framebufferHeight, true); ++ ++ if (false) { ++ this.dispose(); ++ jContentPane = null; ++ } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } ++ private void unSwing() { ++ jContentPane = null; ++ topPanel = null; ++ topPanelLocal = null; ++ topPanelRemote = null; ++ topPanelCenter = null; ++ statusPanel = null; ++ remotePanel = null; ++ localPanel = null; ++ buttonPanel = null; ++ sendButton = null; ++ receiveButton = null; ++ deleteButton = null; ++ newFolderButton = null; ++ stopButton = null; ++ closeButton = null; ++ dummyButton = null; ++ localDrivesComboBox = null; ++ remoteDrivesComboBox = null; ++ localMachineLabel = null; ++ remoteMachineLabel = null; ++ localTopButton = null; ++ remoteTopButton = null; ++ localScrollPane = null; ++ localFileTable = null; ++ remoteScrollPane = null; ++ remoteFileTable = null; ++ remoteLocation = null; ++ localLocation = null; ++ localStatus = null; ++ remoteStatus = null; ++ historyComboBox = null; ++ jProgressBar = null; ++ connectionStatus = null; ++ viewButton = null; ++ refreshButton = null; ++ } ++ ++ public void doReset() ++ { ++ try { ++ this.setVisible(false); ++ this.dispose(); ++ jContentPane = null; ++ try {Thread.sleep(500);} catch (InterruptedException e) {} ++ viewer.ftp_init(); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } + ++ public void doOpen() ++ { ++ try { ++ this.setVisible(true); ++ if (false) { ++ this.initialize(); ++ } ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } + private void doDelete() + { +- System.out.println("Delete Button Pressed"); ++// System.out.println("Delete Button Pressed"); + //Call this method to delete a file at server + if(selectedTable.equals("remote")) + { +- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be deleted + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + +@@ -916,7 +1259,7 @@ + // sf@2004 - Delete prompt + if (remoteList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -926,18 +1269,22 @@ + } + else + { +- String sFileName = ((String) this.localFileTable.getSelectedValue()); ++ Object selected = this.localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be deleted + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + // sf@2004 - Delete prompt + if (localList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -952,21 +1299,25 @@ + + private void doReceive() + { +- System.out.println("Received Button Pressed"); ++// System.out.println("Received Button Pressed"); + +- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be transfered + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + + // sf@2004 - Overwrite prompt + if (localList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -979,23 +1330,101 @@ + viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); + } + ++// begin runge/x11vnc ++ private void doRefresh() ++ { ++ System.out.println("Refreshing Local and Remote."); ++ refreshLocalLocation(); ++ refreshRemoteLocation(); ++ } ++ ++ 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; ++ } ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return null; ++ } ++ String sFileName = ((String) selected); ++ ++ 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); ++ System.out.println("remoteFileName: " + remoteFileName); ++if (false) { ++ char[] b = remoteFileName.toCharArray(); ++ for (int n = 0; n < b.length; n++) { ++ System.out.print(Integer.toHexString(b[n]) + " "); ++ } ++ System.out.println(""); ++ for (int n = 0; n < b.length; n++) { ++ System.out.print(b[n]); ++ } ++ System.out.println(""); ++} ++ ++ 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"); ++// System.out.println("Send Button Pressed"); + +- String sFileName = ((String) this.localFileTable.getSelectedValue()); ++ Object selected = this.localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be transfered + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + + // sf@2004 - Overwrite prompt + if (remoteList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -1013,6 +1442,7 @@ + // + private void doStop() + { ++ System.out.println("** Current Transfer Aborted **"); + viewer.rfb.fAbort = true; + } + /** +@@ -1024,6 +1454,14 @@ + System.out.println("History: " + message); + historyComboBox.insertItemAt(new String(message), 0); + } ++ ++ public void receivedRemoteDirectoryName(String str) { ++ if (doingShortcutDir) { ++ if (str.length() > 1) { ++ remoteLocation.setText(str); ++ } ++ } ++ } + + /** + * This method updates the file table to the current selection of the remoteComboBox +@@ -1034,11 +1472,44 @@ + remoteSelection = null; + + if (!updateDriveList) { +- String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; +- viewer.rfb.readServerDirectory(drive); +- remoteLocation.setText(drive); ++//System.out.println("changeRemoteDrive-A " + drive); // begin runge/x11vnc ++ Object selected = remoteDrivesComboBox.getSelectedItem(); ++ if (selected != null) { ++ String instr = selected.toString(); ++ if (instr != null) { ++System.out.println("changeRemoteDrive: instr='" + instr + "'"); ++ String drive = instr.substring(0,1)+ ":\\"; ++ if (instr.startsWith(" [")) { ++ int idx = instr.lastIndexOf(']'); ++ if (idx > 2) { ++ drive = instr.substring(2, idx); ++ } else { ++ drive = instr.substring(2); ++ } ++ if (drive.equals("Home")) { ++ drive = ""; ++ } ++ drive += "\\"; ++ doingShortcutDir = true; ++ } else { ++ doingShortcutDir = false; ++ drive = saveRemoteHack(drive); ++ } ++ gotShortcutDir = false; ++ viewer.rfb.readServerDirectory(drive); ++ if (!gotShortcutDir) { ++ remoteLocation.setText(drive); ++ } ++ } else { ++System.out.println("changeRemoteDrive: instr null"); ++ } ++ } else { ++System.out.println("changeRemoteDrive: selection null"); ++ } ++//System.out.println("changeRemoteDrive-B " + drive); // end runge/x11vnc + } + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + /** +@@ -1048,6 +1519,7 @@ + private void changeLocalDrive() + { + File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); ++System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc + if(currentDrive.canRead()) + { + localSelection = null; +@@ -1057,9 +1529,11 @@ + else + { + localList.clear(); ++ localListInfo.clear(); + 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 +@@ -1098,10 +1572,18 @@ + selectedTable = "remote"; + localFileTable.setBackground(new Color(238, 238, 238)); + remoteFileTable.setBackground(new Color(255, 255, 255)); +- String name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++ Object selected = remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String selstr = selected.toString(); ++ if (selstr == null) { ++ return; ++ } ++ String name = selstr.substring(1); + if( !name.substring(0, 2).equals(" [")) + remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); +- ++ + } + + /* +@@ -1115,10 +1597,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 + */ +@@ -1126,20 +1636,29 @@ + String name = null; + String action = null; + String drive = null; +- name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++ Object selected = remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sname = selected.toString(); ++ if (sname == null) { ++ return; ++ } ++ name = sname.substring(1); + + if (name.equals("[..]")) + { + action = "up"; + remoteSelection = null; + drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1); +- // JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); ++ // JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); + int index = drive.lastIndexOf("\\"); + drive = drive.substring(0, index + 1); + + remoteLocation.setText(drive); + viewer.rfb.readServerDirectory(drive); + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]")) +@@ -1149,6 +1668,7 @@ + remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); + drive = remoteLocation.getText(); + // ?? ++ viewRemote(); // runge/x11vnc + } + else + { +@@ -1159,10 +1679,12 @@ + remoteLocation.setText(drive); + viewer.rfb.readServerDirectory(drive); + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + //remoteLocation.setText(drive); + } ++ + /** + * Updates the Local File Table based on selection. Called from MouseClicked handler + */ +@@ -1188,6 +1710,7 @@ + else if (currentSelection.isFile()) + { + localSelection = currentSelection.getAbsoluteFile(); ++ viewLocal(); // runge/x11vnc + } + else if (currentSelection.isDirectory()) + { +@@ -1201,13 +1724,22 @@ + * + */ + private String getTrimmedSelection(){ +- String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1); +- if(currentSelection.substring(0,1).equals("[") && +- currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ +- return currentSelection.substring(1,currentSelection.length()-1); +- } else { +- return currentSelection; +- } ++ String currentSelection = ""; ++ Object selected = localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return currentSelection; ++ } ++ String selstr = selected.toString(); ++ if (selstr == null) { ++ return currentSelection; ++ } ++ currentSelection = selstr.substring(1); ++ if(currentSelection.substring(0,1).equals("[") && ++ currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ ++ return currentSelection.substring(1,currentSelection.length()-1); ++ } else { ++ return currentSelection; ++ } + } + + /* +@@ -1241,36 +1773,148 @@ + return null; + } + ++ String timeStr(long t) { ++ Date date = new Date(t); ++ return date.toString(); ++ } ++ String dotPast(double f, int n) { ++ String fs = "" + f; ++ int i = fs.lastIndexOf(".") + n; ++ if (i >= 0) { ++ int len = fs.length(); ++ if (i >= len) { ++ i = len-1; ++ } ++ fs = fs.substring(0, i); ++ } ++ return fs; ++ } ++ String sizeStr(int s) { ++ if (s < 0) { ++ return s + "? B"; ++ } else if (s < 1024) { ++ return s + " B"; ++ } else if (s < 1024 * 1024) { ++ double k = s / 1024.0; ++ String ks = dotPast(k, 3); ++ ++ return s + " (" + ks + " KB)"; ++ } else { ++ double m = s / (1024.0*1024.0); ++ String ms = dotPast(m, 3); ++ return s + " (" + ms + " MB)"; ++ } ++ } ++ ++ int max_char(String text) { ++ int maxc = 0; ++ char chars[] = text.toCharArray(); ++ for (int n = 0; n < chars.length; n++) { ++ if ((int) chars[n] > maxc) { ++ maxc = (int) chars[n]; ++ } ++ } ++ return maxc; ++ } + + /* + * Navigates the local file structure up or down one directory + */ + public void changeLocalDirectory(File dir) + { +- currentLocalDirectory = dir; // Updates Global ++ dir = saveLocalHack(dir); // runge/x11vnc ++ ++ if (dir == null) { ++ connectionStatus.setText("Error changing local directory."); ++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++ historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ + File allFiles[] = dir.listFiles(); // Reads files + String[] contents = dir.list(); + ++ if (contents == null || allFiles == null) { ++ connectionStatus.setText("Error changing local directory."); ++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++ historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ ++ currentLocalDirectory = dir; // Updates Global ++// 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]); ++ } ++ } else { ++ return; ++ } ++// end runge/x11vnc ++ + localList.clear(); ++ localListInfo.clear(); + localList.addElement(" [..]"); ++ localListInfo.addElement(" [..]"); ++ ++ ArrayList DirInfo = new ArrayList(); ++ ArrayList FilInfo = new ArrayList(); ++ ++ Charset charset = Charset.forName("ISO-8859-1"); ++ CharsetDecoder decoder = charset.newDecoder(); ++ CharsetEncoder encoder = charset.newEncoder(); + + // Populate the Lists + for (int i = 0; i < contents.length; i++) + { +- if (allFiles[i].isDirectory()) ++ String f1 = contents[i]; ++ ++if (false) { ++ ++System.out.println("max_char: " + max_char(f1) + " " + f1); ++ if (max_char(f1) > 255) { ++ try { ++System.out.println("bbuf1"); ++ ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray())); ++System.out.println("bbuf2"); ++ CharBuffer cbuf = decoder.decode(bbuf); ++System.out.println("bbuf3"); ++ f1 = cbuf.toString(); ++System.out.println("did bbuf: " + f1); ++ } catch (Exception e) { ++ ; ++ } ++ } ++} ++ ++ String f2 = f1; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ String s = f2 + " \tLastmod: " + timeStr(allFiles[i].lastModified()) + " \t\tSize: " + sizeStr((int) allFiles[i].length()); ++ if (allFiles[i].isDirectory()) { + // localList.addElement("[" + contents[i] + "]"); +- DirsList.add(" [" + contents[i] + "]"); // sf@2004 +- else +- { ++ DirsList.add(" [" + f1 + "]"); // sf@2004 ++ DirInfo.add(s); ++ } else { + // localList.addElement(contents[i]); +- FilesList.add(" " + contents[i]); // sf@2004 ++ FilesList.add(" " + f1); // sf@2004 ++ FilInfo.add(s); + } + } + // sf@2004 +- for (int i = 0; i < DirsList.size(); i++) ++ for (int i = 0; i < DirsList.size(); i++) { + localList.addElement(DirsList.get(i)); +- for (int i = 0; i < FilesList.size(); i++) ++ localListInfo.addElement(DirInfo.get(i)); ++ } ++ for (int i = 0; i < FilesList.size(); i++) { + localList.addElement(FilesList.get(i)); ++ localListInfo.addElement(FilInfo.get(i)); ++ } + + FilesList.clear(); + DirsList.clear(); +@@ -1296,3 +1940,147 @@ + } + + } // @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; ++ TextViewer me; ++ ++ public TextViewer(String s, File f, boolean d) { ++ ++ delete_it = d; ++ file = f; ++ me = this; ++ ++ JScrollPane scrollPane = new JScrollPane(textArea, ++ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ++ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ++ ++ textArea.setEditable(false); ++ textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); ++ ++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK); ++ AbstractAction escapeAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ cleanse(); ++ me.dispose(); ++ } ++ }; ++ textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++ textArea.getInputMap().put(stroke, "escapeAction"); ++ textArea.getActionMap().put("escapeAction", escapeAction); ++ ++ 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/Makefile JavaViewer/Makefile +--- JavaViewer.orig/Makefile 2006-05-29 09:06:32.000000000 -0400 ++++ JavaViewer/Makefile 2010-05-18 20:53:32.000000000 -0400 +@@ -4,6 +4,7 @@ + + CP = cp + JC = javac ++JC_ARGS = -target 1.4 -source 1.4 + JAR = jar + ARCHIVE = VncViewer.jar + PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc +@@ -20,7 +21,7 @@ + all: $(CLASSES) $(ARCHIVE) + + $(CLASSES): $(SOURCES) +- $(JC) -O $(SOURCES) ++ $(JC) $(JC_ARGS) -O $(SOURCES) + + $(ARCHIVE): $(CLASSES) + $(JAR) cf $(ARCHIVE) $(CLASSES) +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 2010-11-30 22:13:58.000000000 -0500 +@@ -31,6 +31,7 @@ + import java.net.Socket; + import java.util.*; + import java.util.zip.*; ++import java.text.DateFormat; + + + class RfbProto { +@@ -86,8 +87,11 @@ + + // sf@2004 - FileTransfer part + ArrayList remoteDirsList; ++ ArrayList remoteDirsListInfo; + ArrayList remoteFilesList; ++ ArrayList remoteFilesListInfo; + ArrayList a; ++ ArrayList b; + boolean fFTInit = true; // sf@2004 + boolean fFTAllowed = true; + boolean fAbort = false; +@@ -199,6 +203,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 +215,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)); +@@ -215,9 +243,12 @@ + osw = new OutputStreamWriter(sock.getOutputStream()); + inDirectory2 = false; + a = new ArrayList(); ++ b = new ArrayList(); + // sf@2004 + remoteDirsList = new ArrayList(); ++ remoteDirsListInfo = new ArrayList(); + remoteFilesList = new ArrayList(); ++ remoteFilesListInfo = new ArrayList(); + + sendFileSource = ""; + } +@@ -420,7 +451,13 @@ + // + + int readServerMessageType() throws IOException { +- int msgType = is.readUnsignedByte(); ++ int msgType; ++ try { ++ msgType = is.readUnsignedByte(); ++ } catch (Exception e) { ++ viewer.disconnect(); ++ return -1; ++ } + + // If the session is being recorded: + if (rec != null) { +@@ -600,6 +637,7 @@ + contentParamT = is.readUnsignedByte(); + contentParamT = contentParamT << 8; + contentParam = contentParam | contentParamT; ++//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam); + if (contentType == rfbRDrivesList || contentType == rfbDirPacket) + { + readDriveOrDirectory(contentParam); +@@ -610,7 +648,7 @@ + } + else if (contentType == rfbFilePacket) + { +- receiveFileChunk(); ++ receiveFileChunk(); + } + else if (contentType == rfbEndOfFile) + { +@@ -618,6 +656,10 @@ + } + else if (contentType == rfbAbortFileTransfer) + { ++ System.out.println("rfbAbortFileTransfer: fFileReceptionRunning=" ++ + fFileReceptionRunning + " fAbort=" ++ + fAbort + " fFileReceptionError=" ++ + fFileReceptionError); + if (fFileReceptionRunning) + { + endOfReceiveFile(false); // Error +@@ -626,6 +668,11 @@ + { + // sf@2004 - Todo: Add TestPermission + // System.out.println("File Transfer Aborted!"); ++ ++ // runge: seems like we must at least read the remaining ++ // 8 bytes of the header, right? ++ int size = is.readInt(); ++ int length = is.readInt(); + } + + } +@@ -645,6 +692,7 @@ + { + System.out.println("ContentType: " + contentType); + } ++//System.out.println("FTM: done"); + } + + //Refactored from readRfbFileTransferMsg() +@@ -662,6 +710,7 @@ + + //Refactored from readRfbFileTransferMsg() + public void readDriveOrDirectory(int contentParam) throws IOException { ++//System.out.println("RDOD: " + contentParam + " " + inDirectory2); + if (contentParam == rfbADrivesList) + { + readFTPMsgDriveList(); +@@ -688,13 +737,21 @@ + + // Internally used. Write an Rfb message to the server + void writeRfbFileTransferMsg( +- int contentType, +- int contentParam, +- long size, // 0 : compression not supported - 1 : compression supported +- long length, +- String text) throws IOException ++ int contentType, ++ int contentParam, ++ long size, // 0 : compression not supported - 1 : compression supported ++ long length, ++ String text) throws IOException + { + byte b[] = new byte[12]; ++ byte byteArray[]; ++ ++ if (viewer.dsmActive) { ++ // need to send the rfbFileTransfer msg type twice for the plugin... ++ byte b2[] = new byte[1]; ++ b2[0] = (byte) rfbFileTransfer; ++ os.write(b2); ++ } + + b[0] = (byte) rfbFileTransfer; + b[1] = (byte) contentType; +@@ -702,7 +759,7 @@ + + byte by = 0; + long c = 0; +- length++; ++ + c = size & 0xFF000000; + by = (byte) (c >>> 24); + b[4] = by; +@@ -716,6 +773,32 @@ + by = (byte) c; + b[7] = by; + ++ if (text != null) { ++ byte byteArray0[] = text.getBytes(); ++ int maxc = max_char(text); ++ if (maxc > 255) { ++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")"); ++ byteArray0 = text.getBytes("UTF-8"); ++ } else if (maxc > 127) { ++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")"); ++ byteArray0 = text.getBytes("ISO-8859-1"); ++ } ++ byteArray = new byte[byteArray0.length + 1]; ++ for (int i = 0; i < byteArray0.length; i++) { ++ byteArray[i] = byteArray0[i]; ++ } ++ byteArray[byteArray.length - 1] = 0; ++System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length); ++ ++ // will equal length for ascii, ISO-8859-1, more for UTF-8 ++ length = byteArray.length; ++ ++ //length++; // used to not include null byte at end. ++ } else { ++ String moo = "moo"; ++ byteArray = moo.getBytes(); ++ } ++ + c = length & 0xFF000000; + by = (byte) (c >>> 24); + b[8] = by; +@@ -729,29 +812,91 @@ + by = (byte) c; + b[11] = by; + os.write(b); ++ ++//System.out.println("size: " + size + " length: " + length + " text: " + text); + + + if (text != null) + { +- byte byteArray[] = text.getBytes(); +- byte byteArray2[] = new byte[byteArray.length + 1]; +- for (int i = 0; i < byteArray.length; i++) { +- byteArray2[i] = byteArray[i]; ++ os.write(byteArray); ++ } ++ } ++ ++ int max_char(String text) { ++ int maxc = 0; ++ char chars[] = text.toCharArray(); ++ for (int n = 0; n < chars.length; n++) { ++ if ((int) chars[n] > maxc) { ++ maxc = (int) chars[n]; + } +- byteArray2[byteArray2.length - 1] = 0; +- os.write(byteArray2); + } +- ++ return maxc; + } + ++ String guess_encoding(char[] chars) { ++ boolean saw_high_char = false; ++ ++ for (int i = 0; i < chars.length; i++) { ++ if (chars[i] == '\0') { ++ break; ++ } ++ if (chars[i] >= 128) { ++ saw_high_char = true; ++ break; ++ } ++ } ++ if (!saw_high_char) { ++ return "ASCII"; ++ } ++ char prev = 1; ++ boolean valid_utf8 = true; ++ int n = 0; ++ for (int i = 0; i < chars.length; i++) { ++ if (chars[i] == '\0') { ++ break; ++ } ++ char c = chars[i]; ++ if (prev < 128 && c >= 128) { ++ if (c >> 5 == 0x6) { ++ n = 1; ++ } else if (c >> 4 == 0xe) { ++ n = 2; ++ } else if (c >> 3 == 0x1e) { ++ n = 3; ++ } else if (c >> 2 == 0x3e) { ++ n = 4; ++ } else { ++ valid_utf8 = false; ++ break; ++ } ++ } else { ++ if (n > 0) { ++ if (c < 128) { ++ valid_utf8 = false; ++ break; ++ } ++ n--; ++ } ++ } ++ ++ prev = c; ++ } ++ if (valid_utf8) { ++ return "UTF-8"; ++ } else { ++ return "ISO-8859-1"; ++ } ++ } ++ ++ + //Internally used. Write an rfb message to the server for sending files ONLY + int writeRfbFileTransferMsgForSendFile( +- int contentType, +- int contentParam, +- long size, +- long length, +- String source +- ) throws IOException ++ int contentType, ++ int contentParam, ++ long size, ++ long length, ++ String source ++ ) throws IOException + { + File f = new File(source); + fis = new FileInputStream(f); +@@ -768,50 +913,47 @@ + + while (bytesRead!=-1) + { +- counter += bytesRead; +- myDeflater.setInput(byteBuffer, 0, bytesRead); +- myDeflater.finish(); +- compressedSize = myDeflater.deflate(CompressionBuffer); +- myDeflater.reset(); +- // If the compressed data is larger than the original one, we're dealing with +- // already compressed data +- if (compressedSize > bytesRead) +- fCompress = false; +- this.writeRfbFileTransferMsg( +- contentType, +- contentParam, +- (fCompress ? 1 : 0), +- (fCompress ? compressedSize-1 : bytesRead-1), +- null +- ); +- // Todo: Test write error ! +- os.write( +- fCompress ? CompressionBuffer : byteBuffer, +- 0, +- fCompress ? compressedSize : bytesRead +- ); +- +- // Todo: test read error ! +- bytesRead = fis.read(byteBuffer); +- +- // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); +- viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); +- viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); +- +- if (fAbort == true) +- { +- fAbort = false; +- fError = true; +- break; +- } +- try +- { +- Thread.sleep(5); +- } +- catch(InterruptedException e) +- { +- System.err.println("Interrupted"); +- } ++ counter += bytesRead; ++ myDeflater.setInput(byteBuffer, 0, bytesRead); ++ myDeflater.finish(); ++ compressedSize = myDeflater.deflate(CompressionBuffer); ++ myDeflater.reset(); ++ // If the compressed data is larger than the original one, we're dealing with ++ // already compressed data ++ if (compressedSize > bytesRead) ++ fCompress = false; ++ this.writeRfbFileTransferMsg( ++ contentType, ++ contentParam, ++ (fCompress ? 1 : 0), ++// RUNGE (fCompress ? compressedSize-1 : bytesRead-1), ++ (fCompress ? compressedSize : bytesRead), ++ null ++ ); ++ // Todo: Test write error ! ++ os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead); ++ ++ // Todo: test read error ! ++ bytesRead = fis.read(byteBuffer); ++ ++ // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); ++ viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); ++ viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); ++ ++ if (fAbort == true) ++ { ++ fAbort = false; ++ fError = true; ++ break; ++ } ++ try ++ { ++ Thread.sleep(5); ++ } ++ catch(InterruptedException e) ++ { ++ System.err.println("Interrupted"); ++ } + } + + writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null); +@@ -831,24 +973,30 @@ + { + System.out.print((char) is.readUnsignedByte()); + } ++ System.out.println(""); ++ ++ if (size == rfbRErrorCmd || size == -1) { ++ viewer.ftp.enableButtons(); ++ viewer.ftp.connectionStatus.setText("Remote file not available for writing."); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ return; ++ } + +- int ret = writeRfbFileTransferMsgForSendFile( +- rfbFilePacket, +- 0, +- 0, +- 0, +- sendFileSource); ++ int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource); + + viewer.ftp.refreshRemoteLocation(); + if (ret != 1) + { + viewer.ftp.connectionStatus.setText(" > Error - File NOT sent"); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) ++ + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0); + } + else + { + viewer.ftp.connectionStatus.setText(" > File sent"); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) ++ + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0); + } + viewer.ftp.historyComboBox.setSelectedIndex(0); + viewer.ftp.enableButtons(); +@@ -907,7 +1055,7 @@ + //Handles acknowledgement that the file has been deleted on the server + void deleteRemoteFileFeedback() throws IOException + { +- is.readInt(); ++ int ret = is.readInt(); + int length = is.readInt(); + String f = ""; + for (int i = 0; i < length; i++) +@@ -916,7 +1064,11 @@ + } + + viewer.ftp.refreshRemoteLocation(); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++ if (ret == -1) { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0); ++ } else { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++ } + viewer.ftp.historyComboBox.setSelectedIndex(0); + } + +@@ -926,12 +1078,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbCommand, +- rfbCFileDelete, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -943,7 +1090,7 @@ + // Handles acknowledgement that the directory has been created on the server + void createRemoteDirectoryFeedback() throws IOException + { +- is.readInt(); ++ int ret = is.readInt(); + int length = is.readInt(); + String f=""; + for (int i = 0; i < length; i++) +@@ -951,7 +1098,11 @@ + f += (char)is.readUnsignedByte(); + } + viewer.ftp.refreshRemoteLocation(); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++ if (ret == -1) { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0); ++ } else { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++ } + viewer.ftp.historyComboBox.setSelectedIndex(0); + } + +@@ -961,12 +1112,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbCommand, +- rfbCDirCreate, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -979,15 +1125,13 @@ + { + try + { ++//System.out.println("requestRemoteFile text: " + text); ++//System.out.println("requestRemoteFile leng: " + text.length()); + String temp = text; + receivePath = localPath; + +- writeRfbFileTransferMsg( +- rfbFileTransferRequest, +- 0, +- 1, // 0 : compression not supported - 1 : compression supported +- temp.length(), +- temp); ++ // 0 : compression not supported - 1 : compression supported ++ writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp); + } + catch (IOException e) + { +@@ -1004,6 +1148,9 @@ + viewer.ftp.disableButtons(); + int size = is.readInt(); + int length = is.readInt(); ++ ++//System.out.println("receiveFileHeader size: " + size); ++//System.out.println("receiveFileHeader leng: " + length); + + String tempName = ""; + for (int i = 0; i < length; i++) +@@ -1011,6 +1158,15 @@ + tempName += (char) is.readUnsignedByte(); + } + ++ if (size == rfbRErrorCmd || size == -1) { ++ fFileReceptionRunning = false; ++ viewer.ftp.enableButtons(); ++ viewer.ftp.connectionStatus.setText("Remote file not available for reading."); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ + // sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for + // backward compatibility reasons...) + int sizeH = is.readInt(); +@@ -1021,7 +1177,16 @@ + fileSize=0; + fileChunkCounter = 0; + String fileName = receivePath; +- fos = new FileOutputStream(fileName); ++ try { ++ fos = new FileOutputStream(fileName); ++ } catch (Exception e) { ++ fFileReceptionRunning = false; ++ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ viewer.ftp.enableButtons(); ++ return; ++ } + writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null); + } + +@@ -1085,7 +1250,13 @@ + fAbort = false; + fFileReceptionError = true; + writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); +- ++ ++ //runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent. ++ try {Thread.sleep(500);} catch (InterruptedException e) {} ++ viewer.ftp.enableButtons(); ++ viewer.ftp.refreshLocalLocation(); ++ viewer.ftp.connectionStatus.setText(" > Error - File NOT received"); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0); + } + // sf@2004 - For old FT protocole only + /* +@@ -1104,7 +1275,7 @@ + int length = is.readInt(); + fileSize=0; + fos.close(); +- ++ + viewer.ftp.refreshLocalLocation(); + if (fReceptionOk && !fFileReceptionError) + { +@@ -1132,12 +1303,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbDirContentRequest, +- rfbRDirContent, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -1197,11 +1363,80 @@ + str += temp; + } + } ++ // runge ++ viewer.ftp.receivedRemoteDirectoryName(str); + // viewer.ftp.changeRemoteDirectory(str); + + } + } + ++ int zogswap(int n) { ++ long l = n; ++ if (l < 0) { ++ l += 0x100000000L; ++ } ++ l = l & 0xFFFFFFFF; ++ l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24); ++ return (int) l; ++ } ++ ++ int windozeToUnix(int L, int H) { ++ long L2 = zogswap(L); ++ long H2 = zogswap(H); ++ long unix = (H2 << 32) + L2; ++ unix -= 11644473600L * 10000000L; ++ unix /= 10000000L; ++ //System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2); ++ return (int) unix; ++ } ++ ++ String timeStr(int t, int h) { ++ if (h == 0) { ++ // x11vnc/libvncserver unix ++ t = zogswap(t); ++ } else { ++ // ultra (except if h==0 by chance) ++ t = windozeToUnix(t, h); ++ } ++ long tl = (long) t; ++ Date date = new Date(tl * 1000); ++ if (true) { ++ return date.toString(); ++ } else { ++ return DateFormat.getDateTimeInstance().format(date); ++ } ++ } ++ ++ String dotPast(double f, int n) { ++ String fs = "" + f; ++ int i = fs.lastIndexOf(".") + n; ++ if (i >= 0) { ++ int len = fs.length(); ++ if (i >= len) { ++ i = len-1; ++ } ++ fs = fs.substring(0, i); ++ } ++ return fs; ++ } ++ String sizeStr(int s) { ++ s = zogswap(s); ++ if (s < 0) { ++ return s + "? B"; ++ } else if (s < 1024) { ++ return s + " B"; ++ } else if (s < 1024 * 1024) { ++ double k = s / 1024.0; ++ String ks = dotPast(k, 3); ++ ++ return s + " (" + ks + " KB)"; ++ } else { ++ double m = s / (1024.0*1024.0); ++ String ms = dotPast(m, 3); ++ return s + " (" + ms + " MB)"; ++ } ++ } ++ + //Internally used to receive directory content from server + //Here, the server sends one file/directory with it's attributes + void readFTPMsgDirectoryListContent() throws IOException +@@ -1217,17 +1452,32 @@ + dwReserved0, + dwReserved1; + long ftCreationTime, ftLastAccessTime, ftLastWriteTime; ++ int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL; ++ int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH; + char cFileName, cAlternateFileName; + int length = 0; + is.readInt(); + length = is.readInt(); ++ ++ char[] chars = new char[4*length]; ++ int char_cnt = 0; ++ for (int i = 0; i < chars.length; i++) { ++ chars[i] = '\0'; ++ } ++ + dwFileAttributes = is.readInt(); + length -= 4; +- ftCreationTime = is.readLong(); ++ //ftCreationTime = is.readLong(); ++ ftCreationTimeL = is.readInt(); ++ ftCreationTimeH = is.readInt(); + length -= 8; +- ftLastAccessTime = is.readLong(); ++ //ftLastAccessTime = is.readLong(); ++ ftLastAccessTimeL = is.readInt(); ++ ftLastAccessTimeH = is.readInt(); + length -= 8; +- ftLastWriteTime = is.readLong(); ++ //ftLastWriteTime = is.readLong(); ++ ftLastWriteTimeL = is.readInt(); ++ ftLastWriteTimeH = is.readInt(); + length -= 8; + nFileSizeHigh = is.readInt(); + length -= 4; +@@ -1239,10 +1489,12 @@ + length -= 4; + cFileName = (char) is.readUnsignedByte(); + length--; ++ chars[char_cnt++] = cFileName; + while (cFileName != '\0') + { + fileName += cFileName; + cFileName = (char) is.readUnsignedByte(); ++ chars[char_cnt++] = cFileName; + length--; + } + cAlternateFileName = (char) is.readByte(); +@@ -1253,7 +1505,28 @@ + cAlternateFileName = (char) is.readUnsignedByte(); + length--; + } +- if (dwFileAttributes == 268435456 ++ String guessed = guess_encoding(chars); ++ if (!guessed.equals("ASCII")) { ++ System.out.println("guess: " + guessed + "\t" + fileName); ++ } ++ if (guessed.equals("UTF-8")) { ++ try { ++ byte[] bytes = new byte[char_cnt-1]; ++ for (int i=0; i < char_cnt-1; i++) { ++ bytes[i] = (byte) chars[i]; ++ } ++ String newstr = new String(bytes, "UTF-8"); ++ fileName = newstr; ++ } catch (Exception e) { ++ System.out.println("failed to convert bytes to UTF-8 based string"); ++ } ++ } ++ for (int i = 0; i < char_cnt; i++) { ++ //System.out.println("char[" + i + "]\t" + (int) chars[i]); ++ } ++ if (fileName.length() <= 0) { ++ ; ++ } else if (dwFileAttributes == 268435456 + || dwFileAttributes == 369098752 + || dwFileAttributes == 285212672 + || dwFileAttributes == 271056896 +@@ -1263,11 +1536,74 @@ + || dwFileAttributes == 369623040) + { + fileName = " [" + fileName + "]"; +- remoteDirsList.add(fileName); // sf@2004 +- } +- else +- { +- remoteFilesList.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; ++ } ++ } ++ //String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " " + fileName; ++ String f2 = fileName; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); ++ //s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL); ++ if (i >= 0) { ++ remoteDirsList.add(i, fileName); ++ remoteDirsListInfo.add(i, s); ++ } else { ++ remoteDirsList.add(fileName); ++ remoteDirsListInfo.add(s); ++ } ++// end runge/x11vnc ++ } else { ++// 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; ++ } ++ } ++ String f2 = fileName; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ ++if (false) { ++System.out.println("fileName: " + f2); ++System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL); ++System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH); ++System.out.println("nFileSizeLow: " + nFileSizeLow); ++} ++ ++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); ++ //s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + " Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow); ++ if (i >= 0) { ++ remoteFilesList.add(i, fileName); ++ remoteFilesListInfo.add(i, s); ++ } else { ++ remoteFilesList.add(fileName); ++ remoteFilesListInfo.add(s); ++ } ++// end runge/x11vnc + } + + // a.add(fileName); +@@ -1282,14 +1618,32 @@ + + // sf@2004 + a.clear(); +- for (int i = 0; i < remoteDirsList.size(); i++) ++ b.clear(); ++ for (int i = 0; i < remoteDirsList.size(); i++) { + a.add(remoteDirsList.get(i)); +- for (int i = 0; i < remoteFilesList.size(); i++) ++ b.add(remoteDirsListInfo.get(i)); ++ } ++ for (int i = 0; i < remoteFilesList.size(); i++) { + a.add(remoteFilesList.get(i)); ++ ++ b.add(remoteFilesListInfo.get(i)); ++ } + remoteDirsList.clear(); ++ remoteDirsListInfo.clear(); + remoteFilesList.clear(); ++ remoteFilesListInfo.clear(); + +- viewer.ftp.printDirectory(a); ++// begin runge/x11vnc ++ // Hack for double listing at startup... probably libvncserver bug.. ++ readServerDriveListCnt++; ++ if (readServerDriveListCnt == 2) { ++ if (System.currentTimeMillis() - readServerDriveListTime < 2000) { ++//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); ++ return; ++ } ++ } ++// end runge/x11vnc ++ viewer.ftp.printDirectory(a, b); + } + + //Internally used to signify the drive requested is not ready +@@ -1299,6 +1653,8 @@ + System.out.println("Remote Drive unavailable"); + viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)"); + viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable"); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); + } + + //Call this method to request the list of drives on the server. +@@ -1306,12 +1662,11 @@ + { + try + { +- viewer.rfb.writeRfbFileTransferMsg( +- RfbProto.rfbDirContentRequest, +- RfbProto.rfbRDrivesList, +- 0, +- 0, +- null); ++ viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null); ++// begin runge/x11vnc ++ readServerDriveListCnt = 0; ++ readServerDriveListTime = System.currentTimeMillis(); ++// end runge/x11vnc + } + catch (IOException e) + { +@@ -1355,21 +1710,21 @@ + int h, + boolean incremental) + throws IOException { +- if (!viewer.ftp.isVisible()) { +- byte[] b = new byte[10]; ++ if (!viewer.ftp.isVisible()) { ++ byte[] b = new byte[10]; + +- b[0] = (byte) FramebufferUpdateRequest; +- b[1] = (byte) (incremental ? 1 : 0); +- b[2] = (byte) ((x >> 8) & 0xff); +- b[3] = (byte) (x & 0xff); +- b[4] = (byte) ((y >> 8) & 0xff); +- b[5] = (byte) (y & 0xff); +- b[6] = (byte) ((w >> 8) & 0xff); +- b[7] = (byte) (w & 0xff); +- b[8] = (byte) ((h >> 8) & 0xff); +- b[9] = (byte) (h & 0xff); ++ b[0] = (byte) FramebufferUpdateRequest; ++ b[1] = (byte) (incremental ? 1 : 0); ++ b[2] = (byte) ((x >> 8) & 0xff); ++ b[3] = (byte) (x & 0xff); ++ b[4] = (byte) ((y >> 8) & 0xff); ++ b[5] = (byte) (y & 0xff); ++ b[6] = (byte) ((w >> 8) & 0xff); ++ b[7] = (byte) (w & 0xff); ++ b[8] = (byte) ((h >> 8) & 0xff); ++ b[9] = (byte) (h & 0xff); + +- os.write(b); ++ os.write(b); + } + } + +@@ -1482,7 +1837,13 @@ + b[6] = (byte) ((text.length() >> 8) & 0xff); + b[7] = (byte) (text.length() & 0xff); + +- System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++ if (false && max_char(text) > 255) { ++ System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length()); ++ } else if (max_char(text) > 127) { ++ System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length()); ++ } else { ++ System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++ } + + os.write(b); + // } +@@ -1506,6 +1867,37 @@ + final static int META_MASK = InputEvent.META_MASK; + final static int ALT_MASK = InputEvent.ALT_MASK; + ++ void writeWheelEvent(MouseWheelEvent evt) throws IOException { ++ eventBufLen = 0; ++ ++ int x = evt.getX(); ++ int y = evt.getY(); ++ ++ if (x < 0) x = 0; ++ if (y < 0) y = 0; ++ ++ int ptrmask; ++ ++ int clicks = evt.getWheelRotation(); ++ System.out.println("writeWheelEvent: clicks: " + clicks); ++ if (clicks > 0) { ++ ptrmask = 16; ++ } else if (clicks < 0) { ++ ptrmask = 8; ++ } else { ++ return; ++ } ++ ++ eventBuf[eventBufLen++] = (byte) PointerEvent; ++ eventBuf[eventBufLen++] = (byte) ptrmask; ++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (x & 0xff); ++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (y & 0xff); ++ ++ os.write(eventBuf, 0, eventBufLen); ++ } ++ + // + // Write a pointer event message. We may need to send modifier key events + // around it to set the correct modifier state. +@@ -1610,6 +2002,21 @@ + + boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); + ++ if (viewer.debugKeyboard) { ++ System.out.println("----------------------------------------"); ++ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); ++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); ++ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); ++ System.out.println("evt.getID: " + evt.getID()); ++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); ++ System.out.println("evt.isActionKey: " + evt.isActionKey()); ++ System.out.println("evt.isControlDown: " + evt.isControlDown()); ++ System.out.println("evt.getModifiers: " + evt.getModifiers()); ++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); ++ System.out.println("evt.paramString: " + evt.paramString()); ++ } ++ ++ + int key; + if (evt.isActionKey()) { + +@@ -1685,6 +2092,9 @@ + default : + return; + } ++ if (key == 0xffc2 && viewer.mapF5_to_atsign) { ++ key = 0x40; ++ } + + } else { + +@@ -1794,6 +2204,16 @@ + int oldModifiers = 0; + + void writeModifierKeyEvents(int newModifiers) { ++ if(viewer.forbid_Ctrl_Alt) { ++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { ++ int orig = newModifiers; ++ newModifiers &= ~ALT_MASK; ++ newModifiers &= ~CTRL_MASK; ++ if (viewer.debugKeyboard) { ++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); ++ } ++ } ++ } + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + +diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java +--- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 ++++ JavaViewer/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 +@@ -0,0 +1,2067 @@ ++/* ++ * 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, 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. ++ * ++ */ ++ ++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; ++ boolean debug_certs = false; ++ ++ /* sockets */ ++ SSLSocket socket = null; ++ SSLSocketFactory factory; ++ ++ /* fallback for Proxy connection */ ++ boolean proxy_in_use = false; ++ boolean proxy_failure = false; ++ public DataInputStream is = null; ++ public OutputStream os = null; ++ ++ /* strings from user WRT proxy: */ ++ String proxy_auth_string = 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 trustsrv_ctx; ++ SSLContext trusturl_ctx; ++ SSLContext trustone_ctx; ++ ++ /* corresponding trust managers */ ++ TrustManager[] trustAllCerts; ++ TrustManager[] trustSrvCert; ++ TrustManager[] trustUrlCert; ++ TrustManager[] trustOneCert; ++ ++ /* client-side SSL auth key (oneTimeKey=...) */ ++ KeyManager[] mykey = null; ++ ++ boolean user_wants_to_see_cert = true; ++ String cert_fail = null; ++ ++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ ++ java.security.cert.Certificate[] trustallCerts = null; ++ java.security.cert.Certificate[] trustsrvCerts = null; ++ java.security.cert.Certificate[] trusturlCerts = null; ++ ++ /* utility to decode hex oneTimeKey=... and serverCert=... */ ++ 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; ++ ++ debug_certs = v.debugCerts; ++ ++ /* we will first try default factory for certification: */ ++ ++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); ++ ++ dbg("SSL startup: " + host + " " + port); ++ ++ ++ /* create trust managers to be used if initial handshake fails: */ ++ ++ trustAllCerts = new TrustManager[] { ++ /* ++ * this one accepts everything. Only used if user ++ * has disabled checking (trustAllVncCerts=yes) ++ * or when we grab the cert to show it to them in ++ * a dialog and ask them to manually verify/accept it. ++ */ ++ 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 and stored in ++ * trusturlCerts. ++ */ ++ 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 (URL)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trusturlCerts' */ ++ if (trusturlCerts == null) { ++ throw new CertificateException( ++ "No Trust url Certs array."); ++ } ++ if (trusturlCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust url Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trusturlCerts.length) { ++ throw new CertificateException( ++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trusturlCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("URL: cert mismatch at i=" + i); ++ dbg("URL: cert mismatch cert" + certs[i]); ++ dbg("URL: cert mismatch url" + trusturlCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("URL: cert info at i=" + i); ++ dbg("URL: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("URL: cert info url" + trusturlCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != URL Cert Chain."); ++ } ++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustSrvCert = new TrustManager[] { ++ /* ++ * this one accepts cert given to us in the serverCert ++ * Applet Parameter we were started with. It is ++ * currently a fatal error if the VNC Server's cert ++ * doesn't match it. ++ */ ++ 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 (SRV)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustsrvCerts' */ ++ if (trustsrvCerts == null) { ++ throw new CertificateException( ++ "No Trust srv Certs array."); ++ } ++ if (trustsrvCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust srv Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustsrvCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustsrvCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("SRV: cert mismatch at i=" + i); ++ dbg("SRV: cert mismatch cert" + certs[i]); ++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("SRV: cert info at i=" + i); ++ dbg("SRV: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("SRV: cert info srv" + trustsrvCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); ++ } ++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustOneCert = new TrustManager[] { ++ /* ++ * this one accepts only the retrieved server ++ * cert by SSLSocket by this applet we stored in ++ * trustallCerts that user has accepted or applet ++ * parameter trustAllVncCerts=yes is set. This is ++ * for when we reconnect after the user has manually ++ * accepted the trustall cert in the dialog (or set ++ * trustAllVncCerts=yes applet param.) ++ */ ++ 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 (ONE)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustallCerts' */ ++ 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 (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustallCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustallCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("ONE: cert mismatch at i=" + i); ++ dbg("ONE: cert mismatch cert" + certs[i]); ++ dbg("ONE: cert mismatch all" + trustallCerts[i]); ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("ONE: cert info at i=" + i); ++ dbg("ONE: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("ONE: cert info all" + trustallCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != TRUSTALL Cert Chain."); ++ } ++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ /* ++ * The above TrustManagers are used: ++ * ++ * 1) to retrieve the server cert in case of failure to ++ * display it to the user in a dialog. ++ * 2) to subsequently connect to the server if user agrees. ++ */ ++ ++ /* ++ * build oneTimeKey cert+key if supplied in applet parameter: ++ */ ++ 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); ++ if (debug_certs) { ++ dbg("one time key " + ff); ++ } ++ ++ 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)); ++ if (debug_certs) { ++ dbg("one time cert" + 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(); ++ } ++ ++ /* ++ * build serverCert cert if supplied in applet parameter: ++ */ ++ if (viewer.serverCert != null) { ++ CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++ byte[] cert = hex2bytes(viewer.serverCert); ++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++ trustsrvCerts = new Certificate[c.toArray().length]; ++ if (c.size() == 1) { ++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++ trustsrvCerts[0] = tmpcert; ++ } else { ++ trustsrvCerts = (Certificate[]) c.toArray(); ++ } ++ } ++ ++ /* the trust loc certs context: */ ++ try { ++ trustloc_ctx = SSLContext.getInstance("SSL"); ++ ++ /* ++ * below is a failed attempt to get jvm's default ++ * trust manager using null (below) makes it so ++ * for HttpsURLConnection the server cannot be ++ * verified (no prompting.) ++ */ ++ if (false) { ++ boolean didit = false; ++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); ++ tmf.init((KeyStore) null); ++ TrustManager [] tml = tmf.getTrustManagers(); ++ for (int i = 0; i < tml.length; i++) { ++ TrustManager tm = tml[i]; ++ if (tm instanceof X509TrustManager) { ++ TrustManager tm1[] = new TrustManager[1]; ++ tm1[0] = tm; ++ trustloc_ctx.init(mykey, tm1, null); ++ didit = true; ++ break; ++ } ++ } ++ if (!didit) { ++ trustloc_ctx.init(mykey, null, null); ++ } ++ } else { ++ /* we have to set trust manager to null */ ++ trustloc_ctx.init(mykey, null, null); ++ } ++ ++ } catch (Exception e) { ++ String msg = "SSL trustloc_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust all certs context: */ ++ 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); ++ } ++ ++ /* the trust url certs context: */ ++ 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); ++ } ++ ++ /* the trust srv certs context: */ ++ try { ++ trustsrv_ctx = SSLContext.getInstance("SSL"); ++ trustsrv_ctx.init(mykey, trustSrvCert, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trustsrv_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust the one cert from server context: */ ++ 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); ++ } ++ } ++ ++ /* ++ * we call this early on to 1) check for a proxy, 2) grab ++ * Browser/JVM accepted HTTPS cert. ++ */ ++ public void check_for_proxy_and_grab_vnc_server_cert() { ++ ++ trusturlCerts = null; ++ proxy_in_use = false; ++ ++ if (viewer.ignoreProxy) { ++ /* applet param says skip it. */ ++ /* the downside is we do not set trusturlCerts for comparison later... */ ++ /* nor do we autodetect x11vnc for GET=1. */ ++ return; ++ } ++ ++ dbg("------------------------------------------------"); ++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); ++ ++ dbg("TRYING HTTPS:"); ++ String ustr = "https://" + host + ":"; ++ if (viewer.httpsPort != null) { ++ ustr += viewer.httpsPort; ++ } else { ++ ustr += port; ++ } ++ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTPS URL connection to host:port */ ++ URL url = new URL(ustr); ++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); ++ ++ if (mykey != null) { ++ /* with oneTimeKey (mykey) we can't use the default SSL context */ ++ if (trustsrvCerts != null) { ++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); ++ } else if (trustloc_ctx != null) { ++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); ++ } ++ } ++ ++ https.setUseCaches(false); ++ https.setRequestMethod("GET"); ++ https.setRequestProperty("Pragma", "No-Cache"); ++ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++ https.setDoInput(true); ++ ++ dbg("trying https.connect()"); ++ https.connect(); ++ ++ dbg("trying https.getServerCertificates()"); ++ 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; ++ dbg("An HTTPS proxy is in use. There may be connection problems."); ++ } ++ ++ dbg("trying https.getContent()"); ++ Object output = https.getContent(); ++ dbg("trying https.disconnect()"); ++ https.disconnect(); ++ if (! viewer.GET) { ++ String header = https.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (1), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ ++ } catch(Exception e) { ++ dbg("HttpsURLConnection: " + e.getMessage()); ++ } ++ ++ if (proxy_in_use) { ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } else if (trusturlCerts != null && !viewer.forceProxy) { ++ /* Allow user to require HTTP check? use forceProxy for now. */ ++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } ++ ++ /* ++ * XXX need to remember scenario where this extra check ++ * gives useful info. User's Browser proxy settings? ++ */ ++ dbg("TRYING HTTP:"); ++ ustr = "http://" + host + ":" + port; ++ ustr += viewer.urlPrefix + "/index.vnc"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ ++ 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); ++ ++ dbg("trying http.connect()"); ++ http.connect(); ++ ++ if (http.usingProxy()) { ++ proxy_in_use = true; ++ dbg("An HTTP proxy is in use. There may be connection problems."); ++ } ++ dbg("trying http.getContent()"); ++ Object output = http.getContent(); ++ dbg("trying http.disconnect()"); ++ http.disconnect(); ++ if (! viewer.GET) { ++ String header = http.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (2), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ } catch(Exception e) { ++ dbg("HttpURLConnection: " + e.getMessage()); ++ } ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ } ++ ++ public Socket connectSock() throws IOException { ++ /* ++ * first try a https connection to detect a proxy, and ++ * grab the VNC server cert at the same time: ++ */ ++ check_for_proxy_and_grab_vnc_server_cert(); ++ ++ boolean srv_cert = false; ++ ++ if (trustsrvCerts != null) { ++ /* applet parameter suppled serverCert */ ++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); ++ factory = trustsrv_ctx.getSocketFactory(); ++ srv_cert = true; ++ } else if (viewer.trustAllVncCerts) { ++ /* trust all certs (no checking) */ ++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); ++ factory = trustall_ctx.getSocketFactory(); ++ } else if (trusturlCerts != null) { ++ /* trust certs the Browser/JVM accepted in check_for_proxy... */ ++ dbg("using trusturl_ctx"); ++ factory = trusturl_ctx.getSocketFactory(); ++ } else { ++ /* trust the local defaults */ ++ 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"); ++ } ++ ++ int timeout = 6; ++ if (timeout > 0) { ++ socket = (SSLSocket) factory.createSocket(); ++ InetSocketAddress inetaddr = new InetSocketAddress(host, port); ++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); ++ socket.connect(inetaddr, timeout * 1000); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } ++ ++ } catch (Exception esock) { ++ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); ++ } ++ } else { ++ /* n.b. socket is left in error state to cause ex. below. */ ++ } ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The Server Connection Verified OK on 1st try."); ++ ++ java.security.cert.Certificate[] currentTrustedCerts; ++ BrowserCertsDialog bcd; ++ ++ SSLSession sess = socket.getSession(); ++ currentTrustedCerts = sess.getPeerCertificates(); ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-1 keeping socket."); ++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy."); ++ } ++ 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 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.serverCert != null && trustsrvCerts != null) { ++ dbg("viewer.serverCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.trustUrlVncCert) { ++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else { ++ /* have a dialog with the user: */ ++ bcd = new BrowserCertsDialog(serv, host + ":" + port); ++ dbg("browser certs dialog begin."); ++ bcd.queryUser(); ++ dbg("browser certs dialog finished."); ++ ++ if (bcd.showCertDialog) { ++ String msg = "user wants to see cert"; ++ dbg(msg); ++ user_wants_to_see_cert = true; ++ if (cert_fail == null) { ++ cert_fail = "user-view"; ++ } ++ throw new SSLHandshakeException(msg); ++ } else { ++ user_wants_to_see_cert = false; ++ dbg("browser certs dialog: user said yes, accept it"); ++ } ++ } ++ ++ } catch (SSLHandshakeException eh) { ++ dbg("SSLHandshakeException: could not automatically verify Server."); ++ dbg("msg: " + eh.getMessage()); ++ ++ ++ /* send a cleanup string just in case: */ ++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; ++ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!"); ++ } ++ ++ /* reload */ ++ ++ socket = null; ++ ++ String reason = null; ++ ++ if (srv_cert) { ++ /* for serverCert usage we make this a fatal error. */ ++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); ++ /* see below in TrustDialog were we describe this case to user anyway */ ++ } ++ ++ /* ++ * Reconnect, trusting any cert, so we can grab ++ * the cert to show it to the user in a dialog ++ * for him to manually accept. This 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); ++ } ++ ++ if (debug_certs) { ++ dbg("trusturlCerts: " + trusturlCerts); ++ dbg("trustsrvCerts: " + trustsrvCerts); ++ } ++ if (trusturlCerts == null && cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); ++ ++ /* grab the cert: */ ++ try { ++ SSLSession sess = socket.getSession(); ++ trustallCerts = sess.getPeerCertificates(); ++ } catch (Exception e) { ++ throw new Exception("Could not get " + ++ "Peer Certificate"); ++ } ++ if (debug_certs) { ++ dbg("trustallCerts: " + trustallCerts); ++ } ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); ++ } else if (! browser_cert_match()) { ++ /* ++ * close socket now, we will reopen after ++ * dialog if user agrees to use the cert. ++ */ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!"); ++ } ++ socket = null; ++ ++ /* dialog with user to accept cert or not: */ ++ ++ TrustDialog td= new TrustDialog(host, port, ++ trustallCerts); ++ ++ if (cert_fail == null) { ++ ; ++ } else if (cert_fail.equals("user-view")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " You Asked to View the Certificate."; ++ } else if (cert_fail.equals("server-cert-mismatch")) { ++ /* this is now fatal error, see above. */ ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Certificate\n" ++ + " specified in the supplied 'serverCert' Applet Parameter."; ++ } else if (cert_fail.equals("cert-mismatch")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Website's\n" ++ + " HTTPS Certificate (that you previously accepted; either\n" ++ + " manually or automatically via Certificate Authority.)"; ++ } else if (cert_fail.equals("missing-certs")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " Not all Certificates could be obtained to check."; ++ } ++ ++ if (! td.queryUser(reason)) { ++ 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()); ++ } ++ ++ /* reload again: */ ++ ++ if (socket != null) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!!"); ++ } ++ socket = null; ++ } ++ ++ /* ++ * Now connect a 3rd time, using the cert ++ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); ++ ++ } catch (Exception ehand3) { ++ dbg("** Could not TrustAll/TrustOne Verify Server #3."); ++ ++ throw new IOException(ehand3.getMessage()); ++ } ++ } ++ ++ /* we have socket (possibly null) at this point, so proceed: */ ++ ++ /* handle x11vnc GET=1, if applicable: */ ++ 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: " + str); ++ OutputStream os = socket.getOutputStream(); ++ String type = "os"; ++ ++ if (type == "os") { ++ os.write(str.getBytes()); ++ os.flush(); ++ System.out.println("used OutputStream"); ++ } else if (type == "bs") { ++ BufferedOutputStream bs = new BufferedOutputStream(os); ++ bs.write(str.getBytes()); ++ bs.flush(); ++ System.out.println("used BufferedOutputStream"); ++ } else if (type == "ds") { ++ DataOutputStream ds = new DataOutputStream(os); ++ ds.write(str.getBytes()); ++ ds.flush(); ++ System.out.println("used DataOutputStream"); ++ } ++ 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."); ++ dbg(""); ++ ++ /* could be null, let caller handle that. */ ++ return (Socket) socket; ++ } ++ ++ boolean browser_cert_match() { ++ String msg = "Browser URL accept previously accepted cert"; ++ ++ if (user_wants_to_see_cert) { ++ return false; ++ } ++ ++ if (viewer.serverCert != null || trustsrvCerts != null) { ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (trustallCerts != null && trusturlCerts != null) { ++ if (trustallCerts.length == trusturlCerts.length) { ++ boolean ok = true; ++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ ++ for (int i = 0; i < trusturlCerts.length; i++) { ++ if (! trustallCerts[i].equals(trusturlCerts[i])) { ++ dbg("BCM: cert mismatch at i=" + i); ++ dbg("BCM: cert mismatch url" + trusturlCerts[i]); ++ dbg("BCM: cert mismatch all" + trustallCerts[i]); ++ ok = false; ++ } ++ } ++ if (ok) { ++ System.out.println(msg); ++ if (cert_fail == null) { ++ cert_fail = "did-not-fail"; ++ } ++ return true; ++ } else { ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ return false; ++ } ++ } ++ } ++ if (cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ return false; ++ } ++ ++ 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; ++ } ++ ++ /* this will do the proxy CONNECT negotiation and hook us up. */ ++ ++ private void proxy_helper(String proxyHost, int proxyPort) { ++ ++ boolean proxy_auth = false; ++ String proxy_auth_basic_realm = ""; ++ String hp = host + ":" + port; ++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); ++ ++ /* we loop here a few times trying for the password case */ ++ for (int k=0; k < 2; k++) { ++ dbg("proxy_in_use psocket: " + k); ++ ++ if (proxySock != null) { ++ try { ++ proxySock.close(); ++ } catch (Exception e) { ++ dbg("proxy socket is grumpy."); ++ } ++ } ++ ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("1-a sadly, returning a null socket"); ++ return; ++ } ++ ++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n"; ++ ++ dbg("requesting via proxy: " + req1); ++ ++ if (proxy_auth) { ++ if (proxy_auth_string == null) { ++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); ++ pp.queryUser(); ++ proxy_auth_string = pp.getAuth(); ++ } ++ //dbg("auth1: " + proxy_auth_string); ++ ++ String auth2 = Base64Coder.encodeString(proxy_auth_string); ++ //dbg("auth2: " + auth2); ++ ++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; ++ //dbg("req1: " + req1); ++ ++ dbg("added Proxy-Authorization: Basic ... to request"); ++ } ++ req1 += "\r\n"; ++ ++ try { ++ proxy_os.write(req1.getBytes()); ++ String reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply.trim()); ++ ++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { ++ proxy_auth = true; ++ proxySock.close(); ++ } else 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; ++ } ++ } ++ } catch(Exception e) { ++ dbg("some proxy socket problem: " + e.getMessage()); ++ } ++ ++ /* read the rest of the HTTP headers */ ++ while (true) { ++ String line = readline(proxy_is); ++ dbg("proxy line: " + line.trim()); ++ if (proxy_auth) { ++ String uc = line.toLowerCase(); ++ if (uc.indexOf("proxy-authenticate:") == 0) { ++ if (uc.indexOf(" basic ") >= 0) { ++ int idx = uc.indexOf(" realm"); ++ if (idx >= 0) { ++ proxy_auth_basic_realm = uc.substring(idx+1); ++ } ++ } ++ } ++ } ++ if (line.equals("\r\n") || line.equals("\n")) { ++ break; ++ } ++ } ++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { ++ /* we only try once for the non-password case: */ ++ break; ++ } ++ } ++ } ++ ++ 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) { ++ /* sandboxed applet might not be able to read it. */ ++ dbg("props failed: " + e.getMessage()); ++ } ++ if (viewer.proxyHost != null) { ++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); ++ proxyHost = viewer.proxyHost; ++ if (viewer.proxyPort != null) { ++ proxyPort = gint(viewer.proxyPort); ++ } else { ++ proxyPort = 8080; ++ } ++ ++ } else if (props != null) { ++ dbg("\n---------------\nAll props:"); ++ props.list(System.out); ++ dbg("\n---------------\n\n"); ++ ++ /* scrape throught properties looking for proxy info: */ ++ ++ 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.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; ++ } ++ } ++ ++ 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 (proxyHost != null && proxyPort > 0) { ++ break; ++ } ++ ++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 ++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { ++ continue; ++ } ++ if (v2.indexOf("http") < 0) { ++ 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) { ++ j = p.indexOf("http"); ++ 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()); ++ } ++ ++ proxy_helper(proxyHost, proxyPort); ++ if (proxySock == null) { ++ return null; ++ } ++ } 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("proxy socket problem-2: " + 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(String reason) { ++ ++ /* 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"); ++ } ++ if (reason != null) { ++ reason += "\n\n"; ++ } ++ ++ text = "\n" +++ "Unable to verify the identity of\n" +++ "\n" +++ " " + host + ":" + port + "\n" +++ "\n" +++ infostr +++ "\n" +++ "as a trusted VNC server.\n" +++ "\n" +++ reason +++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" +++ "is due to one of the following:\n" +++ "\n" +++ " - Your requesting to View the Certificate before accepting.\n" +++ "\n" +++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" +++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" +++ "\n" +++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" +++ " the Apache Web server has a certificate *different* from the VNC server's.\n" +++ "\n" +++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" +++ " obtained by this applet to compare the VNC Server Certificate against.\n" +++ "\n" +++ " - The VNC Server's Certificate does not match the one specified in the\n" +++ " supplied 'serverCert' Java Applet Parameter.\n" +++ "\n" +++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" +++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" +++ "automatically authenticate this VNC Server.\n" +++ "\n" +++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" +++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" +++ "certificate (except for the Apache portal case above where they don't match.)\n" +++ "\n" +++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" +++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" +++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); ++ dialog.hide(); ++ ++ } else if (evt.getSource() == cancel) { ++ /* Cancel button clicked */ ++ trust_this_session = false; ++ ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++ ++ 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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++class ProxyPasswdDialog implements ActionListener { ++ String guessedHost = null; ++ String guessedPort = null; ++ String guessedUser = null; ++ String guessedPasswd = null; ++ String realm = 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 entry1; ++ TextField entry2; ++ String reply1 = ""; ++ String reply2 = ""; ++ ++ ProxyPasswdDialog (String h, int p, String realm) { ++ guessedHost = h; ++ try { ++ guessedPort = Integer.toString(p); ++ } catch (Exception e) { ++ guessedPort = "8080"; ++ } ++ this.realm = realm; ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Proxy Requires Username and Password"); ++ ++ dialog = new Dialog(frame, true); ++ ++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); ++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); ++ entry1 = new TextField(30); ++ entry2 = new TextField(30); ++ entry2.setEchoChar('*'); ++ ok = new Button("OK"); ++ ok.addActionListener(this); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", label); ++ dialog.add("Center", entry1); ++ dialog.add("South", entry2); ++ dialog.add("East", ok); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til OK or Cancel pressed. */ ++ return; ++ } ++ ++ public String getAuth() { ++ return reply1 + ":" + reply2; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == ok) { ++ reply1 = entry1.getText(); ++ reply2 = entry2.getText(); ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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 and/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 += "If the VNC Server connection times out while you are reading this\n"; ++m += "dialog, then restart the connection and try again.\n"; ++m += "\n"; ++m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; ++m += "\n"; ++ ++ TextArea textarea = new TextArea(m, 22, 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. */ ++ System.out.println("done show()"); ++ return; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == yes) { ++ showCertDialog = false; ++ //dialog.dispose(); ++ dialog.hide(); ++ } else if (evt.getSource() == no) { ++ showCertDialog = true; ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ System.out.println("done actionPerformed()"); ++ } ++} ++ ++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 ""; ++ } ++ } ++} ++ ++class Base64Coder { ++ ++ // Mapping table from 6-bit nibbles to Base64 characters. ++ private static char[] map1 = new char[64]; ++ static { ++ int i=0; ++ for (char c='A'; c<='Z'; c++) map1[i++] = c; ++ for (char c='a'; c<='z'; c++) map1[i++] = c; ++ for (char c='0'; c<='9'; c++) map1[i++] = c; ++ map1[i++] = '+'; map1[i++] = '/'; } ++ ++ // Mapping table from Base64 characters to 6-bit nibbles. ++ private static byte[] map2 = new byte[128]; ++ static { ++ for (int i=0; iin. ++ * @return A character array with the Base64 encoded data. ++ */ ++ public static char[] encode (byte[] in, int iLen) { ++ int oDataLen = (iLen*4+2)/3; // output length without padding ++ int oLen = ((iLen+2)/3)*4; // output length including padding ++ char[] out = new char[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++] & 0xff; ++ int i1 = ip < iLen ? in[ip++] & 0xff : 0; ++ int i2 = ip < iLen ? in[ip++] & 0xff : 0; ++ int o0 = i0 >>> 2; ++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); ++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); ++ int o3 = i2 & 0x3F; ++ out[op++] = map1[o0]; ++ out[op++] = map1[o1]; ++ out[op] = op < oDataLen ? map1[o2] : '='; op++; ++ out[op] = op < oDataLen ? map1[o3] : '='; op++; } ++ return out; } ++ ++ /** ++ * Decodes a string from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return A String containing the decoded data. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static String decodeString (String s) { ++ return new String(decode(s)); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (String s) { ++ return decode(s.toCharArray()); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * No blanks or line breaks are allowed within the Base64 encoded data. ++ * @param in a character array containing the Base64 encoded data. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (char[] in) { ++ int iLen = in.length; ++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); ++ while (iLen > 0 && in[iLen-1] == '=') iLen--; ++ int oLen = (iLen*3) / 4; ++ byte[] out = new byte[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++]; ++ int i1 = in[ip++]; ++ int i2 = ip < iLen ? in[ip++] : 'A'; ++ int i3 = ip < iLen ? in[ip++] : 'A'; ++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int b0 = map2[i0]; ++ int b1 = map2[i1]; ++ int b2 = map2[i2]; ++ int b3 = map2[i3]; ++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int o0 = ( b0 <<2) | (b1>>>4); ++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); ++ int o2 = ((b2 & 3)<<6) | b3; ++ out[op++] = (byte)o0; ++ if (op 0) + { + viewer.options.oldEightBitColors = viewer.options.eightBitColors; +@@ -237,6 +265,9 @@ + } + else + { ++// begin runge/x11vnc ++ viewer.options.oldEightBitColors = viewer.options.eightBitColors; ++// end runge/x11vnc + rfb.writeSetPixelFormat( + 32, + 24, +@@ -376,12 +407,14 @@ + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + +- rfb.writeFramebufferUpdateRequest( +- 0, +- 0, +- rfb.framebufferWidth, +- rfb.framebufferHeight, +- false); ++ if (!viewer.graftFtp) { ++ rfb.writeFramebufferUpdateRequest( ++ 0, ++ 0, ++ rfb.framebufferWidth, ++ rfb.framebufferHeight, ++ false); ++ } + + // + // main dispatch loop +@@ -390,6 +423,9 @@ + while (true) { + // Read message type from the server. + int msgType = rfb.readServerMessageType(); ++ if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) { ++ System.out.println("msgType:" + msgType); ++ } + + // Process the message depending on its type. + switch (msgType) { +@@ -1332,6 +1368,9 @@ + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } ++ public void mouseWheelMoved(MouseWheelEvent evt) { ++ processLocalMouseWheelEvent(evt); ++ } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { +@@ -1367,6 +1406,19 @@ + evt.consume(); + } + ++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { ++ if (viewer.rfb != null && rfb.inNormalProtocol) { ++ synchronized(rfb) { ++ try { ++ rfb.writeWheelEvent(evt); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ rfb.notify(); ++ } ++ } ++ } ++ + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { +@@ -1532,9 +1584,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 +1622,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 2010-03-27 18:00:28.000000000 -0400 +@@ -41,6 +41,7 @@ + import java.io.*; + import java.net.*; + import javax.swing.*; ++import java.util.Date; + + public class VncViewer extends java.applet.Applet + implements java.lang.Runnable, WindowListener { +@@ -80,11 +81,11 @@ + GridBagLayout gridbag; + ButtonPanel buttonPanel; + AuthPanel authenticator; +- VncCanvas vc; ++ VncCanvas vc = null; + OptionsFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; +- FTPFrame ftp; // KMC: FTP Frame declaration ++ FTPFrame ftp = null; // KMC: FTP Frame declaration + + // Control session recording. + Object recordingSync; +@@ -96,7 +97,7 @@ + + // Variables read from parameter values. + String host; +- int port; ++ int port, vncserverport; + String passwordParam; + String encPasswordParam; + boolean showControls; +@@ -115,28 +116,75 @@ + int i; + // mslogon support 2 end + ++// begin runge/x11vnc ++boolean disableSSL; ++boolean GET; ++String CONNECT; ++String urlPrefix; ++String httpsPort; ++String oneTimeKey; ++String serverCert; ++String ftpDropDown; ++String proxyHost; ++String proxyPort; ++boolean forceProxy; ++boolean ignoreProxy; ++boolean trustAllVncCerts; ++boolean trustUrlVncCert; ++boolean debugCerts; ++boolean debugKeyboard; ++boolean mapF5_to_atsign; ++boolean forbid_Ctrl_Alt; ++ ++boolean ignoreMSLogonCheck; ++boolean delayAuthPanel; ++boolean ftpOnly; ++boolean graftFtp; ++boolean dsmActive; ++ ++boolean gotAuth; ++int authGot; ++// end runge/x11vnc ++ ++ + // + // init() + // + ++public void ftp_init() { ++ boolean show = false; ++ if (ftp != null) { ++ show = true; ++ } ++ ftp = null; ++ ++ ftp = new FTPFrame(this); // KMC: FTPFrame creation ++ ++ if (show) { ++ ftp.doOpen(); ++ rfb.readServerDriveList(); ++ } ++} ++ + public void init() { + + readParameters(); + + if (inSeparateFrame) { +- vncFrame = new Frame("Ultr@VNC"); +- if (!inAnApplet) { +- vncFrame.add("Center", this); +- } +- vncContainer = vncFrame; ++ vncFrame = new Frame("Ultr@VNC"); ++ if (!inAnApplet) { ++ vncFrame.add("Center", this); ++ } ++ vncContainer = vncFrame; + } else { +- vncContainer = this; ++ vncContainer = this; + } + + recordingSync = new Object(); + + options = new OptionsFrame(this); + clipboard = new ClipboardFrame(this); ++ + // authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate() + if (RecordingFrame.checkSecurity()) + rec = new RecordingFrame(this); +@@ -147,10 +195,11 @@ + cursorUpdatesDef = null; + eightBitColorsDef = null; + +- if (inSeparateFrame) ++ if (inSeparateFrame && vncFrame != null) + vncFrame.addWindowListener(this); + +- ftp = new FTPFrame(this); // KMC: FTPFrame creation ++ ftp_init(); ++ + rfbThread = new Thread(this); + rfbThread.start(); + } +@@ -186,6 +235,30 @@ + gbc.weightx = 1.0; + gbc.weighty = 1.0; + ++ if (ftpOnly) { ++ if (showControls) { ++ buttonPanel.enableButtons(); ++ } ++ ActionListener taskPerformer = new ActionListener() { ++ public void actionPerformed(ActionEvent evt) { ++ vncFrame.setVisible(false); ++ ftp.setSavedLocations(); ++ if (ftp.isVisible()) { ++ ftp.doClose(); ++ } else { ++ ftp.doOpen(); ++ } ++ rfb.readServerDriveList(); ++ } ++ }; ++ Timer t = new Timer(300, taskPerformer); ++ t.setRepeats(false); ++ t.start(); ++ ++ vc.processNormalProtocol(); ++ return; ++ } ++ + // Add ScrollPanel to applet mode + + // Create a panel which itself is resizeable and can hold +@@ -286,6 +359,24 @@ + + void connectAndAuthenticate() throws Exception { + ++ if (graftFtp) { ++ rfb = new RfbProto(host, port, this); ++ rfb.desktopName = "ftponly"; ++ rfb.framebufferWidth = 12; ++ rfb.framebufferHeight = 12; ++ rfb.bitsPerPixel = 32; ++ rfb.depth = 24; ++ rfb.trueColour = true; ++ rfb.redMax = 255; ++ rfb.greenMax = 255; ++ rfb.blueMax = 255; ++ rfb.redShift = 16; ++ rfb.greenShift = 8; ++ rfb.blueShift = 0; ++ rfb.inNormalProtocol = true; ++ return; ++ } ++ + // If "ENCPASSWORD" parameter is set, decrypt the password into + // the passwordParam string. + +@@ -336,7 +427,22 @@ + // + + +- prologueDetectAuthProtocol() ; ++// begin runge/x11vnc ++ gotAuth = false; ++ if (delayAuthPanel) { ++ if (tryAuthenticate(null, null)) { ++ if (inSeparateFrame) { ++ vncFrame.pack(); ++ vncFrame.show(); ++ } ++ return; ++ } ++ } ++// prologueDetectAuthProtocol() ; ++ if (ignoreMSLogonCheck == false) { ++ prologueDetectAuthProtocol() ; ++ } ++// end runge/x11vnc + + authenticator = new AuthPanel(mslogon); + +@@ -371,6 +477,7 @@ + //mslogon support end + } + ++ int tries = 0; + while (true) { + // Wait for user entering a password, or a username and a password + synchronized(authenticator) { +@@ -390,6 +497,13 @@ + break; + //mslogon support end + ++// begin runge/x11vnc ++ gotAuth = false; ++ if (++tries > 2) { ++ throw new Exception("Incorrect password entered " + tries + " times."); ++ } ++// end runge/x11vnc ++ + // Retry on authentication failure. + authenticator.retry(); + } +@@ -405,9 +519,11 @@ + + void prologueDetectAuthProtocol() throws Exception { + +- rfb = new RfbProto(host, port, this); ++ if (!gotAuth) { ++ rfb = new RfbProto(host, port, this); + +- rfb.readVersionMsg(); ++ rfb.readVersionMsg(); ++ } + + System.out.println("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); +@@ -431,16 +547,36 @@ + + boolean tryAuthenticate(String us, String pw) throws Exception { + +- rfb = new RfbProto(host, port, this); ++ int authScheme; + +- rfb.readVersionMsg(); ++ if (!gotAuth) { ++ rfb = new RfbProto(host, port, this); + +- System.out.println("RFB server supports protocol version " + +- rfb.serverMajor + "." + rfb.serverMinor); ++ rfb.readVersionMsg(); + +- rfb.writeVersionMsg(); ++ System.out.println("RFB server supports protocol version: " + ++ rfb.serverMajor + "." + rfb.serverMinor); + +- int authScheme = rfb.readAuthScheme(); ++ rfb.writeVersionMsg(); ++ ++ authScheme = rfb.readAuthScheme(); ++ ++ gotAuth = true; ++ authGot = authScheme; ++ } else { ++ authScheme = authGot; ++ } ++// begin runge/x11vnc ++ if (delayAuthPanel && pw == null) { ++ if (authScheme == RfbProto.NoAuth) { ++ System.out.println("No authentication needed"); ++ return true; ++ } else { ++ return false; ++ } ++ } ++System.out.println("as: " + authScheme); ++// end runge/x11vnc + + switch (authScheme) { + +@@ -629,6 +765,10 @@ + + void doProtocolInitialisation() throws IOException { + ++ if (graftFtp) { ++ return; ++ } ++ + rfb.writeClientInit(); + + rfb.readServerInit(); +@@ -774,9 +914,28 @@ + fatalError("HOST parameter not specified"); + } + } ++ Date d = new Date(); ++ System.out.println("-\nSSL VNC Java Applet starting. " + d); + +- String str = readParameter("PORT", true); +- port = Integer.parseInt(str); ++ port = 0; ++ String str = readParameter("PORT", false); ++ if (str != null) { ++ port = Integer.parseInt(str); ++ } ++ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). ++ vncserverport = 0; ++ str = readParameter("VNCSERVERPORT", false); ++ if (str != null) { ++ vncserverport = Integer.parseInt(str); ++ } ++ if (port == 0 && vncserverport == 0) { ++ fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); ++ } ++ if (port == 0) { ++ // Nevertheless, fall back to vncserverport if we have to. ++ System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); ++ port = vncserverport; ++ } + + if (inAnApplet) { + str = readParameter("Open New Window", false); +@@ -804,6 +963,158 @@ + 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 + "'"); ++ ++ ftpDropDown = readParameter("ftpDropDown", false); ++ if (ftpDropDown != null) { ++ ftpDropDown = ftpDropDown.replaceAll("%2F", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("%2f", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("_2F_", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("%20", " "); ++ System.out.println("ftpDropDown: '" + ftpDropDown + "'"); ++ } ++ ++ ++ oneTimeKey = readParameter("oneTimeKey", false); ++ if (oneTimeKey != null) { ++ System.out.println("oneTimeKey is set."); ++ } ++ ++ serverCert = readParameter("serverCert", false); ++ if (serverCert != null) { ++ System.out.println("serverCert is set."); ++ } ++ ++ forceProxy = false; ++ proxyHost = null; ++ proxyPort = null; ++ str = readParameter("forceProxy", false); ++ if (str != null) { ++ if (str.equalsIgnoreCase("Yes")) { ++ forceProxy = true; ++ } else if (str.equalsIgnoreCase("No")) { ++ forceProxy = false; ++ } else { ++ forceProxy = true; ++ String[] pieces = str.split(" "); ++ proxyHost = new String(pieces[0]); ++ if (pieces.length >= 2) { ++ proxyPort = new String(pieces[1]); ++ } else { ++ proxyPort = new String("8080"); ++ } ++ } ++ } ++ str = readParameter("proxyHost", false); ++ if (str != null) { ++ proxyHost = new String(str); ++ } ++ str = readParameter("proxyPort", false); ++ if (str != null) { ++ proxyPort = new String(str); ++ } ++ if (proxyHost != null && proxyPort == null) { ++ proxyPort = new String("8080"); ++ } ++ ++ 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; ++ } ++ debugCerts = false; ++ str = readParameter("debugCerts", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugCerts = true; ++ } ++ debugKeyboard = false; ++ str = readParameter("debugKeyboard", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugKeyboard = true; ++ } ++ mapF5_to_atsign = false; ++ str = readParameter("mapF5_to_atsign", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ mapF5_to_atsign = true; ++ } ++ forbid_Ctrl_Alt = false; ++ str = readParameter("forbid_Ctrl_Alt", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ forbid_Ctrl_Alt = true; ++ } ++ ignoreMSLogonCheck = false; ++ str = readParameter("ignoreMSLogonCheck", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ ignoreMSLogonCheck = true; ++ } ++ ftpOnly = false; ++ str = readParameter("ftpOnly", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ ftpOnly = true; ++ } ++ graftFtp = false; ++ str = readParameter("graftFtp", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ graftFtp = true; ++ } ++ dsmActive = false; ++ str = readParameter("dsmActive", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ dsmActive = true; ++ } ++ delayAuthPanel = false; ++ str = readParameter("delayAuthPanel", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ delayAuthPanel = true; ++ } ++// end runge/x11vnc + } + + public String readParameter(String name, boolean required) { -- cgit v1.2.3 From bdd7e25d2dd47c3a5d8c95366bc111dc0a176128 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Wed, 9 Nov 2011 20:25:32 +0100 Subject: Move the java stuff into webclients/java-applet. --- configure.ac | 3 +- webclients/Makefile.am | 7 +- webclients/VncViewer.jar | Bin 35462 -> 0 bytes webclients/index.vnc | 2 +- webclients/java-applet/Makefile.am | 5 + webclients/java-applet/VncViewer.jar | Bin 0 -> 35462 bytes .../java-applet/javaviewer.pseudo_proxy.patch | 141 + webclients/java-applet/ssl/Makefile.am | 2 + webclients/java-applet/ssl/README | 338 ++ .../java-applet/ssl/SignedUltraViewerSSL.jar | Bin 0 -> 113117 bytes webclients/java-applet/ssl/SignedVncViewer.jar | Bin 0 -> 89208 bytes webclients/java-applet/ssl/UltraViewerSSL.jar | Bin 0 -> 110040 bytes webclients/java-applet/ssl/VncViewer.jar | Bin 0 -> 86228 bytes webclients/java-applet/ssl/index.vnc | 26 + webclients/java-applet/ssl/onetimekey | 65 + webclients/java-applet/ssl/proxy.vnc | 73 + webclients/java-applet/ssl/ss_vncviewer | 3676 +++++++++++++ ...-vncviewer-cursor-colors+no-tab-traversal.patch | 111 + .../tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch | 2600 +++++++++ webclients/java-applet/ssl/ultra.vnc | 28 + webclients/java-applet/ssl/ultraproxy.vnc | 28 + webclients/java-applet/ssl/ultrasigned.vnc | 28 + .../ssl/ultravnc-102-JavaViewer-ssl-etc.patch | 5494 ++++++++++++++++++++ webclients/javaviewer.pseudo_proxy.patch | 141 - webclients/ssl/Makefile.am | 2 - webclients/ssl/README | 338 -- webclients/ssl/SignedUltraViewerSSL.jar | Bin 113117 -> 0 bytes webclients/ssl/SignedVncViewer.jar | Bin 89208 -> 0 bytes webclients/ssl/UltraViewerSSL.jar | Bin 110040 -> 0 bytes webclients/ssl/VncViewer.jar | Bin 86228 -> 0 bytes webclients/ssl/index.vnc | 26 - webclients/ssl/onetimekey | 65 - webclients/ssl/proxy.vnc | 73 - webclients/ssl/ss_vncviewer | 3676 ------------- ...-vncviewer-cursor-colors+no-tab-traversal.patch | 111 - .../tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch | 2600 --------- webclients/ssl/ultra.vnc | 28 - webclients/ssl/ultraproxy.vnc | 28 - webclients/ssl/ultrasigned.vnc | 28 - .../ssl/ultravnc-102-JavaViewer-ssl-etc.patch | 5494 -------------------- 40 files changed, 12621 insertions(+), 12616 deletions(-) delete mode 100644 webclients/VncViewer.jar create mode 100644 webclients/java-applet/Makefile.am create mode 100644 webclients/java-applet/VncViewer.jar create mode 100644 webclients/java-applet/javaviewer.pseudo_proxy.patch create mode 100644 webclients/java-applet/ssl/Makefile.am create mode 100644 webclients/java-applet/ssl/README create mode 100644 webclients/java-applet/ssl/SignedUltraViewerSSL.jar create mode 100644 webclients/java-applet/ssl/SignedVncViewer.jar create mode 100644 webclients/java-applet/ssl/UltraViewerSSL.jar create mode 100644 webclients/java-applet/ssl/VncViewer.jar create mode 100644 webclients/java-applet/ssl/index.vnc create mode 100755 webclients/java-applet/ssl/onetimekey create mode 100644 webclients/java-applet/ssl/proxy.vnc create mode 100755 webclients/java-applet/ssl/ss_vncviewer create mode 100644 webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch create mode 100644 webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch create mode 100644 webclients/java-applet/ssl/ultra.vnc create mode 100644 webclients/java-applet/ssl/ultraproxy.vnc create mode 100644 webclients/java-applet/ssl/ultrasigned.vnc create mode 100644 webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch delete mode 100644 webclients/javaviewer.pseudo_proxy.patch delete mode 100644 webclients/ssl/Makefile.am delete mode 100644 webclients/ssl/README delete mode 100644 webclients/ssl/SignedUltraViewerSSL.jar delete mode 100644 webclients/ssl/SignedVncViewer.jar delete mode 100644 webclients/ssl/UltraViewerSSL.jar delete mode 100644 webclients/ssl/VncViewer.jar delete mode 100644 webclients/ssl/index.vnc delete mode 100755 webclients/ssl/onetimekey delete mode 100644 webclients/ssl/proxy.vnc delete mode 100755 webclients/ssl/ss_vncviewer delete mode 100644 webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch delete mode 100644 webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch delete mode 100644 webclients/ssl/ultra.vnc delete mode 100644 webclients/ssl/ultraproxy.vnc delete mode 100644 webclients/ssl/ultrasigned.vnc delete mode 100644 webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch diff --git a/configure.ac b/configure.ac index 94ec6bc..39728d9 100644 --- a/configure.ac +++ b/configure.ac @@ -917,7 +917,8 @@ AC_CONFIG_FILES([Makefile examples/android/Makefile vncterm/Makefile webclients/Makefile - webclients/ssl/Makefile + webclients/java-applet/Makefile + webclients/java-applet/ssl/Makefile libvncclient/Makefile client_examples/Makefile test/Makefile diff --git a/webclients/Makefile.am b/webclients/Makefile.am index c5497a8..6c2db84 100644 --- a/webclients/Makefile.am +++ b/webclients/Makefile.am @@ -1,5 +1,4 @@ -EXTRA_DIST=VncViewer.jar index.vnc javaviewer.pseudo_proxy.patch - -SUBDIRS = ssl -DIST_SUBDIRS = ssl +SUBDIRS = java-applet +DIST_SUBDIRS = java-applet +EXTRA_DIST=index.vnc novnc diff --git a/webclients/VncViewer.jar b/webclients/VncViewer.jar deleted file mode 100644 index 602fdb9..0000000 Binary files a/webclients/VncViewer.jar and /dev/null differ diff --git a/webclients/index.vnc b/webclients/index.vnc index 34ad25d..8254a70 100644 --- a/webclients/index.vnc +++ b/webclients/index.vnc @@ -10,7 +10,7 @@ $USER's $DESKTOP desktop ($DISPLAY) - diff --git a/webclients/java-applet/Makefile.am b/webclients/java-applet/Makefile.am new file mode 100644 index 0000000..d6d10e4 --- /dev/null +++ b/webclients/java-applet/Makefile.am @@ -0,0 +1,5 @@ +EXTRA_DIST=VncViewer.jar javaviewer.pseudo_proxy.patch + +SUBDIRS = ssl +DIST_SUBDIRS = ssl + diff --git a/webclients/java-applet/VncViewer.jar b/webclients/java-applet/VncViewer.jar new file mode 100644 index 0000000..602fdb9 Binary files /dev/null and b/webclients/java-applet/VncViewer.jar differ diff --git a/webclients/java-applet/javaviewer.pseudo_proxy.patch b/webclients/java-applet/javaviewer.pseudo_proxy.patch new file mode 100644 index 0000000..4d2f36e --- /dev/null +++ b/webclients/java-applet/javaviewer.pseudo_proxy.patch @@ -0,0 +1,141 @@ +diff -ru vnc_javasrc/OptionsFrame.java proxy_vnc_javasrc/OptionsFrame.java +--- vnc_javasrc/OptionsFrame.java Fri Jul 5 08:17:23 2002 ++++ proxy_vnc_javasrc/OptionsFrame.java Thu Aug 22 23:24:44 2002 +@@ -70,6 +70,12 @@ + + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; ++ ++ Label proxyHostLabel; ++ TextField proxyHostEdit; ++ Label proxyPortLabel; ++ TextField proxyPortEdit; ++ + Button closeButton; + VncViewer viewer; + +@@ -93,6 +99,9 @@ + boolean shareDesktop; + boolean viewOnly; + ++ String proxyHost; ++ int proxyPort; ++ + // + // Constructor. Set up the labels and choices from the names and values + // arrays. +@@ -126,6 +135,32 @@ + } + } + ++ // TODO: find a way to set these to defaults from browser ++ proxyPort = viewer.readIntParameter("Use Proxy Port", -1); ++ if(proxyPort>-1) { ++ proxyHost = viewer.readParameter("Use Proxy Host", false); ++ if(proxyHost == null) ++ proxyHost = viewer.host; ++ ++ proxyHostLabel = new Label("Proxy Host"); ++ gbc.gridwidth = 1; ++ gridbag.setConstraints(proxyHostLabel,gbc); ++ add(proxyHostLabel); ++ proxyHostEdit = new TextField(); ++ gbc.gridwidth = GridBagConstraints.REMAINDER; ++ gridbag.setConstraints(proxyHostEdit,gbc); ++ add(proxyHostEdit); ++ ++ proxyPortLabel = new Label("Proxy Port"); ++ gbc.gridwidth = 1; ++ gridbag.setConstraints(proxyPortLabel,gbc); ++ add(proxyPortLabel); ++ proxyPortEdit = new TextField(); ++ gbc.gridwidth = GridBagConstraints.REMAINDER; ++ gridbag.setConstraints(proxyPortEdit,gbc); ++ add(proxyPortEdit); ++ } ++ + closeButton = new Button("Close"); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gridbag.setConstraints(closeButton, gbc); +@@ -161,6 +196,11 @@ + } + } + ++ if(proxyPort>-1) { ++ proxyPortEdit.setText(Integer.toString(proxyPort)); ++ proxyHostEdit.setText(proxyHost); ++ } ++ + // Make the booleans and encodings array correspond to the state of the GUI + + setEncodings(); +@@ -361,8 +401,12 @@ + // + + public void actionPerformed(ActionEvent evt) { +- if (evt.getSource() == closeButton) ++ if (evt.getSource() == closeButton) { + setVisible(false); ++ proxyHost = proxyHostEdit.getText(); ++ proxyPort = Integer.parseInt(proxyPortEdit.getText()); ++ System.err.println("proxy is " + proxyHost + ":" + proxyPort); ++ } + } + + // +diff -ru vnc_javasrc/RfbProto.java proxy_vnc_javasrc/RfbProto.java +--- vnc_javasrc/RfbProto.java Sun Aug 4 18:39:35 2002 ++++ proxy_vnc_javasrc/RfbProto.java Thu Aug 22 22:53:53 2002 +@@ -119,12 +119,51 @@ + viewer = v; + host = h; + port = p; +- sock = new Socket(host, port); ++ if(viewer.options.proxyPort>-1) ++ sock = new Socket(viewer.options.proxyHost, viewer.options.proxyPort); ++ else ++ sock = new Socket(host, port); + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); ++ if(viewer.options.proxyPort>-1) ++ negotiateProxy(host,port); + } + ++ // this is inefficient as hell, but only used once per connection ++ String readLine() { ++ byte[] ba = new byte[1]; ++ String s = new String(""); ++ ++ ba[0]=0; ++ try { ++ while(ba[0] != 0xa) { ++ ba[0] = (byte)is.readUnsignedByte(); ++ s += new String(ba); ++ } ++ } catch(Exception e) { ++ e.printStackTrace(); ++ } ++ return s; ++ } ++ ++ void negotiateProxy(String realHost,int realPort) throws IOException { ++ String line; ++ ++ // this would be the correct way, but we want to trick strict proxies. ++ // line = "CONNECT " + realHost + ":" + realPort + " HTTP/1.1\r\nHost: " + realHost + ":" + realPort + "\r\n\r\n"; ++ line = "GET " + realHost + ":" + realPort + "/proxied.connection HTTP/1.0\r\nPragma: No-Cache\r\nProxy-Connection: Keep-Alive\r\n\r\n"; ++ os.write(line.getBytes()); ++ ++ line = readLine(); ++ System.err.println("Proxy said: " + line); ++ if(!(line.substring(0,7)+line.substring(8,12)).equalsIgnoreCase("HTTP/1. 200")) { ++ IOException e = new IOException(line); ++ throw e; ++ } ++ while(!line.equals("\r\n") && !line.equals("\n")) ++ line = readLine(); ++ } + + void close() { + try { diff --git a/webclients/java-applet/ssl/Makefile.am b/webclients/java-applet/ssl/Makefile.am new file mode 100644 index 0000000..fd1c201 --- /dev/null +++ b/webclients/java-applet/ssl/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc + diff --git a/webclients/java-applet/ssl/README b/webclients/java-applet/ssl/README new file mode 100644 index 0000000..b244cf1 --- /dev/null +++ b/webclients/java-applet/ssl/README @@ -0,0 +1,338 @@ +This directory contains a patched Java applet VNC viewer that is SSL +enabled. + +The patches in the *.patch files are relative to the source tarball: + + tightvnc-1.3dev7_javasrc.tar.gz + +currently (4/06) available here: + + http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download + +It also includes some simple patches to: + + - fix richcursor colors + + - make the Java Applet cursor (not the cursor drawn to the canvas + framebuffer) invisible when it is inside the canvas. + + - allow Tab (and some other) keystrokes to be sent to the vnc + server instead of doing widget traversal. + + +This SSL applet should work with any VNC viewer that has an SSL tunnel in +front of it. It has been tested on x11vnc and using the stunnel tunnel +to other VNC servers. + +By default this Vnc Viewer will only do SSL. To do unencrypted traffic +see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc). + +Proxies: they are a general problem with java socket applets (a socket +connection does not go through the proxy). See the info in the proxy.vnc +file for a workaround. It uses SignedVncViewer.jar which is simply +a signed version of VncViewer.jar. The basic idea is the user clicks +"Yes" to trust the applet and then it can connect directly to the proxy +and issue a CONNECT request. + +This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun +Java plugin. It may not work on older releases or different vendor VM's. +Send full Java Console output for failures. + +--------------------------------------------------------------- +Tips: + +When doing single-port proxy connections (e.g. both VNC and HTTPS +thru port 5900) it helps to move through the 'do you trust this site' +dialogs quickly. x11vnc has to wait to see if the traffic is VNC or +HTTP and this can cause timeouts if you don't move thru them quickly. + +You may have to restart your browser completely if it gets into a +weird state. For one case we saw the JVM requesting VncViewer.class +even when no such file exists. + + +--------------------------------------------------------------- +Extras: + +ss_vncviewer (not Java): + + Wrapper script for native VNC viewer to connect to x11vnc in + SSL mode. Script launches stunnel(8) and then connects to it + via localhost which in turn is then redirected to x11vnc via an + SSL tunnel. stunnel(8) must be installed and available in PATH. + + +Running Java SSL VncViewer from the command line: + + From this directory: + + java -cp ./VncViewer.jar VncViewer HOST PORT + + substitute and with the actual values. + You can add any other parameters, e.g.: ignoreProxy yes + +--------------------------------------------------------------- +UltraVNC: + +The UltraVNC java viewer has also been patched to support SSL. Various +bugs in the UltraVNC java viewer were also fixed. This viewer can be +useful because is support UltraVNC filetransfer, and so it works on +Unix, etc. + +UltraViewerSSL.jar +SignedUltraViewerSSL.jar +ultra.vnc +ultraproxy.vnc +ultravnc-102-JavaViewer-ssl-etc.patch + +--------------------------------------------------------------- +Applet Parameters: + +Some additional applet parameters can be set via the URL, e.g. + + http://host:5800/?param=value + http://host:5800/ultra.vnc?param=value + https://host:5900/ultra.vnc?param=value + +etc. If running java from command line as show above, it comes +in as java ... VncViewer param value ... + +There is a limitation with libvncserver that param and value can +only be alphanumeric, underscore, "+" (for space), or "." + +We have added some applet parameters to the stock VNC java +viewers. Here are the applet parameters: + +Both TightVNC and UltraVNC Java viewers: + + HOST + string, default: none. + The Hostname to connect to. + + PORT + number, default: 0 + The VNC server port to connect to. + + Open New Window + yes/no, default: no + Run applet in separate frame. + + Show Controls + yes/no, default: yes + Show Controls button panel. + + Show Offline Desktop + yes/no, default: no + Do we continue showing desktop on remote disconnect? + + Defer screen updates + number, default: 20 + Milliseconds delay + + Defer cursor updates + number, default: 10 + Milliseconds delay + + Defer update requests + number, default: 50 + Milliseconds delay + + PASSWORD + string, default: none + VNC session password in plain text. + + ENCPASSWORD + string, default: none + VNC session password in encrypted in DES with KNOWN FIXED + key. It is a hex string. This is like the ~/.vnc/passwd format. + + + The following are added by x11vnc and/or ssvnc project + + VNCSERVERPORT + number, default: 0 + Like PORT, but if there is a firewall this is the Actual VNC + server port. PORT might be a redir port on the firewall. + + DisableSSL + yes/no, default: no + Do unencrypted connection, no SSL. + + httpsPort + number, default: none + When checking for proxy, use this at the url port number. + + CONNECT + string, default: none + Sets to host:port for the CONNECT line to a Web proxy. + The Web proxy should connect us to it. + + GET + yes/no, default: no + Set to do a special HTTP GET (/request.https.vnc.connection) + to the vnc server that will cause it to switch to VNC instead. + This is to speedup/make more robust, the single port HTTPS and VNC + mode of x11vnc (e.g. both services thru port 5900, etc) + + urlPrefix + string, default: none + set to a string that will be prefixed to all URL's when contacting + the VNC server. Idea is a special proxy will use this to indicate + internal hostname, etc. + + oneTimeKey + string, default: none + set a special hex "key" to correspond to an SSL X.509 cert+key. + See the 'onetimekey' helper script. Can also be PROMPT to prompt + the user to paste the hex key string in. + + This provides a Client-Side cert+key that the client will use to + authenticate itself by SSL To the VNC Server. + + This is to try to work around the problem that the Java applet + cannot keep an SSL keystore on disk, etc. E.g. if they log + into an HTTPS website via password they are authenticated and + encrypted, then the website can safely put oneTimeKey=... on the + URL. The Vncviewer authenticates the VNC server with this key. + + Note that there is currently a problem in that if x11vnc requires + Client Certificates the user cannot download the index.vnc HTML + and VncViewer.jar from the same x11vnc. Those need to come from + a different x11vnc or from a web server. + + Note that the HTTPS website can also put the VNC Password + (e.g. a temporary/one-time one) in the parameter PASSWORD. + The Java Applet will automatically supply this VNC password + instead of prompting. + + serverCert + string, default: none + set a special hex "cert" to correspond to an SSL X.509 cert + See the 'onetimekey -certonly' helper script. + + This provides a Server-Side cert that the client will authenticate + the VNC Server against by SSL. + + This is to try to work around the problem that the Java applet + cannot keep an SSL keystore on disk, etc. E.g. if they log + into an HTTPS website via password they are authenticated and + encrypted, then the website can safely put serverCert=... on the + URL. + + Of course the VNC Server is sending this string to the Java + Applet, so this is only reasonable security if the VNC Viewer + already trusts the HTTPS retrieval of the URL + serverCert param + that it gets. This should be done over HTTPS not HTTP. + + proxyHost + string, default: none + Do not try to guess the proxy's hostname, use the value in + proxyHost. Does not imply forceProxy (below.) + + proxyPort + string, default: none + Do not try to guess the proxy's port number, use the value in + proxyPort. Does not imply forceProxy (below.) + + forceProxy + yes/no, default: no + Assume there is a proxy and force its use. + + If a string other than "yes" or "no" is given, it implies "yes" + and uses the string for proxyHost and proxyPort (see above). + In this case the string must be of the form "hostname+port". + Note that it is "+" and not ":" before the port number. + + ignoreProxy + yes/no, default: no + Don't check for a proxy, assume there is none. + + trustAllVncCerts + yes/no, default: no + Automatically trust any cert received from the VNC server + (obviously this could be dangerous and lead to man in the + middle attack). Do not ask the user to verify any of these + certs from the VNC server. + + trustUrlVncCert + yes/no, default: no + Automatically trust any cert that the web browsers has accepted. + E.g. the user said "Yes" or "Continue" to a web browser dialog + regarding a certificate. If we get the same cert (chain) from + the VNC server we trust it without prompting the user. + + debugCerts + yes/no, default: no + Print out every cert in the Server, TrustUrl, TrustAll chains. + + +TightVNC Java viewer only: + + Offer Relogin + yes/no, default: yes + "Offer Relogin" set to "No" disables "Login again" + + SocketFactory + string, default: none + set Java Socket class factory. + +UltraVNC Java viewer only: + + None. + + The following are added by x11vnc and/or ssvnc project + + ftpDropDown + string, default: none + Sets the file transfer "drives" dropdown to the "." separated + list. Use "+" for space. The default is + + My+Documents.Desktop.Home + + for 3 entries in the dropdown in addition to the "drives" + (e.g. C:\) These items should be expanded properly by the VNC + Server. x11vnc will prepend $HOME to them, which is normally + what one wants. To include a "/" use "_2F_". Another example: + + Home.Desktop.bin_2F_linux + + If an item is prefixed with "TOP_" then the item is inserted at + the top of the drop down rather than being appended to the end. + E.g. to try to initially load the user homedir instead of /: + + TOP_Home.My+Documents.Desktop + + If ftpDropDown is set to the empty string, "", then no special + locations, [Desktop] etc., are placed in the drop down. Only the + ultravnc "drives" will appear. + + ftpOnly + yes/no, default: no + The VNC viewer only shows the filetransfer panel, no desktop + is displayed. + + graftFtp + yes/no, default: no + As ftpOnly, the VNC viewer only shows the filetransfer panel, + no desktop is displayed, however it is "grafted" onto an existing + SSVNC unix vncviewer. The special SSVNC vncviewer merges the two + channels. + + dsmActive + yes/no, default: no + Special usage mode with the SSVNC unix vncviewer. The UltraVNC + DSM encryption is active. Foolishly, UltraVNC DSM encryption + *MODIFIES* the VNC protocol when active (it is not a pure tunnel). + This option indicates to modify the VNC protocol to make this work. + Usually only used with graftFtp and SSVNC unix vncviewer. + + delayAuthPanel + yes/no, default: no + This is another special usage mode with the SSVNC unix vncviewer. + A login panel is delayed (not shown at startup.) Could be useful + for non SSVNC usage too. + + ignoreMSLogonCheck + yes/no, default: no + Similar to delayAuthPanel, do not put up a popup asking for + Windows username, etc. diff --git a/webclients/java-applet/ssl/SignedUltraViewerSSL.jar b/webclients/java-applet/ssl/SignedUltraViewerSSL.jar new file mode 100644 index 0000000..6c18737 Binary files /dev/null and b/webclients/java-applet/ssl/SignedUltraViewerSSL.jar differ diff --git a/webclients/java-applet/ssl/SignedVncViewer.jar b/webclients/java-applet/ssl/SignedVncViewer.jar new file mode 100644 index 0000000..95c0b0b Binary files /dev/null and b/webclients/java-applet/ssl/SignedVncViewer.jar differ diff --git a/webclients/java-applet/ssl/UltraViewerSSL.jar b/webclients/java-applet/ssl/UltraViewerSSL.jar new file mode 100644 index 0000000..45259fd Binary files /dev/null and b/webclients/java-applet/ssl/UltraViewerSSL.jar differ diff --git a/webclients/java-applet/ssl/VncViewer.jar b/webclients/java-applet/ssl/VncViewer.jar new file mode 100644 index 0000000..9453c6f Binary files /dev/null and b/webclients/java-applet/ssl/VncViewer.jar differ diff --git a/webclients/java-applet/ssl/index.vnc b/webclients/java-applet/ssl/index.vnc new file mode 100644 index 0000000..ec520dc --- /dev/null +++ b/webclients/java-applet/ssl/index.vnc @@ -0,0 +1,26 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/java-applet/ssl/onetimekey b/webclients/java-applet/ssl/onetimekey new file mode 100755 index 0000000..bf57c8f --- /dev/null +++ b/webclients/java-applet/ssl/onetimekey @@ -0,0 +1,65 @@ +#!/bin/sh +# +# usage: onetimekey path/to/mycert.pem +# onetimekey -certonly path/to/mycert.pem +# +# Takes an openssl cert+key pem file and turns into a long string +# for the x11vnc SSL VNC Java Viewer. +# +# The Java applet URL parameter can be oneTimeKey= where str is +# the output of this program, or can be oneTimeKey=PROMPT in which +# case the applet will ask you to paste in the string. +# +# The problem trying to be solved here is it is difficult to get +# the Java applet to have or use a keystore with the key saved +# in it. Also, as the name implies, an HTTPS server can create +# a one time key to send to the applet (the user has already +# logged in via password to the HTTPS server). +# +# Note oneTimeKey is to provide a CLIENT Certificate for the viewer +# to authenticate itself to the VNC Server. +# +# There is also the serverCert= Applet parameter. This is +# a cert to authenticate the VNC server against. To create that +# string with this tool specify -certonly as the first argument. + +certonly="" +if [ "X$1" = "X-certonly" ]; then + shift + certonly=1 +fi + +in=$1 +der=/tmp/1time$$.der +touch $der +chmod 600 $der + +openssl pkcs8 -topk8 -nocrypt -in "$in" -out "$der" -outform der + +pbinhex=/tmp/pbinhex.$$ +cat > $pbinhex < + + + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/java-applet/ssl/ss_vncviewer b/webclients/java-applet/ssl/ss_vncviewer new file mode 100755 index 0000000..7e793ff --- /dev/null +++ b/webclients/java-applet/ssl/ss_vncviewer @@ -0,0 +1,3676 @@ +#!/bin/sh +# +# ss_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel +# or an SSH tunnel. +# +# Copyright (c) 2006-2009 by Karl J. Runge +# +# ss_vncviewer 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. +# +# ss_vncviewer 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 ss_vncviewer; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA +# or see . +# +# +# You must have stunnel(8) installed on the system and in your PATH +# (however, see the -ssh option below, in which case you will need ssh(1) +# installed) Note: stunnel is usually installed in an "sbin" subdirectory. +# +# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." +# already running as the VNC server on the remote machine. +# (or use stunnel on the server side for any other VNC server) +# +# +# Usage: ss_vncviewer [cert-args] host:display +# +# e.g.: ss_vncviewer snoopy:0 +# ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" +# +# [cert-args] can be: +# +# -verify /path/to/cacert.pem +# -mycert /path/to/mycert.pem +# -crl /path/to/my_crl.pem (or directory) +# -proxy host:port +# +# -verify specifies a CA cert PEM file (or a self-signed one) for +# authenticating the VNC server. +# +# -mycert specifies this client's cert+key PEM file for the VNC server to +# authenticate this client. +# +# -proxy try host:port as a Web proxy to use the CONNECT method +# to reach the VNC server (e.g. your firewall requires a proxy). +# +# For the "double proxy" case use -proxy host1:port1,host2:port2 +# (the first CONNECT is done through host1:port1 to host2:port2 +# and then a 2nd CONNECT to the destination VNC server.) +# +# Use socks://host:port, socks4://host:port, or socks5://host,port +# to force usage of a SOCKS proxy. Also repeater://host:port and +# sslrepeater://host:port. +# +# -showcert Only fetch the certificate using the 'openssl s_client' +# command (openssl(1) must in installed). On ssvnc 1.0.27 and +# later the bundled command 'ultravnc_dsm_helper' is used. +# +# See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on +# SSL certificates with VNC. +# +# A few other args (not related to SSL and certs): +# +# -2nd Run the vncviewer a 2nd time if the first connections fails. +# +# -ssh Use ssh instead of stunnel SSL. ssh(1) must be installed and you +# must be able to log into the remote machine via ssh. +# +# In this case "host:display" may be of the form "user@host:display" +# where "user@host" is used for the ssh login (see ssh(1) manpage). +# +# If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port" +# "user@gwhost" or "user@gwhost:port". "gwhost" is an incoming ssh +# gateway machine (the VNC server is not running there), an ssh -L +# redir is used to "host" in "host:display" from "gwhost". Any "user@" +# part must be in the -proxy string (not in "host:display"). +# +# Under -proxy use "gwhost:port" if connecting to any ssh port +# other than the default (22). (even for the non-gateway case, +# -proxy must be used to specify a non-standard ssh port) +# +# A "double ssh" can be specified via a -proxy string with the two +# hosts separated by a comma: +# +# [user1@]host1[:port1],[user2@]host2[:port2] +# +# in which case a ssh to host1 and thru it via a -L redir a 2nd +# ssh is established to host2. +# +# Examples: +# +# ss_vncviewer -ssh bob@bobs-home.net:0 +# ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0 +# +# ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0 +# ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0 +# +# ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0 +# +# -sshcmd cmd Run "cmd" via ssh instead of the default "sleep 15" +# e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900' +# +# -sshargs "args" pass "args" to the ssh process, e.g. -L/-R port redirs. +# +# -sshssl Tunnel the SSL connection thru a SSH connection. The tunnel as +# under -ssh is set up and the SSL connection goes thru it. Use +# this if you want to have and end-to-end SSL connection but must +# go thru a SSH gateway host (e.g. not the vnc server). Or use +# this if you need to tunnel additional services via -R and -L +# (see -sshargs above). +# +# ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0 +# +# -listen (or -reverse) set up a reverse connection. +# +# -alpha turn on cursor alphablending hack if you are using the +# enhanced tightvnc vncviewer. +# +# -grab turn on XGrabServer hack if you are using the enhanced tightvnc +# vncviewer (e.g. for fullscreen mode in some windowmanagers like +# fvwm that do not otherwise work in fullscreen mode) +# +# +# set VNCVIEWERCMD to whatever vncviewer command you want to use. +# +VNCIPCMD=${VNCVIEWERCMD:-vncip} +VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer} +if [ "X$SSVNC_TURBOVNC" != "X" ]; then + if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then + : + else + if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then + VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc" + fi + fi +fi +# +# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc. +# + +# turn on verbose debugging output +if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then + set -xv +fi + +PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH + +localhost="localhost" +if uname | grep Darwin >/dev/null; then + localhost="127.0.0.1" +fi + +# work out which stunnel to use (debian installs as stunnel4) +stunnel_set_here="" +if [ "X$STUNNEL" = "X" ]; then + check_stunnel=1 + if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then + if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then + type stunnel > /dev/null 2>&1 + if [ $? = 0 ]; then + # found ours + STUNNEL=stunnel + check_stunnel=0 + fi + fi + fi + if [ "X$check_stunnel" = "X1" ]; then + type stunnel4 > /dev/null 2>&1 + if [ $? = 0 ]; then + STUNNEL=stunnel4 + else + STUNNEL=stunnel + fi + fi + stunnel_set_here=1 +fi + +help() { + tail -n +2 "$0" | sed -e '/^$/ q' +} + +secondtry="" +gotalpha="" +use_ssh="" +use_sshssl="" +direct_connect="" +ssh_sleep=15 + +# sleep longer in -listen mode: +if echo "$*" | grep '.*-listen' > /dev/null; then + ssh_sleep=1800 +fi + + +ssh_cmd="" +# env override of ssh_cmd: +if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then + ssh_cmd="$SS_VNCVIEWER_SSH_CMD" +fi + +ssh_args="" +showcert="" +reverse="" + +ciphers="" +anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH" +anondh_set="" +stunnel_debug="6" +if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then + stunnel_debug="7" +fi + +if [ "X$1" = "X-viewerflavor" ]; then + # special case, try to guess which viewer: + # + if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + echo "unknown" + exit 0 + fi + if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then + echo "cotvnc" + exit 0 + fi + if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then + echo "ultravnc" + exit 0 + fi + # OK, run it for help output... + str=`$VNCVIEWERCMD -h 2>&1 | head -n 5` + if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then + echo "tightvnc" + elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then + echo "realvnc3" + elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then + echo "realvnc4" + elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then + echo "realvnc4" + else + echo "unknown" + fi + exit 0 +fi +if [ "X$1" = "X-viewerhelp" ]; then + $VNCVIEWERCMD -h 2>&1 + exit 0 +fi + +# grab our cmdline options: +while [ "X$1" != "X" ] +do + case $1 in + "-verify") shift; verify="$1" + ;; + "-mycert") shift; mycert="$1" + ;; + "-crl") shift; crl="$1" + ;; + "-proxy") shift; proxy="$1" + ;; + "-ssh") use_ssh=1 + ;; + "-sshssl") use_ssh=1 + use_sshssl=1 + ;; + "-sshcmd") shift; ssh_cmd="$1" + ;; + "-sshargs") shift; ssh_args="$1" + ;; + "-anondh") ciphers="ciphers=$anondh" + ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1 + export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH + anondh_set=1 + ;; + "-ciphers") shift; ciphers="ciphers=$1" + ;; + "-alpha") gotalpha=1 + ;; + "-showcert") showcert=1 + ;; + "-listen") reverse=1 + ;; + "-reverse") reverse=1 + ;; + "-2nd") secondtry=1 + ;; + "-grab") VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER + ;; + "-x11cursor") VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR + ;; + "-rawlocal") VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL + ;; + "-scale") shift; SSVNC_SCALE="$1"; export SSVNC_SCALE + ;; + "-onelisten") SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + ;; + "-sendclipboard") VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD + ;; + "-sendalways") VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS + ;; + "-recvtext") shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT + ;; + "-escape") shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE + ;; + "-ssvnc_encodings") shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS + ;; + "-ssvnc_extra_opts") shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS + ;; + "-rfbversion") shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION + ;; + "-nobell") VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL + ;; + "-popupfix") VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX + ;; + "-realvnc4") VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4 + ;; + "-h"*) help; exit 0 + ;; + "--h"*) help; exit 0 + ;; + *) break + ;; + esac + shift +done + +# maxconn is something we added to stunnel, this disables it: +if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +elif [ "X$reverse" != "X" ]; then + STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` +else + # new way (our patches). other than the above, we set these: + if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then + STUNNEL_ONCE=1; export STUNNEL_ONCE + fi + if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then + STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS + fi +fi +# always set this one: +if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then + STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG +fi + +# this is the -t ssh option (gives better keyboard response thru SSH tunnel) +targ="-t" +if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then + targ="" +fi + +# set the alpha blending env. hack: +if [ "X$gotalpha" = "X1" ]; then + VNCVIEWER_ALPHABLEND=1 + export VNCVIEWER_ALPHABLEND +else + NO_ALPHABLEND=1 + export NO_ALPHABLEND +fi + +if [ "X$reverse" != "X" ]; then + ssh_sleep=1800 + if [ "X$proxy" != "X" ]; then + # check proxy usage under reverse connection: + if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then + echo "" + if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then + : + else + echo "*Warning*: SSL -listen and a Web proxy does not make sense." + sleep 2 + fi + elif echo "$proxy" | grep "," > /dev/null; then + : + else + echo "" + echo "*Warning*: -listen and a single proxy/gateway does not make sense." + sleep 2 + fi + + # we now try to PPROXY_LOOP_THYSELF, set this var to disable that. + #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + fi +fi +if [ "X$ssh_cmd" = "X" ]; then + # if no remote ssh cmd, sleep a bit: + ssh_cmd="sleep $ssh_sleep" +fi + +# this should be a host:display: +# +orig="$1" +shift + +dL="-L" +if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then + dL="-h" +fi + +have_uvnc_dsm_helper_showcert="" +if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then + if type ultravnc_dsm_helper >/dev/null 2>&1; then + if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then + have_uvnc_dsm_helper_showcert=1 + fi + fi +fi +have_uvnc_dsm_helper_ipv6="" +if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if type ultravnc_dsm_helper >/dev/null 2>&1; then + if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then + have_uvnc_dsm_helper_ipv6=1 + fi + fi +fi + +rchk() { + # a kludge to set $RANDOM if we are not bash: + if [ "X$BASH_VERSION" = "X" ]; then + RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'` + fi +} +rchk + +# a portable, but not absolutely safe, tmp file creator +mytmp() { + tf=$1 + if type mktemp > /dev/null 2>&1; then + # if we have mktemp(1), use it: + tf2="$tf.XXXXXX" + tf2=`mktemp "$tf2"` + if [ "X$tf2" != "X" -a -f "$tf2" ]; then + if [ "X$DEBUG_MKTEMP" != "X" ]; then + echo "mytmp-mktemp: $tf2" 1>&2 + fi + echo "$tf2" + return + fi + fi + # fallback to multiple cmds: + rm -rf "$tf" || exit 1 + if [ -d "$tf" ]; then + echo "tmp file $tf still exists as a directory." + exit 1 + elif [ $dL "$tf" ]; then + echo "tmp file $tf still exists as a symlink." + exit 1 + elif [ -f "$tf" ]; then + echo "tmp file $tf still exists." + exit 1 + fi + touch "$tf" || exit 1 + chmod 600 "$tf" || exit 1 + rchk + if [ "X$DEBUG_MKTEMP" != "X" ]; then + echo "mytmp-touch: $tf" 1>&2 + fi + echo "$tf" +} + +# set up special case of ultravnc single click III mode: +if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then + pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'` + pstr1=`echo "$pstr" | sed -e 's/+.*$//'` + pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'` + SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER + orig=$pstr1 + echo + echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''" + proxy="" +fi +if echo "$proxy" | egrep "vencrypt://" > /dev/null; then + vtmp="/tmp/ss_handshake${RANDOM}.$$.txt" + vtmp=`mytmp "$vtmp"` + SSVNC_PREDIGESTED_HANDSHAKE="$vtmp" + export SSVNC_PREDIGESTED_HANDSHAKE + if [ "X$SSVNC_USE_OURS" = "X" ]; then + NEED_VENCRYPT_VIEWER_BRIDGE=1 + fi +fi +if [ "X$SSVNC_USE_OURS" = "X" ]; then + VNCVIEWERCMD_EXTRA_OPTS="" +fi + + +# check -ssh and -mycert/-verify conflict: +if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then + if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then + echo "-mycert and -verify cannot be used in -ssh mode" + exit 1 + fi +fi + +# direct mode Vnc:// means show no warnings. +# direct mode vnc:// will show warnings. +if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then + SSVNC_NO_ENC_WARN=1 + export SSVNC_NO_ENC_WARN + orig=`echo "$orig" | sed -e 's/^...:/vnc:/'` +fi + +# interprest the pseudo URL proto:// strings: +if echo "$orig" | grep '^vnc://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc://,,'` + verify="" + mycert="" + crl="" + use_ssh="" + use_sshssl="" + direct_connect=1 +elif echo "$orig" | grep '^vncs://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncs://,,'` +elif echo "$orig" | grep '^vncssl://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncssl://,,'` +elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'` +elif echo "$orig" | grep '^vncssh://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vncssh://,,'` + use_ssh=1 +elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then + orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'` + use_ssh=1 +fi + +if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + verify="" + mycert="" + crl="" + use_ssh="" + use_sshssl="" + direct_connect=1 + if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then + SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM + fi +fi + +# rsh mode is an internal/secret thing only I use. +rsh="" +if echo "$orig" | grep '^rsh://' > /dev/null; then + use_ssh=1 + rsh=1 + orig=`echo "$orig" | sed -e 's,rsh://,,'` +elif echo "$orig" | grep '^rsh:' > /dev/null; then + use_ssh=1 + rsh=1 + orig=`echo "$orig" | sed -e 's,rsh:,,'` +fi + +# play around with host:display port: +if echo "$orig" | grep ':[0-9][0-9]*$' > /dev/null; then + : +else + # add or assume :0 if no ':' + if [ "X$reverse" = "X" ]; then + orig="$orig:0" + elif [ "X$orig" = "X" ]; then + orig=":0" + fi +fi + +# extract host and disp number: + +# try to see if it is ipv6 address: +ipv6=0 +if echo "$orig" | grep '\[' > /dev/null; then + # ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900 + host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'` + disp=`echo "$orig" | sed -e 's/^.*\]://'` + ipv6=1 +elif echo "$orig" | grep ':..*:' > /dev/null; then + # ipv6 fe80::219:dbff:fee5:3f92%eth1:5900 + host=`echo "$orig" | sed -e 's/:[^:]*$//'` + disp=`echo "$orig" | sed -e 's/^.*://'` + ipv6=1 +else + # regular host:port + host=`echo "$orig" | awk -F: '{print $1}'` + disp=`echo "$orig" | awk -F: '{print $2}'` +fi + +if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then + STUNNEL_LISTEN=$host + echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN" +fi + +if [ "X$host" = "X" ]; then + host=$localhost +fi + +if [ "X$SSVNC_IPV6" = "X0" ]; then + # disable checking for it. + ipv6=0 +#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then +# ipv6=0 +elif [ "X$ipv6" = "X1" ]; then + : +elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then + : +else + # regular hostname, can't be sure... + gout="" + if type getent > /dev/null 2>/dev/null; then + gout=`getent hosts "$host" 2>/dev/null` + fi + if echo "$gout" | grep ':.*:' > /dev/null; then + if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then + : + else + echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1` + ipv6=1 + fi + fi + if [ "X$ipv6" = "X0" ]; then + hout="" + if type host > /dev/null 2>/dev/null; then + host "$host" >/dev/null 2>&1 + host "$host" >/dev/null 2>&1 + hout=`host "$host" 2>/dev/null` + fi + if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then + if echo "$hout" | grep -i 'has address' > /dev/null; then + : + else + echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1` + ipv6=1 + fi + fi + fi + if [ "X$ipv6" = "X0" ]; then + dout="" + if type dig > /dev/null 2>/dev/null; then + dout=`dig -t any "$host" 2>/dev/null` + fi + if echo "$dout" | grep -i "^$host" | grep '[ ]AAAA[ ]' > /dev/null; then + if echo "$dout" | grep -i "^$host" | grep '[ ]A[ ]' > /dev/null; then + : + else + echo "ipv6: "`echo "$dout" | grep -i '[ ]AAAA[ ]' | head -n 1` + ipv6=1 + fi + fi + fi + if [ "X$ipv6" = "X0" ]; then + sout=`env LOOKUP="$host" \ + perl -e ' eval {use Socket}; exit 0 if $@; + eval {use Socket6}; exit 0 if $@; + @res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM); + $ipv4 = 0; + $ipv6 = 0; + $ip6 = ""; + while (scalar(@res) >= 5) { + ($family, $socktype, $proto, $saddr, $canon, @res) = @res; + $ipv4 = 1 if $family == AF_INET; + $ipv6 = 1 if $family == AF_INET6; + if ($family == AF_INET6 && $ip6 eq "") { + my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV); + $ip6 = $host; + } + } + if (! $ipv4 && $ipv6) { + print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n"; + } + exit 0; + ' 2>/dev/null` + if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then + echo "$sout" + ipv6=1 + fi + fi +fi +if [ "X$ipv6" = "X1" ]; then + echo "ipv6: addr=$host disp=$disp" +fi +if [ "X$disp" = "X" ]; then + port="" # probably -listen mode. +elif [ $disp -lt 0 ]; then + # negative means use |n| without question: + port=`expr 0 - $disp` +elif [ $disp -lt 200 ]; then + # less than 200 means 5900+n + if [ "X$reverse" = "X" ]; then + port=`expr $disp + 5900` + else + port=`expr $disp + 5500` + fi +else + # otherwise use the number directly, e.g. 443, 2345 + port=$disp +fi + +if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then + if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then + if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then + : + elif [ "X$SSVNC_USE_OURS" = "X1" ]; then + # requires 1.0.27 and later ssvncviewer binary + : + else + proxy="ipv6://$host:$port" + echo "direct connect: set proxy=$proxy" + fi + fi +fi + +# (possibly) tell the vncviewer to only listen on lo: +if [ "X$reverse" != "X" ]; then + if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then + VNCVIEWER_LISTEN_LOCALHOST=1 + export VNCVIEWER_LISTEN_LOCALHOST + fi +fi + +# try to find an open listening port via netstat(1): +inuse="" +if uname | grep Linux > /dev/null; then + inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'` +elif uname | grep SunOS > /dev/null; then + inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'` +elif uname | egrep -i 'bsd|darwin' > /dev/null; then + inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'` +# add others... +fi + +# this is a crude attempt for unique ports tags, etc. +date_sec=`date +%S` + +# these are special cases of no vnc, e.g. sleep or xmessage. +# these are for using ssvnc as a general port redirector. +if echo "$VNCVIEWERCMD" | grep '^sleep[ ][ ]*[0-9][0-9]*' > /dev/null; then + if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then + p=`echo "$VNCVIEWERCMD" | awk '{print $3}'` + if [ "X$p" != "X" ]; then + SS_VNCVIEWER_LISTEN_PORT=$p + fi + fi + p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'` + VNCVIEWERCMD="eval sleep $p2; echo Local " +elif echo "$VNCVIEWERCMD" | grep '^xmessage[ ][ ]*[0-9][0-9]*' > /dev/null; then + if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then + p=`echo "$VNCVIEWERCMD" | awk '{print $2}'` + SS_VNCVIEWER_LISTEN_PORT=$p + fi +fi + +# utility to find a free port to listen on. +findfree() { + try0=$1 + try=$try0 + use0="" + + if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then + echo "$SS_VNCVIEWER_LISTEN_PORT" + return + fi + if [ $try -ge 6000 ]; then + fmax=`expr $try + 1000` + else + fmax=6000 + fi + + while [ $try -lt $fmax ] + do + if [ "X$inuse" = "X" ]; then + break + fi + if echo "$inuse" | grep -w $try > /dev/null; then + : + else + use0=$try + break + fi + try=`expr $try + 1` + done + if [ "X$use0" = "X" ]; then + use0=`expr $date_sec + $try0` + fi + + echo $use0 +} + +# utility for exiting; kills some helper processes, +# removes files, etc. +final() { + echo "" + if [ "X$tmp_cfg" != "X" ]; then + rm -f $tmp_cfg + fi + if [ "X$SS_VNCVIEWER_RM" != "X" ]; then + rm -f $SS_VNCVIEWER_RM 2>/dev/null + fi + if [ "X$tcert" != "X" ]; then + rm -f $tcert + fi + if [ "X$pssh" != "X" ]; then + echo "Terminating background ssh process" + echo kill -TERM "$pssh" + kill -TERM "$pssh" 2>/dev/null + sleep 1 + kill -KILL "$pssh" 2>/dev/null + pssh="" + fi + if [ "X$stunnel_pid" != "X" ]; then + echo "Terminating background stunnel process" + echo kill -TERM "$stunnel_pid" + kill -TERM "$stunnel_pid" 2>/dev/null + sleep 1 + kill -KILL "$stunnel_pid" 2>/dev/null + stunnel_pid="" + fi + if [ "X$dsm_pid" != "X" ]; then + echo "Terminating background ultravnc_dsm_helper process" + echo kill -TERM "$dsm_pid" + kill -TERM "$dsm_pid" 2>/dev/null + sleep 1 + kill -KILL "$dsm_pid" 2>/dev/null + stunnel_pid="" + fi + if [ "X$tail_pid" != "X" ]; then + kill -TERM $tail_pid + fi + if [ "X$tail_pid2" != "X" ]; then + kill -TERM $tail_pid2 + fi +} + +if [ "X$reverse" = "X" ]; then + # normal connections try 5930-5999: + if [ "X$showcert" = "X" ]; then + use=`findfree 5930` + else + # move away from normal place for (possibly many) -showcert + pstart=`date +%S` + pstart=`expr 6130 + $pstart + $pstart` + use=`findfree $pstart` + fi + if [ $use -ge 5900 ]; then + N=`expr $use - 5900` + else + N=$use + fi +else + # reverse connections: + p2=`expr $port + 30` + use=`findfree $p2` + if [ $use -ge 5500 ]; then + N=`expr $use - 5500` + else + N=$use + fi +fi + +# this is for my special use of ss_vncip -> vncip viewer. +if echo "$0" | grep vncip > /dev/null; then + VNCVIEWERCMD="$VNCIPCMD" +fi + +if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then + : +elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then + VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS" +fi + +# trick for the undocumented rsh://host:port method. +rsh_setup() { + if echo "$ssh_host" | grep '@' > /dev/null; then + ul=`echo "$ssh_host" | awk -F@ '{print $1}'` + ul="-l $ul" + ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'` + else + ul="" + fi + ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'` +} + +# trick for the undocumented rsh://host:port method. +rsh_viewer() { + trap "final" 0 2 15 + if [ "X$PORT" = "X" ]; then + exit 1 + elif [ $PORT -ge 5900 ]; then + vdpy=`expr $PORT - 5900` + else + vdpy=":$PORT" + fi + stty sane + echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy + echo "" + $VNCVIEWERCMD "$@" $ssh_host:$vdpy + if [ $? != 0 ]; then + sleep 2 + $VNCVIEWERCMD "$@" $ssh_host:$vdpy + fi +} + +check_perl() { + if type "$1" > /dev/null 2>&1; then + : + elif [ ! -x "$1" ]; then + echo "" + echo "*******************************************************" + echo "** Problem finding the Perl command '$1': **" + echo "" + type "perl" + echo "" + echo "** Perhaps you need to install the Perl package. **" + echo "*******************************************************" + echo "" + sleep 5 + fi +} + +# this is the PPROXY tool. used only here for now... +pcode() { + tf=$1 + PPROXY_PROXY=$proxy; export PPROXY_PROXY + PPROXY_DEST="$host:$port"; export PPROXY_DEST + check_perl /usr/bin/perl + + cod='#!/usr/bin/perl + +# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for +# client connections. +# Also acts as a VeNCrypt bridge (by redirecting to stunnel.) + +use IO::Socket::INET; + +my $have_inet6 = ""; +eval "use IO::Socket::INET6;"; +$have_inet6 = 1 if $@ eq ""; + +#my $have_sock6 = ""; +#eval "use Socket; use Socket6;"; +#$have_sock6 = 1 if $@ eq ""; + +if (exists $ENV{PPROXY_LOOP_THYSELF}) { + # used for reverse vnc, run a repeating outer loop. + print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n"; + my $rm = $ENV{PPROXY_REMOVE}; + my $lp = $ENV{PPROXY_LOOP_THYSELF}; + delete $ENV{PPROXY_REMOVE}; + delete $ENV{PPROXY_LOOP_THYSELF}; + $ENV{PPROXY_LOOP_THYSELF_MASTER} = $$; + my $pid = $$; + my $dbg = 0; + my $c = 0; + use POSIX ":sys_wait_h"; + while (1) { + $pid = fork(); + last if ! defined $pid; + if ($pid eq "0") { + last; + } + $c++; + print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n"; + while (1) { + waitpid(-1, WNOHANG); + fsleep(0.25); + if (! kill 0, $pid) { + print STDERR "PPROXY_LOOP: child=$pid gone.\n"; + last; + } + print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg; + if (! -f $lp) { + print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n"; + kill TERM, $pid; + fsleep(0.1); + wait; + last; + } + print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg; + } + last if ! -f $lp; + fsleep(0.25); + } + if ($pid ne "0") { + unlink($0) if $rm; + exit 0; + } +} + +if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) { + print STDERR "PPROXY_PID: $$\n"; + sleep $ENV{PPROXY_SLEEP}; +} + +foreach my $var (qw( + PPROXY_DEST + PPROXY_KILLPID + PPROXY_LISTEN + PPROXY_PROXY + PPROXY_REMOVE + PPROXY_REPEATER + PPROXY_REVERSE + PPROXY_SLEEP + PPROXY_SOCKS + PPROXY_VENCRYPT + PPROXY_VENCRYPT_VIEWER_BRIDGE + )) { + if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) { + print STDERR "$var: $ENV{$var}\n"; + } +} + +if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) { + if ($ENV{PPROXY_SOCKS} eq "5") { + $ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}"; + } else { + $ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}"; + } +} + +my $rfbSecTypeAnonTls = 18; +my $rfbSecTypeVencrypt = 19; + +my $rfbVencryptPlain = 256; +my $rfbVencryptTlsNone = 257; +my $rfbVencryptTlsVnc = 258; +my $rfbVencryptTlsPlain = 259; +my $rfbVencryptX509None = 260; +my $rfbVencryptX509Vnc = 261; +my $rfbVencryptX509Plain = 262; + +my $handshake_file = ""; +if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE}) { + $handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE}; +} + +my $have_gettimeofday = 0; +eval "use Time::HiRes;"; +if ($@ eq "") { + $have_gettimeofday = 1; +} +sub gettime { + my $t = "0.0"; + if ($have_gettimeofday) { + $t = Time::HiRes::gettimeofday(); + } + return $t; +} + +my $listen_handle = ""; +my $sock = ""; +my $parent = $$; + +my $initial_data = ""; + +if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { + my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}); + do_vencrypt_viewer_bridge($from, $to); + exit 0; +} + +my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3); +my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", ""); + +($first, $mode_1st) = url_parse($first); + +my ($proxy_host, $proxy_port) = ($first, ""); +if ($proxy_host =~ /^(.*):(\d+)$/) { + $proxy_host = $1; + $proxy_port = $2; +} +my $connect = $ENV{PPROXY_DEST}; + +if ($second ne "") { + ($second, $mode_2nd) = url_parse($second); +} + +if ($third ne "") { + ($third, $mode_3rd) = url_parse($third); +} + + +print STDERR "\n"; +print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n"; +print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n"; +print STDERR "proxy_host: $proxy_host\n"; +print STDERR "proxy_port: $proxy_port\n"; +print STDERR "proxy_connect: $connect\n"; +print STDERR "pproxy_params: $ENV{PPROXY_PROXY}\n"; +print STDERR "pproxy_listen: $ENV{PPROXY_LISTEN}\n"; +print STDERR "pproxy_reverse: $ENV{PPROXY_REVERSE}\n"; +print STDERR "io_socket_inet6: $have_inet6\n"; +print STDERR "\n"; +if (! $have_inet6) { + print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n"; +} + +if (1) { + print STDERR "pproxy 1st: $first\t- $mode_1st\n"; + print STDERR "pproxy 2nd: $second\t- $mode_2nd\n"; + print STDERR "pproxy 3rd: $third\t- $mode_3rd\n"; + print STDERR "\n"; +} + +sub pdie { + my $msg = shift; + kill_proxy_pids(); + die "$msg"; +} + +if ($ENV{PPROXY_REVERSE} ne "") { + my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, ""); + if ($rhost =~ /^(.*):(\d+)$/) { + $rhost = $1; + $rport = $2; + } + $rport = 5900 unless $rport; + my $emsg = ""; + $listen_handle = IO::Socket::INET->new( + PeerAddr => $rhost, + PeerPort => $rport, + Proto => "tcp" + ); + $emsg = $!; + if (! $listen_handle && $have_inet6) { + eval {$listen_handle = IO::Socket::INET6->new( + PeerAddr => $rhost, + PeerPort => $rport, + Proto => "tcp" + );}; + $emsg .= " / $!"; + } + if (! $listen_handle) { + pdie "pproxy: $emsg -- PPROXY_REVERSE\n"; + } + print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n"; + +} elsif ($ENV{PPROXY_LISTEN} ne "") { + my $listen_sock = ""; + my $maxtry = 12; + my $sleep = 5; + my $p2 = ""; + my $emsg = ""; + for (my $i=0; $i < $maxtry; $i++) { + my ($if, $p) = ("", $ENV{PPROXY_LISTEN}); + if ($p =~ /^(.*):(\d+)$/) { + $if = $1; + $p = $2; + } + $p2 = "*:$p"; + if ($if eq "") { + $if = "localhost"; + } + print STDERR "pproxy interface: $if\n"; + + $emsg = ""; + if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) { + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => "::", + LocalPort => $p, + Proto => "tcp" + );}; + $p2 = ":::$p"; + } elsif ($if =~ /^INADDR_ANY/) { + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalPort => $p, + Proto => "tcp" + ); + } elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) { + $p2 = "::1:$p"; + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => "::1", + LocalPort => $p, + Proto => "tcp" + );}; + $p2 = "::1:$p"; + } else { + $p2 = "$if:$p"; + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalAddr => $if, + LocalPort => $p, + Proto => "tcp" + ); + $emsg = $!; + + if (! $listen_sock && $have_inet6) { + print STDERR "PPROXY_LISTEN: retry with INET6\n"; + eval {$listen_sock = IO::Socket::INET6->new( + Listen => 2, + ReuseAddr => 1, + Domain => AF_INET6, + LocalAddr => $if, + LocalPort => $p, + Proto => "tcp" + );}; + $emsg .= " / $!"; + } + } + if (! $listen_sock) { + if ($i < $maxtry - 1) { + warn "pproxy: $emsg $!\n"; + warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n"; + sleep $sleep; + } + } else { + last; + } + } + if (! $listen_sock) { + pdie "pproxy: $emsg -- PPROXY_LISTEN\n"; + } + print STDERR "pproxy: listening on $p2\n"; + my $ip; + ($listen_handle, $ip) = $listen_sock->accept(); + my $err = $!; + close $listen_sock; + if (! $listen_handle) { + pdie "pproxy: $err\n"; + } + + if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { + my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; + if ($sml ne "" && $sml ne "0") { + setpgrp(0, 0); + if (fork()) { + close $viewer_sock; + wait; + exit 0; + } + if (fork()) { + close $viewer_sock; + exit 0; + } + setpgrp(0, 0); + $parent = $$; + } + } +} + +$sock = IO::Socket::INET->new( + PeerAddr => $proxy_host, + PeerPort => $proxy_port, + Proto => "tcp" +); + +my $err = ""; + +if (! $sock && $have_inet6) { + $err = $!; + + print STDERR "pproxy: $!\n"; + + eval {$sock = IO::Socket::INET6->new( + PeerAddr => $proxy_host, + PeerPort => $proxy_port, + Proto => "tcp" + );}; + $err .= " / $!"; +} + +if (! $sock && ($proxy_host =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i || $proxy_host =~ /^::ffff:([\da-f]+:[\da-f]+)$/i)) { + print STDERR "pproxy: $!\n"; + my $ipv4_addr = $1; + if ($ipv4_addr =~ /:/) { + my ($a, $b) = split(/:/, $ipv4_addr); + $a = hex($a); + $b = hex($b); + $ipv4_addr = sprintf("%d.", ($a & 0xff00) >> 8); + $ipv4_addr .= sprintf("%d.", ($a & 0x00ff)); + $ipv4_addr .= sprintf("%d.", ($b & 0xff00) >> 8); + $ipv4_addr .= sprintf("%d", ($b & 0x00ff)); + } + + print STDERR "pproxy: re-trying with ipv4 addr: $ipv4_addr\n"; + + eval {$sock = IO::Socket::INET->new( + PeerAddr => $ipv4_addr, + PeerPort => $proxy_port, + Proto => "tcp" + );}; + $err .= " / $!"; +} + +if (! $sock) { + unlink($0) if $ENV{PPROXY_REMOVE}; + pdie "pproxy: $err\n"; +} + +unlink($0) if $ENV{PPROXY_REMOVE}; + +if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) { + print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n"; + my $tmp_swap = $sock; + $sock = $listen_handle; + $listen_handle = $tmp_swap; +} + +$cur_proxy = $first; +setmode($mode_1st); + +if ($second ne "") { + connection($second, 1); + + setmode($mode_2nd); + $cur_proxy = $second; + + if ($third ne "") { + connection($third, 2); + setmode($mode_3rd); + $cur_proxy = $third; + connection($connect, 3); + } else { + connection($connect, 2); + } +} else { + connection($connect, 1); +} + +sub kill_proxy_pids() { + if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { + return; + } + if ($ENV{PPROXY_KILLPID}) { + foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) { + if ($p =~ /^(\+|-)/) { + $p = $parent + $p; + } + print STDERR "kill TERM, $p (PPROXY_KILLPID)\n"; + kill "TERM", $p; + } + } +} + +sub xfer { + my($in, $out) = @_; + $RIN = $WIN = $EIN = ""; + $ROUT = ""; + vec($RIN, fileno($in), 1) = 1; + vec($WIN, fileno($in), 1) = 1; + $EIN = $RIN | $WIN; + + while (1) { + my $nf = 0; + while (! $nf) { + $nf = select($ROUT=$RIN, undef, undef, undef); + } + my $len = sysread($in, $buf, 8192); + if (! defined($len)) { + next if $! =~ /^Interrupted/; + print STDERR "pproxy[$$]: $!\n"; + last; + } elsif ($len == 0) { + print STDERR "pproxy[$$]: Input is EOF.\n"; + last; + } + my $offset = 0; + my $quit = 0; + while ($len) { + my $written = syswrite($out, $buf, $len, $offset); + if (! defined $written) { + print STDERR "pproxy[$$]: Output is EOF. $!\n"; + $quit = 1; + last; + } + $len -= $written; + $offset += $written; + } + last if $quit; + } + close($out); + close($in); + print STDERR "pproxy[$$]: finished xfer.\n"; +} + +sub handler { + print STDERR "pproxy[$$]: got SIGTERM.\n"; + close $listen_handle if $listen_handle; + close $sock if $sock; + exit; +} + +sub xfer_both { + $child = fork; + + if (! defined $child) { + kill_proxy_pids(); + exit 1; + } + + $SIG{TERM} = "handler"; + + if ($child) { + if ($listen_handle) { + print STDERR "pproxy parent[$$] listen_handle -> socket\n"; + xfer($listen_handle, $sock); + } else { + print STDERR "pproxy parent[$$] STDIN -> socket\n"; + xfer(STDIN, $sock); + } + select(undef, undef, undef, 0.25); + if (kill 0, $child) { + select(undef, undef, undef, 0.9); + if (kill 0, $child) { + print STDERR "pproxy[$$]: kill TERM child $child\n"; + kill "TERM", $child; + } else { + print STDERR "pproxy[$$]: child $child gone.\n"; + } + } + } else { + select(undef, undef, undef, 0.05); + if ($listen_handle) { + print STDERR "pproxy child [$$] socket -> listen_handle\n"; + if ($initial_data ne "") { + my $len = length $initial_data; + print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; + syswrite($listen_handle, $initial_data, $len); + } else { + print STDERR "\n"; + } + xfer($sock, $listen_handle); + } else { + print STDERR "pproxy child [$$] socket -> STDOUT\n"; + if ($initial_data ne "") { + my $len = length $initial_data; + print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; + syswrite(STDOUT, $initial_data, $len); + } else { + print STDERR "\n"; + } + xfer($sock, STDOUT); + } + select(undef, undef, undef, 0.25); + if (kill 0, $parent) { + select(undef, undef, undef, 0.8); + if (kill 0, $parent) { + print STDERR "pproxy[$$]: kill TERM parent $parent\n"; + kill "TERM", $parent; + } else { + print STDERR "pproxy[$$]: parent $parent gone.\n"; + } + } + } + + kill_proxy_pids(); +} + +xfer_both(); + +exit; + +sub fsleep { + select(undef, undef, undef, shift); +} + +sub url_parse { + my $hostport = shift; + my $mode = "http"; + if ($hostport =~ m,^socks4?://(\S*)$,i) { + $mode = "socks4"; + $hostport = $1; + } elsif ($hostport =~ m,^socks5://(\S*)$,i) { + $mode = "socks5"; + $hostport = $1; + } elsif ($hostport =~ m,^https?://(\S*)$,i) { + $mode = "http"; + $hostport = $1; + } elsif ($hostport =~ m,^ipv6://(\S*)$,i) { + $mode = "ipv6"; + $hostport = $1; + } elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) { + # ultravnc repeater proxy. + $hostport = $1; + $mode = "repeater:$2"; + if ($hostport !~ /:\d+$/) { + $hostport .= ":5900"; + } + } elsif ($hostport =~ m,^vencrypt://(\S*)$,i) { + # vencrypt handshake. + $hostport = $1; + my $m = "connect"; + if ($hostpost =~ /^(\S+)\+(\S+)$/) { + $hostport = $1; + $mode = $2; + } + $mode = "vencrypt:$m"; + if ($hostport !~ /:\d+$/) { + $hostport .= ":5900"; + } + } + return ($hostport, $mode); +} + +sub setmode { + my $mode = shift; + $ENV{PPROXY_REPEATER} = ""; + $ENV{PPROXY_VENCRYPT} = ""; + if ($mode =~ /^socks/) { + if ($mode =~ /^socks5/) { + $ENV{PPROXY_SOCKS} = 5; + } else { + $ENV{PPROXY_SOCKS} = 1; + } + } elsif ($mode =~ /^ipv6/i) { + $ENV{PPROXY_SOCKS} = 0; + } elsif ($mode =~ /^repeater:(.*)/) { + $ENV{PPROXY_REPEATER} = $1; + $ENV{PPROXY_SOCKS} = ""; + } elsif ($mode =~ /^vencrypt:(.*)/) { + $ENV{PPROXY_VENCRYPT} = $1; + $ENV{PPROXY_SOCKS} = ""; + } else { + $ENV{PPROXY_SOCKS} = ""; + } +} + +sub connection { + my ($CONNECT, $w) = @_; + + my $con = ""; + my $msg = ""; + + if ($ENV{PPROXY_SOCKS} eq "5") { + # SOCKS5 + my ($h, $p) = ($CONNECT, ""); + if ($h =~ /^(.*):(\d+)$/) { + $h = $1; + $p = $2; + } + $con .= pack("C", 0x05); + $con .= pack("C", 0x01); + $con .= pack("C", 0x00); + + $msg = "SOCKS5 via $cur_proxy to $h:$p\n\n"; + print STDERR "proxy_request$w: $msg"; + + syswrite($sock, $con, length($con)); + + my ($n1, $n2, $n3, $n4, $n5, $n6); + my ($r1, $r2, $r3, $r4, $r5, $r6); + my ($s1, $s2, $s3, $s4, $s5, $s6); + + $n1 = sysread($sock, $r1, 1); + $n2 = sysread($sock, $r2, 1); + + $s1 = unpack("C", $r1); + $s2 = unpack("C", $r2); + if ($s1 != 0x05 || $s2 != 0x00) { + print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n"; + close $sock; + exit(1); + } + + $con = ""; + $con .= pack("C", 0x05); + $con .= pack("C", 0x01); + $con .= pack("C", 0x00); + $con .= pack("C", 0x03); + $con .= pack("C", length($h)); + $con .= $h; + $con .= pack("C", $p >> 8); + $con .= pack("C", $p & 0xff); + + syswrite($sock, $con, length($con)); + + $n1 = sysread($sock, $r1, 1); + $n2 = sysread($sock, $r2, 1); + $n3 = sysread($sock, $r3, 1); + $n4 = sysread($sock, $r4, 1); + $s1 = unpack("C", $r1); + $s2 = unpack("C", $r2); + $s3 = unpack("C", $r3); + $s4 = unpack("C", $r4); + + if ($s4 == 0x1) { + sysread($sock, $r5, 4 + 2); + } elsif ($s4 == 0x3) { + sysread($sock, $r5, 1); + $s5 = unpack("C", $r5); + sysread($sock, $r6, $s5 + 2); + } elsif ($s4 == 0x4) { + sysread($sock, $r5, 16 + 2); + } + + if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) { + print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n"; + close $sock; + exit(1); + } + + } elsif ($ENV{PPROXY_SOCKS} eq "1") { + # SOCKS4 SOCKS4a + my ($h, $p) = ($CONNECT, ""); + if ($h =~ /^(.*):(\d+)$/) { + $h = $1; + $p = $2; + } + $con .= pack("C", 0x04); + $con .= pack("C", 0x01); + $con .= pack("n", $p); + + my $SOCKS_4a = 0; + if ($h eq "localhost" || $h eq "127.0.0.1") { + $con .= pack("C", 127); + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 1); + } elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { + $con .= pack("C", $1); + $con .= pack("C", $2); + $con .= pack("C", $3); + $con .= pack("C", $4); + } else { + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 0); + $con .= pack("C", 3); + $SOCKS_4a = 1; + } + + $con .= "nobody"; + $con .= pack("C", 0); + + $msg = "SOCKS4 via $cur_proxy to $h:$p\n\n"; + if ($SOCKS_4a) { + $con .= $h; + $con .= pack("C", 0); + $msg =~ s/SOCKS4/SOCKS4a/; + } + print STDERR "proxy_request$w: $msg"; + syswrite($sock, $con, length($con)); + + my $ok = 1; + for (my $i = 0; $i < 8; $i++) { + my $c; + sysread($sock, $c, 1); + my $s = unpack("C", $c); + if ($i == 0) { + $ok = 0 if $s != 0x0; + } elsif ($i == 1) { + $ok = 0 if $s != 0x5a; + } + } + if (! $ok) { + print STDERR "SOCKS4 failed.\n"; + close $sock; + exit(1); + } + } elsif ($ENV{PPROXY_SOCKS} eq "0") { + # hack for ipv6 "proxy", nothing to do, assume INET6 call worked. + ; + } elsif ($ENV{PPROXY_REPEATER} ne "") { + my $rep = $ENV{PPROXY_REPEATER}; + print STDERR "repeater: $rep\n"; + $rep .= pack("x") x 250; + syswrite($sock, $rep, 250); + + my $rfb = ""; + + my $ok = 1; + for (my $i = 0; $i < 12; $i++) { + my $c; + last if $ENV{PPROXY_GENERIC_REPEATER}; + sysread($sock, $c, 1); + print STDERR $c; + $rfb .= $c; + } + if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) { + $initial_data = $rfb; + $rfb =~ s/\n//g; + print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n"; + } + } elsif ($ENV{PPROXY_VENCRYPT} ne "") { + my $vencrypt = $ENV{PPROXY_VENCRYPT}; + vencrypt_dialog($vencrypt); + + } else { + # Web Proxy: + $con = "CONNECT $CONNECT HTTP/1.1\r\n"; + $con .= "Host: $CONNECT\r\n"; + $con .= "Connection: close\r\n\r\n"; + $msg = $con; + + print STDERR "proxy_request$w: via $cur_proxy:\n$msg"; + syswrite($sock, $con, length($con)); + + my $rep = ""; + my $n = 0; + while ($rep !~ /\r\n\r\n/ && $n < 30000) { + my $c; + sysread($sock, $c, 1); + print STDERR $c; + $rep .= $c; + $n++; + } + if ($rep !~ m,HTTP/.* 200,) { + print STDERR "HTTP CONNECT failed.\n"; + close $sock; + exit(1); + } + } +} + +sub vdie { + append_handshake("done\n"); + close $sock; + kill_proxy_pids(); + exit(1); +} + +sub anontls_handshake { + my ($vmode, $db) = @_; + + print STDERR "\nPPROXY: Doing ANONTLS Handshake\n"; + + my $psec = pack("C", $rfbSecTypeAnonTls); + syswrite($sock, $psec, 1); + + append_handshake("done\n"); +} + +sub vencrypt_handshake { + + my ($vmode, $db) = @_; + + print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n"; + + my $psec = pack("C", $rfbSecTypeVencrypt); + + if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { + my $fake = $ENV{SSVNC_TEST_SEC_TYPE}; + print STDERR "PPROXY: sending sec-type: $fake\n"; + $psec = pack("C", $fake); + } + + syswrite($sock, $psec, 1); + + my $vmajor; + my $vminor; + sysread($sock, $vmajor, 1); + sysread($sock, $vminor, 1); + + vdie if $vmajor eq "" || $vminor eq ""; + + $vmajor = unpack("C", $vmajor); + $vminor = unpack("C", $vminor); + print STDERR "server vencrypt version $vmajor.$vminor\n" if $db; + + if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { + print STDERR "PPROXY: continuing on in test mode.\n"; + } else { + vdie if $vmajor ne 0; + vdie if $vminor < 2; + } + + $vmajor = pack("C", 0); + $vminor = pack("C", 2); + append_handshake("subversion=0.2\n"); + + syswrite($sock, $vmajor, 1); + syswrite($sock, $vminor, 1); + + my $result; + sysread($sock, $result, 1); + print STDERR "result empty\n" if $db && $result eq ""; + + vdie if $result eq ""; + $result = unpack("C", $result); + print STDERR "result=$result\n" if $db; + + vdie if $result ne 0; + + my $nsubtypes; + sysread($sock, $nsubtypes, 1); + + vdie if $nsubtypes eq ""; + $nsubtypes = unpack("C", $nsubtypes); + print STDERR "nsubtypes=$nsubtypes\n" if $db; + + my %subtypes; + + for (my $i = 0; $i < $nsubtypes; $i++) { + my $subtype = ""; + sysread($sock, $subtype, 4); + vdie if length($subtype) != 4; + + # XXX fix 64bit. + $subtype = unpack("N", $subtype); + print STDERR "subtype: $subtype\n" if $db; + $subtypes{$subtype} = 1; + append_handshake("sst$i=$subtype\n"); + } + + my $subtype = 0; + if (exists $subtypes{$rfbVencryptX509None}) { + $subtype = $rfbVencryptX509None; + print STDERR "selected rfbVencryptX509None\n" if $db; + } elsif (exists $subtypes{$rfbVencryptX509Vnc}) { + $subtype = $rfbVencryptX509Vnc; + print STDERR "selected rfbVencryptX509Vnc\n" if $db; + } elsif (exists $subtypes{$rfbVencryptX509Plain}) { + $subtype = $rfbVencryptX509Plain; + print STDERR "selected rfbVencryptX509Plain\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsNone}) { + $subtype = $rfbVencryptTlsNone; + print STDERR "selected rfbVencryptTlsNone\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsVnc}) { + $subtype = $rfbVencryptTlsVnc; + print STDERR "selected rfbVencryptTlsVnc\n" if $db; + } elsif (exists $subtypes{$rfbVencryptTlsPlain}) { + $subtype = $rfbVencryptTlsPlain; + print STDERR "selected rfbVencryptTlsPlain\n" if $db; + } + + if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { + my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE}; + print STDERR "PPROXY: sending sec-subtype: $fake\n"; + $subtype = $fake; + } + + append_handshake("subtype=$subtype\n"); + + my $pst = pack("N", $subtype); + syswrite($sock, $pst, 4); + + if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { + print STDERR "PPROXY: continuing on in test mode.\n"; + } else { + vdie if $subtype == 0; + } + + my $ok; + sysread($sock, $ok, 1); + $ok = unpack("C", $ok); + print STDERR "ok=$ok\n" if $db; + + append_handshake("done\n"); + + vdie if $ok == 0; +} + +sub vencrypt_dialog { + my $vmode = shift; + my $db = 0; + + $db = 1 if exists $ENV{SS_DEBUG}; + $db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG}; + + append_handshake("mode=$vmode\n"); + + my $server_rfb = ""; + #syswrite($sock, $rep, 250); + for (my $i = 0; $i < 12; $i++) { + my $c; + sysread($sock, $c, 1); + $server_rfb .= $c; + print STDERR $c; + } + print STDERR "server_rfb: $server_rfb\n" if $db; + append_handshake("server=$server_rfb"); + + my $minor = ""; + if ($server_rfb =~ /^RFB 003\.(\d+)/) { + $minor = $1; + } else { + vdie; + } + my $viewer_rfb = "RFB 003.008\n"; + if ($minor < 7) { + vdie; + } elsif ($minor == 7) { + $viewer_rfb = "RFB 003.007\n"; + } + my $nsec; + my $t1 = gettime(); + my $t0 = gettime(); + + syswrite($sock, $viewer_rfb, 12); + sysread($sock, $nsec, 1); + + $t1 = gettime(); + $t1 = sprintf("%.6f", $t1 - $t0); + + append_handshake("viewer=$viewer_rfb"); + append_handshake("latency=$t1\n"); + + vdie if $nsec eq ""; + + $nsec = unpack("C", $nsec); + + print STDERR "nsec: $nsec\n" if $db; + vdie if $nsec eq 0 || $nsec > 100; + + my %sectypes = (); + + for (my $i = 0; $i < $nsec; $i++) { + my $sec; + sysread($sock, $sec, 1); + vdie if $sec eq ""; + $sec = unpack("C", $sec); + print STDERR "sec: $sec\n" if $db; + $sectypes{$sec} = 1; + } + + if (exists $sectypes{$rfbSecTypeVencrypt}) { + print STDERR "found rfbSecTypeVencrypt\n" if $db; + append_handshake("sectype=$rfbSecTypeVencrypt\n"); + vencrypt_handshake($vmode, $db); + } elsif (exists $sectypes{$rfbSecTypeAnonTls}) { + print STDERR "found rfbSecTypeAnonTls\n" if $db; + append_handshake("sectype=$rfbSecTypeAnonTls\n"); + anontls_handshake($vmode, $db); + } else { + print STDERR "No supported sec-type found\n" if $db; + vdie; + } +} + +sub append_handshake { + my $str = shift; + if ($handshake_file) { + if (open(HSF, ">>$handshake_file")) { + print HSF $str; + close HSF; + } + } +} + +sub do_vencrypt_viewer_bridge { + my ($listen, $connect) = @_; + print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n"; + my $db = 0; + my $backwards = 0; + if ($listen < 0) { + $backwards = 1; + $listen = -$listen; + } + if ($handshake_file eq "") { + die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n"; + } + my $listen_sock; + my $maxtry = 12; + my $sleep = 5; + for (my $i=0; $i < $maxtry; $i++) { + $listen_sock = IO::Socket::INET->new( + Listen => 2, + ReuseAddr => 1, + LocalAddr => "127.0.0.1", + LocalPort => $listen, + Proto => "tcp" + ); + if (! $listen_sock) { + if ($i < $maxtry - 1) { + warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; + warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n"; + sleep $sleep; + } + } else { + last; + } + } + if (! $listen_sock) { + die "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n"; + my ($viewer_sock, $ip) = $listen_sock->accept(); + my $err = $!; + close $listen_sock; + if (! $viewer_sock) { + die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; + } + if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { + my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; + if ($sml ne "" && $sml ne "0") { + setpgrp(0, 0); + if (fork()) { + close $viewer_sock; + wait; + exit 0; + } + if (fork()) { + close $viewer_sock; + exit 0; + } + setpgrp(0, 0); + $parent = $$; + } + } + print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db; + + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n"; + my $server_sock = IO::Socket::INET->new( + PeerAddr => "127.0.0.1", + PeerPort => $connect, + Proto => "tcp" + ); + print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db; + if (! $server_sock) { + my $err = $!; + die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; + } + + if ($backwards) { + print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n"; + my $t = $viewer_sock; + $viewer_sock = $server_sock; + $server_sock = $t; + } + + my %hs = (); + my $dt = 0.2; + my $slept = 0.0; + while ($slept < 20.0) { + select(undef, undef, undef, $dt); + $slept += $dt; + if (-f $handshake_file && open(HSF, "<$handshake_file")) { + my $done = 0; + %hs = (); + my $str = ""; + while () { + print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG}; + $str .= "vencrypt_viewer_bridge[$$]: $_"; + chomp; + if ($_ eq "done") { + $done = 1; + } else { + my ($k, $v) = split(/=/, $_, 2); + if ($k ne "" && $v ne "") { + $hs{$k} = $v; + } + } + } + close HSF; + if ($done) { + print STDERR "\n" . $str; + last; + } + } + } + if (! exists $hs{server}) { + $hs{server} = "RFB 003.008"; + } + if (! exists $hs{sectype}) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n"; + } + syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1); + my $viewer_rfb = ""; + for (my $i = 0; $i < 12; $i++) { + my $c; + sysread($viewer_sock, $c, 1); + $viewer_rfb .= $c; + print STDERR $c; + } + my $viewer_major = 3; + my $viewer_minor = 8; + if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) { + $viewer_major = $1; + $viewer_minor = $2; + } + my $u0 = pack("C", 0); + my $u1 = pack("C", 1); + my $u2 = pack("C", 2); + if ($hs{sectype} == $rfbSecTypeAnonTls) { + unlink($handshake_file); + print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n"; + if ($viewer_major > 3 || $viewer_minor >= 7) { + ; # setup ok, proceed to xfer. + } else { + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; + my $n; + sysread($server_sock, $n, 1); + $n = unpack("C", $n); + if ($n == 0) { + die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n"; + } + my %types; + for (my $i = 0; $i < $n; $i++) { + my $t; + sysread($server_sock, $t, 1); + $t = unpack("C", $t); + $types{$t} = 1; + } + my $use = 1; # None + if (exists $types{1}) { + $use = 1; # None + } elsif (exists $types{2}) { + $use = 2; # VncAuth + } else { + die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n"; + } + + # send 4 bytes sectype to viewer: + # (note this should be MSB, network byte order...) + my $up = pack("C", $use); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $up, 1); + # and tell server the one we selected: + syswrite($server_sock, $up, 1); + if ($use == 1) { + # even None has security result, so read it here and discard it. + my $sr = ""; + sysread($server_sock, $sr, 4); + } + } + } elsif ($hs{sectype} == $rfbSecTypeVencrypt) { + print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n"; + if (! exists $hs{subtype}) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n"; + } + my $fake_type = "None"; + my $plain = 0; + my $sub_type = $hs{subtype}; + if ($sub_type == $rfbVencryptTlsNone) { + $fake_type = "None"; + } elsif ($sub_type == $rfbVencryptTlsVnc) { + $fake_type = "VncAuth"; + } elsif ($sub_type == $rfbVencryptTlsPlain) { + $fake_type = "None"; + $plain = 1; + } elsif ($sub_type == $rfbVencryptX509None) { + $fake_type = "None"; + } elsif ($sub_type == $rfbVencryptX509Vnc) { + $fake_type = "VncAuth"; + } elsif ($sub_type == $rfbVencryptX509Plain) { + $fake_type = "None"; + $plain = 1; + } + if ($plain) { + if (!open(W, ">$handshake_file")) { + unlink($handshake_file); + die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n"; + } + print W <<"END"; + + proc print_out {} { + global user pass env + + if [info exists env(SSVNC_UP_DEBUG)] { + toplevel .b + button .b.b -text "user=\$user pass=\$pass" -command {destroy .b} + pack .b.b + update + tkwait window .b + } + + if [info exists env(SSVNC_UP_FILE)] { + set fh "" + catch {set fh [open \$env(SSVNC_UP_FILE) w]} + if {\$fh != ""} { + puts \$fh user=\$user\\npass=\$pass + flush \$fh + close \$fh + return + } + } + puts stdout user=\$user\\npass=\$pass + flush stdout + } + + proc center_win {w} { + update + set W [winfo screenwidth \$w] + set W [expr \$W + 1] + wm geometry \$w +\$W+0 + update + set x [expr [winfo screenwidth \$w]/2 - [winfo width \$w]/2] + set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2] + + wm geometry \$w +\$x+\$y + wm deiconify \$w + update + } + + wm withdraw . + + global env + set up {} + if [info exists env(SSVNC_UNIXPW)] { + set rm 0 + set up \$env(SSVNC_UNIXPW) + if [regexp {^rm:} \$up] { + set rm 1 + regsub {^rm:} \$up {} up + } + if [file exists \$up] { + set fh "" + set f \$up + catch {set fh [open \$up r]} + if {\$fh != ""} { + gets \$fh u + gets \$fh p + close \$fh + set up "\$u@\$p" + } + if {\$rm} { + catch {file delete \$f} + } + } + } elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] { + set up \$env(SSVNC_VENCRYPT_USERPASS) + } + #puts stderr up=\$up + if {\$up != ""} { + if [regexp {@} \$up] { + global user pass + set user \$up + set pass \$up + regsub {@.*\$} \$user "" user + regsub {^[^@]*@} \$pass "" pass + print_out + exit + } + } + + wm title . {VeNCrypt Viewer Bridge User/Pass} + + set user {} + set pass {} + + label .l -text {SSVNC VeNCrypt Viewer Bridge} + + frame .f0 + frame .f0.fL + label .f0.fL.la -text {Username: } + label .f0.fL.lb -text {Password: } + + pack .f0.fL.la .f0.fL.lb -side top + + frame .f0.fR + entry .f0.fR.ea -width 24 -textvariable user + entry .f0.fR.eb -width 24 -textvariable pass -show * + + pack .f0.fR.ea .f0.fR.eb -side top -fill x + + pack .f0.fL -side left + pack .f0.fR -side right -expand 1 -fill x + + button .no -text Cancel -command {destroy .} + button .ok -text Done -command {print_out; destroy .} + + center_win . + pack .l .f0 .no .ok -side top -fill x + update + wm deiconify . + + bind .f0.fR.ea {focus .f0.fR.eb} + bind .f0.fR.eb {print_out; destroy .} + focus .f0.fR.ea + + wm resizable . 1 0 + wm minsize . [winfo reqwidth .] [winfo reqheight .] +END + close W; + + #system("cat $handshake_file"); + my $w = "wish"; + if ($ENV{WISH}) { + $w = $ENV{WISH}; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt VencryptPlain user and passwd.\n"; + my $res = ""; + if (`uname` =~ /Darwin/) { + my $mtmp = `mktemp /tmp/hsup.XXXXXX`; + chomp $mtmp; + system("env SSVNC_UP_FILE=$mtmp $w $handshake_file"); + $res = `cat $mtmp`; + unlink $mtmp; + } else { + $res = `$w $handshake_file`; + } + my $user = ""; + my $pass = ""; + if ($res =~ /user=(\S*)/) { + $user = $1; + } + if ($res =~ /pass=(\S*)/) { + $pass = $1; + } + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n"; + my $ulen = pack("C", length($user)); + my $plen = pack("C", length($pass)); + # (note this should be MSB, network byte order...) + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $ulen, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $u0, 1); + syswrite($server_sock, $plen, 1); + syswrite($server_sock, $user, length($user)); + syswrite($server_sock, $pass, length($pass)); + } + unlink($handshake_file); + + my $ft = 0; + if ($fake_type eq "None") { + $ft = 1; + } elsif ($fake_type eq "VncAuth") { + $ft = 2; + } else { + die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n"; + } + my $fp = pack("C", $ft); + if ($viewer_major > 3 || $viewer_minor >= 7) { + syswrite($viewer_sock, $u1, 1); + syswrite($viewer_sock, $fp, 1); + my $cr; + sysread($viewer_sock, $cr, 1); + $cr = unpack("C", $cr); + if ($cr != $ft) { + die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n"; + } + } else { + print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; + # send 4 bytes sect type to viewer: + # (note this should be MSB, network byte order...) + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $u0, 1); + syswrite($viewer_sock, $fp, 1); + if ($ft == 1) { + # even None has security result, so read it here and discard it. + my $sr = ""; + sysread($server_sock, $sr, 4); + } + } + } + + $listen_handle = $viewer_sock; + $sock = $server_sock; + + xfer_both(); +} +' + # ' + # xpg_echo will expand \n \r, etc. + # try to unset and then test for it. + if type shopt > /dev/null 2>&1; then + shopt -u xpg_echo >/dev/null 2>&1 + fi + v='print STDOUT "abc\n";' + echo "$v" > $tf + chmod 700 $tf + + lc=`wc -l $tf | awk '{print $1}'` + if [ "X$lc" = "X1" ]; then + echo "$cod" > $tf + else + printf "%s" "$cod" > $tf + echo "" >> $tf + fi + # prime perl + perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1 +} + +# make_tcert is no longer invoked via the ssvnc gui (Listen mode). +# make_tcert is for testing only now via -mycert BUILTIN +make_tcert() { + tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$" + tcert=`mytmp "$tcert"` + cat > $tcert < Advanced -> Private SSH KnownHosts file' (or set" + echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts" + echo "** Warning: file. That file holds the 'localhost' cert for this specific" + echo "** Warning: connection. This yields a both secure and convenient solution." + echo "" +} + +space_expand() { + str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'` + echo "$str" +} + +# handle ssh case: +# +if [ "X$use_ssh" = "X1" ]; then + # + # USING SSH + # + ssh_port="22" + ssh_host="$host" + vnc_host="$localhost" + ssh_UKHF="" + localhost_extra="" + # let user override ssh via $SSH + ssh=${SSH:-"ssh -x"} + + sshword=`echo "$ssh" | awk '{print $1}'` + if [ "X$sshword" != "X" ]; then + if [ -x "$sshword" ]; then + : + elif type "$sshword" > /dev/null 2>&1; then + : + else + echo "" + echo "*********************************************************" + echo "** Problem finding the SSH command '$sshword': **" + echo "" + type "$sshword" + echo "" + echo "** Perhaps you need to install the SSH client package. **" + echo "*********************************************************" + echo "" + sleep 5 + fi + fi + + ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes" + if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then + ssh_NHAFL="" + fi + if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then + ssh_NHAFL="" + + ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE" + ssh_args="$ssh_args $ssh_UKHF" + if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then + touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 + fi + chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 + fi + did_ssh_NHAFL="" + + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then + SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD" + fi + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then + echo "" + echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD" + fi + + if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then + plvar=LD_PRELOAD + if uname | grep Darwin >/dev/null; then + plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES" + fi + ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh" + else + SSVNC_LIM_ACCEPT_PRELOAD="" + fi + + ssh_vencrypt_proxy="" + # We handle vencrypt for SSH+SSL mode. + if echo "$proxy" | grep 'vencrypt://' > /dev/null; then + proxynew="" + for part in `echo "$proxy" | tr ',' ' '` + do + if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then + ssh_vencrypt_proxy=$part + else + if [ "X$proxynew" = "X" ]; then + proxynew="$part" + else + proxynew="$proxynew,$part" + fi + fi + done + proxy=$proxynew + fi + Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy + + # note that user must supply http:// for web proxy in SSH and SSH+SSL. + # No xxxx:// implies ssh server+port. + # + if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then + # Handle Web or SOCKS proxy(ies) for the initial connect. + Kecho host=$host + Kecho port=$port + pproxy="" + sproxy1="" + sproxy_rest="" + for part in `echo "$proxy" | tr ',' ' '` + do + Kecho proxy_part=$part + if [ "X$part" = "X" ]; then + continue + elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then + pproxy="$pproxy,$part" + else + if [ "X$sproxy1" = "X" ]; then + sproxy1="$part" + else + sproxy_rest="$sproxy_rest,$part" + fi + fi + done + pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'` + sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'` + + Kecho pproxy=$pproxy + Kecho sproxy1=$sproxy1 + Kecho sproxy_rest=$sproxy_rest + + sproxy1_host="" + sproxy1_port="" + sproxy1_user="" + + if [ "X$sproxy1" != "X" ]; then + # XXX fix ipv6 ip adder here and below. + sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'` + sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` + sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` + if [ "X$sproxy1_host" = "X" ]; then + sproxy1_host=$sproxy1_user + sproxy1_user="" + else + sproxy1_user="${sproxy1_user}@" + fi + sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'` + if [ "X$sproxy1_port" = "X" ]; then + sproxy1_port="22" + fi + else + sproxy1_host=`echo "$host" | awk -F: '{print $1}'` + sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` + sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` + if [ "X$sproxy1_host" = "X" ]; then + sproxy1_host=$sproxy1_user + sproxy1_user="" + else + sproxy1_user="${sproxy1_user}@" + fi + sproxy1_port=`echo "$host" | awk -F: '{print $2}'` + if [ "X$sproxy1_port" = "X" ]; then + sproxy1_port="22" + fi + fi + + Kecho sproxy1_host=$sproxy1_host + Kecho sproxy1_port=$sproxy1_port + Kecho sproxy1_user=$sproxy1_user + + ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl" + ptmp=`mytmp "$ptmp"` + PPROXY_REMOVE=1; export PPROXY_REMOVE + proxy=$pproxy + port_save=$port + host_save=$host + if [ "X$sproxy1_host" != "X" ]; then + host=$sproxy1_host + fi + if [ "X$sproxy1_port" != "X" ]; then + port=$sproxy1_port + fi + host=`echo "$host" | sed -e 's/^.*@//'` + port=`echo "$port" | sed -e 's/^.*://'` + pcode "$ptmp" + port=$port_save + host=$host_save + + nd=`findfree 6600` + PPROXY_LISTEN=$nd; export PPROXY_LISTEN + # XXX no reverse forever PPROXY_LOOP_THYSELF ... + $ptmp & + sleep 1 + if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then + NHAFL_warning + ssh_args="$ssh_args $ssh_NHAFL" + did_ssh_NHAFL=1 + fi + sleep 1 + if [ "X$sproxy1" = "X" ]; then + u="" + if echo "$host" | grep '@' > /dev/null; then + u=`echo "$host" | sed -e 's/@.*$/@/'` + fi + + proxy="${u}$localhost:$nd" + else + proxy="${sproxy1_user}$localhost:$nd" + fi + localhost_extra=".2" + if [ "X$sproxy_rest" != "X" ]; then + proxy="$proxy,$sproxy_rest" + fi + Kecho proxy=$proxy + fi + + if echo "$proxy" | grep "," > /dev/null; then + + proxy1=`echo "$proxy" | awk -F, '{print $1}'` + proxy2=`echo "$proxy" | awk -F, '{print $2}'` + + # user1@gw1.com:port1,user2@ws2:port2 + ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'` + ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'` + if [ "X$ssh_port1" != "X" ]; then + ssh_port1="-p $ssh_port1" + fi + ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'` + ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'` + ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'` + if [ "X$ssh_host2" = "X" ]; then + ssh_host2=$ssh_user2 + ssh_user2="" + else + ssh_user2="${ssh_user2}@" + fi + ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'` + if [ "X$ssh_port2" = "X" ]; then + ssh_port2="22" + fi + proxport=`findfree 3500` + if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then + NHAFL_warning + did_ssh_NHAFL=1 + sleep 1 + fi + echo + echo "Running 1st ssh proxy:" + ukhf="" + if [ "X$ssh_UKHF" != "X" ]; then + ukhf="$ssh_UKHF$localhost_extra" + fi + if echo "$ssh_host1" | grep '%' > /dev/null; then + uath=`space_expand "$ssh_host1"` + else + uath="$ssh_host1" + fi + echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\"" + echo "" + $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30" + ssh_args="$ssh_args $ssh_NHAFL" + sleep 1 + stty sane + proxy="${ssh_user2}$localhost:$proxport" + fi + + if [ "X$proxy" != "X" ]; then + ssh_port=`echo "$proxy" | awk -F: '{print $2}'` + if [ "X$ssh_port" = "X" ]; then + ssh_port="22" + fi + ssh_host=`echo "$proxy" | awk -F: '{print $1}'` + vnc_host="$host" + fi + + echo "" + echo "Running ssh:" + sz=`echo "$ssh_cmd" | wc -c` + if [ "$sz" -gt 300 ]; then + info="..." + else + info="$ssh_cmd" + fi + + C="" + if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then + C="-C" + fi + + getport="" + teeport="" + if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then + getport=1 + if echo "$ssh_cmd" | egrep "P= " > /dev/null; then + teeport=1 + fi + + PORT="" + ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ ]*//' -e 's/P=//'` + SSVNC_NO_ENC_WARN=1 + if [ "X$use_sshssl" = "X" ]; then + direct_connect=1 + fi + fi + if [ "X$getport" != "X" ]; then + ssh_redir="-D ${use}" + elif [ "X$reverse" = "X" ]; then + ssh_redir="-L ${use}:${vnc_host}:${port}" + else + ssh_redir="-R ${port}:${vnc_host}:${use}" + fi + pmark=`sh -c 'echo $$'` + + # the -t option actually speeds up typing response via VNC!! + if [ "X$ssh_port" = "X22" ]; then + ssh_port="" + else + ssh_port="-p $ssh_port" + fi + + if echo "$ssh_host" | grep '%' > /dev/null; then + uath=`space_expand "$ssh_host"` + else + uath="$ssh_host" + fi + if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then + echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd" + exit $? + + elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then + echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" + rc=$? + + elif [ "X$getport" != "X" ]; then + tport=/tmp/ss_vncviewer_tport${RANDOM}.$$ + tport=`mytmp "$tport"` + tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$ + tport2=`mytmp "$tport2"` + + if [ "X$rsh" != "X1" ]; then + if echo "$ssh_cmd" | grep "sudo " > /dev/null; then + echo "" + echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one" + echo "will require no password..." + echo "" + targ="-t" + $ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty" + echo "" + fi + echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2 + if [ "X$teeport" = "X1" ]; then + tail -f $tport 1>&2 & + tail_pid=$! + tail -f $tport2 1>&2 & + tail_pid2=$! + fi + rc=$? + else + rsh_setup + echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" + echo "" + rsh $ul "$ssh_host" "$ssh_cmd" > $tport & + sleep 1 + rc=0 + fi + + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + + stty sane + i=0 + if type perl > /dev/null 2>&1; then + imax=50 + sleepit="perl -e 'select(undef, undef, undef, 0.20)'" + else + imax=10 + sleepit="sleep 1" + fi + while [ $i -lt $imax ]; do + #echo $sleepit + eval $sleepit + PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'` + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + break + fi + vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'` + if [ "X$vnss" != "X" ]; then + PORT=`echo "$vnss" | awk -F: '{print $2}'` + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + if [ $PORT -lt 100 ]; then + PORT=`expr $PORT + 5900` + fi + fi + if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then + vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1` + echo "vncserver string: $vnss" 1>&2 + break + fi + fi + i=`expr $i + 1` + done + + echo "found: PORT='$PORT'" 1>&2 + lh6="" + if [ "X$SSVNC_PORT_IPV6" != "X" ]; then + lh6=1 + elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then + lh6=1 + fi + if [ "X$lh6" = "X1" ]; then + echo "set SOCKS5 localhost to ::1" 1>&2 + fi + rm -f $tport $tport2 + if [ "X$rsh" = "X1" ]; then + rsh_viewer "$@" + exit $? + fi + PPROXY_SOCKS=5 + if [ "X$SSVNC_SOCKS5" != "X" ]; then + PPROXY_SOCKS=5 + elif [ "X$SSVNC_SOCKS4" != "X" ]; then + PPROXY_SOCKS=1 + fi + export PPROXY_SOCKS + if [ "X$lh6" = "X" ]; then + host="$localhost" + else + host="::1" + fi + port="$PORT" + proxy="$localhost:$use" + + else + if [ "X$rsh" != "X1" ]; then + echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" + echo "" + $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" + rc=$? + else + rsh_setup + echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" + echo "" + rsh $ul "$ssh_host" "$ssh_cmd" & + sleep 1 + PORT=$port + rsh_viewer "$@" + exit $? + fi + fi + + if [ "$rc" != "0" ]; then + echo "" + echo "ssh to \"$uath\" failed." + exit 1 + fi + stty sane + + c=0 + pssh="" + while [ $c -lt 40 ] + do + p=`expr $pmark + $c` + pout=`ps -p "$p" 2>/dev/null | grep -v '^[ ]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'` + if echo "$pout" | grep "ssh" > /dev/null; then + if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then + : + elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then + : + else + pssh=$p + break + fi + fi + c=`expr $c + 1` + done + if [ "X$getport" != "X" ]; then + : + elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then + sleep 2 + elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then + #echo T sleep 1 + sleep 1 + elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then + #echo T sleep 2 + sleep 2 + else + # let any command get started a bit. + #echo T sleep 5 + sleep 5 + fi + echo "" + #reset + stty sane + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + echo "ssh_pid='$pssh'"; echo + if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then + if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo + fi + echo "Running viewer:" + + trap "final" 0 2 15 + if [ "X$reverse" = "X" ]; then + echo "$VNCVIEWERCMD" "$@" $localhost:$N + echo "" + $VNCVIEWERCMD "$@" $localhost:$N + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" $localhost:$N + fi + fi + else + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + N2=$N + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + N2=`echo "$N2" | sed -e 's/://g'` + if [ $N2 -le 200 ]; then + N2=`expr $N2 + 5500` + fi + fi + echo "$VNCVIEWERCMD" "$@" -listen $N2 + echo "" + $VNCVIEWERCMD "$@" -listen $N2 + fi + + exit $? + else + use2=`findfree 5960` + host0=$host + port0=$port + host=$localhost + port=$use + use=$use2 + N=`expr $use - 5900` + if [ "X$getport" != "X" ]; then + host="$host0" + port="$port0" + else + proxy="" + fi + if [ "X$ssh_vencrypt_proxy" != "X" ]; then + ssh_vencrypt_proxy="vencrypt://$host:$port" + if [ "X$proxy" = "X" ]; then + proxy=$ssh_vencrypt_proxy + else + proxy="$proxy,$ssh_vencrypt_proxy" + fi + Kecho "proxy_now=$proxy" + unset PPROXY_LISTEN + fi + fi +fi + +if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then + if type $STUNNEL > /dev/null 2>&1; then + : + else + echo "" + echo "***************************************************************" + echo "** Problem finding the Stunnel command '$STUNNEL': **" + echo "" + type $STUNNEL + echo "" + echo "** Perhaps you need to install the stunnel/stunnel4 package. **" + echo "***************************************************************" + echo "" + sleep 5 + fi +fi + +# create the stunnel config file: +if [ "X$verify" != "X" ]; then + if [ -d $verify ]; then + verify="CApath = $verify" + else + verify="CAfile = $verify" + fi + verify="$verify +verify = 2" +fi +if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then + verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'` +fi +if [ "X$mycert" != "X" ]; then + cert="cert = $mycert" +fi +if [ "X$crl" != "X" ]; then + if [ -d $crl ]; then + crl="CRLpath = $crl" + else + crl="CRLfile = $crl" + fi +fi + +if [ "X$showcert" = "X1" ]; then + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + : + elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then + : + elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then + proxy="ipv6://$host:$port" + fi +fi + +if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then + proxy=reverse_direct +fi + +ptmp="" +if [ "X$proxy" != "X" ]; then + ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl" + ptmp=`mytmp "$ptmp"` + PPROXY_REMOVE=1; export PPROXY_REMOVE + pcode "$ptmp" + if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then + if uname | egrep 'Darwin|SunOS' >/dev/null; then + vout=`echo "$proxy" | grep -i vencrypt` + if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then + # need to exec for reverse vencrypt + connect="exec = $ptmp" + else + # on mac and solaris we need to listen on socket instead of stdio: + nd=`findfree 6700` + PPROXY_LISTEN=$nd + export PPROXY_LISTEN + if [ "X$reverse" = "X" ]; then + $ptmp & + fi + sleep 2 + host="$localhost" + port="$nd" + connect="connect = $localhost:$nd" + fi + else + # otherwise on unix we can exec it: + connect="exec = $ptmp" + fi + else + connect="exec = $ptmp" + fi +else + connect="connect = $host:$port" +fi + +# handle showcert case: +# +if [ "X$showcert" = "X1" ]; then + if [ "X$proxy" != "X" ]; then + PPROXY_LISTEN=$use + export PPROXY_LISTEN + if [ "X$SS_DEBUG" != "X" ]; then + $ptmp & + else + $ptmp 2>/dev/null & + fi + sleep 1 + more_sleep=1 + if uname | grep Linux > /dev/null; then + if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then + more_sleep="" + fi + elif uname | grep SunOS > /dev/null; then + if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then + more_sleep="" + fi + elif uname | egrep -i 'bsd|darwin' > /dev/null; then + if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then + more_sleep="" + fi + fi + if [ "X$more_sleep" = "X1" ]; then + sleep 1 + fi + host="$localhost" + port="$use" + fi + cipher_args="" + if [ "X$ciphers" != "X" ]; then + cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'` + fi + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + : + elif type openssl > /dev/null 2>&1; then + : + else + echo "" + echo "********************************************************" + echo "** Problem finding the OpenSSL command 'openssl': **" + echo "" + type openssl 2>&1 + echo "" + echo "** Perhaps you need to install the 'openssl' package. **" + echo "********************************************************" + echo "" + fi + #echo "openssl s_client $cipher_args -connect $host:$port" + if [ "X$reverse" = "X" ]; then + if type host > /dev/null 2>/dev/null; then + host $host >/dev/null 2>&1 + host $host >/dev/null 2>&1 + fi + timeout=15 + if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then + timeout=$SSVNC_FETCH_TIMEOUT + fi + if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then + if type pkill >/dev/null 2>&1; then + (sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 & + fi + ultravnc_dsm_helper showcert $host:$port 2>&1 + else + if type pkill >/dev/null 2>&1; then + (sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 & + fi + openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null + fi + rc=$? + else + tcert="" + if [ "X$mycert" = "X" ]; then + tcert=`make_tcert` + cert_args="-cert $tcert -CAfile $tcert" + else + cert_args="-cert $mycert -CAfile $mycert" + fi + tmp_out=/tmp/showcert_out${RANDOM}.$$ + tmp_out=`mytmp "$tmp_out"` + tmp_err=/tmp/showcert_err${RANDOM}.$$ + tmp_err=`mytmp "$tmp_err"` + + #echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2 + + # assume we have perl: + check_perl perl + + perl -e " + \$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\"); + exit 1 unless \$p; + while (1) { + sleep 1; + if (!open(F, \"<$tmp_out\")) { + kill \$p; + exit 1; + } + while () { + if (/RFB 00/) { + fsleep(0.25); + print O \"RFB 000.000\\n\"; + fsleep(1.00); + kill \$p; + fsleep(0.25); + exit 0; + } + } + close F; + } + sub fsleep { + select(undef, undef, undef, shift); + } + "; + + echo "" + cat $tmp_out + echo "" + echo "----2----" + cat $tmp_err + if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then + rc=0 + else + rc=1 + fi + + rm -f $tmp_out $tmp_err + fi + if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then + rm -f $SSVNC_PREDIGESTED_HANDSHAKE + fi + if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then + exit 0 + else + exit $rc + fi +fi + +# handle direct connect case: +# +if [ "X$direct_connect" != "X" ]; then + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + SSVNC_NO_ENC_WARN=1 + echo "" + echo "Using UltraVNC DSM Plugin key for encryption:" + echo "" + ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo " $ustr PORT HOST:PORT" + echo "" + elif [ "X$getport" = "X" ]; then + echo "" + echo "Running viewer for direct connection:" + if echo X"$@" | grep chatonly > /dev/null; then + : + else + echo "" + echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **" + echo "" + fi + fi + x="" + if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then + if [ "X$getport" = "X" ]; then + sleep 1 + fi + elif type printf > /dev/null 2>&1; then + printf "Are you sure you want to continue? [y]/n " + read x + else + echo -n "Are you sure you want to continue? [y]/n " + read x + fi + if [ "X$x" = "Xn" ]; then + exit 1 + fi + echo "" + if [ "X$ptmp" != "X" ]; then + if [ "X$reverse" = "X" ]; then + PPROXY_LISTEN=$use + export PPROXY_LISTEN + else + if [ "X$proxy" = "Xreverse_direct" ]; then + PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`" + PPROXY_DEST="$localhost:$use" + PPROXY_PROXY="ipv6://$localhost:$use" # not always ipv6.. + export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY + pps=1 + else + PPROXY_REVERSE="$localhost:$use" + export PPROXY_LISTEN + pps=3 + fi + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + pps=2 + fi + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + pps=`expr $pps + $SSVNC_EXTRA_SLEEP` + fi + PPROXY_SLEEP=$pps; export PPROXY_SLEEP; + PPROXY_KILLPID=+1; export PPROXY_KILLPID; + fi + + $ptmp & + + if [ "X$reverse" = "X" ]; then + #sleep 2 + #echo T sleep 1 + sleep 1 + fi + host="$localhost" + disp="$N" + port=`expr $disp + 5900` + fi + if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "T sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP + fi + if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo + fi + if [ "X$reverse" = "X" ]; then + hostdisp="$host:$disp" + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if [ "X$SSVNC_USE_OURS" = "X1" ]; then + hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port" + else + pf=`findfree 5970` + cmd="$SSVNC_ULTRA_DSM -$pf $host:$port" + pf=`expr $pf - 5900` + hostdisp="$localhost:$pf" + ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "Running:" + echo + echo "$ustr &" + echo + $cmd & + dsm_pid=$! + sleep 2 + fi + fi + hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "$VNCVIEWERCMD" "$@" "$hostdisp2" + trap "final" 0 2 15 + echo "" + $VNCVIEWERCMD "$@" "$hostdisp" + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" "$hostdisp" + fi + fi + else + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + trap "final" 0 2 15 + if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then + if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then + echo "NOTE: The ultravnc_dsm_helper only runs once. So after the first LISTEN" + echo " ends you must restart the Listening mode. You may also need to" + echo " Press Ctrl-C to stop the viewer and restart for another connection." + echo "" + fi + #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE + VNCVIEWER_LISTEN_LOCALHOST=1 + export VNCVIEWER_LISTEN_LOCALHOST + dport=`expr 5500 + $disp` + cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use" + ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` + echo "Running:" + echo + echo "$ustr &" + echo + if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then + $cmd & + dsm_pid=$! + else + while [ 1 ]; do $cmd; sleep 1; done & + dsm_pid=$! + fi + sleep 2 + disp=$use + if [ $disp -ge 5500 ]; then + disp=`expr $disp - 5500` + fi + fi + disp2=$disp + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + disp2=`echo "$disp2" | sed -e 's/://g'` + if [ $disp2 -le 200 ]; then + disp2=`expr $disp2 + 5500` + fi + fi + echo "$VNCVIEWERCMD" "$@" -listen $disp2 + echo "" + $VNCVIEWERCMD "$@" -listen $disp2 + if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF + fi + fi + exit $? +fi + +tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$ +tmp_cfg=`mytmp "$tmp_cfg"` + +stunnel_exec="" +if [ "X$SSVNC_USE_OURS" != "X1" ]; then + : +elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then + stunnel_exec="#" +fi + +if [ "X$reverse" = "X" ]; then + + if echo "$proxy" | grep "^repeater://" > /dev/null; then + if [ "X$cert" = "XBUILTIN" ]; then + ttcert=`make_tcert` + cert="cert = $ttcert" + fi + # Note for listen mode, an empty cert will cause stunnel to fail. + # The ssvnc gui will have already taken care of this. + fi + + cat > "$tmp_cfg" < /dev/null; then + hloc="$localhost:" + pv=`findfree 5570` + proxy="vencrypt:$pv:$port" + port=$pv + if [ "X$anondh_set" = "X1" ]; then + # not needed for ANONDH in this mode + #ciphers="ciphers = ADH:@STRENGTH" + : + fi + fi + cat > "$tmp_cfg" < /dev/null 2>&1 + $STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty & + stunnel_pid=$! + echo "" + + # pause here to let the user supply a possible passphrase for the + # mycert key: + if [ "X$mycert" != "X" ]; then + nsl=10 + dsl=0 + if [ ! -f $mycert ]; then + dsl=0 + elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then + dsl=1 + fi + if [ "X$dsl" = "X1" ]; then + echo "" + echo "(** pausing $nsl secs for possible certificate passphrase dialog **)" + echo "" + sleep $nsl + echo "(** done pausing for passphrase **)" + echo "" + fi + fi + #echo T sleep 1 + sleep 1 + rm -f "$tmp_cfg" +fi + + +echo "" +if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then + echo "sleep $SSVNC_EXTRA_SLEEP" + sleep $SSVNC_EXTRA_SLEEP +fi +if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then + (sh -c "$SSVNC_EXTRA_COMMAND") & + echo "($SSVNC_EXTRA_COMMAND) &"; echo +fi + +if [ "X$reverse" = "X" ]; then + if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then + port1=`expr 5900 + $N` # stunnel port + port2=`findfree 5970` # bridge port (viewer connects to it.) + N=`expr $port2 - 5900` + env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp & + sleep 1 + fi + echo "Running viewer:" + vnc_hp=$localhost:$N + if [ "X$stunnel_exec" != "X" ]; then + vnc_hp="exec=$STUNNEL $tmp_cfg" + fi + echo "$VNCVIEWERCMD" "$@" "$vnc_hp" + trap "final" 0 2 15 + echo "" + $VNCVIEWERCMD "$@" "$vnc_hp" + if [ $? != 0 ]; then + echo "vncviewer command failed: $?" + if [ "X$secondtry" = "X1" ]; then + sleep 2 + $VNCVIEWERCMD "$@" "$vnc_hp" + fi + fi +else + echo "Running viewer:" + echo "" + echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." + echo "" + trap "final" 0 2 15 + N2=$N + N2_trim=`echo "$N2" | sed -e 's/://g'` + if [ $N2_trim -le 200 ]; then + N2_trim=`expr $N2_trim + 5500` + fi + if [ "X$proxy" != "X" ]; then + if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then + pstunnel=`echo "$proxy" | awk -F: '{print $2}'` + plisten=`echo "$proxy" | awk -F: '{print $3}'` + IF=INADDR_ANY + if [ "X$STUNNEL_LISTEN" != "X" ]; then + IF=$STUNNEL_LISTEN + fi + PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE + PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN + PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY + PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST + STUNNEL_ONCE=1; export STUNNEL_ONCE + STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS + if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then + port1=`expr 5500 + $N2` + port2=`findfree 5580` + N2=`expr $port2 - 5500` + N2_trim=`echo "$N2" | sed -e 's/://g'` + if [ $N2_trim -le 200 ]; then + N2_trim=`expr $N2_trim + 5500` + fi + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF + fi + env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp & + sleep 1 + fi + else + PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE + PPROXY_SLEEP=1; export PPROXY_SLEEP; + fi + PPROXY_KILLPID=+1; export PPROXY_KILLPID; + if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then + PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"` + export PPROXY_LOOP_THYSELF + fi + $ptmp & + # Important to have no extra pids generated between here and VNCVIEWERCMD + fi + if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then + N2=$N2_trim + fi + echo "$VNCVIEWERCMD" "$@" -listen $N2 + echo "" + $VNCVIEWERCMD "$@" -listen $N2 + + if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF + fi + if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then + rm -f $PPROXY_LOOP_THYSELF0 + fi +fi + +sleep 1 diff --git a/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch new file mode 100644 index 0000000..bc10f3c --- /dev/null +++ b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch @@ -0,0 +1,111 @@ +--- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400 ++++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500 +@@ -28,13 +28,14 @@ + import java.lang.*; + import java.util.zip.*; + ++import java.util.Collections; + + // + // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. + // + + class VncCanvas extends Canvas +- implements KeyListener, MouseListener, MouseMotionListener { ++ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { + + VncViewer viewer; + RfbProto rfb; +@@ -81,6 +82,20 @@ + cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); + cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); + ++ // 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); ++ + colors = new Color[256]; + for (int i = 0; i < 256; i++) + colors[i] = new Color(cm8.getRGB(i)); +@@ -169,6 +184,7 @@ + inputEnabled = true; + addMouseListener(this); + addMouseMotionListener(this); ++ addMouseWheelListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(true); + } +@@ -177,6 +193,7 @@ + inputEnabled = false; + removeMouseListener(this); + removeMouseMotionListener(this); ++ removeMouseWheelListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(false); + } +@@ -1190,6 +1207,9 @@ + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } ++ public void mouseWheelMoved(MouseWheelEvent evt) { ++ processLocalMouseWheelEvent(evt); ++ } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { +@@ -1221,6 +1241,19 @@ + evt.consume(); + } + ++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { ++ if (viewer.rfb != null && rfb.inNormalProtocol) { ++ synchronized(rfb) { ++ try { ++ rfb.writeWheelEvent(evt); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ rfb.notify(); ++ } ++ } ++ } ++ + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { +@@ -1387,9 +1420,9 @@ + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 | +- (pixBuf[i * 4 + 1] & 0xFF) << 16 | +- (pixBuf[i * 4 + 2] & 0xFF) << 8 | +- (pixBuf[i * 4 + 3] & 0xFF); ++ (pixBuf[i * 4 + 2] & 0xFF) << 16 | ++ (pixBuf[i * 4 + 1] & 0xFF) << 8 | ++ (pixBuf[i * 4 + 0] & 0xFF); + } + } else { + result = 0; // Transparent pixel +@@ -1403,9 +1436,9 @@ + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 | +- (pixBuf[i * 4 + 1] & 0xFF) << 16 | +- (pixBuf[i * 4 + 2] & 0xFF) << 8 | +- (pixBuf[i * 4 + 3] & 0xFF); ++ (pixBuf[i * 4 + 2] & 0xFF) << 16 | ++ (pixBuf[i * 4 + 1] & 0xFF) << 8 | ++ (pixBuf[i * 4 + 0] & 0xFF); + } + } else { + result = 0; // Transparent pixel diff --git a/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch new file mode 100644 index 0000000..801234a --- /dev/null +++ b/webclients/java-applet/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch @@ -0,0 +1,2600 @@ +diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile +--- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 ++++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400 +@@ -4,6 +4,7 @@ + + CP = cp + JC = javac ++JC_ARGS = -target 1.4 -source 1.4 + JAR = jar + ARCHIVE = VncViewer.jar + MANIFEST = MANIFEST.MF +@@ -15,25 +16,29 @@ + DesCipher.class CapabilityInfo.class CapsContainer.class \ + RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \ + SocketFactory.class HTTPConnectSocketFactory.class \ +- HTTPConnectSocket.class ReloginPanel.class ++ HTTPConnectSocket.class ReloginPanel.class \ ++ SSLSocketToMe.class ++ ++SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class + + SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ + OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ + DesCipher.java CapabilityInfo.java CapsContainer.java \ + RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \ + SocketFactory.java HTTPConnectSocketFactory.java \ +- HTTPConnectSocket.java ReloginPanel.java ++ HTTPConnectSocket.java ReloginPanel.java \ ++ SSLSocketToMe.java + + all: $(CLASSES) $(ARCHIVE) + + $(CLASSES): $(SOURCES) +- $(JC) -target 1.1 -O $(SOURCES) ++ $(JC) $(JC_ARGS) -O $(SOURCES) + + $(ARCHIVE): $(CLASSES) $(MANIFEST) +- $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) ++ $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES) + + install: $(CLASSES) $(ARCHIVE) +- $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) ++ $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) + + export:: $(CLASSES) $(ARCHIVE) $(PAGES) + @$(ExportJavaClasses) +diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java +--- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500 ++++ vnc_javasrc/RfbProto.java 2010-11-30 22:05:12.000000000 -0500 +@@ -199,7 +199,21 @@ + host = h; + port = p; + +- if (viewer.socketFactory == null) { ++ 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 if (viewer.socketFactory == null) { + sock = new Socket(host, port); + } else { + try { +@@ -255,7 +269,7 @@ + || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) + { + throw new Exception("Host " + host + " port " + port + +- " is not an RFB server"); ++ " is not an RFB server: " + b); + } + + serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); +@@ -892,6 +906,38 @@ + final static int ALT_MASK = InputEvent.ALT_MASK; + + ++ void writeWheelEvent(MouseWheelEvent evt) throws IOException { ++ ++ eventBufLen = 0; ++ ++ int x = evt.getX(); ++ int y = evt.getY(); ++ ++ if (x < 0) x = 0; ++ if (y < 0) y = 0; ++ ++ int ptrmask; ++ ++ int clicks = evt.getWheelRotation(); ++ System.out.println("writeWheelEvent: clicks: " + clicks); ++ if (clicks > 0) { ++ ptrmask = 16; ++ } else if (clicks < 0) { ++ ptrmask = 8; ++ } else { ++ return; ++ } ++ ++ eventBuf[eventBufLen++] = (byte) PointerEvent; ++ eventBuf[eventBufLen++] = (byte) ptrmask; ++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (x & 0xff); ++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (y & 0xff); ++ ++ os.write(eventBuf, 0, eventBufLen); ++ } ++ + // + // Write a pointer event message. We may need to send modifier key events + // around it to set the correct modifier state. +@@ -992,6 +1038,19 @@ + boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); + + int key; ++ if (viewer.debugKeyboard) { ++ System.out.println("----------------------------------------"); ++ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); ++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); ++ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); ++ System.out.println("evt.getID: " + evt.getID()); ++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); ++ System.out.println("evt.isActionKey: " + evt.isActionKey()); ++ System.out.println("evt.isControlDown: " + evt.isControlDown()); ++ System.out.println("evt.getModifiers: " + evt.getModifiers()); ++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); ++ System.out.println("evt.paramString: " + evt.paramString()); ++ } + if (evt.isActionKey()) { + + // +@@ -1025,6 +1084,13 @@ + return; + } + ++ if(key == 0xffc2 && viewer.mapF5_to_atsign) { ++ if (viewer.debugKeyboard) { ++ System.out.println("Mapping: F5 -> AT "); ++ } ++ key = 0x40; ++ } ++ + } else { + + // +@@ -1036,6 +1102,7 @@ + + key = keyChar; + ++ + if (key < 0x20) { + if (evt.isControlDown()) { + key += 0x60; +@@ -1121,6 +1188,16 @@ + int oldModifiers = 0; + + void writeModifierKeyEvents(int newModifiers) { ++ if(viewer.forbid_Ctrl_Alt) { ++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { ++ int orig = newModifiers; ++ newModifiers &= ~ALT_MASK; ++ newModifiers &= ~CTRL_MASK; ++ if (viewer.debugKeyboard) { ++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); ++ } ++ } ++ } + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + +diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java +--- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 ++++ vnc_javasrc/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 +@@ -0,0 +1,2067 @@ ++/* ++ * 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, 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. ++ * ++ */ ++ ++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; ++ boolean debug_certs = false; ++ ++ /* sockets */ ++ SSLSocket socket = null; ++ SSLSocketFactory factory; ++ ++ /* fallback for Proxy connection */ ++ boolean proxy_in_use = false; ++ boolean proxy_failure = false; ++ public DataInputStream is = null; ++ public OutputStream os = null; ++ ++ /* strings from user WRT proxy: */ ++ String proxy_auth_string = 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 trustsrv_ctx; ++ SSLContext trusturl_ctx; ++ SSLContext trustone_ctx; ++ ++ /* corresponding trust managers */ ++ TrustManager[] trustAllCerts; ++ TrustManager[] trustSrvCert; ++ TrustManager[] trustUrlCert; ++ TrustManager[] trustOneCert; ++ ++ /* client-side SSL auth key (oneTimeKey=...) */ ++ KeyManager[] mykey = null; ++ ++ boolean user_wants_to_see_cert = true; ++ String cert_fail = null; ++ ++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ ++ java.security.cert.Certificate[] trustallCerts = null; ++ java.security.cert.Certificate[] trustsrvCerts = null; ++ java.security.cert.Certificate[] trusturlCerts = null; ++ ++ /* utility to decode hex oneTimeKey=... and serverCert=... */ ++ 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; ++ ++ debug_certs = v.debugCerts; ++ ++ /* we will first try default factory for certification: */ ++ ++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); ++ ++ dbg("SSL startup: " + host + " " + port); ++ ++ ++ /* create trust managers to be used if initial handshake fails: */ ++ ++ trustAllCerts = new TrustManager[] { ++ /* ++ * this one accepts everything. Only used if user ++ * has disabled checking (trustAllVncCerts=yes) ++ * or when we grab the cert to show it to them in ++ * a dialog and ask them to manually verify/accept it. ++ */ ++ 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 and stored in ++ * trusturlCerts. ++ */ ++ 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 (URL)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trusturlCerts' */ ++ if (trusturlCerts == null) { ++ throw new CertificateException( ++ "No Trust url Certs array."); ++ } ++ if (trusturlCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust url Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trusturlCerts.length) { ++ throw new CertificateException( ++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trusturlCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("URL: cert mismatch at i=" + i); ++ dbg("URL: cert mismatch cert" + certs[i]); ++ dbg("URL: cert mismatch url" + trusturlCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("URL: cert info at i=" + i); ++ dbg("URL: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("URL: cert info url" + trusturlCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != URL Cert Chain."); ++ } ++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustSrvCert = new TrustManager[] { ++ /* ++ * this one accepts cert given to us in the serverCert ++ * Applet Parameter we were started with. It is ++ * currently a fatal error if the VNC Server's cert ++ * doesn't match it. ++ */ ++ 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 (SRV)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustsrvCerts' */ ++ if (trustsrvCerts == null) { ++ throw new CertificateException( ++ "No Trust srv Certs array."); ++ } ++ if (trustsrvCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust srv Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustsrvCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustsrvCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("SRV: cert mismatch at i=" + i); ++ dbg("SRV: cert mismatch cert" + certs[i]); ++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("SRV: cert info at i=" + i); ++ dbg("SRV: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("SRV: cert info srv" + trustsrvCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); ++ } ++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustOneCert = new TrustManager[] { ++ /* ++ * this one accepts only the retrieved server ++ * cert by SSLSocket by this applet we stored in ++ * trustallCerts that user has accepted or applet ++ * parameter trustAllVncCerts=yes is set. This is ++ * for when we reconnect after the user has manually ++ * accepted the trustall cert in the dialog (or set ++ * trustAllVncCerts=yes applet param.) ++ */ ++ 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 (ONE)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustallCerts' */ ++ 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 (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustallCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustallCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("ONE: cert mismatch at i=" + i); ++ dbg("ONE: cert mismatch cert" + certs[i]); ++ dbg("ONE: cert mismatch all" + trustallCerts[i]); ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("ONE: cert info at i=" + i); ++ dbg("ONE: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("ONE: cert info all" + trustallCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != TRUSTALL Cert Chain."); ++ } ++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ /* ++ * The above TrustManagers are used: ++ * ++ * 1) to retrieve the server cert in case of failure to ++ * display it to the user in a dialog. ++ * 2) to subsequently connect to the server if user agrees. ++ */ ++ ++ /* ++ * build oneTimeKey cert+key if supplied in applet parameter: ++ */ ++ 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); ++ if (debug_certs) { ++ dbg("one time key " + ff); ++ } ++ ++ 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)); ++ if (debug_certs) { ++ dbg("one time cert" + 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(); ++ } ++ ++ /* ++ * build serverCert cert if supplied in applet parameter: ++ */ ++ if (viewer.serverCert != null) { ++ CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++ byte[] cert = hex2bytes(viewer.serverCert); ++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++ trustsrvCerts = new Certificate[c.toArray().length]; ++ if (c.size() == 1) { ++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++ trustsrvCerts[0] = tmpcert; ++ } else { ++ trustsrvCerts = (Certificate[]) c.toArray(); ++ } ++ } ++ ++ /* the trust loc certs context: */ ++ try { ++ trustloc_ctx = SSLContext.getInstance("SSL"); ++ ++ /* ++ * below is a failed attempt to get jvm's default ++ * trust manager using null (below) makes it so ++ * for HttpsURLConnection the server cannot be ++ * verified (no prompting.) ++ */ ++ if (false) { ++ boolean didit = false; ++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); ++ tmf.init((KeyStore) null); ++ TrustManager [] tml = tmf.getTrustManagers(); ++ for (int i = 0; i < tml.length; i++) { ++ TrustManager tm = tml[i]; ++ if (tm instanceof X509TrustManager) { ++ TrustManager tm1[] = new TrustManager[1]; ++ tm1[0] = tm; ++ trustloc_ctx.init(mykey, tm1, null); ++ didit = true; ++ break; ++ } ++ } ++ if (!didit) { ++ trustloc_ctx.init(mykey, null, null); ++ } ++ } else { ++ /* we have to set trust manager to null */ ++ trustloc_ctx.init(mykey, null, null); ++ } ++ ++ } catch (Exception e) { ++ String msg = "SSL trustloc_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust all certs context: */ ++ 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); ++ } ++ ++ /* the trust url certs context: */ ++ 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); ++ } ++ ++ /* the trust srv certs context: */ ++ try { ++ trustsrv_ctx = SSLContext.getInstance("SSL"); ++ trustsrv_ctx.init(mykey, trustSrvCert, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trustsrv_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust the one cert from server context: */ ++ 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); ++ } ++ } ++ ++ /* ++ * we call this early on to 1) check for a proxy, 2) grab ++ * Browser/JVM accepted HTTPS cert. ++ */ ++ public void check_for_proxy_and_grab_vnc_server_cert() { ++ ++ trusturlCerts = null; ++ proxy_in_use = false; ++ ++ if (viewer.ignoreProxy) { ++ /* applet param says skip it. */ ++ /* the downside is we do not set trusturlCerts for comparison later... */ ++ /* nor do we autodetect x11vnc for GET=1. */ ++ return; ++ } ++ ++ dbg("------------------------------------------------"); ++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); ++ ++ dbg("TRYING HTTPS:"); ++ String ustr = "https://" + host + ":"; ++ if (viewer.httpsPort != null) { ++ ustr += viewer.httpsPort; ++ } else { ++ ustr += port; ++ } ++ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTPS URL connection to host:port */ ++ URL url = new URL(ustr); ++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); ++ ++ if (mykey != null) { ++ /* with oneTimeKey (mykey) we can't use the default SSL context */ ++ if (trustsrvCerts != null) { ++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); ++ } else if (trustloc_ctx != null) { ++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); ++ } ++ } ++ ++ https.setUseCaches(false); ++ https.setRequestMethod("GET"); ++ https.setRequestProperty("Pragma", "No-Cache"); ++ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++ https.setDoInput(true); ++ ++ dbg("trying https.connect()"); ++ https.connect(); ++ ++ dbg("trying https.getServerCertificates()"); ++ 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; ++ dbg("An HTTPS proxy is in use. There may be connection problems."); ++ } ++ ++ dbg("trying https.getContent()"); ++ Object output = https.getContent(); ++ dbg("trying https.disconnect()"); ++ https.disconnect(); ++ if (! viewer.GET) { ++ String header = https.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (1), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ ++ } catch(Exception e) { ++ dbg("HttpsURLConnection: " + e.getMessage()); ++ } ++ ++ if (proxy_in_use) { ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } else if (trusturlCerts != null && !viewer.forceProxy) { ++ /* Allow user to require HTTP check? use forceProxy for now. */ ++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } ++ ++ /* ++ * XXX need to remember scenario where this extra check ++ * gives useful info. User's Browser proxy settings? ++ */ ++ dbg("TRYING HTTP:"); ++ ustr = "http://" + host + ":" + port; ++ ustr += viewer.urlPrefix + "/index.vnc"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ ++ 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); ++ ++ dbg("trying http.connect()"); ++ http.connect(); ++ ++ if (http.usingProxy()) { ++ proxy_in_use = true; ++ dbg("An HTTP proxy is in use. There may be connection problems."); ++ } ++ dbg("trying http.getContent()"); ++ Object output = http.getContent(); ++ dbg("trying http.disconnect()"); ++ http.disconnect(); ++ if (! viewer.GET) { ++ String header = http.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (2), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ } catch(Exception e) { ++ dbg("HttpURLConnection: " + e.getMessage()); ++ } ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ } ++ ++ public Socket connectSock() throws IOException { ++ /* ++ * first try a https connection to detect a proxy, and ++ * grab the VNC server cert at the same time: ++ */ ++ check_for_proxy_and_grab_vnc_server_cert(); ++ ++ boolean srv_cert = false; ++ ++ if (trustsrvCerts != null) { ++ /* applet parameter suppled serverCert */ ++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); ++ factory = trustsrv_ctx.getSocketFactory(); ++ srv_cert = true; ++ } else if (viewer.trustAllVncCerts) { ++ /* trust all certs (no checking) */ ++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); ++ factory = trustall_ctx.getSocketFactory(); ++ } else if (trusturlCerts != null) { ++ /* trust certs the Browser/JVM accepted in check_for_proxy... */ ++ dbg("using trusturl_ctx"); ++ factory = trusturl_ctx.getSocketFactory(); ++ } else { ++ /* trust the local defaults */ ++ 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"); ++ } ++ ++ int timeout = 6; ++ if (timeout > 0) { ++ socket = (SSLSocket) factory.createSocket(); ++ InetSocketAddress inetaddr = new InetSocketAddress(host, port); ++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); ++ socket.connect(inetaddr, timeout * 1000); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } ++ ++ } catch (Exception esock) { ++ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); ++ } ++ } else { ++ /* n.b. socket is left in error state to cause ex. below. */ ++ } ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The Server Connection Verified OK on 1st try."); ++ ++ java.security.cert.Certificate[] currentTrustedCerts; ++ BrowserCertsDialog bcd; ++ ++ SSLSession sess = socket.getSession(); ++ currentTrustedCerts = sess.getPeerCertificates(); ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-1 keeping socket."); ++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy."); ++ } ++ 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 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.serverCert != null && trustsrvCerts != null) { ++ dbg("viewer.serverCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.trustUrlVncCert) { ++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else { ++ /* have a dialog with the user: */ ++ bcd = new BrowserCertsDialog(serv, host + ":" + port); ++ dbg("browser certs dialog begin."); ++ bcd.queryUser(); ++ dbg("browser certs dialog finished."); ++ ++ if (bcd.showCertDialog) { ++ String msg = "user wants to see cert"; ++ dbg(msg); ++ user_wants_to_see_cert = true; ++ if (cert_fail == null) { ++ cert_fail = "user-view"; ++ } ++ throw new SSLHandshakeException(msg); ++ } else { ++ user_wants_to_see_cert = false; ++ dbg("browser certs dialog: user said yes, accept it"); ++ } ++ } ++ ++ } catch (SSLHandshakeException eh) { ++ dbg("SSLHandshakeException: could not automatically verify Server."); ++ dbg("msg: " + eh.getMessage()); ++ ++ ++ /* send a cleanup string just in case: */ ++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; ++ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!"); ++ } ++ ++ /* reload */ ++ ++ socket = null; ++ ++ String reason = null; ++ ++ if (srv_cert) { ++ /* for serverCert usage we make this a fatal error. */ ++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); ++ /* see below in TrustDialog were we describe this case to user anyway */ ++ } ++ ++ /* ++ * Reconnect, trusting any cert, so we can grab ++ * the cert to show it to the user in a dialog ++ * for him to manually accept. This 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); ++ } ++ ++ if (debug_certs) { ++ dbg("trusturlCerts: " + trusturlCerts); ++ dbg("trustsrvCerts: " + trustsrvCerts); ++ } ++ if (trusturlCerts == null && cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); ++ ++ /* grab the cert: */ ++ try { ++ SSLSession sess = socket.getSession(); ++ trustallCerts = sess.getPeerCertificates(); ++ } catch (Exception e) { ++ throw new Exception("Could not get " + ++ "Peer Certificate"); ++ } ++ if (debug_certs) { ++ dbg("trustallCerts: " + trustallCerts); ++ } ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); ++ } else if (! browser_cert_match()) { ++ /* ++ * close socket now, we will reopen after ++ * dialog if user agrees to use the cert. ++ */ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!"); ++ } ++ socket = null; ++ ++ /* dialog with user to accept cert or not: */ ++ ++ TrustDialog td= new TrustDialog(host, port, ++ trustallCerts); ++ ++ if (cert_fail == null) { ++ ; ++ } else if (cert_fail.equals("user-view")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " You Asked to View the Certificate."; ++ } else if (cert_fail.equals("server-cert-mismatch")) { ++ /* this is now fatal error, see above. */ ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Certificate\n" ++ + " specified in the supplied 'serverCert' Applet Parameter."; ++ } else if (cert_fail.equals("cert-mismatch")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Website's\n" ++ + " HTTPS Certificate (that you previously accepted; either\n" ++ + " manually or automatically via Certificate Authority.)"; ++ } else if (cert_fail.equals("missing-certs")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " Not all Certificates could be obtained to check."; ++ } ++ ++ if (! td.queryUser(reason)) { ++ 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()); ++ } ++ ++ /* reload again: */ ++ ++ if (socket != null) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!!"); ++ } ++ socket = null; ++ } ++ ++ /* ++ * Now connect a 3rd time, using the cert ++ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); ++ ++ } catch (Exception ehand3) { ++ dbg("** Could not TrustAll/TrustOne Verify Server #3."); ++ ++ throw new IOException(ehand3.getMessage()); ++ } ++ } ++ ++ /* we have socket (possibly null) at this point, so proceed: */ ++ ++ /* handle x11vnc GET=1, if applicable: */ ++ 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: " + str); ++ OutputStream os = socket.getOutputStream(); ++ String type = "os"; ++ ++ if (type == "os") { ++ os.write(str.getBytes()); ++ os.flush(); ++ System.out.println("used OutputStream"); ++ } else if (type == "bs") { ++ BufferedOutputStream bs = new BufferedOutputStream(os); ++ bs.write(str.getBytes()); ++ bs.flush(); ++ System.out.println("used BufferedOutputStream"); ++ } else if (type == "ds") { ++ DataOutputStream ds = new DataOutputStream(os); ++ ds.write(str.getBytes()); ++ ds.flush(); ++ System.out.println("used DataOutputStream"); ++ } ++ 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."); ++ dbg(""); ++ ++ /* could be null, let caller handle that. */ ++ return (Socket) socket; ++ } ++ ++ boolean browser_cert_match() { ++ String msg = "Browser URL accept previously accepted cert"; ++ ++ if (user_wants_to_see_cert) { ++ return false; ++ } ++ ++ if (viewer.serverCert != null || trustsrvCerts != null) { ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (trustallCerts != null && trusturlCerts != null) { ++ if (trustallCerts.length == trusturlCerts.length) { ++ boolean ok = true; ++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ ++ for (int i = 0; i < trusturlCerts.length; i++) { ++ if (! trustallCerts[i].equals(trusturlCerts[i])) { ++ dbg("BCM: cert mismatch at i=" + i); ++ dbg("BCM: cert mismatch url" + trusturlCerts[i]); ++ dbg("BCM: cert mismatch all" + trustallCerts[i]); ++ ok = false; ++ } ++ } ++ if (ok) { ++ System.out.println(msg); ++ if (cert_fail == null) { ++ cert_fail = "did-not-fail"; ++ } ++ return true; ++ } else { ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ return false; ++ } ++ } ++ } ++ if (cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ return false; ++ } ++ ++ 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; ++ } ++ ++ /* this will do the proxy CONNECT negotiation and hook us up. */ ++ ++ private void proxy_helper(String proxyHost, int proxyPort) { ++ ++ boolean proxy_auth = false; ++ String proxy_auth_basic_realm = ""; ++ String hp = host + ":" + port; ++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); ++ ++ /* we loop here a few times trying for the password case */ ++ for (int k=0; k < 2; k++) { ++ dbg("proxy_in_use psocket: " + k); ++ ++ if (proxySock != null) { ++ try { ++ proxySock.close(); ++ } catch (Exception e) { ++ dbg("proxy socket is grumpy."); ++ } ++ } ++ ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("1-a sadly, returning a null socket"); ++ return; ++ } ++ ++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n"; ++ ++ dbg("requesting via proxy: " + req1); ++ ++ if (proxy_auth) { ++ if (proxy_auth_string == null) { ++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); ++ pp.queryUser(); ++ proxy_auth_string = pp.getAuth(); ++ } ++ //dbg("auth1: " + proxy_auth_string); ++ ++ String auth2 = Base64Coder.encodeString(proxy_auth_string); ++ //dbg("auth2: " + auth2); ++ ++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; ++ //dbg("req1: " + req1); ++ ++ dbg("added Proxy-Authorization: Basic ... to request"); ++ } ++ req1 += "\r\n"; ++ ++ try { ++ proxy_os.write(req1.getBytes()); ++ String reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply.trim()); ++ ++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { ++ proxy_auth = true; ++ proxySock.close(); ++ } else 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; ++ } ++ } ++ } catch(Exception e) { ++ dbg("some proxy socket problem: " + e.getMessage()); ++ } ++ ++ /* read the rest of the HTTP headers */ ++ while (true) { ++ String line = readline(proxy_is); ++ dbg("proxy line: " + line.trim()); ++ if (proxy_auth) { ++ String uc = line.toLowerCase(); ++ if (uc.indexOf("proxy-authenticate:") == 0) { ++ if (uc.indexOf(" basic ") >= 0) { ++ int idx = uc.indexOf(" realm"); ++ if (idx >= 0) { ++ proxy_auth_basic_realm = uc.substring(idx+1); ++ } ++ } ++ } ++ } ++ if (line.equals("\r\n") || line.equals("\n")) { ++ break; ++ } ++ } ++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { ++ /* we only try once for the non-password case: */ ++ break; ++ } ++ } ++ } ++ ++ 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) { ++ /* sandboxed applet might not be able to read it. */ ++ dbg("props failed: " + e.getMessage()); ++ } ++ if (viewer.proxyHost != null) { ++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); ++ proxyHost = viewer.proxyHost; ++ if (viewer.proxyPort != null) { ++ proxyPort = gint(viewer.proxyPort); ++ } else { ++ proxyPort = 8080; ++ } ++ ++ } else if (props != null) { ++ dbg("\n---------------\nAll props:"); ++ props.list(System.out); ++ dbg("\n---------------\n\n"); ++ ++ /* scrape throught properties looking for proxy info: */ ++ ++ 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.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; ++ } ++ } ++ ++ 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 (proxyHost != null && proxyPort > 0) { ++ break; ++ } ++ ++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 ++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { ++ continue; ++ } ++ if (v2.indexOf("http") < 0) { ++ 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) { ++ j = p.indexOf("http"); ++ 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()); ++ } ++ ++ proxy_helper(proxyHost, proxyPort); ++ if (proxySock == null) { ++ return null; ++ } ++ } 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("proxy socket problem-2: " + 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(String reason) { ++ ++ /* 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"); ++ } ++ if (reason != null) { ++ reason += "\n\n"; ++ } ++ ++ text = "\n" +++ "Unable to verify the identity of\n" +++ "\n" +++ " " + host + ":" + port + "\n" +++ "\n" +++ infostr +++ "\n" +++ "as a trusted VNC server.\n" +++ "\n" +++ reason +++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" +++ "is due to one of the following:\n" +++ "\n" +++ " - Your requesting to View the Certificate before accepting.\n" +++ "\n" +++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" +++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" +++ "\n" +++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" +++ " the Apache Web server has a certificate *different* from the VNC server's.\n" +++ "\n" +++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" +++ " obtained by this applet to compare the VNC Server Certificate against.\n" +++ "\n" +++ " - The VNC Server's Certificate does not match the one specified in the\n" +++ " supplied 'serverCert' Java Applet Parameter.\n" +++ "\n" +++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" +++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" +++ "automatically authenticate this VNC Server.\n" +++ "\n" +++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" +++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" +++ "certificate (except for the Apache portal case above where they don't match.)\n" +++ "\n" +++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" +++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" +++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); ++ dialog.hide(); ++ ++ } else if (evt.getSource() == cancel) { ++ /* Cancel button clicked */ ++ trust_this_session = false; ++ ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++ ++ 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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++class ProxyPasswdDialog implements ActionListener { ++ String guessedHost = null; ++ String guessedPort = null; ++ String guessedUser = null; ++ String guessedPasswd = null; ++ String realm = 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 entry1; ++ TextField entry2; ++ String reply1 = ""; ++ String reply2 = ""; ++ ++ ProxyPasswdDialog (String h, int p, String realm) { ++ guessedHost = h; ++ try { ++ guessedPort = Integer.toString(p); ++ } catch (Exception e) { ++ guessedPort = "8080"; ++ } ++ this.realm = realm; ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Proxy Requires Username and Password"); ++ ++ dialog = new Dialog(frame, true); ++ ++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); ++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); ++ entry1 = new TextField(30); ++ entry2 = new TextField(30); ++ entry2.setEchoChar('*'); ++ ok = new Button("OK"); ++ ok.addActionListener(this); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", label); ++ dialog.add("Center", entry1); ++ dialog.add("South", entry2); ++ dialog.add("East", ok); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til OK or Cancel pressed. */ ++ return; ++ } ++ ++ public String getAuth() { ++ return reply1 + ":" + reply2; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == ok) { ++ reply1 = entry1.getText(); ++ reply2 = entry2.getText(); ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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 and/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 += "If the VNC Server connection times out while you are reading this\n"; ++m += "dialog, then restart the connection and try again.\n"; ++m += "\n"; ++m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; ++m += "\n"; ++ ++ TextArea textarea = new TextArea(m, 22, 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. */ ++ System.out.println("done show()"); ++ return; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == yes) { ++ showCertDialog = false; ++ //dialog.dispose(); ++ dialog.hide(); ++ } else if (evt.getSource() == no) { ++ showCertDialog = true; ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ System.out.println("done actionPerformed()"); ++ } ++} ++ ++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 ""; ++ } ++ } ++} ++ ++class Base64Coder { ++ ++ // Mapping table from 6-bit nibbles to Base64 characters. ++ private static char[] map1 = new char[64]; ++ static { ++ int i=0; ++ for (char c='A'; c<='Z'; c++) map1[i++] = c; ++ for (char c='a'; c<='z'; c++) map1[i++] = c; ++ for (char c='0'; c<='9'; c++) map1[i++] = c; ++ map1[i++] = '+'; map1[i++] = '/'; } ++ ++ // Mapping table from Base64 characters to 6-bit nibbles. ++ private static byte[] map2 = new byte[128]; ++ static { ++ for (int i=0; iin. ++ * @return A character array with the Base64 encoded data. ++ */ ++ public static char[] encode (byte[] in, int iLen) { ++ int oDataLen = (iLen*4+2)/3; // output length without padding ++ int oLen = ((iLen+2)/3)*4; // output length including padding ++ char[] out = new char[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++] & 0xff; ++ int i1 = ip < iLen ? in[ip++] & 0xff : 0; ++ int i2 = ip < iLen ? in[ip++] & 0xff : 0; ++ int o0 = i0 >>> 2; ++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); ++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); ++ int o3 = i2 & 0x3F; ++ out[op++] = map1[o0]; ++ out[op++] = map1[o1]; ++ out[op] = op < oDataLen ? map1[o2] : '='; op++; ++ out[op] = op < oDataLen ? map1[o3] : '='; op++; } ++ return out; } ++ ++ /** ++ * Decodes a string from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return A String containing the decoded data. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static String decodeString (String s) { ++ return new String(decode(s)); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (String s) { ++ return decode(s.toCharArray()); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * No blanks or line breaks are allowed within the Base64 encoded data. ++ * @param in a character array containing the Base64 encoded data. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (char[] in) { ++ int iLen = in.length; ++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); ++ while (iLen > 0 && in[iLen-1] == '=') iLen--; ++ int oLen = (iLen*3) / 4; ++ byte[] out = new byte[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++]; ++ int i1 = in[ip++]; ++ int i2 = ip < iLen ? in[ip++] : 'A'; ++ int i3 = ip < iLen ? in[ip++] : 'A'; ++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int b0 = map2[i0]; ++ int b1 = map2[i1]; ++ int b2 = map2[i2]; ++ int b3 = map2[i3]; ++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int o0 = ( b0 <<2) | (b1>>>4); ++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); ++ int o2 = ((b2 & 3)<<6) | b3; ++ out[op++] = (byte)o0; ++ if (op= 2) { ++ proxyPort = new String(pieces[1]); ++ } else { ++ proxyPort = new String("8080"); ++ } ++ } ++ } ++ str = readParameter("proxyHost", false); ++ if (str != null) { ++ proxyHost = new String(str); ++ } ++ str = readParameter("proxyPort", false); ++ if (str != null) { ++ proxyPort = new String(str); ++ } ++ if (proxyHost != null && proxyPort == null) { ++ proxyPort = new String("8080"); ++ } ++ ++ 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; ++ } ++ debugCerts = false; ++ str = readParameter("debugCerts", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugCerts = true; ++ } ++ debugKeyboard = false; ++ str = readParameter("debugKeyboard", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugKeyboard = true; ++ } ++ mapF5_to_atsign = false; ++ str = readParameter("mapF5_to_atsign", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ mapF5_to_atsign = true; ++ } ++ forbid_Ctrl_Alt = false; ++ str = readParameter("forbid_Ctrl_Alt", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ forbid_Ctrl_Alt = true; ++ } + } + + public String readParameter(String name, boolean required) { diff --git a/webclients/java-applet/ssl/ultra.vnc b/webclients/java-applet/ssl/ultra.vnc new file mode 100644 index 0000000..3c57445 --- /dev/null +++ b/webclients/java-applet/ssl/ultra.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/java-applet/ssl/ultraproxy.vnc b/webclients/java-applet/ssl/ultraproxy.vnc new file mode 100644 index 0000000..fd842c4 --- /dev/null +++ b/webclients/java-applet/ssl/ultraproxy.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/java-applet/ssl/ultrasigned.vnc b/webclients/java-applet/ssl/ultrasigned.vnc new file mode 100644 index 0000000..a711655 --- /dev/null +++ b/webclients/java-applet/ssl/ultrasigned.vnc @@ -0,0 +1,28 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + + +$PARAMS + +
+x11vnc site + diff --git a/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch new file mode 100644 index 0000000..3309860 --- /dev/null +++ b/webclients/java-applet/ssl/ultravnc-102-JavaViewer-ssl-etc.patch @@ -0,0 +1,5494 @@ +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-31 15:40:45.000000000 -0400 +@@ -43,30 +43,36 @@ + viewer = v; + + setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); +- disconnectButton = new Button("Disconnect"); ++ if (v.ftpOnly) { ++ disconnectButton = new Button("Quit"); ++ } else { ++ disconnectButton = new Button("Close"); ++ } + disconnectButton.setEnabled(false); + add(disconnectButton); + disconnectButton.addActionListener(this); +- optionsButton = new Button("Options"); +- add(optionsButton); +- optionsButton.addActionListener(this); +- clipboardButton = new Button("Clipboard"); +- clipboardButton.setEnabled(false); +- add(clipboardButton); +- clipboardButton.addActionListener(this); +- if (viewer.rec != null) { +- recordButton = new Button("Record"); +- add(recordButton); +- recordButton.addActionListener(this); +- } +- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); +- ctrlAltDelButton.setEnabled(false); +- add(ctrlAltDelButton); +- ctrlAltDelButton.addActionListener(this); +- refreshButton = new Button("Refresh"); +- refreshButton.setEnabled(false); +- add(refreshButton); +- refreshButton.addActionListener(this); ++ if (!v.ftpOnly) { ++ optionsButton = new Button("Options"); ++ add(optionsButton); ++ optionsButton.addActionListener(this); ++ clipboardButton = new Button("Clipboard"); ++ clipboardButton.setEnabled(false); ++ add(clipboardButton); ++ clipboardButton.addActionListener(this); ++ if (viewer.rec != null) { ++ recordButton = new Button("Record"); ++ add(recordButton); ++ recordButton.addActionListener(this); ++ } ++ ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); ++ ctrlAltDelButton.setEnabled(false); ++ add(ctrlAltDelButton); ++ ctrlAltDelButton.addActionListener(this); ++ refreshButton = new Button("Refresh"); ++ refreshButton.setEnabled(false); ++ add(refreshButton); ++ refreshButton.addActionListener(this); ++ } + ftpButton = new Button("File Transfer"); + ftpButton.setEnabled(false); + add(ftpButton); +@@ -79,9 +85,10 @@ + + public void enableButtons() { + disconnectButton.setEnabled(true); ++ ftpButton.setEnabled(true); ++ if (viewer.ftpOnly) {return;} + clipboardButton.setEnabled(true); + refreshButton.setEnabled(true); +- ftpButton.setEnabled(true); + } + + // +@@ -89,6 +96,9 @@ + // + + public void disableButtonsOnDisconnect() { ++ ftpButton.setEnabled(false); ++ if (viewer.ftpOnly) {return;} ++ + remove(disconnectButton); + disconnectButton = new Button("Hide desktop"); + disconnectButton.setEnabled(true); +@@ -99,7 +109,6 @@ + clipboardButton.setEnabled(false); + ctrlAltDelButton.setEnabled(false); + refreshButton.setEnabled(false); +- ftpButton.setEnabled(false); + + validate(); + } +@@ -110,6 +119,7 @@ + // + + public void enableRemoteAccessControls(boolean enable) { ++ if (viewer.ftpOnly) {return;} + ctrlAltDelButton.setEnabled(enable); + } + +@@ -163,9 +173,19 @@ + } + else if (evt.getSource() == ftpButton) + { +- viewer.ftp.setVisible(!viewer.ftp.isVisible()); ++// begin runge/x11vnc ++ if (viewer.ftpOnly) { ++ viewer.vncFrame.setVisible(false); ++ } ++ viewer.ftp.setSavedLocations(); ++ if (viewer.ftp.isVisible()) { ++ viewer.ftp.doClose(); ++ } else { ++ viewer.ftp.doOpen(); ++ } ++// end runge/x11vnc + 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 2009-01-13 09:48:30.000000000 -0500 +@@ -24,8 +24,17 @@ + import java.io.*; + import java.util.ArrayList; + import java.util.Vector; ++import java.util.Date; + import javax.swing.*; + ++import java.nio.ByteBuffer; ++import java.nio.CharBuffer; ++import java.nio.charset.*; ++ ++// begin runge/x11vnc ++import java.util.Arrays; ++// end runge/x11vnc ++ + + /* + * Created on Feb 25, 2004 +@@ -74,12 +83,31 @@ + public javax.swing.JTextField connectionStatus = null; + public boolean updateDriveList; + private Vector remoteList = null; ++ private Vector remoteListInfo = null; + private Vector localList = null; ++ private Vector localListInfo = null; + private File currentLocalDirectory = null; // Holds the current local Directory + private File currentRemoteDirectory = null; // Holds the current remote Directory + 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; ++ private javax.swing.JButton refreshButton = 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; ++ private int lastRemoteIndex = -1; ++ private int lastLocalIndex = -1; ++ private boolean doingShortcutDir = false; ++ private boolean gotShortcutDir = false; ++ private boolean ignore_events = false; ++// end runge/x11vnc + + // sf@2004 - Separate directories and files for better lisibility + private ArrayList DirsList; +@@ -125,11 +153,61 @@ + + void refreshRemoteLocation() + { ++ ++//System.out.println("refreshRemoteLocation1"); + remoteList.clear(); ++ remoteListInfo.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 +@@ -143,6 +221,9 @@ + int size = str.length(); + String driveType = null; + String[] drive = new String[str.length() / 3]; ++ int idx = 0, C_drive = -1, O_drive = -1; ++ ++System.out.println("ComboBox: Str '" + str + "'"); + + // Loop through the string to create a String[] + for (int i = 0; i < size; i = i + 3) { +@@ -150,26 +231,68 @@ + driveType = str.substring(i + 2, i + 3); + if (driveType.compareTo("f") == 0) + drive[i / 3] += "\\ Floppy"; +- if (driveType.compareTo("l") == 0) ++ if (driveType.compareTo("l") == 0) { + drive[i / 3] += "\\ Local Disk"; ++ if (drive[i/3].substring(0,1).toUpperCase().equals("C")) { ++ C_drive = idx; ++ } else if (O_drive < 0) { ++ O_drive = idx; ++ } ++ } + if (driveType.compareTo("c") == 0) + drive[i / 3] += "\\ CD-ROM"; + if (driveType.compareTo("n") == 0) + drive[i / 3] += "\\ Network"; + + remoteDrivesComboBox.addItem(drive[i / 3]); ++System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'"); ++ idx++; ++ } ++ ++ // runge ++ if (viewer.ftpDropDown != null) { ++ String[] dd = viewer.ftpDropDown.split("\\."); ++ for (int i=0; i < dd.length; i++) { ++ if (!dd[i].equals("")) { ++ String s = dd[i]; ++ if (s.startsWith("TOP_")) { ++ s = s.substring(4); ++ remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0); ++ } else { ++ remoteDrivesComboBox.addItem(" [" + s + "]"); ++ } ++ } ++ } ++ } else { ++ remoteDrivesComboBox.addItem(" [My Documents]"); ++ remoteDrivesComboBox.addItem(" [Desktop]"); ++ remoteDrivesComboBox.addItem(" [Home]"); + } ++ + //sf@ - Select Drive C:as default if possible + boolean bFound = false; +- for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) +- { +- if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) +- { +- remoteDrivesComboBox.setSelectedIndex(i); ++ ++ if (false) { ++ for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) { ++ if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) { ++ remoteDrivesComboBox.setSelectedIndex(i); ++ bFound = true; ++ } ++ } ++ } else { ++ if (C_drive >= 0) { ++ remoteDrivesComboBox.setSelectedIndex(C_drive); ++ bFound = true; ++System.out.println("ComboBox: C_drive index: " + C_drive); ++ } else if (O_drive >= 0) { ++ remoteDrivesComboBox.setSelectedIndex(O_drive); + bFound = true; ++System.out.println("ComboBox: Other_drive index: " + O_drive); + } + } ++ + if (!bFound) remoteDrivesComboBox.setSelectedIndex(0); ++ + updateDriveList = false; + return drive; + } +@@ -185,6 +308,8 @@ + stopButton.setVisible(true); + stopButton.setEnabled(true); + receiveButton.setEnabled(false); ++ viewButton.setEnabled(false); // runge/x11vnc ++ refreshButton.setEnabled(false); + remoteTopButton.setEnabled(false); + sendButton.setEnabled(false); + remoteFileTable.setEnabled(false); +@@ -207,6 +332,8 @@ + stopButton.setVisible(false); + stopButton.setEnabled(false); + receiveButton.setEnabled(true); ++ viewButton.setEnabled(true); // runge/x11vnc ++ refreshButton.setEnabled(true); + remoteTopButton.setEnabled(true); + sendButton.setEnabled(true); + remoteFileTable.setEnabled(true); +@@ -221,10 +348,11 @@ + /* + * Print Directory prints out all the contents of a directory + */ +- void printDirectory(ArrayList a) { ++ void printDirectory(ArrayList a, ArrayList b) { + + for (int i = 0; i < a.size(); i++) { + remoteList.addElement(a.get(i)); ++ remoteListInfo.addElement(b.get(i)); + } + remoteFileTable.setListData(remoteList); + } +@@ -235,10 +363,12 @@ + * @return void + */ + private void initialize() { ++ ignore_events = true; + this.setSize(794, 500); + this.setContentPane(getJContentPane()); ++ ignore_events = false; + updateDriveList = true; +- } ++ } + /** + * This method initializes jContentPane. This is the main content pane + * +@@ -253,6 +383,33 @@ + jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST); + jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST); + jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER); ++ ++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); ++ AbstractAction escapeAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ System.out.println("Escape Pressed"); ++ if (viewer.ftpOnly) { ++ System.out.println("exiting..."); ++ System.exit(0); ++ } else { ++ doClose(); ++ } ++ } ++ }; ++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++ jContentPane.getInputMap().put(stroke, "escapeAction"); ++ jContentPane.getActionMap().put("escapeAction", escapeAction); ++ ++ stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK); ++ AbstractAction resetAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ System.out.println("Ctrl-R Pressed"); ++ doReset(); ++ } ++ }; ++ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction"); ++ jContentPane.getInputMap().put(stroke, "resetAction"); ++ jContentPane.getActionMap().put("resetAction", resetAction); + } + return jContentPane; + } +@@ -270,6 +427,7 @@ + topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER); + topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST); + topPanelLocal.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelLocal"); + } + return topPanelLocal; + } +@@ -288,6 +446,7 @@ + topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER); + topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST); + topPanelRemote.setBackground(java.awt.Color.lightGray); ++//System.out.println("getTopPanelRemote"); + } + return topPanelRemote; + } +@@ -301,6 +460,7 @@ + if (topPanelCenter == null) { + topPanelCenter = new javax.swing.JPanel(); + topPanelCenter.add(getDummyButton(), null); ++//System.out.println("getTopPanelCenter"); + } + return topPanelCenter; + } +@@ -328,6 +488,7 @@ + topPanel.add(getRemoteTopButton(), null); + topPanel.setBackground(java.awt.Color.lightGray); + */ ++//System.out.println("getTopPanel"); + } + return topPanel; + } +@@ -348,6 +509,7 @@ + statusPanel.add(getJProgressBar(), null); + statusPanel.add(getConnectionStatus(), null); + statusPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getStatusPanel"); + + } + return statusPanel; +@@ -368,6 +530,7 @@ + remotePanel.add(getRemoteScrollPane(), null); + remotePanel.add(getRemoteStatus(), null); + remotePanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getRemotePanel"); + } + return remotePanel; + } +@@ -390,6 +553,7 @@ + localPanel.setComponentOrientation( + java.awt.ComponentOrientation.UNKNOWN); + localPanel.setName("localPanel"); ++//System.out.println("getLocalPanel"); + } + return localPanel; + } +@@ -405,12 +569,15 @@ + buttonPanel = new javax.swing.JPanel(); + buttonPanel.setLayout(null); + buttonPanel.add(getReceiveButton(), null); ++ buttonPanel.add(getRefreshButton(), null); // runge/x11vnc ++ buttonPanel.add(getViewButton(), null); // runge/x11vnc + buttonPanel.add(getNewFolderButton(), null); + buttonPanel.add(getCloseButton(), null); + buttonPanel.add(getDeleteButton(), null); + buttonPanel.add(getSendButton(), null); + buttonPanel.add(getStopButton(), null); + buttonPanel.setBackground(java.awt.Color.lightGray); ++//System.out.println("getButtonPanel"); + } + return buttonPanel; + } +@@ -422,10 +589,11 @@ + 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); ++//System.out.println("getSendButton"); + + } + return sendButton; +@@ -438,7 +606,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 +621,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 +636,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 +644,39 @@ + return newFolderButton; + } + ++// begin runge/x11vnc ++ /** ++ * This method initializes refreshButton ++ * ++ * @return javax.swing.JButton ++ */ ++ private javax.swing.JButton getRefreshButton() { ++ if (refreshButton == null) { ++ refreshButton = new javax.swing.JButton(); ++ refreshButton.setBounds(15, 170, 107, 25); ++ refreshButton.setText("Refresh"); ++ refreshButton.setName("refreshButton"); ++ refreshButton.addActionListener(this); ++ } ++ return refreshButton; ++ } ++ /** ++ * This method initializes viewButton ++ * ++ * @return javax.swing.JButton ++ */ ++ private javax.swing.JButton getViewButton() { ++ if (viewButton == null) { ++ viewButton = new javax.swing.JButton(); ++ viewButton.setBounds(15, 200, 107, 25); ++ viewButton.setText("View File"); ++ viewButton.setName("viewButton"); ++ viewButton.addActionListener(this); ++ } ++ return viewButton; ++ } ++// end runge/x11vnc ++ + /** + * This method initializes stopButton + * +@@ -486,7 +687,7 @@ + if (stopButton == null) + { + stopButton = new javax.swing.JButton(); +- stopButton.setBounds(20, 200, 97, 25); ++ stopButton.setBounds(15, 230, 107, 25); // runge/x11vnc + stopButton.setText("Stop"); + stopButton.setName("stopButton"); + stopButton.addActionListener(this); +@@ -503,8 +704,12 @@ + private javax.swing.JButton getCloseButton() { + if (closeButton == null) { + closeButton = new javax.swing.JButton(); +- closeButton.setBounds(20, 325, 97, 25); +- closeButton.setText("Close"); ++ closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc ++ if (viewer.ftpOnly) { ++ closeButton.setText("Quit"); ++ } else { ++ closeButton.setText("Close"); ++ } + closeButton.setName("closeButton"); + closeButton.addActionListener(this); + } +@@ -551,6 +756,7 @@ + //Select the second entry (e.g. C:\) + // localDrivesComboBox.setSelectedIndex(1); + localDrivesComboBox.addActionListener(this); ++//System.out.println("getLocalDrivesComboBox"); + } + updateDriveList = false; + return localDrivesComboBox; +@@ -567,6 +773,7 @@ + remoteDrivesComboBox.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + remoteDrivesComboBox.addActionListener(this); ++//System.out.println("getRemoteDrivesComboBox"); + + } + return remoteDrivesComboBox; +@@ -587,6 +794,7 @@ + localMachineLabel.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 11)); + localMachineLabel.setEditable(false); ++//System.out.println("getLocalMachineLabel"); + } + return localMachineLabel; + } +@@ -622,6 +830,7 @@ + localTopButton.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + localTopButton.addActionListener(this); ++//System.out.println("getLocalTopButton"); + } + return localTopButton; + } +@@ -638,6 +847,7 @@ + remoteTopButton.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); + remoteTopButton.addActionListener(this); ++//System.out.println("getRemoteTopButton"); + } + return remoteTopButton; + } +@@ -650,9 +860,24 @@ + private javax.swing.JList getLocalFileTable() { + if (localFileTable == null) { + localList = new Vector(0); ++ localListInfo = new Vector(0); + localFileTable = new JList(localList); ++ MouseMotionListener mlisten = new MouseMotionAdapter() { ++ public void mouseMoved(MouseEvent e) { ++ int index = localFileTable.locationToIndex(e.getPoint()); ++ if (index == lastLocalIndex) { ++ return; ++ } else if (index < 0) { ++ return; ++ } ++ lastLocalIndex = index; ++ connectionStatus.setText((String) localListInfo.get(index)); ++ } ++ }; + localFileTable.addMouseListener(this); ++ localFileTable.addMouseMotionListener(mlisten); + localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getLocalFileTable"); + } + return localFileTable; + } +@@ -669,6 +894,7 @@ + localScrollPane.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + localScrollPane.setName("localFileList"); ++//System.out.println("getLocalScrollPane"); + } + return localScrollPane; + } +@@ -680,10 +906,25 @@ + private javax.swing.JList getRemoteFileTable() { + if (remoteFileTable == null) { + remoteList = new Vector(0); ++ remoteListInfo = new Vector(0); + remoteFileTable = new JList(remoteList); ++ MouseMotionListener mlisten = new MouseMotionAdapter() { ++ public void mouseMoved(MouseEvent e) { ++ int index = remoteFileTable.locationToIndex(e.getPoint()); ++ if (index == lastRemoteIndex) { ++ return; ++ } else if (index < 0) { ++ return; ++ } ++ lastRemoteIndex = index; ++ connectionStatus.setText((String) remoteListInfo.get(index)); ++ } ++ }; + remoteFileTable.addMouseListener(this); ++ remoteFileTable.addMouseMotionListener(mlisten); + remoteFileTable.setSelectedValue("C:\\", false); + remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); ++//System.out.println("getRemoteFileTable"); + + } + return remoteFileTable; +@@ -698,6 +939,7 @@ + remoteScrollPane = new javax.swing.JScrollPane(); + remoteScrollPane.setViewportView(getRemoteFileTable()); + remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418)); ++//System.out.println("getRemoteScrollPane"); + } + return remoteScrollPane; + } +@@ -716,6 +958,7 @@ + remoteLocation.setBackground(new Color(255,255,238)); + remoteLocation.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getRemoteLocation"); + } + return remoteLocation; + } +@@ -732,6 +975,7 @@ + localLocation.setBackground( new Color(255,255,238)); + localLocation.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getLocalLocation"); + } + return localLocation; + } +@@ -748,6 +992,7 @@ + localStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + localStatus.setEditable(false); ++//System.out.println("getLocalStatus"); + } + return localStatus; + } +@@ -764,6 +1009,7 @@ + remoteStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); + remoteStatus.setEditable(false); ++//System.out.println("getRemoteStatus"); + } + return remoteStatus; + } +@@ -777,9 +1023,10 @@ + historyComboBox = new javax.swing.JComboBox(); + historyComboBox.setFont( + new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); +- historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0); ++ historyComboBox.insertItemAt(new String("Pulldown to view history; Press Escape to Close/Quit; Press Ctrl-R to Reset Panel."),0); + historyComboBox.setSelectedIndex(0); + historyComboBox.addActionListener(this); ++//System.out.println("getHistoryComboBox"); + } + return historyComboBox; + } +@@ -791,6 +1038,7 @@ + private javax.swing.JProgressBar getJProgressBar() { + if (jProgressBar == null) { + jProgressBar = new javax.swing.JProgressBar(); ++//System.out.println("getJProgressBar"); + } + return jProgressBar; + } +@@ -806,6 +1054,7 @@ + connectionStatus.setBackground(java.awt.Color.lightGray); + connectionStatus.setFont( + new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); ++//System.out.println("getConnectionStatus"); + } + connectionStatus.setEditable(false); + return connectionStatus; +@@ -815,7 +1064,12 @@ + * Implements Action listener. + */ + public void actionPerformed(ActionEvent evt) { +- System.out.println(evt.getSource()); ++// System.out.println(evt.getSource()); ++ ++ if (ignore_events) { ++ System.out.println("ignore_events: " + evt.getSource()); ++ return; ++ } + + if (evt.getSource() == closeButton) + { // Close Button +@@ -829,15 +1083,27 @@ + { + 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); ++ ++ // are these really needed? changeRemoteDrive() does them at the end. ++ if (false) { ++ remoteList.clear(); ++ remoteListInfo.clear(); ++ remoteFileTable.setListData(remoteList); ++ } + } + else if (evt.getSource() == localTopButton) + { +@@ -845,12 +1111,17 @@ + } + else if (evt.getSource() == remoteTopButton) + { ++//System.out.println("remoteTopButton"); // runge/x11vnc + changeRemoteDrive(); + } + else if(evt.getSource() == deleteButton) + { + doDelete(); + } ++ else if(evt.getSource() == refreshButton) ++ { ++ doRefresh(); ++ } + else if(evt.getSource()==newFolderButton) + { + doNewFolder(); +@@ -864,7 +1135,7 @@ + + private void doNewFolder() + { +- String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); ++ String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); + if(selectedTable.equals("remote")) + { + name = remoteLocation.getText()+name; +@@ -880,34 +1151,106 @@ + historyComboBox.setSelectedIndex(0); + } + } +- private void doClose() ++ public void doClose() + { ++ if (viewer.ftpOnly) { ++ viewer.disconnect(); ++ return; ++ } + try { + this.setVisible(false); +- viewer.rfb.writeFramebufferUpdateRequest( +- 0, +- 0, +- viewer.rfb.framebufferWidth, +- viewer.rfb.framebufferHeight, +- true); ++ viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth, ++ viewer.rfb.framebufferHeight, true); ++ ++ if (false) { ++ this.dispose(); ++ jContentPane = null; ++ } + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } ++ private void unSwing() { ++ jContentPane = null; ++ topPanel = null; ++ topPanelLocal = null; ++ topPanelRemote = null; ++ topPanelCenter = null; ++ statusPanel = null; ++ remotePanel = null; ++ localPanel = null; ++ buttonPanel = null; ++ sendButton = null; ++ receiveButton = null; ++ deleteButton = null; ++ newFolderButton = null; ++ stopButton = null; ++ closeButton = null; ++ dummyButton = null; ++ localDrivesComboBox = null; ++ remoteDrivesComboBox = null; ++ localMachineLabel = null; ++ remoteMachineLabel = null; ++ localTopButton = null; ++ remoteTopButton = null; ++ localScrollPane = null; ++ localFileTable = null; ++ remoteScrollPane = null; ++ remoteFileTable = null; ++ remoteLocation = null; ++ localLocation = null; ++ localStatus = null; ++ remoteStatus = null; ++ historyComboBox = null; ++ jProgressBar = null; ++ connectionStatus = null; ++ viewButton = null; ++ refreshButton = null; ++ } ++ ++ public void doReset() ++ { ++ try { ++ this.setVisible(false); ++ this.dispose(); ++ jContentPane = null; ++ try {Thread.sleep(500);} catch (InterruptedException e) {} ++ viewer.ftp_init(); ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } + ++ public void doOpen() ++ { ++ try { ++ this.setVisible(true); ++ if (false) { ++ this.initialize(); ++ } ++ } catch (Exception e) { ++ // TODO Auto-generated catch block ++ e.printStackTrace(); ++ } ++ } + private void doDelete() + { +- System.out.println("Delete Button Pressed"); ++// System.out.println("Delete Button Pressed"); + //Call this method to delete a file at server + if(selectedTable.equals("remote")) + { +- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be deleted + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + +@@ -916,7 +1259,7 @@ + // sf@2004 - Delete prompt + if (remoteList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -926,18 +1269,22 @@ + } + else + { +- String sFileName = ((String) this.localFileTable.getSelectedValue()); ++ Object selected = this.localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be deleted + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + // sf@2004 - Delete prompt + if (localList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -952,21 +1299,25 @@ + + private void doReceive() + { +- System.out.println("Received Button Pressed"); ++// System.out.println("Received Button Pressed"); + +- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be transfered + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + + // sf@2004 - Overwrite prompt + if (localList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -979,23 +1330,101 @@ + viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); + } + ++// begin runge/x11vnc ++ private void doRefresh() ++ { ++ System.out.println("Refreshing Local and Remote."); ++ refreshLocalLocation(); ++ refreshRemoteLocation(); ++ } ++ ++ 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; ++ } ++ Object selected = this.remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return null; ++ } ++ String sFileName = ((String) selected); ++ ++ 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); ++ System.out.println("remoteFileName: " + remoteFileName); ++if (false) { ++ char[] b = remoteFileName.toCharArray(); ++ for (int n = 0; n < b.length; n++) { ++ System.out.print(Integer.toHexString(b[n]) + " "); ++ } ++ System.out.println(""); ++ for (int n = 0; n < b.length; n++) { ++ System.out.print(b[n]); ++ } ++ System.out.println(""); ++} ++ ++ 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"); ++// System.out.println("Send Button Pressed"); + +- String sFileName = ((String) this.localFileTable.getSelectedValue()); ++ Object selected = this.localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sFileName = ((String) selected); + + // sf@2004 - Directory can't be transfered + if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) + { +- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); ++ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); + return; + } + + // sf@2004 - Overwrite prompt + if (remoteList.contains(sFileName)) + { +- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); ++ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); + if (r == JOptionPane.NO_OPTION) + return; + } +@@ -1013,6 +1442,7 @@ + // + private void doStop() + { ++ System.out.println("** Current Transfer Aborted **"); + viewer.rfb.fAbort = true; + } + /** +@@ -1024,6 +1454,14 @@ + System.out.println("History: " + message); + historyComboBox.insertItemAt(new String(message), 0); + } ++ ++ public void receivedRemoteDirectoryName(String str) { ++ if (doingShortcutDir) { ++ if (str.length() > 1) { ++ remoteLocation.setText(str); ++ } ++ } ++ } + + /** + * This method updates the file table to the current selection of the remoteComboBox +@@ -1034,11 +1472,44 @@ + remoteSelection = null; + + if (!updateDriveList) { +- String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; +- viewer.rfb.readServerDirectory(drive); +- remoteLocation.setText(drive); ++//System.out.println("changeRemoteDrive-A " + drive); // begin runge/x11vnc ++ Object selected = remoteDrivesComboBox.getSelectedItem(); ++ if (selected != null) { ++ String instr = selected.toString(); ++ if (instr != null) { ++System.out.println("changeRemoteDrive: instr='" + instr + "'"); ++ String drive = instr.substring(0,1)+ ":\\"; ++ if (instr.startsWith(" [")) { ++ int idx = instr.lastIndexOf(']'); ++ if (idx > 2) { ++ drive = instr.substring(2, idx); ++ } else { ++ drive = instr.substring(2); ++ } ++ if (drive.equals("Home")) { ++ drive = ""; ++ } ++ drive += "\\"; ++ doingShortcutDir = true; ++ } else { ++ doingShortcutDir = false; ++ drive = saveRemoteHack(drive); ++ } ++ gotShortcutDir = false; ++ viewer.rfb.readServerDirectory(drive); ++ if (!gotShortcutDir) { ++ remoteLocation.setText(drive); ++ } ++ } else { ++System.out.println("changeRemoteDrive: instr null"); ++ } ++ } else { ++System.out.println("changeRemoteDrive: selection null"); ++ } ++//System.out.println("changeRemoteDrive-B " + drive); // end runge/x11vnc + } + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + /** +@@ -1048,6 +1519,7 @@ + private void changeLocalDrive() + { + File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); ++System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc + if(currentDrive.canRead()) + { + localSelection = null; +@@ -1057,9 +1529,11 @@ + else + { + localList.clear(); ++ localListInfo.clear(); + 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 +@@ -1098,10 +1572,18 @@ + selectedTable = "remote"; + localFileTable.setBackground(new Color(238, 238, 238)); + remoteFileTable.setBackground(new Color(255, 255, 255)); +- String name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++ Object selected = remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String selstr = selected.toString(); ++ if (selstr == null) { ++ return; ++ } ++ String name = selstr.substring(1); + if( !name.substring(0, 2).equals(" [")) + remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); +- ++ + } + + /* +@@ -1115,10 +1597,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 + */ +@@ -1126,20 +1636,29 @@ + String name = null; + String action = null; + String drive = null; +- name = (remoteFileTable.getSelectedValue().toString()).substring(1); ++ Object selected = remoteFileTable.getSelectedValue(); ++ if (selected == null) { ++ return; ++ } ++ String sname = selected.toString(); ++ if (sname == null) { ++ return; ++ } ++ name = sname.substring(1); + + if (name.equals("[..]")) + { + action = "up"; + remoteSelection = null; + drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1); +- // JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); ++ // JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); + int index = drive.lastIndexOf("\\"); + drive = drive.substring(0, index + 1); + + remoteLocation.setText(drive); + viewer.rfb.readServerDirectory(drive); + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]")) +@@ -1149,6 +1668,7 @@ + remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); + drive = remoteLocation.getText(); + // ?? ++ viewRemote(); // runge/x11vnc + } + else + { +@@ -1159,10 +1679,12 @@ + remoteLocation.setText(drive); + viewer.rfb.readServerDirectory(drive); + remoteList.clear(); ++ remoteListInfo.clear(); + remoteFileTable.setListData(remoteList); + } + //remoteLocation.setText(drive); + } ++ + /** + * Updates the Local File Table based on selection. Called from MouseClicked handler + */ +@@ -1188,6 +1710,7 @@ + else if (currentSelection.isFile()) + { + localSelection = currentSelection.getAbsoluteFile(); ++ viewLocal(); // runge/x11vnc + } + else if (currentSelection.isDirectory()) + { +@@ -1201,13 +1724,22 @@ + * + */ + private String getTrimmedSelection(){ +- String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1); +- if(currentSelection.substring(0,1).equals("[") && +- currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ +- return currentSelection.substring(1,currentSelection.length()-1); +- } else { +- return currentSelection; +- } ++ String currentSelection = ""; ++ Object selected = localFileTable.getSelectedValue(); ++ if (selected == null) { ++ return currentSelection; ++ } ++ String selstr = selected.toString(); ++ if (selstr == null) { ++ return currentSelection; ++ } ++ currentSelection = selstr.substring(1); ++ if(currentSelection.substring(0,1).equals("[") && ++ currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ ++ return currentSelection.substring(1,currentSelection.length()-1); ++ } else { ++ return currentSelection; ++ } + } + + /* +@@ -1241,36 +1773,148 @@ + return null; + } + ++ String timeStr(long t) { ++ Date date = new Date(t); ++ return date.toString(); ++ } ++ String dotPast(double f, int n) { ++ String fs = "" + f; ++ int i = fs.lastIndexOf(".") + n; ++ if (i >= 0) { ++ int len = fs.length(); ++ if (i >= len) { ++ i = len-1; ++ } ++ fs = fs.substring(0, i); ++ } ++ return fs; ++ } ++ String sizeStr(int s) { ++ if (s < 0) { ++ return s + "? B"; ++ } else if (s < 1024) { ++ return s + " B"; ++ } else if (s < 1024 * 1024) { ++ double k = s / 1024.0; ++ String ks = dotPast(k, 3); ++ ++ return s + " (" + ks + " KB)"; ++ } else { ++ double m = s / (1024.0*1024.0); ++ String ms = dotPast(m, 3); ++ return s + " (" + ms + " MB)"; ++ } ++ } ++ ++ int max_char(String text) { ++ int maxc = 0; ++ char chars[] = text.toCharArray(); ++ for (int n = 0; n < chars.length; n++) { ++ if ((int) chars[n] > maxc) { ++ maxc = (int) chars[n]; ++ } ++ } ++ return maxc; ++ } + + /* + * Navigates the local file structure up or down one directory + */ + public void changeLocalDirectory(File dir) + { +- currentLocalDirectory = dir; // Updates Global ++ dir = saveLocalHack(dir); // runge/x11vnc ++ ++ if (dir == null) { ++ connectionStatus.setText("Error changing local directory."); ++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++ historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ + File allFiles[] = dir.listFiles(); // Reads files + String[] contents = dir.list(); + ++ if (contents == null || allFiles == null) { ++ connectionStatus.setText("Error changing local directory."); ++ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); ++ historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ ++ currentLocalDirectory = dir; // Updates Global ++// 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]); ++ } ++ } else { ++ return; ++ } ++// end runge/x11vnc ++ + localList.clear(); ++ localListInfo.clear(); + localList.addElement(" [..]"); ++ localListInfo.addElement(" [..]"); ++ ++ ArrayList DirInfo = new ArrayList(); ++ ArrayList FilInfo = new ArrayList(); ++ ++ Charset charset = Charset.forName("ISO-8859-1"); ++ CharsetDecoder decoder = charset.newDecoder(); ++ CharsetEncoder encoder = charset.newEncoder(); + + // Populate the Lists + for (int i = 0; i < contents.length; i++) + { +- if (allFiles[i].isDirectory()) ++ String f1 = contents[i]; ++ ++if (false) { ++ ++System.out.println("max_char: " + max_char(f1) + " " + f1); ++ if (max_char(f1) > 255) { ++ try { ++System.out.println("bbuf1"); ++ ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray())); ++System.out.println("bbuf2"); ++ CharBuffer cbuf = decoder.decode(bbuf); ++System.out.println("bbuf3"); ++ f1 = cbuf.toString(); ++System.out.println("did bbuf: " + f1); ++ } catch (Exception e) { ++ ; ++ } ++ } ++} ++ ++ String f2 = f1; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ String s = f2 + " \tLastmod: " + timeStr(allFiles[i].lastModified()) + " \t\tSize: " + sizeStr((int) allFiles[i].length()); ++ if (allFiles[i].isDirectory()) { + // localList.addElement("[" + contents[i] + "]"); +- DirsList.add(" [" + contents[i] + "]"); // sf@2004 +- else +- { ++ DirsList.add(" [" + f1 + "]"); // sf@2004 ++ DirInfo.add(s); ++ } else { + // localList.addElement(contents[i]); +- FilesList.add(" " + contents[i]); // sf@2004 ++ FilesList.add(" " + f1); // sf@2004 ++ FilInfo.add(s); + } + } + // sf@2004 +- for (int i = 0; i < DirsList.size(); i++) ++ for (int i = 0; i < DirsList.size(); i++) { + localList.addElement(DirsList.get(i)); +- for (int i = 0; i < FilesList.size(); i++) ++ localListInfo.addElement(DirInfo.get(i)); ++ } ++ for (int i = 0; i < FilesList.size(); i++) { + localList.addElement(FilesList.get(i)); ++ localListInfo.addElement(FilInfo.get(i)); ++ } + + FilesList.clear(); + DirsList.clear(); +@@ -1296,3 +1940,147 @@ + } + + } // @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; ++ TextViewer me; ++ ++ public TextViewer(String s, File f, boolean d) { ++ ++ delete_it = d; ++ file = f; ++ me = this; ++ ++ JScrollPane scrollPane = new JScrollPane(textArea, ++ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, ++ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); ++ ++ textArea.setEditable(false); ++ textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); ++ ++ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK); ++ AbstractAction escapeAction = new AbstractAction() { ++ public void actionPerformed(ActionEvent actionEvent) { ++ cleanse(); ++ me.dispose(); ++ } ++ }; ++ textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); ++ textArea.getInputMap().put(stroke, "escapeAction"); ++ textArea.getActionMap().put("escapeAction", escapeAction); ++ ++ 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/Makefile JavaViewer/Makefile +--- JavaViewer.orig/Makefile 2006-05-29 09:06:32.000000000 -0400 ++++ JavaViewer/Makefile 2010-05-18 20:53:32.000000000 -0400 +@@ -4,6 +4,7 @@ + + CP = cp + JC = javac ++JC_ARGS = -target 1.4 -source 1.4 + JAR = jar + ARCHIVE = VncViewer.jar + PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc +@@ -20,7 +21,7 @@ + all: $(CLASSES) $(ARCHIVE) + + $(CLASSES): $(SOURCES) +- $(JC) -O $(SOURCES) ++ $(JC) $(JC_ARGS) -O $(SOURCES) + + $(ARCHIVE): $(CLASSES) + $(JAR) cf $(ARCHIVE) $(CLASSES) +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 2010-11-30 22:13:58.000000000 -0500 +@@ -31,6 +31,7 @@ + import java.net.Socket; + import java.util.*; + import java.util.zip.*; ++import java.text.DateFormat; + + + class RfbProto { +@@ -86,8 +87,11 @@ + + // sf@2004 - FileTransfer part + ArrayList remoteDirsList; ++ ArrayList remoteDirsListInfo; + ArrayList remoteFilesList; ++ ArrayList remoteFilesListInfo; + ArrayList a; ++ ArrayList b; + boolean fFTInit = true; // sf@2004 + boolean fFTAllowed = true; + boolean fAbort = false; +@@ -199,6 +203,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 +215,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)); +@@ -215,9 +243,12 @@ + osw = new OutputStreamWriter(sock.getOutputStream()); + inDirectory2 = false; + a = new ArrayList(); ++ b = new ArrayList(); + // sf@2004 + remoteDirsList = new ArrayList(); ++ remoteDirsListInfo = new ArrayList(); + remoteFilesList = new ArrayList(); ++ remoteFilesListInfo = new ArrayList(); + + sendFileSource = ""; + } +@@ -420,7 +451,13 @@ + // + + int readServerMessageType() throws IOException { +- int msgType = is.readUnsignedByte(); ++ int msgType; ++ try { ++ msgType = is.readUnsignedByte(); ++ } catch (Exception e) { ++ viewer.disconnect(); ++ return -1; ++ } + + // If the session is being recorded: + if (rec != null) { +@@ -600,6 +637,7 @@ + contentParamT = is.readUnsignedByte(); + contentParamT = contentParamT << 8; + contentParam = contentParam | contentParamT; ++//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam); + if (contentType == rfbRDrivesList || contentType == rfbDirPacket) + { + readDriveOrDirectory(contentParam); +@@ -610,7 +648,7 @@ + } + else if (contentType == rfbFilePacket) + { +- receiveFileChunk(); ++ receiveFileChunk(); + } + else if (contentType == rfbEndOfFile) + { +@@ -618,6 +656,10 @@ + } + else if (contentType == rfbAbortFileTransfer) + { ++ System.out.println("rfbAbortFileTransfer: fFileReceptionRunning=" ++ + fFileReceptionRunning + " fAbort=" ++ + fAbort + " fFileReceptionError=" ++ + fFileReceptionError); + if (fFileReceptionRunning) + { + endOfReceiveFile(false); // Error +@@ -626,6 +668,11 @@ + { + // sf@2004 - Todo: Add TestPermission + // System.out.println("File Transfer Aborted!"); ++ ++ // runge: seems like we must at least read the remaining ++ // 8 bytes of the header, right? ++ int size = is.readInt(); ++ int length = is.readInt(); + } + + } +@@ -645,6 +692,7 @@ + { + System.out.println("ContentType: " + contentType); + } ++//System.out.println("FTM: done"); + } + + //Refactored from readRfbFileTransferMsg() +@@ -662,6 +710,7 @@ + + //Refactored from readRfbFileTransferMsg() + public void readDriveOrDirectory(int contentParam) throws IOException { ++//System.out.println("RDOD: " + contentParam + " " + inDirectory2); + if (contentParam == rfbADrivesList) + { + readFTPMsgDriveList(); +@@ -688,13 +737,21 @@ + + // Internally used. Write an Rfb message to the server + void writeRfbFileTransferMsg( +- int contentType, +- int contentParam, +- long size, // 0 : compression not supported - 1 : compression supported +- long length, +- String text) throws IOException ++ int contentType, ++ int contentParam, ++ long size, // 0 : compression not supported - 1 : compression supported ++ long length, ++ String text) throws IOException + { + byte b[] = new byte[12]; ++ byte byteArray[]; ++ ++ if (viewer.dsmActive) { ++ // need to send the rfbFileTransfer msg type twice for the plugin... ++ byte b2[] = new byte[1]; ++ b2[0] = (byte) rfbFileTransfer; ++ os.write(b2); ++ } + + b[0] = (byte) rfbFileTransfer; + b[1] = (byte) contentType; +@@ -702,7 +759,7 @@ + + byte by = 0; + long c = 0; +- length++; ++ + c = size & 0xFF000000; + by = (byte) (c >>> 24); + b[4] = by; +@@ -716,6 +773,32 @@ + by = (byte) c; + b[7] = by; + ++ if (text != null) { ++ byte byteArray0[] = text.getBytes(); ++ int maxc = max_char(text); ++ if (maxc > 255) { ++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")"); ++ byteArray0 = text.getBytes("UTF-8"); ++ } else if (maxc > 127) { ++ System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")"); ++ byteArray0 = text.getBytes("ISO-8859-1"); ++ } ++ byteArray = new byte[byteArray0.length + 1]; ++ for (int i = 0; i < byteArray0.length; i++) { ++ byteArray[i] = byteArray0[i]; ++ } ++ byteArray[byteArray.length - 1] = 0; ++System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length); ++ ++ // will equal length for ascii, ISO-8859-1, more for UTF-8 ++ length = byteArray.length; ++ ++ //length++; // used to not include null byte at end. ++ } else { ++ String moo = "moo"; ++ byteArray = moo.getBytes(); ++ } ++ + c = length & 0xFF000000; + by = (byte) (c >>> 24); + b[8] = by; +@@ -729,29 +812,91 @@ + by = (byte) c; + b[11] = by; + os.write(b); ++ ++//System.out.println("size: " + size + " length: " + length + " text: " + text); + + + if (text != null) + { +- byte byteArray[] = text.getBytes(); +- byte byteArray2[] = new byte[byteArray.length + 1]; +- for (int i = 0; i < byteArray.length; i++) { +- byteArray2[i] = byteArray[i]; ++ os.write(byteArray); ++ } ++ } ++ ++ int max_char(String text) { ++ int maxc = 0; ++ char chars[] = text.toCharArray(); ++ for (int n = 0; n < chars.length; n++) { ++ if ((int) chars[n] > maxc) { ++ maxc = (int) chars[n]; + } +- byteArray2[byteArray2.length - 1] = 0; +- os.write(byteArray2); + } +- ++ return maxc; + } + ++ String guess_encoding(char[] chars) { ++ boolean saw_high_char = false; ++ ++ for (int i = 0; i < chars.length; i++) { ++ if (chars[i] == '\0') { ++ break; ++ } ++ if (chars[i] >= 128) { ++ saw_high_char = true; ++ break; ++ } ++ } ++ if (!saw_high_char) { ++ return "ASCII"; ++ } ++ char prev = 1; ++ boolean valid_utf8 = true; ++ int n = 0; ++ for (int i = 0; i < chars.length; i++) { ++ if (chars[i] == '\0') { ++ break; ++ } ++ char c = chars[i]; ++ if (prev < 128 && c >= 128) { ++ if (c >> 5 == 0x6) { ++ n = 1; ++ } else if (c >> 4 == 0xe) { ++ n = 2; ++ } else if (c >> 3 == 0x1e) { ++ n = 3; ++ } else if (c >> 2 == 0x3e) { ++ n = 4; ++ } else { ++ valid_utf8 = false; ++ break; ++ } ++ } else { ++ if (n > 0) { ++ if (c < 128) { ++ valid_utf8 = false; ++ break; ++ } ++ n--; ++ } ++ } ++ ++ prev = c; ++ } ++ if (valid_utf8) { ++ return "UTF-8"; ++ } else { ++ return "ISO-8859-1"; ++ } ++ } ++ ++ + //Internally used. Write an rfb message to the server for sending files ONLY + int writeRfbFileTransferMsgForSendFile( +- int contentType, +- int contentParam, +- long size, +- long length, +- String source +- ) throws IOException ++ int contentType, ++ int contentParam, ++ long size, ++ long length, ++ String source ++ ) throws IOException + { + File f = new File(source); + fis = new FileInputStream(f); +@@ -768,50 +913,47 @@ + + while (bytesRead!=-1) + { +- counter += bytesRead; +- myDeflater.setInput(byteBuffer, 0, bytesRead); +- myDeflater.finish(); +- compressedSize = myDeflater.deflate(CompressionBuffer); +- myDeflater.reset(); +- // If the compressed data is larger than the original one, we're dealing with +- // already compressed data +- if (compressedSize > bytesRead) +- fCompress = false; +- this.writeRfbFileTransferMsg( +- contentType, +- contentParam, +- (fCompress ? 1 : 0), +- (fCompress ? compressedSize-1 : bytesRead-1), +- null +- ); +- // Todo: Test write error ! +- os.write( +- fCompress ? CompressionBuffer : byteBuffer, +- 0, +- fCompress ? compressedSize : bytesRead +- ); +- +- // Todo: test read error ! +- bytesRead = fis.read(byteBuffer); +- +- // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); +- viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); +- viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); +- +- if (fAbort == true) +- { +- fAbort = false; +- fError = true; +- break; +- } +- try +- { +- Thread.sleep(5); +- } +- catch(InterruptedException e) +- { +- System.err.println("Interrupted"); +- } ++ counter += bytesRead; ++ myDeflater.setInput(byteBuffer, 0, bytesRead); ++ myDeflater.finish(); ++ compressedSize = myDeflater.deflate(CompressionBuffer); ++ myDeflater.reset(); ++ // If the compressed data is larger than the original one, we're dealing with ++ // already compressed data ++ if (compressedSize > bytesRead) ++ fCompress = false; ++ this.writeRfbFileTransferMsg( ++ contentType, ++ contentParam, ++ (fCompress ? 1 : 0), ++// RUNGE (fCompress ? compressedSize-1 : bytesRead-1), ++ (fCompress ? compressedSize : bytesRead), ++ null ++ ); ++ // Todo: Test write error ! ++ os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead); ++ ++ // Todo: test read error ! ++ bytesRead = fis.read(byteBuffer); ++ ++ // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); ++ viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); ++ viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); ++ ++ if (fAbort == true) ++ { ++ fAbort = false; ++ fError = true; ++ break; ++ } ++ try ++ { ++ Thread.sleep(5); ++ } ++ catch(InterruptedException e) ++ { ++ System.err.println("Interrupted"); ++ } + } + + writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null); +@@ -831,24 +973,30 @@ + { + System.out.print((char) is.readUnsignedByte()); + } ++ System.out.println(""); ++ ++ if (size == rfbRErrorCmd || size == -1) { ++ viewer.ftp.enableButtons(); ++ viewer.ftp.connectionStatus.setText("Remote file not available for writing."); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ return; ++ } + +- int ret = writeRfbFileTransferMsgForSendFile( +- rfbFilePacket, +- 0, +- 0, +- 0, +- sendFileSource); ++ int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource); + + viewer.ftp.refreshRemoteLocation(); + if (ret != 1) + { + viewer.ftp.connectionStatus.setText(" > Error - File NOT sent"); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) ++ + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0); + } + else + { + viewer.ftp.connectionStatus.setText(" > File sent"); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) ++ + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0); + } + viewer.ftp.historyComboBox.setSelectedIndex(0); + viewer.ftp.enableButtons(); +@@ -907,7 +1055,7 @@ + //Handles acknowledgement that the file has been deleted on the server + void deleteRemoteFileFeedback() throws IOException + { +- is.readInt(); ++ int ret = is.readInt(); + int length = is.readInt(); + String f = ""; + for (int i = 0; i < length; i++) +@@ -916,7 +1064,11 @@ + } + + viewer.ftp.refreshRemoteLocation(); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++ if (ret == -1) { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0); ++ } else { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); ++ } + viewer.ftp.historyComboBox.setSelectedIndex(0); + } + +@@ -926,12 +1078,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbCommand, +- rfbCFileDelete, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -943,7 +1090,7 @@ + // Handles acknowledgement that the directory has been created on the server + void createRemoteDirectoryFeedback() throws IOException + { +- is.readInt(); ++ int ret = is.readInt(); + int length = is.readInt(); + String f=""; + for (int i = 0; i < length; i++) +@@ -951,7 +1098,11 @@ + f += (char)is.readUnsignedByte(); + } + viewer.ftp.refreshRemoteLocation(); +- viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++ if (ret == -1) { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0); ++ } else { ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); ++ } + viewer.ftp.historyComboBox.setSelectedIndex(0); + } + +@@ -961,12 +1112,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbCommand, +- rfbCDirCreate, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -979,15 +1125,13 @@ + { + try + { ++//System.out.println("requestRemoteFile text: " + text); ++//System.out.println("requestRemoteFile leng: " + text.length()); + String temp = text; + receivePath = localPath; + +- writeRfbFileTransferMsg( +- rfbFileTransferRequest, +- 0, +- 1, // 0 : compression not supported - 1 : compression supported +- temp.length(), +- temp); ++ // 0 : compression not supported - 1 : compression supported ++ writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp); + } + catch (IOException e) + { +@@ -1004,6 +1148,9 @@ + viewer.ftp.disableButtons(); + int size = is.readInt(); + int length = is.readInt(); ++ ++//System.out.println("receiveFileHeader size: " + size); ++//System.out.println("receiveFileHeader leng: " + length); + + String tempName = ""; + for (int i = 0; i < length; i++) +@@ -1011,6 +1158,15 @@ + tempName += (char) is.readUnsignedByte(); + } + ++ if (size == rfbRErrorCmd || size == -1) { ++ fFileReceptionRunning = false; ++ viewer.ftp.enableButtons(); ++ viewer.ftp.connectionStatus.setText("Remote file not available for reading."); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ return; ++ } ++ + // sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for + // backward compatibility reasons...) + int sizeH = is.readInt(); +@@ -1021,7 +1177,16 @@ + fileSize=0; + fileChunkCounter = 0; + String fileName = receivePath; +- fos = new FileOutputStream(fileName); ++ try { ++ fos = new FileOutputStream(fileName); ++ } catch (Exception e) { ++ fFileReceptionRunning = false; ++ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); ++ viewer.ftp.enableButtons(); ++ return; ++ } + writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null); + } + +@@ -1085,7 +1250,13 @@ + fAbort = false; + fFileReceptionError = true; + writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); +- ++ ++ //runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent. ++ try {Thread.sleep(500);} catch (InterruptedException e) {} ++ viewer.ftp.enableButtons(); ++ viewer.ftp.refreshLocalLocation(); ++ viewer.ftp.connectionStatus.setText(" > Error - File NOT received"); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0); + } + // sf@2004 - For old FT protocole only + /* +@@ -1104,7 +1275,7 @@ + int length = is.readInt(); + fileSize=0; + fos.close(); +- ++ + viewer.ftp.refreshLocalLocation(); + if (fReceptionOk && !fFileReceptionError) + { +@@ -1132,12 +1303,7 @@ + try + { + String temp = text; +- writeRfbFileTransferMsg( +- rfbDirContentRequest, +- rfbRDirContent, +- 0, +- temp.length(), +- temp); ++ writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp); + } + catch (IOException e) + { +@@ -1197,11 +1363,80 @@ + str += temp; + } + } ++ // runge ++ viewer.ftp.receivedRemoteDirectoryName(str); + // viewer.ftp.changeRemoteDirectory(str); + + } + } + ++ int zogswap(int n) { ++ long l = n; ++ if (l < 0) { ++ l += 0x100000000L; ++ } ++ l = l & 0xFFFFFFFF; ++ l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24); ++ return (int) l; ++ } ++ ++ int windozeToUnix(int L, int H) { ++ long L2 = zogswap(L); ++ long H2 = zogswap(H); ++ long unix = (H2 << 32) + L2; ++ unix -= 11644473600L * 10000000L; ++ unix /= 10000000L; ++ //System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2); ++ return (int) unix; ++ } ++ ++ String timeStr(int t, int h) { ++ if (h == 0) { ++ // x11vnc/libvncserver unix ++ t = zogswap(t); ++ } else { ++ // ultra (except if h==0 by chance) ++ t = windozeToUnix(t, h); ++ } ++ long tl = (long) t; ++ Date date = new Date(tl * 1000); ++ if (true) { ++ return date.toString(); ++ } else { ++ return DateFormat.getDateTimeInstance().format(date); ++ } ++ } ++ ++ String dotPast(double f, int n) { ++ String fs = "" + f; ++ int i = fs.lastIndexOf(".") + n; ++ if (i >= 0) { ++ int len = fs.length(); ++ if (i >= len) { ++ i = len-1; ++ } ++ fs = fs.substring(0, i); ++ } ++ return fs; ++ } ++ String sizeStr(int s) { ++ s = zogswap(s); ++ if (s < 0) { ++ return s + "? B"; ++ } else if (s < 1024) { ++ return s + " B"; ++ } else if (s < 1024 * 1024) { ++ double k = s / 1024.0; ++ String ks = dotPast(k, 3); ++ ++ return s + " (" + ks + " KB)"; ++ } else { ++ double m = s / (1024.0*1024.0); ++ String ms = dotPast(m, 3); ++ return s + " (" + ms + " MB)"; ++ } ++ } ++ + //Internally used to receive directory content from server + //Here, the server sends one file/directory with it's attributes + void readFTPMsgDirectoryListContent() throws IOException +@@ -1217,17 +1452,32 @@ + dwReserved0, + dwReserved1; + long ftCreationTime, ftLastAccessTime, ftLastWriteTime; ++ int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL; ++ int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH; + char cFileName, cAlternateFileName; + int length = 0; + is.readInt(); + length = is.readInt(); ++ ++ char[] chars = new char[4*length]; ++ int char_cnt = 0; ++ for (int i = 0; i < chars.length; i++) { ++ chars[i] = '\0'; ++ } ++ + dwFileAttributes = is.readInt(); + length -= 4; +- ftCreationTime = is.readLong(); ++ //ftCreationTime = is.readLong(); ++ ftCreationTimeL = is.readInt(); ++ ftCreationTimeH = is.readInt(); + length -= 8; +- ftLastAccessTime = is.readLong(); ++ //ftLastAccessTime = is.readLong(); ++ ftLastAccessTimeL = is.readInt(); ++ ftLastAccessTimeH = is.readInt(); + length -= 8; +- ftLastWriteTime = is.readLong(); ++ //ftLastWriteTime = is.readLong(); ++ ftLastWriteTimeL = is.readInt(); ++ ftLastWriteTimeH = is.readInt(); + length -= 8; + nFileSizeHigh = is.readInt(); + length -= 4; +@@ -1239,10 +1489,12 @@ + length -= 4; + cFileName = (char) is.readUnsignedByte(); + length--; ++ chars[char_cnt++] = cFileName; + while (cFileName != '\0') + { + fileName += cFileName; + cFileName = (char) is.readUnsignedByte(); ++ chars[char_cnt++] = cFileName; + length--; + } + cAlternateFileName = (char) is.readByte(); +@@ -1253,7 +1505,28 @@ + cAlternateFileName = (char) is.readUnsignedByte(); + length--; + } +- if (dwFileAttributes == 268435456 ++ String guessed = guess_encoding(chars); ++ if (!guessed.equals("ASCII")) { ++ System.out.println("guess: " + guessed + "\t" + fileName); ++ } ++ if (guessed.equals("UTF-8")) { ++ try { ++ byte[] bytes = new byte[char_cnt-1]; ++ for (int i=0; i < char_cnt-1; i++) { ++ bytes[i] = (byte) chars[i]; ++ } ++ String newstr = new String(bytes, "UTF-8"); ++ fileName = newstr; ++ } catch (Exception e) { ++ System.out.println("failed to convert bytes to UTF-8 based string"); ++ } ++ } ++ for (int i = 0; i < char_cnt; i++) { ++ //System.out.println("char[" + i + "]\t" + (int) chars[i]); ++ } ++ if (fileName.length() <= 0) { ++ ; ++ } else if (dwFileAttributes == 268435456 + || dwFileAttributes == 369098752 + || dwFileAttributes == 285212672 + || dwFileAttributes == 271056896 +@@ -1263,11 +1536,74 @@ + || dwFileAttributes == 369623040) + { + fileName = " [" + fileName + "]"; +- remoteDirsList.add(fileName); // sf@2004 +- } +- else +- { +- remoteFilesList.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; ++ } ++ } ++ //String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " " + fileName; ++ String f2 = fileName; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); ++ //s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL); ++ if (i >= 0) { ++ remoteDirsList.add(i, fileName); ++ remoteDirsListInfo.add(i, s); ++ } else { ++ remoteDirsList.add(fileName); ++ remoteDirsListInfo.add(s); ++ } ++// end runge/x11vnc ++ } else { ++// 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; ++ } ++ } ++ String f2 = fileName; ++ if (f2.length() < 24) { ++ for (int ik = f2.length(); ik < 24; ik++) { ++ f2 = f2 + " "; ++ } ++ } ++ ++if (false) { ++System.out.println("fileName: " + f2); ++System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL); ++System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH); ++System.out.println("nFileSizeLow: " + nFileSizeLow); ++} ++ ++ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); ++ //s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + " Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow); ++ if (i >= 0) { ++ remoteFilesList.add(i, fileName); ++ remoteFilesListInfo.add(i, s); ++ } else { ++ remoteFilesList.add(fileName); ++ remoteFilesListInfo.add(s); ++ } ++// end runge/x11vnc + } + + // a.add(fileName); +@@ -1282,14 +1618,32 @@ + + // sf@2004 + a.clear(); +- for (int i = 0; i < remoteDirsList.size(); i++) ++ b.clear(); ++ for (int i = 0; i < remoteDirsList.size(); i++) { + a.add(remoteDirsList.get(i)); +- for (int i = 0; i < remoteFilesList.size(); i++) ++ b.add(remoteDirsListInfo.get(i)); ++ } ++ for (int i = 0; i < remoteFilesList.size(); i++) { + a.add(remoteFilesList.get(i)); ++ ++ b.add(remoteFilesListInfo.get(i)); ++ } + remoteDirsList.clear(); ++ remoteDirsListInfo.clear(); + remoteFilesList.clear(); ++ remoteFilesListInfo.clear(); + +- viewer.ftp.printDirectory(a); ++// begin runge/x11vnc ++ // Hack for double listing at startup... probably libvncserver bug.. ++ readServerDriveListCnt++; ++ if (readServerDriveListCnt == 2) { ++ if (System.currentTimeMillis() - readServerDriveListTime < 2000) { ++//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); ++ return; ++ } ++ } ++// end runge/x11vnc ++ viewer.ftp.printDirectory(a, b); + } + + //Internally used to signify the drive requested is not ready +@@ -1299,6 +1653,8 @@ + System.out.println("Remote Drive unavailable"); + viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)"); + viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable"); ++ viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0); ++ viewer.ftp.historyComboBox.setSelectedIndex(0); + } + + //Call this method to request the list of drives on the server. +@@ -1306,12 +1662,11 @@ + { + try + { +- viewer.rfb.writeRfbFileTransferMsg( +- RfbProto.rfbDirContentRequest, +- RfbProto.rfbRDrivesList, +- 0, +- 0, +- null); ++ viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null); ++// begin runge/x11vnc ++ readServerDriveListCnt = 0; ++ readServerDriveListTime = System.currentTimeMillis(); ++// end runge/x11vnc + } + catch (IOException e) + { +@@ -1355,21 +1710,21 @@ + int h, + boolean incremental) + throws IOException { +- if (!viewer.ftp.isVisible()) { +- byte[] b = new byte[10]; ++ if (!viewer.ftp.isVisible()) { ++ byte[] b = new byte[10]; + +- b[0] = (byte) FramebufferUpdateRequest; +- b[1] = (byte) (incremental ? 1 : 0); +- b[2] = (byte) ((x >> 8) & 0xff); +- b[3] = (byte) (x & 0xff); +- b[4] = (byte) ((y >> 8) & 0xff); +- b[5] = (byte) (y & 0xff); +- b[6] = (byte) ((w >> 8) & 0xff); +- b[7] = (byte) (w & 0xff); +- b[8] = (byte) ((h >> 8) & 0xff); +- b[9] = (byte) (h & 0xff); ++ b[0] = (byte) FramebufferUpdateRequest; ++ b[1] = (byte) (incremental ? 1 : 0); ++ b[2] = (byte) ((x >> 8) & 0xff); ++ b[3] = (byte) (x & 0xff); ++ b[4] = (byte) ((y >> 8) & 0xff); ++ b[5] = (byte) (y & 0xff); ++ b[6] = (byte) ((w >> 8) & 0xff); ++ b[7] = (byte) (w & 0xff); ++ b[8] = (byte) ((h >> 8) & 0xff); ++ b[9] = (byte) (h & 0xff); + +- os.write(b); ++ os.write(b); + } + } + +@@ -1482,7 +1837,13 @@ + b[6] = (byte) ((text.length() >> 8) & 0xff); + b[7] = (byte) (text.length() & 0xff); + +- System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++ if (false && max_char(text) > 255) { ++ System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length()); ++ } else if (max_char(text) > 127) { ++ System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length()); ++ } else { ++ System.arraycopy(text.getBytes(), 0, b, 8, text.length()); ++ } + + os.write(b); + // } +@@ -1506,6 +1867,37 @@ + final static int META_MASK = InputEvent.META_MASK; + final static int ALT_MASK = InputEvent.ALT_MASK; + ++ void writeWheelEvent(MouseWheelEvent evt) throws IOException { ++ eventBufLen = 0; ++ ++ int x = evt.getX(); ++ int y = evt.getY(); ++ ++ if (x < 0) x = 0; ++ if (y < 0) y = 0; ++ ++ int ptrmask; ++ ++ int clicks = evt.getWheelRotation(); ++ System.out.println("writeWheelEvent: clicks: " + clicks); ++ if (clicks > 0) { ++ ptrmask = 16; ++ } else if (clicks < 0) { ++ ptrmask = 8; ++ } else { ++ return; ++ } ++ ++ eventBuf[eventBufLen++] = (byte) PointerEvent; ++ eventBuf[eventBufLen++] = (byte) ptrmask; ++ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (x & 0xff); ++ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); ++ eventBuf[eventBufLen++] = (byte) (y & 0xff); ++ ++ os.write(eventBuf, 0, eventBufLen); ++ } ++ + // + // Write a pointer event message. We may need to send modifier key events + // around it to set the correct modifier state. +@@ -1610,6 +2002,21 @@ + + boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); + ++ if (viewer.debugKeyboard) { ++ System.out.println("----------------------------------------"); ++ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); ++ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); ++ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); ++ System.out.println("evt.getID: " + evt.getID()); ++ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); ++ System.out.println("evt.isActionKey: " + evt.isActionKey()); ++ System.out.println("evt.isControlDown: " + evt.isControlDown()); ++ System.out.println("evt.getModifiers: " + evt.getModifiers()); ++ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); ++ System.out.println("evt.paramString: " + evt.paramString()); ++ } ++ ++ + int key; + if (evt.isActionKey()) { + +@@ -1685,6 +2092,9 @@ + default : + return; + } ++ if (key == 0xffc2 && viewer.mapF5_to_atsign) { ++ key = 0x40; ++ } + + } else { + +@@ -1794,6 +2204,16 @@ + int oldModifiers = 0; + + void writeModifierKeyEvents(int newModifiers) { ++ if(viewer.forbid_Ctrl_Alt) { ++ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { ++ int orig = newModifiers; ++ newModifiers &= ~ALT_MASK; ++ newModifiers &= ~CTRL_MASK; ++ if (viewer.debugKeyboard) { ++ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); ++ } ++ } ++ } + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + +diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java +--- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 ++++ JavaViewer/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 +@@ -0,0 +1,2067 @@ ++/* ++ * 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, 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. ++ * ++ */ ++ ++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; ++ boolean debug_certs = false; ++ ++ /* sockets */ ++ SSLSocket socket = null; ++ SSLSocketFactory factory; ++ ++ /* fallback for Proxy connection */ ++ boolean proxy_in_use = false; ++ boolean proxy_failure = false; ++ public DataInputStream is = null; ++ public OutputStream os = null; ++ ++ /* strings from user WRT proxy: */ ++ String proxy_auth_string = 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 trustsrv_ctx; ++ SSLContext trusturl_ctx; ++ SSLContext trustone_ctx; ++ ++ /* corresponding trust managers */ ++ TrustManager[] trustAllCerts; ++ TrustManager[] trustSrvCert; ++ TrustManager[] trustUrlCert; ++ TrustManager[] trustOneCert; ++ ++ /* client-side SSL auth key (oneTimeKey=...) */ ++ KeyManager[] mykey = null; ++ ++ boolean user_wants_to_see_cert = true; ++ String cert_fail = null; ++ ++ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ ++ java.security.cert.Certificate[] trustallCerts = null; ++ java.security.cert.Certificate[] trustsrvCerts = null; ++ java.security.cert.Certificate[] trusturlCerts = null; ++ ++ /* utility to decode hex oneTimeKey=... and serverCert=... */ ++ 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; ++ ++ debug_certs = v.debugCerts; ++ ++ /* we will first try default factory for certification: */ ++ ++ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); ++ ++ dbg("SSL startup: " + host + " " + port); ++ ++ ++ /* create trust managers to be used if initial handshake fails: */ ++ ++ trustAllCerts = new TrustManager[] { ++ /* ++ * this one accepts everything. Only used if user ++ * has disabled checking (trustAllVncCerts=yes) ++ * or when we grab the cert to show it to them in ++ * a dialog and ask them to manually verify/accept it. ++ */ ++ 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 and stored in ++ * trusturlCerts. ++ */ ++ 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 (URL)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trusturlCerts' */ ++ if (trusturlCerts == null) { ++ throw new CertificateException( ++ "No Trust url Certs array."); ++ } ++ if (trusturlCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust url Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trusturlCerts.length) { ++ throw new CertificateException( ++ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trusturlCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("URL: cert mismatch at i=" + i); ++ dbg("URL: cert mismatch cert" + certs[i]); ++ dbg("URL: cert mismatch url" + trusturlCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("URL: cert info at i=" + i); ++ dbg("URL: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("URL: cert info url" + trusturlCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != URL Cert Chain."); ++ } ++ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustSrvCert = new TrustManager[] { ++ /* ++ * this one accepts cert given to us in the serverCert ++ * Applet Parameter we were started with. It is ++ * currently a fatal error if the VNC Server's cert ++ * doesn't match it. ++ */ ++ 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 (SRV)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustsrvCerts' */ ++ if (trustsrvCerts == null) { ++ throw new CertificateException( ++ "No Trust srv Certs array."); ++ } ++ if (trustsrvCerts.length < 1) { ++ throw new CertificateException( ++ "No Trust srv Certs."); ++ } ++ if (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustsrvCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustsrvCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("SRV: cert mismatch at i=" + i); ++ dbg("SRV: cert mismatch cert" + certs[i]); ++ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("SRV: cert info at i=" + i); ++ dbg("SRV: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("SRV: cert info srv" + trustsrvCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); ++ } ++ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ trustOneCert = new TrustManager[] { ++ /* ++ * this one accepts only the retrieved server ++ * cert by SSLSocket by this applet we stored in ++ * trustallCerts that user has accepted or applet ++ * parameter trustAllVncCerts=yes is set. This is ++ * for when we reconnect after the user has manually ++ * accepted the trustall cert in the dialog (or set ++ * trustAllVncCerts=yes applet param.) ++ */ ++ 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 (ONE)"); ++ } ++ public void checkServerTrusted( ++ java.security.cert.X509Certificate[] certs, ++ String authType) throws CertificateException { ++ /* we want to check 'certs' against 'trustallCerts' */ ++ 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 (certs == null) { ++ throw new CertificateException( ++ "No this-certs array."); ++ } ++ if (certs.length < 1) { ++ throw new CertificateException( ++ "No this-certs Certs."); ++ } ++ if (certs.length != trustallCerts.length) { ++ throw new CertificateException( ++ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); ++ } ++ boolean ok = true; ++ for (int i = 0; i < certs.length; i++) { ++ if (! trustallCerts[i].equals(certs[i])) { ++ ok = false; ++ dbg("ONE: cert mismatch at i=" + i); ++ dbg("ONE: cert mismatch cert" + certs[i]); ++ dbg("ONE: cert mismatch all" + trustallCerts[i]); ++ } ++ if (debug_certs) { ++ dbg("\n***********************************************"); ++ dbg("ONE: cert info at i=" + i); ++ dbg("ONE: cert info cert" + certs[i]); ++ dbg("==============================================="); ++ dbg("ONE: cert info all" + trustallCerts[i]); ++ dbg("***********************************************"); ++ } ++ } ++ if (!ok) { ++ throw new CertificateException( ++ "Server Cert Chain != TRUSTALL Cert Chain."); ++ } ++ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); ++ } ++ } ++ }; ++ ++ /* ++ * The above TrustManagers are used: ++ * ++ * 1) to retrieve the server cert in case of failure to ++ * display it to the user in a dialog. ++ * 2) to subsequently connect to the server if user agrees. ++ */ ++ ++ /* ++ * build oneTimeKey cert+key if supplied in applet parameter: ++ */ ++ 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); ++ if (debug_certs) { ++ dbg("one time key " + ff); ++ } ++ ++ 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)); ++ if (debug_certs) { ++ dbg("one time cert" + 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(); ++ } ++ ++ /* ++ * build serverCert cert if supplied in applet parameter: ++ */ ++ if (viewer.serverCert != null) { ++ CertificateFactory cf = CertificateFactory.getInstance("X.509"); ++ byte[] cert = hex2bytes(viewer.serverCert); ++ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); ++ trustsrvCerts = new Certificate[c.toArray().length]; ++ if (c.size() == 1) { ++ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); ++ trustsrvCerts[0] = tmpcert; ++ } else { ++ trustsrvCerts = (Certificate[]) c.toArray(); ++ } ++ } ++ ++ /* the trust loc certs context: */ ++ try { ++ trustloc_ctx = SSLContext.getInstance("SSL"); ++ ++ /* ++ * below is a failed attempt to get jvm's default ++ * trust manager using null (below) makes it so ++ * for HttpsURLConnection the server cannot be ++ * verified (no prompting.) ++ */ ++ if (false) { ++ boolean didit = false; ++ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); ++ tmf.init((KeyStore) null); ++ TrustManager [] tml = tmf.getTrustManagers(); ++ for (int i = 0; i < tml.length; i++) { ++ TrustManager tm = tml[i]; ++ if (tm instanceof X509TrustManager) { ++ TrustManager tm1[] = new TrustManager[1]; ++ tm1[0] = tm; ++ trustloc_ctx.init(mykey, tm1, null); ++ didit = true; ++ break; ++ } ++ } ++ if (!didit) { ++ trustloc_ctx.init(mykey, null, null); ++ } ++ } else { ++ /* we have to set trust manager to null */ ++ trustloc_ctx.init(mykey, null, null); ++ } ++ ++ } catch (Exception e) { ++ String msg = "SSL trustloc_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust all certs context: */ ++ 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); ++ } ++ ++ /* the trust url certs context: */ ++ 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); ++ } ++ ++ /* the trust srv certs context: */ ++ try { ++ trustsrv_ctx = SSLContext.getInstance("SSL"); ++ trustsrv_ctx.init(mykey, trustSrvCert, new ++ java.security.SecureRandom()); ++ ++ } catch (Exception e) { ++ String msg = "SSL trustsrv_ctx FAILED."; ++ dbg(msg); ++ throw new Exception(msg); ++ } ++ ++ /* the trust the one cert from server context: */ ++ 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); ++ } ++ } ++ ++ /* ++ * we call this early on to 1) check for a proxy, 2) grab ++ * Browser/JVM accepted HTTPS cert. ++ */ ++ public void check_for_proxy_and_grab_vnc_server_cert() { ++ ++ trusturlCerts = null; ++ proxy_in_use = false; ++ ++ if (viewer.ignoreProxy) { ++ /* applet param says skip it. */ ++ /* the downside is we do not set trusturlCerts for comparison later... */ ++ /* nor do we autodetect x11vnc for GET=1. */ ++ return; ++ } ++ ++ dbg("------------------------------------------------"); ++ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); ++ ++ dbg("TRYING HTTPS:"); ++ String ustr = "https://" + host + ":"; ++ if (viewer.httpsPort != null) { ++ ustr += viewer.httpsPort; ++ } else { ++ ustr += port; ++ } ++ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTPS URL connection to host:port */ ++ URL url = new URL(ustr); ++ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); ++ ++ if (mykey != null) { ++ /* with oneTimeKey (mykey) we can't use the default SSL context */ ++ if (trustsrvCerts != null) { ++ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); ++ } else if (trustloc_ctx != null) { ++ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); ++ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); ++ } ++ } ++ ++ https.setUseCaches(false); ++ https.setRequestMethod("GET"); ++ https.setRequestProperty("Pragma", "No-Cache"); ++ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); ++ https.setDoInput(true); ++ ++ dbg("trying https.connect()"); ++ https.connect(); ++ ++ dbg("trying https.getServerCertificates()"); ++ 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; ++ dbg("An HTTPS proxy is in use. There may be connection problems."); ++ } ++ ++ dbg("trying https.getContent()"); ++ Object output = https.getContent(); ++ dbg("trying https.disconnect()"); ++ https.disconnect(); ++ if (! viewer.GET) { ++ String header = https.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (1), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ ++ } catch(Exception e) { ++ dbg("HttpsURLConnection: " + e.getMessage()); ++ } ++ ++ if (proxy_in_use) { ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } else if (trusturlCerts != null && !viewer.forceProxy) { ++ /* Allow user to require HTTP check? use forceProxy for now. */ ++ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ return; ++ } ++ ++ /* ++ * XXX need to remember scenario where this extra check ++ * gives useful info. User's Browser proxy settings? ++ */ ++ dbg("TRYING HTTP:"); ++ ustr = "http://" + host + ":" + port; ++ ustr += viewer.urlPrefix + "/index.vnc"; ++ dbg("ustr is: " + ustr); ++ ++ try { ++ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ ++ 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); ++ ++ dbg("trying http.connect()"); ++ http.connect(); ++ ++ if (http.usingProxy()) { ++ proxy_in_use = true; ++ dbg("An HTTP proxy is in use. There may be connection problems."); ++ } ++ dbg("trying http.getContent()"); ++ Object output = http.getContent(); ++ dbg("trying http.disconnect()"); ++ http.disconnect(); ++ if (! viewer.GET) { ++ String header = http.getHeaderField("VNC-Server"); ++ if (header != null && header.startsWith("x11vnc")) { ++ dbg("detected x11vnc server (2), setting GET=1"); ++ viewer.GET = true; ++ } ++ } ++ } catch(Exception e) { ++ dbg("HttpURLConnection: " + e.getMessage()); ++ } ++ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); ++ dbg("------------------------------------------------"); ++ } ++ ++ public Socket connectSock() throws IOException { ++ /* ++ * first try a https connection to detect a proxy, and ++ * grab the VNC server cert at the same time: ++ */ ++ check_for_proxy_and_grab_vnc_server_cert(); ++ ++ boolean srv_cert = false; ++ ++ if (trustsrvCerts != null) { ++ /* applet parameter suppled serverCert */ ++ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); ++ factory = trustsrv_ctx.getSocketFactory(); ++ srv_cert = true; ++ } else if (viewer.trustAllVncCerts) { ++ /* trust all certs (no checking) */ ++ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); ++ factory = trustall_ctx.getSocketFactory(); ++ } else if (trusturlCerts != null) { ++ /* trust certs the Browser/JVM accepted in check_for_proxy... */ ++ dbg("using trusturl_ctx"); ++ factory = trusturl_ctx.getSocketFactory(); ++ } else { ++ /* trust the local defaults */ ++ 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"); ++ } ++ ++ int timeout = 6; ++ if (timeout > 0) { ++ socket = (SSLSocket) factory.createSocket(); ++ InetSocketAddress inetaddr = new InetSocketAddress(host, port); ++ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); ++ socket.connect(inetaddr, timeout * 1000); ++ } else { ++ socket = (SSLSocket) factory.createSocket(host, port); ++ } ++ ++ } catch (Exception esock) { ++ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); ++ } ++ } else { ++ /* n.b. socket is left in error state to cause ex. below. */ ++ } ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The Server Connection Verified OK on 1st try."); ++ ++ java.security.cert.Certificate[] currentTrustedCerts; ++ BrowserCertsDialog bcd; ++ ++ SSLSession sess = socket.getSession(); ++ currentTrustedCerts = sess.getPeerCertificates(); ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-1 keeping socket."); ++ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy."); ++ } ++ 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 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.serverCert != null && trustsrvCerts != null) { ++ dbg("viewer.serverCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else if (viewer.trustUrlVncCert) { ++ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); ++ user_wants_to_see_cert = false; ++ } else { ++ /* have a dialog with the user: */ ++ bcd = new BrowserCertsDialog(serv, host + ":" + port); ++ dbg("browser certs dialog begin."); ++ bcd.queryUser(); ++ dbg("browser certs dialog finished."); ++ ++ if (bcd.showCertDialog) { ++ String msg = "user wants to see cert"; ++ dbg(msg); ++ user_wants_to_see_cert = true; ++ if (cert_fail == null) { ++ cert_fail = "user-view"; ++ } ++ throw new SSLHandshakeException(msg); ++ } else { ++ user_wants_to_see_cert = false; ++ dbg("browser certs dialog: user said yes, accept it"); ++ } ++ } ++ ++ } catch (SSLHandshakeException eh) { ++ dbg("SSLHandshakeException: could not automatically verify Server."); ++ dbg("msg: " + eh.getMessage()); ++ ++ ++ /* send a cleanup string just in case: */ ++ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; ++ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!"); ++ } ++ ++ /* reload */ ++ ++ socket = null; ++ ++ String reason = null; ++ ++ if (srv_cert) { ++ /* for serverCert usage we make this a fatal error. */ ++ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); ++ /* see below in TrustDialog were we describe this case to user anyway */ ++ } ++ ++ /* ++ * Reconnect, trusting any cert, so we can grab ++ * the cert to show it to the user in a dialog ++ * for him to manually accept. This 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); ++ } ++ ++ if (debug_certs) { ++ dbg("trusturlCerts: " + trusturlCerts); ++ dbg("trustsrvCerts: " + trustsrvCerts); ++ } ++ if (trusturlCerts == null && cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ ++ try { ++ socket.startHandshake(); ++ ++ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); ++ ++ /* grab the cert: */ ++ try { ++ SSLSession sess = socket.getSession(); ++ trustallCerts = sess.getPeerCertificates(); ++ } catch (Exception e) { ++ throw new Exception("Could not get " + ++ "Peer Certificate"); ++ } ++ if (debug_certs) { ++ dbg("trustallCerts: " + trustallCerts); ++ } ++ ++ if (viewer.trustAllVncCerts) { ++ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); ++ } else if (! browser_cert_match()) { ++ /* ++ * close socket now, we will reopen after ++ * dialog if user agrees to use the cert. ++ */ ++ try { ++ OutputStream os = socket.getOutputStream(); ++ os.write(getoutstr.getBytes()); ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!"); ++ } ++ socket = null; ++ ++ /* dialog with user to accept cert or not: */ ++ ++ TrustDialog td= new TrustDialog(host, port, ++ trustallCerts); ++ ++ if (cert_fail == null) { ++ ; ++ } else if (cert_fail.equals("user-view")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " You Asked to View the Certificate."; ++ } else if (cert_fail.equals("server-cert-mismatch")) { ++ /* this is now fatal error, see above. */ ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Certificate\n" ++ + " specified in the supplied 'serverCert' Applet Parameter."; ++ } else if (cert_fail.equals("cert-mismatch")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " The VNC Server's Certificate does not match the Website's\n" ++ + " HTTPS Certificate (that you previously accepted; either\n" ++ + " manually or automatically via Certificate Authority.)"; ++ } else if (cert_fail.equals("missing-certs")) { ++ reason = "Reason for this Dialog:\n\n" ++ + " Not all Certificates could be obtained to check."; ++ } ++ ++ if (! td.queryUser(reason)) { ++ 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()); ++ } ++ ++ /* reload again: */ ++ ++ if (socket != null) { ++ try { ++ socket.close(); ++ } catch (Exception e) { ++ dbg("socket is grumpy!!!"); ++ } ++ socket = null; ++ } ++ ++ /* ++ * Now connect a 3rd time, using the cert ++ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); ++ ++ } catch (Exception ehand3) { ++ dbg("** Could not TrustAll/TrustOne Verify Server #3."); ++ ++ throw new IOException(ehand3.getMessage()); ++ } ++ } ++ ++ /* we have socket (possibly null) at this point, so proceed: */ ++ ++ /* handle x11vnc GET=1, if applicable: */ ++ 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: " + str); ++ OutputStream os = socket.getOutputStream(); ++ String type = "os"; ++ ++ if (type == "os") { ++ os.write(str.getBytes()); ++ os.flush(); ++ System.out.println("used OutputStream"); ++ } else if (type == "bs") { ++ BufferedOutputStream bs = new BufferedOutputStream(os); ++ bs.write(str.getBytes()); ++ bs.flush(); ++ System.out.println("used BufferedOutputStream"); ++ } else if (type == "ds") { ++ DataOutputStream ds = new DataOutputStream(os); ++ ds.write(str.getBytes()); ++ ds.flush(); ++ System.out.println("used DataOutputStream"); ++ } ++ 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."); ++ dbg(""); ++ ++ /* could be null, let caller handle that. */ ++ return (Socket) socket; ++ } ++ ++ boolean browser_cert_match() { ++ String msg = "Browser URL accept previously accepted cert"; ++ ++ if (user_wants_to_see_cert) { ++ return false; ++ } ++ ++ if (viewer.serverCert != null || trustsrvCerts != null) { ++ if (cert_fail == null) { ++ cert_fail = "server-cert-mismatch"; ++ } ++ } ++ if (trustallCerts != null && trusturlCerts != null) { ++ if (trustallCerts.length == trusturlCerts.length) { ++ boolean ok = true; ++ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ ++ for (int i = 0; i < trusturlCerts.length; i++) { ++ if (! trustallCerts[i].equals(trusturlCerts[i])) { ++ dbg("BCM: cert mismatch at i=" + i); ++ dbg("BCM: cert mismatch url" + trusturlCerts[i]); ++ dbg("BCM: cert mismatch all" + trustallCerts[i]); ++ ok = false; ++ } ++ } ++ if (ok) { ++ System.out.println(msg); ++ if (cert_fail == null) { ++ cert_fail = "did-not-fail"; ++ } ++ return true; ++ } else { ++ if (cert_fail == null) { ++ cert_fail = "cert-mismatch"; ++ } ++ return false; ++ } ++ } ++ } ++ if (cert_fail == null) { ++ cert_fail = "missing-certs"; ++ } ++ return false; ++ } ++ ++ 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; ++ } ++ ++ /* this will do the proxy CONNECT negotiation and hook us up. */ ++ ++ private void proxy_helper(String proxyHost, int proxyPort) { ++ ++ boolean proxy_auth = false; ++ String proxy_auth_basic_realm = ""; ++ String hp = host + ":" + port; ++ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); ++ ++ /* we loop here a few times trying for the password case */ ++ for (int k=0; k < 2; k++) { ++ dbg("proxy_in_use psocket: " + k); ++ ++ if (proxySock != null) { ++ try { ++ proxySock.close(); ++ } catch (Exception e) { ++ dbg("proxy socket is grumpy."); ++ } ++ } ++ ++ proxySock = psocket(proxyHost, proxyPort); ++ if (proxySock == null) { ++ dbg("1-a sadly, returning a null socket"); ++ return; ++ } ++ ++ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" ++ + "Host: " + hp + "\r\n"; ++ ++ dbg("requesting via proxy: " + req1); ++ ++ if (proxy_auth) { ++ if (proxy_auth_string == null) { ++ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); ++ pp.queryUser(); ++ proxy_auth_string = pp.getAuth(); ++ } ++ //dbg("auth1: " + proxy_auth_string); ++ ++ String auth2 = Base64Coder.encodeString(proxy_auth_string); ++ //dbg("auth2: " + auth2); ++ ++ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; ++ //dbg("req1: " + req1); ++ ++ dbg("added Proxy-Authorization: Basic ... to request"); ++ } ++ req1 += "\r\n"; ++ ++ try { ++ proxy_os.write(req1.getBytes()); ++ String reply = readline(proxy_is); ++ ++ dbg("proxy replied: " + reply.trim()); ++ ++ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { ++ proxy_auth = true; ++ proxySock.close(); ++ } else 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; ++ } ++ } ++ } catch(Exception e) { ++ dbg("some proxy socket problem: " + e.getMessage()); ++ } ++ ++ /* read the rest of the HTTP headers */ ++ while (true) { ++ String line = readline(proxy_is); ++ dbg("proxy line: " + line.trim()); ++ if (proxy_auth) { ++ String uc = line.toLowerCase(); ++ if (uc.indexOf("proxy-authenticate:") == 0) { ++ if (uc.indexOf(" basic ") >= 0) { ++ int idx = uc.indexOf(" realm"); ++ if (idx >= 0) { ++ proxy_auth_basic_realm = uc.substring(idx+1); ++ } ++ } ++ } ++ } ++ if (line.equals("\r\n") || line.equals("\n")) { ++ break; ++ } ++ } ++ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { ++ /* we only try once for the non-password case: */ ++ break; ++ } ++ } ++ } ++ ++ 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) { ++ /* sandboxed applet might not be able to read it. */ ++ dbg("props failed: " + e.getMessage()); ++ } ++ if (viewer.proxyHost != null) { ++ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); ++ proxyHost = viewer.proxyHost; ++ if (viewer.proxyPort != null) { ++ proxyPort = gint(viewer.proxyPort); ++ } else { ++ proxyPort = 8080; ++ } ++ ++ } else if (props != null) { ++ dbg("\n---------------\nAll props:"); ++ props.list(System.out); ++ dbg("\n---------------\n\n"); ++ ++ /* scrape throught properties looking for proxy info: */ ++ ++ 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.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; ++ } ++ } ++ ++ 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 (proxyHost != null && proxyPort > 0) { ++ break; ++ } ++ ++ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 ++ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { ++ continue; ++ } ++ if (v2.indexOf("http") < 0) { ++ 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) { ++ j = p.indexOf("http"); ++ 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()); ++ } ++ ++ proxy_helper(proxyHost, proxyPort); ++ if (proxySock == null) { ++ return null; ++ } ++ } 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("proxy socket problem-2: " + 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(String reason) { ++ ++ /* 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"); ++ } ++ if (reason != null) { ++ reason += "\n\n"; ++ } ++ ++ text = "\n" +++ "Unable to verify the identity of\n" +++ "\n" +++ " " + host + ":" + port + "\n" +++ "\n" +++ infostr +++ "\n" +++ "as a trusted VNC server.\n" +++ "\n" +++ reason +++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" +++ "is due to one of the following:\n" +++ "\n" +++ " - Your requesting to View the Certificate before accepting.\n" +++ "\n" +++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" +++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" +++ "\n" +++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" +++ " the Apache Web server has a certificate *different* from the VNC server's.\n" +++ "\n" +++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" +++ " obtained by this applet to compare the VNC Server Certificate against.\n" +++ "\n" +++ " - The VNC Server's Certificate does not match the one specified in the\n" +++ " supplied 'serverCert' Java Applet Parameter.\n" +++ "\n" +++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" +++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" +++ "automatically authenticate this VNC Server.\n" +++ "\n" +++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" +++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" +++ "certificate (except for the Apache portal case above where they don't match.)\n" +++ "\n" +++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" +++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" +++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); ++ dialog.hide(); ++ ++ } else if (evt.getSource() == cancel) { ++ /* Cancel button clicked */ ++ trust_this_session = false; ++ ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++ ++ 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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++class ProxyPasswdDialog implements ActionListener { ++ String guessedHost = null; ++ String guessedPort = null; ++ String guessedUser = null; ++ String guessedPasswd = null; ++ String realm = 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 entry1; ++ TextField entry2; ++ String reply1 = ""; ++ String reply2 = ""; ++ ++ ProxyPasswdDialog (String h, int p, String realm) { ++ guessedHost = h; ++ try { ++ guessedPort = Integer.toString(p); ++ } catch (Exception e) { ++ guessedPort = "8080"; ++ } ++ this.realm = realm; ++ } ++ ++ public void queryUser() { ++ ++ /* create and display the dialog for unverified cert. */ ++ ++ Frame frame = new Frame("Proxy Requires Username and Password"); ++ ++ dialog = new Dialog(frame, true); ++ ++ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); ++ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); ++ entry1 = new TextField(30); ++ entry2 = new TextField(30); ++ entry2.setEchoChar('*'); ++ ok = new Button("OK"); ++ ok.addActionListener(this); ++ ++ dialog.setLayout(new BorderLayout()); ++ dialog.add("North", label); ++ dialog.add("Center", entry1); ++ dialog.add("South", entry2); ++ dialog.add("East", ok); ++ dialog.pack(); ++ dialog.resize(dialog.preferredSize()); ++ ++ dialog.show(); /* block here til OK or Cancel pressed. */ ++ return; ++ } ++ ++ public String getAuth() { ++ return reply1 + ":" + reply2; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == ok) { ++ reply1 = entry1.getText(); ++ reply2 = entry2.getText(); ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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(); ++ dialog.hide(); ++ } ++ } ++} ++ ++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 and/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 += "If the VNC Server connection times out while you are reading this\n"; ++m += "dialog, then restart the connection and try again.\n"; ++m += "\n"; ++m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; ++m += "\n"; ++ ++ TextArea textarea = new TextArea(m, 22, 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. */ ++ System.out.println("done show()"); ++ return; ++ } ++ ++ public synchronized void actionPerformed(ActionEvent evt) { ++ System.out.println(evt.getActionCommand()); ++ if (evt.getSource() == yes) { ++ showCertDialog = false; ++ //dialog.dispose(); ++ dialog.hide(); ++ } else if (evt.getSource() == no) { ++ showCertDialog = true; ++ //dialog.dispose(); ++ dialog.hide(); ++ } ++ System.out.println("done actionPerformed()"); ++ } ++} ++ ++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 ""; ++ } ++ } ++} ++ ++class Base64Coder { ++ ++ // Mapping table from 6-bit nibbles to Base64 characters. ++ private static char[] map1 = new char[64]; ++ static { ++ int i=0; ++ for (char c='A'; c<='Z'; c++) map1[i++] = c; ++ for (char c='a'; c<='z'; c++) map1[i++] = c; ++ for (char c='0'; c<='9'; c++) map1[i++] = c; ++ map1[i++] = '+'; map1[i++] = '/'; } ++ ++ // Mapping table from Base64 characters to 6-bit nibbles. ++ private static byte[] map2 = new byte[128]; ++ static { ++ for (int i=0; iin. ++ * @return A character array with the Base64 encoded data. ++ */ ++ public static char[] encode (byte[] in, int iLen) { ++ int oDataLen = (iLen*4+2)/3; // output length without padding ++ int oLen = ((iLen+2)/3)*4; // output length including padding ++ char[] out = new char[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++] & 0xff; ++ int i1 = ip < iLen ? in[ip++] & 0xff : 0; ++ int i2 = ip < iLen ? in[ip++] & 0xff : 0; ++ int o0 = i0 >>> 2; ++ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); ++ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); ++ int o3 = i2 & 0x3F; ++ out[op++] = map1[o0]; ++ out[op++] = map1[o1]; ++ out[op] = op < oDataLen ? map1[o2] : '='; op++; ++ out[op] = op < oDataLen ? map1[o3] : '='; op++; } ++ return out; } ++ ++ /** ++ * Decodes a string from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return A String containing the decoded data. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static String decodeString (String s) { ++ return new String(decode(s)); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * @param s a Base64 String to be decoded. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (String s) { ++ return decode(s.toCharArray()); } ++ ++ /** ++ * Decodes a byte array from Base64 format. ++ * No blanks or line breaks are allowed within the Base64 encoded data. ++ * @param in a character array containing the Base64 encoded data. ++ * @return An array containing the decoded data bytes. ++ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. ++ */ ++ public static byte[] decode (char[] in) { ++ int iLen = in.length; ++ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); ++ while (iLen > 0 && in[iLen-1] == '=') iLen--; ++ int oLen = (iLen*3) / 4; ++ byte[] out = new byte[oLen]; ++ int ip = 0; ++ int op = 0; ++ while (ip < iLen) { ++ int i0 = in[ip++]; ++ int i1 = in[ip++]; ++ int i2 = ip < iLen ? in[ip++] : 'A'; ++ int i3 = ip < iLen ? in[ip++] : 'A'; ++ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int b0 = map2[i0]; ++ int b1 = map2[i1]; ++ int b2 = map2[i2]; ++ int b3 = map2[i3]; ++ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) ++ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); ++ int o0 = ( b0 <<2) | (b1>>>4); ++ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); ++ int o2 = ((b2 & 3)<<6) | b3; ++ out[op++] = (byte)o0; ++ if (op 0) + { + viewer.options.oldEightBitColors = viewer.options.eightBitColors; +@@ -237,6 +265,9 @@ + } + else + { ++// begin runge/x11vnc ++ viewer.options.oldEightBitColors = viewer.options.eightBitColors; ++// end runge/x11vnc + rfb.writeSetPixelFormat( + 32, + 24, +@@ -376,12 +407,14 @@ + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + +- rfb.writeFramebufferUpdateRequest( +- 0, +- 0, +- rfb.framebufferWidth, +- rfb.framebufferHeight, +- false); ++ if (!viewer.graftFtp) { ++ rfb.writeFramebufferUpdateRequest( ++ 0, ++ 0, ++ rfb.framebufferWidth, ++ rfb.framebufferHeight, ++ false); ++ } + + // + // main dispatch loop +@@ -390,6 +423,9 @@ + while (true) { + // Read message type from the server. + int msgType = rfb.readServerMessageType(); ++ if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) { ++ System.out.println("msgType:" + msgType); ++ } + + // Process the message depending on its type. + switch (msgType) { +@@ -1332,6 +1368,9 @@ + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } ++ public void mouseWheelMoved(MouseWheelEvent evt) { ++ processLocalMouseWheelEvent(evt); ++ } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { +@@ -1367,6 +1406,19 @@ + evt.consume(); + } + ++ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { ++ if (viewer.rfb != null && rfb.inNormalProtocol) { ++ synchronized(rfb) { ++ try { ++ rfb.writeWheelEvent(evt); ++ } catch (Exception e) { ++ e.printStackTrace(); ++ } ++ rfb.notify(); ++ } ++ } ++ } ++ + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { +@@ -1532,9 +1584,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 +1622,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 2010-03-27 18:00:28.000000000 -0400 +@@ -41,6 +41,7 @@ + import java.io.*; + import java.net.*; + import javax.swing.*; ++import java.util.Date; + + public class VncViewer extends java.applet.Applet + implements java.lang.Runnable, WindowListener { +@@ -80,11 +81,11 @@ + GridBagLayout gridbag; + ButtonPanel buttonPanel; + AuthPanel authenticator; +- VncCanvas vc; ++ VncCanvas vc = null; + OptionsFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; +- FTPFrame ftp; // KMC: FTP Frame declaration ++ FTPFrame ftp = null; // KMC: FTP Frame declaration + + // Control session recording. + Object recordingSync; +@@ -96,7 +97,7 @@ + + // Variables read from parameter values. + String host; +- int port; ++ int port, vncserverport; + String passwordParam; + String encPasswordParam; + boolean showControls; +@@ -115,28 +116,75 @@ + int i; + // mslogon support 2 end + ++// begin runge/x11vnc ++boolean disableSSL; ++boolean GET; ++String CONNECT; ++String urlPrefix; ++String httpsPort; ++String oneTimeKey; ++String serverCert; ++String ftpDropDown; ++String proxyHost; ++String proxyPort; ++boolean forceProxy; ++boolean ignoreProxy; ++boolean trustAllVncCerts; ++boolean trustUrlVncCert; ++boolean debugCerts; ++boolean debugKeyboard; ++boolean mapF5_to_atsign; ++boolean forbid_Ctrl_Alt; ++ ++boolean ignoreMSLogonCheck; ++boolean delayAuthPanel; ++boolean ftpOnly; ++boolean graftFtp; ++boolean dsmActive; ++ ++boolean gotAuth; ++int authGot; ++// end runge/x11vnc ++ ++ + // + // init() + // + ++public void ftp_init() { ++ boolean show = false; ++ if (ftp != null) { ++ show = true; ++ } ++ ftp = null; ++ ++ ftp = new FTPFrame(this); // KMC: FTPFrame creation ++ ++ if (show) { ++ ftp.doOpen(); ++ rfb.readServerDriveList(); ++ } ++} ++ + public void init() { + + readParameters(); + + if (inSeparateFrame) { +- vncFrame = new Frame("Ultr@VNC"); +- if (!inAnApplet) { +- vncFrame.add("Center", this); +- } +- vncContainer = vncFrame; ++ vncFrame = new Frame("Ultr@VNC"); ++ if (!inAnApplet) { ++ vncFrame.add("Center", this); ++ } ++ vncContainer = vncFrame; + } else { +- vncContainer = this; ++ vncContainer = this; + } + + recordingSync = new Object(); + + options = new OptionsFrame(this); + clipboard = new ClipboardFrame(this); ++ + // authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate() + if (RecordingFrame.checkSecurity()) + rec = new RecordingFrame(this); +@@ -147,10 +195,11 @@ + cursorUpdatesDef = null; + eightBitColorsDef = null; + +- if (inSeparateFrame) ++ if (inSeparateFrame && vncFrame != null) + vncFrame.addWindowListener(this); + +- ftp = new FTPFrame(this); // KMC: FTPFrame creation ++ ftp_init(); ++ + rfbThread = new Thread(this); + rfbThread.start(); + } +@@ -186,6 +235,30 @@ + gbc.weightx = 1.0; + gbc.weighty = 1.0; + ++ if (ftpOnly) { ++ if (showControls) { ++ buttonPanel.enableButtons(); ++ } ++ ActionListener taskPerformer = new ActionListener() { ++ public void actionPerformed(ActionEvent evt) { ++ vncFrame.setVisible(false); ++ ftp.setSavedLocations(); ++ if (ftp.isVisible()) { ++ ftp.doClose(); ++ } else { ++ ftp.doOpen(); ++ } ++ rfb.readServerDriveList(); ++ } ++ }; ++ Timer t = new Timer(300, taskPerformer); ++ t.setRepeats(false); ++ t.start(); ++ ++ vc.processNormalProtocol(); ++ return; ++ } ++ + // Add ScrollPanel to applet mode + + // Create a panel which itself is resizeable and can hold +@@ -286,6 +359,24 @@ + + void connectAndAuthenticate() throws Exception { + ++ if (graftFtp) { ++ rfb = new RfbProto(host, port, this); ++ rfb.desktopName = "ftponly"; ++ rfb.framebufferWidth = 12; ++ rfb.framebufferHeight = 12; ++ rfb.bitsPerPixel = 32; ++ rfb.depth = 24; ++ rfb.trueColour = true; ++ rfb.redMax = 255; ++ rfb.greenMax = 255; ++ rfb.blueMax = 255; ++ rfb.redShift = 16; ++ rfb.greenShift = 8; ++ rfb.blueShift = 0; ++ rfb.inNormalProtocol = true; ++ return; ++ } ++ + // If "ENCPASSWORD" parameter is set, decrypt the password into + // the passwordParam string. + +@@ -336,7 +427,22 @@ + // + + +- prologueDetectAuthProtocol() ; ++// begin runge/x11vnc ++ gotAuth = false; ++ if (delayAuthPanel) { ++ if (tryAuthenticate(null, null)) { ++ if (inSeparateFrame) { ++ vncFrame.pack(); ++ vncFrame.show(); ++ } ++ return; ++ } ++ } ++// prologueDetectAuthProtocol() ; ++ if (ignoreMSLogonCheck == false) { ++ prologueDetectAuthProtocol() ; ++ } ++// end runge/x11vnc + + authenticator = new AuthPanel(mslogon); + +@@ -371,6 +477,7 @@ + //mslogon support end + } + ++ int tries = 0; + while (true) { + // Wait for user entering a password, or a username and a password + synchronized(authenticator) { +@@ -390,6 +497,13 @@ + break; + //mslogon support end + ++// begin runge/x11vnc ++ gotAuth = false; ++ if (++tries > 2) { ++ throw new Exception("Incorrect password entered " + tries + " times."); ++ } ++// end runge/x11vnc ++ + // Retry on authentication failure. + authenticator.retry(); + } +@@ -405,9 +519,11 @@ + + void prologueDetectAuthProtocol() throws Exception { + +- rfb = new RfbProto(host, port, this); ++ if (!gotAuth) { ++ rfb = new RfbProto(host, port, this); + +- rfb.readVersionMsg(); ++ rfb.readVersionMsg(); ++ } + + System.out.println("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); +@@ -431,16 +547,36 @@ + + boolean tryAuthenticate(String us, String pw) throws Exception { + +- rfb = new RfbProto(host, port, this); ++ int authScheme; + +- rfb.readVersionMsg(); ++ if (!gotAuth) { ++ rfb = new RfbProto(host, port, this); + +- System.out.println("RFB server supports protocol version " + +- rfb.serverMajor + "." + rfb.serverMinor); ++ rfb.readVersionMsg(); + +- rfb.writeVersionMsg(); ++ System.out.println("RFB server supports protocol version: " + ++ rfb.serverMajor + "." + rfb.serverMinor); + +- int authScheme = rfb.readAuthScheme(); ++ rfb.writeVersionMsg(); ++ ++ authScheme = rfb.readAuthScheme(); ++ ++ gotAuth = true; ++ authGot = authScheme; ++ } else { ++ authScheme = authGot; ++ } ++// begin runge/x11vnc ++ if (delayAuthPanel && pw == null) { ++ if (authScheme == RfbProto.NoAuth) { ++ System.out.println("No authentication needed"); ++ return true; ++ } else { ++ return false; ++ } ++ } ++System.out.println("as: " + authScheme); ++// end runge/x11vnc + + switch (authScheme) { + +@@ -629,6 +765,10 @@ + + void doProtocolInitialisation() throws IOException { + ++ if (graftFtp) { ++ return; ++ } ++ + rfb.writeClientInit(); + + rfb.readServerInit(); +@@ -774,9 +914,28 @@ + fatalError("HOST parameter not specified"); + } + } ++ Date d = new Date(); ++ System.out.println("-\nSSL VNC Java Applet starting. " + d); + +- String str = readParameter("PORT", true); +- port = Integer.parseInt(str); ++ port = 0; ++ String str = readParameter("PORT", false); ++ if (str != null) { ++ port = Integer.parseInt(str); ++ } ++ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). ++ vncserverport = 0; ++ str = readParameter("VNCSERVERPORT", false); ++ if (str != null) { ++ vncserverport = Integer.parseInt(str); ++ } ++ if (port == 0 && vncserverport == 0) { ++ fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); ++ } ++ if (port == 0) { ++ // Nevertheless, fall back to vncserverport if we have to. ++ System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); ++ port = vncserverport; ++ } + + if (inAnApplet) { + str = readParameter("Open New Window", false); +@@ -804,6 +963,158 @@ + 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 + "'"); ++ ++ ftpDropDown = readParameter("ftpDropDown", false); ++ if (ftpDropDown != null) { ++ ftpDropDown = ftpDropDown.replaceAll("%2F", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("%2f", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("_2F_", "/"); ++ ftpDropDown = ftpDropDown.replaceAll("%20", " "); ++ System.out.println("ftpDropDown: '" + ftpDropDown + "'"); ++ } ++ ++ ++ oneTimeKey = readParameter("oneTimeKey", false); ++ if (oneTimeKey != null) { ++ System.out.println("oneTimeKey is set."); ++ } ++ ++ serverCert = readParameter("serverCert", false); ++ if (serverCert != null) { ++ System.out.println("serverCert is set."); ++ } ++ ++ forceProxy = false; ++ proxyHost = null; ++ proxyPort = null; ++ str = readParameter("forceProxy", false); ++ if (str != null) { ++ if (str.equalsIgnoreCase("Yes")) { ++ forceProxy = true; ++ } else if (str.equalsIgnoreCase("No")) { ++ forceProxy = false; ++ } else { ++ forceProxy = true; ++ String[] pieces = str.split(" "); ++ proxyHost = new String(pieces[0]); ++ if (pieces.length >= 2) { ++ proxyPort = new String(pieces[1]); ++ } else { ++ proxyPort = new String("8080"); ++ } ++ } ++ } ++ str = readParameter("proxyHost", false); ++ if (str != null) { ++ proxyHost = new String(str); ++ } ++ str = readParameter("proxyPort", false); ++ if (str != null) { ++ proxyPort = new String(str); ++ } ++ if (proxyHost != null && proxyPort == null) { ++ proxyPort = new String("8080"); ++ } ++ ++ 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; ++ } ++ debugCerts = false; ++ str = readParameter("debugCerts", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugCerts = true; ++ } ++ debugKeyboard = false; ++ str = readParameter("debugKeyboard", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ debugKeyboard = true; ++ } ++ mapF5_to_atsign = false; ++ str = readParameter("mapF5_to_atsign", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ mapF5_to_atsign = true; ++ } ++ forbid_Ctrl_Alt = false; ++ str = readParameter("forbid_Ctrl_Alt", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ forbid_Ctrl_Alt = true; ++ } ++ ignoreMSLogonCheck = false; ++ str = readParameter("ignoreMSLogonCheck", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ ignoreMSLogonCheck = true; ++ } ++ ftpOnly = false; ++ str = readParameter("ftpOnly", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ ftpOnly = true; ++ } ++ graftFtp = false; ++ str = readParameter("graftFtp", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ graftFtp = true; ++ } ++ dsmActive = false; ++ str = readParameter("dsmActive", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ dsmActive = true; ++ } ++ delayAuthPanel = false; ++ str = readParameter("delayAuthPanel", false); ++ if (str != null && str.equalsIgnoreCase("Yes")) { ++ delayAuthPanel = true; ++ } ++// end runge/x11vnc + } + + public String readParameter(String name, boolean required) { diff --git a/webclients/javaviewer.pseudo_proxy.patch b/webclients/javaviewer.pseudo_proxy.patch deleted file mode 100644 index 4d2f36e..0000000 --- a/webclients/javaviewer.pseudo_proxy.patch +++ /dev/null @@ -1,141 +0,0 @@ -diff -ru vnc_javasrc/OptionsFrame.java proxy_vnc_javasrc/OptionsFrame.java ---- vnc_javasrc/OptionsFrame.java Fri Jul 5 08:17:23 2002 -+++ proxy_vnc_javasrc/OptionsFrame.java Thu Aug 22 23:24:44 2002 -@@ -70,6 +70,12 @@ - - Label[] labels = new Label[names.length]; - Choice[] choices = new Choice[names.length]; -+ -+ Label proxyHostLabel; -+ TextField proxyHostEdit; -+ Label proxyPortLabel; -+ TextField proxyPortEdit; -+ - Button closeButton; - VncViewer viewer; - -@@ -93,6 +99,9 @@ - boolean shareDesktop; - boolean viewOnly; - -+ String proxyHost; -+ int proxyPort; -+ - // - // Constructor. Set up the labels and choices from the names and values - // arrays. -@@ -126,6 +135,32 @@ - } - } - -+ // TODO: find a way to set these to defaults from browser -+ proxyPort = viewer.readIntParameter("Use Proxy Port", -1); -+ if(proxyPort>-1) { -+ proxyHost = viewer.readParameter("Use Proxy Host", false); -+ if(proxyHost == null) -+ proxyHost = viewer.host; -+ -+ proxyHostLabel = new Label("Proxy Host"); -+ gbc.gridwidth = 1; -+ gridbag.setConstraints(proxyHostLabel,gbc); -+ add(proxyHostLabel); -+ proxyHostEdit = new TextField(); -+ gbc.gridwidth = GridBagConstraints.REMAINDER; -+ gridbag.setConstraints(proxyHostEdit,gbc); -+ add(proxyHostEdit); -+ -+ proxyPortLabel = new Label("Proxy Port"); -+ gbc.gridwidth = 1; -+ gridbag.setConstraints(proxyPortLabel,gbc); -+ add(proxyPortLabel); -+ proxyPortEdit = new TextField(); -+ gbc.gridwidth = GridBagConstraints.REMAINDER; -+ gridbag.setConstraints(proxyPortEdit,gbc); -+ add(proxyPortEdit); -+ } -+ - closeButton = new Button("Close"); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gridbag.setConstraints(closeButton, gbc); -@@ -161,6 +196,11 @@ - } - } - -+ if(proxyPort>-1) { -+ proxyPortEdit.setText(Integer.toString(proxyPort)); -+ proxyHostEdit.setText(proxyHost); -+ } -+ - // Make the booleans and encodings array correspond to the state of the GUI - - setEncodings(); -@@ -361,8 +401,12 @@ - // - - public void actionPerformed(ActionEvent evt) { -- if (evt.getSource() == closeButton) -+ if (evt.getSource() == closeButton) { - setVisible(false); -+ proxyHost = proxyHostEdit.getText(); -+ proxyPort = Integer.parseInt(proxyPortEdit.getText()); -+ System.err.println("proxy is " + proxyHost + ":" + proxyPort); -+ } - } - - // -diff -ru vnc_javasrc/RfbProto.java proxy_vnc_javasrc/RfbProto.java ---- vnc_javasrc/RfbProto.java Sun Aug 4 18:39:35 2002 -+++ proxy_vnc_javasrc/RfbProto.java Thu Aug 22 22:53:53 2002 -@@ -119,12 +119,51 @@ - viewer = v; - host = h; - port = p; -- sock = new Socket(host, port); -+ if(viewer.options.proxyPort>-1) -+ sock = new Socket(viewer.options.proxyHost, viewer.options.proxyPort); -+ else -+ sock = new Socket(host, port); - is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), - 16384)); - os = sock.getOutputStream(); -+ if(viewer.options.proxyPort>-1) -+ negotiateProxy(host,port); - } - -+ // this is inefficient as hell, but only used once per connection -+ String readLine() { -+ byte[] ba = new byte[1]; -+ String s = new String(""); -+ -+ ba[0]=0; -+ try { -+ while(ba[0] != 0xa) { -+ ba[0] = (byte)is.readUnsignedByte(); -+ s += new String(ba); -+ } -+ } catch(Exception e) { -+ e.printStackTrace(); -+ } -+ return s; -+ } -+ -+ void negotiateProxy(String realHost,int realPort) throws IOException { -+ String line; -+ -+ // this would be the correct way, but we want to trick strict proxies. -+ // line = "CONNECT " + realHost + ":" + realPort + " HTTP/1.1\r\nHost: " + realHost + ":" + realPort + "\r\n\r\n"; -+ line = "GET " + realHost + ":" + realPort + "/proxied.connection HTTP/1.0\r\nPragma: No-Cache\r\nProxy-Connection: Keep-Alive\r\n\r\n"; -+ os.write(line.getBytes()); -+ -+ line = readLine(); -+ System.err.println("Proxy said: " + line); -+ if(!(line.substring(0,7)+line.substring(8,12)).equalsIgnoreCase("HTTP/1. 200")) { -+ IOException e = new IOException(line); -+ throw e; -+ } -+ while(!line.equals("\r\n") && !line.equals("\n")) -+ line = readLine(); -+ } - - void close() { - try { diff --git a/webclients/ssl/Makefile.am b/webclients/ssl/Makefile.am deleted file mode 100644 index fd1c201..0000000 --- a/webclients/ssl/Makefile.am +++ /dev/null @@ -1,2 +0,0 @@ -EXTRA_DIST=VncViewer.jar index.vnc SignedVncViewer.jar proxy.vnc README ss_vncviewer onetimekey UltraViewerSSL.jar SignedUltraViewerSSL.jar ultra.vnc ultrasigned.vnc ultraproxy.vnc - diff --git a/webclients/ssl/README b/webclients/ssl/README deleted file mode 100644 index b244cf1..0000000 --- a/webclients/ssl/README +++ /dev/null @@ -1,338 +0,0 @@ -This directory contains a patched Java applet VNC viewer that is SSL -enabled. - -The patches in the *.patch files are relative to the source tarball: - - tightvnc-1.3dev7_javasrc.tar.gz - -currently (4/06) available here: - - http://prdownloads.sourceforge.net/vnc-tight/tightvnc-1.3dev7_javasrc.tar.gz?download - -It also includes some simple patches to: - - - fix richcursor colors - - - make the Java Applet cursor (not the cursor drawn to the canvas - framebuffer) invisible when it is inside the canvas. - - - allow Tab (and some other) keystrokes to be sent to the vnc - server instead of doing widget traversal. - - -This SSL applet should work with any VNC viewer that has an SSL tunnel in -front of it. It has been tested on x11vnc and using the stunnel tunnel -to other VNC servers. - -By default this Vnc Viewer will only do SSL. To do unencrypted traffic -see the "DisableSSL" applet parameter (e.g. set it to Yes in index.vnc). - -Proxies: they are a general problem with java socket applets (a socket -connection does not go through the proxy). See the info in the proxy.vnc -file for a workaround. It uses SignedVncViewer.jar which is simply -a signed version of VncViewer.jar. The basic idea is the user clicks -"Yes" to trust the applet and then it can connect directly to the proxy -and issue a CONNECT request. - -This applet has been tested on versions 1.4.2 and 1.5.0 of the Sun -Java plugin. It may not work on older releases or different vendor VM's. -Send full Java Console output for failures. - ---------------------------------------------------------------- -Tips: - -When doing single-port proxy connections (e.g. both VNC and HTTPS -thru port 5900) it helps to move through the 'do you trust this site' -dialogs quickly. x11vnc has to wait to see if the traffic is VNC or -HTTP and this can cause timeouts if you don't move thru them quickly. - -You may have to restart your browser completely if it gets into a -weird state. For one case we saw the JVM requesting VncViewer.class -even when no such file exists. - - ---------------------------------------------------------------- -Extras: - -ss_vncviewer (not Java): - - Wrapper script for native VNC viewer to connect to x11vnc in - SSL mode. Script launches stunnel(8) and then connects to it - via localhost which in turn is then redirected to x11vnc via an - SSL tunnel. stunnel(8) must be installed and available in PATH. - - -Running Java SSL VncViewer from the command line: - - From this directory: - - java -cp ./VncViewer.jar VncViewer HOST PORT - - substitute and with the actual values. - You can add any other parameters, e.g.: ignoreProxy yes - ---------------------------------------------------------------- -UltraVNC: - -The UltraVNC java viewer has also been patched to support SSL. Various -bugs in the UltraVNC java viewer were also fixed. This viewer can be -useful because is support UltraVNC filetransfer, and so it works on -Unix, etc. - -UltraViewerSSL.jar -SignedUltraViewerSSL.jar -ultra.vnc -ultraproxy.vnc -ultravnc-102-JavaViewer-ssl-etc.patch - ---------------------------------------------------------------- -Applet Parameters: - -Some additional applet parameters can be set via the URL, e.g. - - http://host:5800/?param=value - http://host:5800/ultra.vnc?param=value - https://host:5900/ultra.vnc?param=value - -etc. If running java from command line as show above, it comes -in as java ... VncViewer param value ... - -There is a limitation with libvncserver that param and value can -only be alphanumeric, underscore, "+" (for space), or "." - -We have added some applet parameters to the stock VNC java -viewers. Here are the applet parameters: - -Both TightVNC and UltraVNC Java viewers: - - HOST - string, default: none. - The Hostname to connect to. - - PORT - number, default: 0 - The VNC server port to connect to. - - Open New Window - yes/no, default: no - Run applet in separate frame. - - Show Controls - yes/no, default: yes - Show Controls button panel. - - Show Offline Desktop - yes/no, default: no - Do we continue showing desktop on remote disconnect? - - Defer screen updates - number, default: 20 - Milliseconds delay - - Defer cursor updates - number, default: 10 - Milliseconds delay - - Defer update requests - number, default: 50 - Milliseconds delay - - PASSWORD - string, default: none - VNC session password in plain text. - - ENCPASSWORD - string, default: none - VNC session password in encrypted in DES with KNOWN FIXED - key. It is a hex string. This is like the ~/.vnc/passwd format. - - - The following are added by x11vnc and/or ssvnc project - - VNCSERVERPORT - number, default: 0 - Like PORT, but if there is a firewall this is the Actual VNC - server port. PORT might be a redir port on the firewall. - - DisableSSL - yes/no, default: no - Do unencrypted connection, no SSL. - - httpsPort - number, default: none - When checking for proxy, use this at the url port number. - - CONNECT - string, default: none - Sets to host:port for the CONNECT line to a Web proxy. - The Web proxy should connect us to it. - - GET - yes/no, default: no - Set to do a special HTTP GET (/request.https.vnc.connection) - to the vnc server that will cause it to switch to VNC instead. - This is to speedup/make more robust, the single port HTTPS and VNC - mode of x11vnc (e.g. both services thru port 5900, etc) - - urlPrefix - string, default: none - set to a string that will be prefixed to all URL's when contacting - the VNC server. Idea is a special proxy will use this to indicate - internal hostname, etc. - - oneTimeKey - string, default: none - set a special hex "key" to correspond to an SSL X.509 cert+key. - See the 'onetimekey' helper script. Can also be PROMPT to prompt - the user to paste the hex key string in. - - This provides a Client-Side cert+key that the client will use to - authenticate itself by SSL To the VNC Server. - - This is to try to work around the problem that the Java applet - cannot keep an SSL keystore on disk, etc. E.g. if they log - into an HTTPS website via password they are authenticated and - encrypted, then the website can safely put oneTimeKey=... on the - URL. The Vncviewer authenticates the VNC server with this key. - - Note that there is currently a problem in that if x11vnc requires - Client Certificates the user cannot download the index.vnc HTML - and VncViewer.jar from the same x11vnc. Those need to come from - a different x11vnc or from a web server. - - Note that the HTTPS website can also put the VNC Password - (e.g. a temporary/one-time one) in the parameter PASSWORD. - The Java Applet will automatically supply this VNC password - instead of prompting. - - serverCert - string, default: none - set a special hex "cert" to correspond to an SSL X.509 cert - See the 'onetimekey -certonly' helper script. - - This provides a Server-Side cert that the client will authenticate - the VNC Server against by SSL. - - This is to try to work around the problem that the Java applet - cannot keep an SSL keystore on disk, etc. E.g. if they log - into an HTTPS website via password they are authenticated and - encrypted, then the website can safely put serverCert=... on the - URL. - - Of course the VNC Server is sending this string to the Java - Applet, so this is only reasonable security if the VNC Viewer - already trusts the HTTPS retrieval of the URL + serverCert param - that it gets. This should be done over HTTPS not HTTP. - - proxyHost - string, default: none - Do not try to guess the proxy's hostname, use the value in - proxyHost. Does not imply forceProxy (below.) - - proxyPort - string, default: none - Do not try to guess the proxy's port number, use the value in - proxyPort. Does not imply forceProxy (below.) - - forceProxy - yes/no, default: no - Assume there is a proxy and force its use. - - If a string other than "yes" or "no" is given, it implies "yes" - and uses the string for proxyHost and proxyPort (see above). - In this case the string must be of the form "hostname+port". - Note that it is "+" and not ":" before the port number. - - ignoreProxy - yes/no, default: no - Don't check for a proxy, assume there is none. - - trustAllVncCerts - yes/no, default: no - Automatically trust any cert received from the VNC server - (obviously this could be dangerous and lead to man in the - middle attack). Do not ask the user to verify any of these - certs from the VNC server. - - trustUrlVncCert - yes/no, default: no - Automatically trust any cert that the web browsers has accepted. - E.g. the user said "Yes" or "Continue" to a web browser dialog - regarding a certificate. If we get the same cert (chain) from - the VNC server we trust it without prompting the user. - - debugCerts - yes/no, default: no - Print out every cert in the Server, TrustUrl, TrustAll chains. - - -TightVNC Java viewer only: - - Offer Relogin - yes/no, default: yes - "Offer Relogin" set to "No" disables "Login again" - - SocketFactory - string, default: none - set Java Socket class factory. - -UltraVNC Java viewer only: - - None. - - The following are added by x11vnc and/or ssvnc project - - ftpDropDown - string, default: none - Sets the file transfer "drives" dropdown to the "." separated - list. Use "+" for space. The default is - - My+Documents.Desktop.Home - - for 3 entries in the dropdown in addition to the "drives" - (e.g. C:\) These items should be expanded properly by the VNC - Server. x11vnc will prepend $HOME to them, which is normally - what one wants. To include a "/" use "_2F_". Another example: - - Home.Desktop.bin_2F_linux - - If an item is prefixed with "TOP_" then the item is inserted at - the top of the drop down rather than being appended to the end. - E.g. to try to initially load the user homedir instead of /: - - TOP_Home.My+Documents.Desktop - - If ftpDropDown is set to the empty string, "", then no special - locations, [Desktop] etc., are placed in the drop down. Only the - ultravnc "drives" will appear. - - ftpOnly - yes/no, default: no - The VNC viewer only shows the filetransfer panel, no desktop - is displayed. - - graftFtp - yes/no, default: no - As ftpOnly, the VNC viewer only shows the filetransfer panel, - no desktop is displayed, however it is "grafted" onto an existing - SSVNC unix vncviewer. The special SSVNC vncviewer merges the two - channels. - - dsmActive - yes/no, default: no - Special usage mode with the SSVNC unix vncviewer. The UltraVNC - DSM encryption is active. Foolishly, UltraVNC DSM encryption - *MODIFIES* the VNC protocol when active (it is not a pure tunnel). - This option indicates to modify the VNC protocol to make this work. - Usually only used with graftFtp and SSVNC unix vncviewer. - - delayAuthPanel - yes/no, default: no - This is another special usage mode with the SSVNC unix vncviewer. - A login panel is delayed (not shown at startup.) Could be useful - for non SSVNC usage too. - - ignoreMSLogonCheck - yes/no, default: no - Similar to delayAuthPanel, do not put up a popup asking for - Windows username, etc. diff --git a/webclients/ssl/SignedUltraViewerSSL.jar b/webclients/ssl/SignedUltraViewerSSL.jar deleted file mode 100644 index 6c18737..0000000 Binary files a/webclients/ssl/SignedUltraViewerSSL.jar and /dev/null differ diff --git a/webclients/ssl/SignedVncViewer.jar b/webclients/ssl/SignedVncViewer.jar deleted file mode 100644 index 95c0b0b..0000000 Binary files a/webclients/ssl/SignedVncViewer.jar and /dev/null differ diff --git a/webclients/ssl/UltraViewerSSL.jar b/webclients/ssl/UltraViewerSSL.jar deleted file mode 100644 index 45259fd..0000000 Binary files a/webclients/ssl/UltraViewerSSL.jar and /dev/null differ diff --git a/webclients/ssl/VncViewer.jar b/webclients/ssl/VncViewer.jar deleted file mode 100644 index 9453c6f..0000000 Binary files a/webclients/ssl/VncViewer.jar and /dev/null differ diff --git a/webclients/ssl/index.vnc b/webclients/ssl/index.vnc deleted file mode 100644 index ec520dc..0000000 --- a/webclients/ssl/index.vnc +++ /dev/null @@ -1,26 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - -$PARAMS - -
-x11vnc site - diff --git a/webclients/ssl/onetimekey b/webclients/ssl/onetimekey deleted file mode 100755 index bf57c8f..0000000 --- a/webclients/ssl/onetimekey +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -# -# usage: onetimekey path/to/mycert.pem -# onetimekey -certonly path/to/mycert.pem -# -# Takes an openssl cert+key pem file and turns into a long string -# for the x11vnc SSL VNC Java Viewer. -# -# The Java applet URL parameter can be oneTimeKey= where str is -# the output of this program, or can be oneTimeKey=PROMPT in which -# case the applet will ask you to paste in the string. -# -# The problem trying to be solved here is it is difficult to get -# the Java applet to have or use a keystore with the key saved -# in it. Also, as the name implies, an HTTPS server can create -# a one time key to send to the applet (the user has already -# logged in via password to the HTTPS server). -# -# Note oneTimeKey is to provide a CLIENT Certificate for the viewer -# to authenticate itself to the VNC Server. -# -# There is also the serverCert= Applet parameter. This is -# a cert to authenticate the VNC server against. To create that -# string with this tool specify -certonly as the first argument. - -certonly="" -if [ "X$1" = "X-certonly" ]; then - shift - certonly=1 -fi - -in=$1 -der=/tmp/1time$$.der -touch $der -chmod 600 $der - -openssl pkcs8 -topk8 -nocrypt -in "$in" -out "$der" -outform der - -pbinhex=/tmp/pbinhex.$$ -cat > $pbinhex < - - - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - -$PARAMS - -
-x11vnc site - diff --git a/webclients/ssl/ss_vncviewer b/webclients/ssl/ss_vncviewer deleted file mode 100755 index 7e793ff..0000000 --- a/webclients/ssl/ss_vncviewer +++ /dev/null @@ -1,3676 +0,0 @@ -#!/bin/sh -# -# ss_vncviewer: wrapper for vncviewer to use an stunnel SSL tunnel -# or an SSH tunnel. -# -# Copyright (c) 2006-2009 by Karl J. Runge -# -# ss_vncviewer 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. -# -# ss_vncviewer 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 ss_vncviewer; if not, write to the Free Software -# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA -# or see . -# -# -# You must have stunnel(8) installed on the system and in your PATH -# (however, see the -ssh option below, in which case you will need ssh(1) -# installed) Note: stunnel is usually installed in an "sbin" subdirectory. -# -# You should have "x11vnc -ssl ..." or "x11vnc -stunnel ..." -# already running as the VNC server on the remote machine. -# (or use stunnel on the server side for any other VNC server) -# -# -# Usage: ss_vncviewer [cert-args] host:display -# -# e.g.: ss_vncviewer snoopy:0 -# ss_vncviewer snoopy:0 -encodings "copyrect tight zrle hextile" -# -# [cert-args] can be: -# -# -verify /path/to/cacert.pem -# -mycert /path/to/mycert.pem -# -crl /path/to/my_crl.pem (or directory) -# -proxy host:port -# -# -verify specifies a CA cert PEM file (or a self-signed one) for -# authenticating the VNC server. -# -# -mycert specifies this client's cert+key PEM file for the VNC server to -# authenticate this client. -# -# -proxy try host:port as a Web proxy to use the CONNECT method -# to reach the VNC server (e.g. your firewall requires a proxy). -# -# For the "double proxy" case use -proxy host1:port1,host2:port2 -# (the first CONNECT is done through host1:port1 to host2:port2 -# and then a 2nd CONNECT to the destination VNC server.) -# -# Use socks://host:port, socks4://host:port, or socks5://host,port -# to force usage of a SOCKS proxy. Also repeater://host:port and -# sslrepeater://host:port. -# -# -showcert Only fetch the certificate using the 'openssl s_client' -# command (openssl(1) must in installed). On ssvnc 1.0.27 and -# later the bundled command 'ultravnc_dsm_helper' is used. -# -# See http://www.karlrunge.com/x11vnc/faq.html#faq-ssl-ca for details on -# SSL certificates with VNC. -# -# A few other args (not related to SSL and certs): -# -# -2nd Run the vncviewer a 2nd time if the first connections fails. -# -# -ssh Use ssh instead of stunnel SSL. ssh(1) must be installed and you -# must be able to log into the remote machine via ssh. -# -# In this case "host:display" may be of the form "user@host:display" -# where "user@host" is used for the ssh login (see ssh(1) manpage). -# -# If -proxy is supplied it can be of the forms: "gwhost" "gwhost:port" -# "user@gwhost" or "user@gwhost:port". "gwhost" is an incoming ssh -# gateway machine (the VNC server is not running there), an ssh -L -# redir is used to "host" in "host:display" from "gwhost". Any "user@" -# part must be in the -proxy string (not in "host:display"). -# -# Under -proxy use "gwhost:port" if connecting to any ssh port -# other than the default (22). (even for the non-gateway case, -# -proxy must be used to specify a non-standard ssh port) -# -# A "double ssh" can be specified via a -proxy string with the two -# hosts separated by a comma: -# -# [user1@]host1[:port1],[user2@]host2[:port2] -# -# in which case a ssh to host1 and thru it via a -L redir a 2nd -# ssh is established to host2. -# -# Examples: -# -# ss_vncviewer -ssh bob@bobs-home.net:0 -# ss_vncviewer -ssh -sshcmd 'x11vnc -localhost' bob@bobs-home.net:0 -# -# ss_vncviewer -ssh -proxy fred@mygate.com:2022 mymachine:0 -# ss_vncviewer -ssh -proxy bob@bobs-home.net:2222 localhost:0 -# -# ss_vncviewer -ssh -proxy fred@gw-host,fred@peecee localhost:0 -# -# -sshcmd cmd Run "cmd" via ssh instead of the default "sleep 15" -# e.g. -sshcmd 'x11vnc -display :0 -localhost -rfbport 5900' -# -# -sshargs "args" pass "args" to the ssh process, e.g. -L/-R port redirs. -# -# -sshssl Tunnel the SSL connection thru a SSH connection. The tunnel as -# under -ssh is set up and the SSL connection goes thru it. Use -# this if you want to have and end-to-end SSL connection but must -# go thru a SSH gateway host (e.g. not the vnc server). Or use -# this if you need to tunnel additional services via -R and -L -# (see -sshargs above). -# -# ss_vncviewer -sshssl -proxy fred@mygate.com mymachine:0 -# -# -listen (or -reverse) set up a reverse connection. -# -# -alpha turn on cursor alphablending hack if you are using the -# enhanced tightvnc vncviewer. -# -# -grab turn on XGrabServer hack if you are using the enhanced tightvnc -# vncviewer (e.g. for fullscreen mode in some windowmanagers like -# fvwm that do not otherwise work in fullscreen mode) -# -# -# set VNCVIEWERCMD to whatever vncviewer command you want to use. -# -VNCIPCMD=${VNCVIEWERCMD:-vncip} -VNCVIEWERCMD=${VNCVIEWERCMD:-vncviewer} -if [ "X$SSVNC_TURBOVNC" != "X" ]; then - if echo "$VNCVIEWERCMD" | grep '\.turbovnc' > /dev/null; then - : - else - if type "$VNCVIEWERCMD.turbovnc" > /dev/null 2>/dev/null; then - VNCVIEWERCMD="$VNCVIEWERCMD.turbovnc" - fi - fi -fi -# -# Same for STUNNEL, e.g. set it to /path/to/stunnel or stunnel4, etc. -# - -# turn on verbose debugging output -if [ "X$SS_DEBUG" != "X" -a "X$SS_DEBUG" != "X0" ]; then - set -xv -fi - -PATH=$PATH:/usr/sbin:/usr/local/sbin:/dist/sbin; export PATH - -localhost="localhost" -if uname | grep Darwin >/dev/null; then - localhost="127.0.0.1" -fi - -# work out which stunnel to use (debian installs as stunnel4) -stunnel_set_here="" -if [ "X$STUNNEL" = "X" ]; then - check_stunnel=1 - if [ "X$SSVNC_BASEDIRNAME" != "X" ]; then - if [ -x "$SSVNC_BASEDIRNAME/stunnel" ]; then - type stunnel > /dev/null 2>&1 - if [ $? = 0 ]; then - # found ours - STUNNEL=stunnel - check_stunnel=0 - fi - fi - fi - if [ "X$check_stunnel" = "X1" ]; then - type stunnel4 > /dev/null 2>&1 - if [ $? = 0 ]; then - STUNNEL=stunnel4 - else - STUNNEL=stunnel - fi - fi - stunnel_set_here=1 -fi - -help() { - tail -n +2 "$0" | sed -e '/^$/ q' -} - -secondtry="" -gotalpha="" -use_ssh="" -use_sshssl="" -direct_connect="" -ssh_sleep=15 - -# sleep longer in -listen mode: -if echo "$*" | grep '.*-listen' > /dev/null; then - ssh_sleep=1800 -fi - - -ssh_cmd="" -# env override of ssh_cmd: -if [ "X$SS_VNCVIEWER_SSH_CMD" != "X" ]; then - ssh_cmd="$SS_VNCVIEWER_SSH_CMD" -fi - -ssh_args="" -showcert="" -reverse="" - -ciphers="" -anondh="ALL:RC4+RSA:+SSLv2:@STRENGTH" -anondh_set="" -stunnel_debug="6" -if [ "X$SS_DEBUG" != "X" -o "X$SSVNC_VENCRYPT_DEBUG" != "X" -o "X$SSVNC_STUNNEL_DEBUG" != "X" ]; then - stunnel_debug="7" -fi - -if [ "X$1" = "X-viewerflavor" ]; then - # special case, try to guess which viewer: - # - if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - echo "unknown" - exit 0 - fi - if echo "$VNCVIEWERCMD" | grep -i chicken.of > /dev/null; then - echo "cotvnc" - exit 0 - fi - if echo "$VNCVIEWERCMD" | grep -i ultra > /dev/null; then - echo "ultravnc" - exit 0 - fi - # OK, run it for help output... - str=`$VNCVIEWERCMD -h 2>&1 | head -n 5` - if echo "$str" | grep -i 'TightVNC.viewer' > /dev/null; then - echo "tightvnc" - elif echo "$str" | grep -i 'VNC viewer version 3' > /dev/null; then - echo "realvnc3" - elif echo "$str" | grep -i 'VNC viewer .*Edition 4' > /dev/null; then - echo "realvnc4" - elif echo "$str" | grep -i 'RealVNC.Ltd' > /dev/null; then - echo "realvnc4" - else - echo "unknown" - fi - exit 0 -fi -if [ "X$1" = "X-viewerhelp" ]; then - $VNCVIEWERCMD -h 2>&1 - exit 0 -fi - -# grab our cmdline options: -while [ "X$1" != "X" ] -do - case $1 in - "-verify") shift; verify="$1" - ;; - "-mycert") shift; mycert="$1" - ;; - "-crl") shift; crl="$1" - ;; - "-proxy") shift; proxy="$1" - ;; - "-ssh") use_ssh=1 - ;; - "-sshssl") use_ssh=1 - use_sshssl=1 - ;; - "-sshcmd") shift; ssh_cmd="$1" - ;; - "-sshargs") shift; ssh_args="$1" - ;; - "-anondh") ciphers="ciphers=$anondh" - ULTRAVNC_DSM_HELPER_SHOWCERT_ADH=1 - export ULTRAVNC_DSM_HELPER_SHOWCERT_ADH - anondh_set=1 - ;; - "-ciphers") shift; ciphers="ciphers=$1" - ;; - "-alpha") gotalpha=1 - ;; - "-showcert") showcert=1 - ;; - "-listen") reverse=1 - ;; - "-reverse") reverse=1 - ;; - "-2nd") secondtry=1 - ;; - "-grab") VNCVIEWER_GRAB_SERVER=1; export VNCVIEWER_GRAB_SERVER - ;; - "-x11cursor") VNCVIEWER_X11CURSOR=1; export VNCVIEWER_X11CURSOR - ;; - "-rawlocal") VNCVIEWER_RAWLOCAL=1; export VNCVIEWER_RAWLOCAL - ;; - "-scale") shift; SSVNC_SCALE="$1"; export SSVNC_SCALE - ;; - "-onelisten") SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - ;; - "-sendclipboard") VNCVIEWER_SEND_CLIPBOARD=1; export VNCVIEWER_SEND_CLIPBOARD - ;; - "-sendalways") VNCVIEWER_SEND_ALWAYS=1; export VNCVIEWER_SEND_ALWAYS - ;; - "-recvtext") shift; VNCVIEWER_RECV_TEXT="$1"; export VNCVIEWER_RECV_TEXT - ;; - "-escape") shift; VNCVIEWER_ESCAPE="$1"; export VNCVIEWER_ESCAPE - ;; - "-ssvnc_encodings") shift; VNCVIEWER_ENCODINGS="$1"; export VNCVIEWER_ENCODINGS - ;; - "-ssvnc_extra_opts") shift; VNCVIEWERCMD_EXTRA_OPTS="$1"; export VNCVIEWERCMD_EXTRA_OPTS - ;; - "-rfbversion") shift; VNCVIEWER_RFBVERSION="$1"; export VNCVIEWER_RFBVERSION - ;; - "-nobell") VNCVIEWER_NOBELL=1; export VNCVIEWER_NOBELL - ;; - "-popupfix") VNCVIEWER_POPUP_FIX=1; export VNCVIEWER_POPUP_FIX - ;; - "-realvnc4") VNCVIEWER_IS_REALVNC4=1; export VNCVIEWER_IS_REALVNC4 - ;; - "-h"*) help; exit 0 - ;; - "--h"*) help; exit 0 - ;; - *) break - ;; - esac - shift -done - -# maxconn is something we added to stunnel, this disables it: -if [ "X$SS_VNCVIEWER_NO_MAXCONN" != "X" ]; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -elif echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -elif [ "X$reverse" != "X" ]; then - STUNNEL_EXTRA_OPTS=`echo "$STUNNEL_EXTRA_OPTS" | sed -e 's/maxconn/#maxconn/'` -else - # new way (our patches). other than the above, we set these: - if [ "X$SKIP_STUNNEL_ONCE" = "X" ]; then - STUNNEL_ONCE=1; export STUNNEL_ONCE - fi - if [ "X$SKIP_STUNNEL_MAX_CLIENTS" = "X" ]; then - STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS - fi -fi -# always set this one: -if [ "X$SKIP_STUNNEL_NO_SYSLOG" = "X" ]; then - STUNNEL_NO_SYSLOG=1; export STUNNEL_NO_SYSLOG -fi - -# this is the -t ssh option (gives better keyboard response thru SSH tunnel) -targ="-t" -if [ "X$SS_VNCVIEWER_NO_T" != "X" ]; then - targ="" -fi - -# set the alpha blending env. hack: -if [ "X$gotalpha" = "X1" ]; then - VNCVIEWER_ALPHABLEND=1 - export VNCVIEWER_ALPHABLEND -else - NO_ALPHABLEND=1 - export NO_ALPHABLEND -fi - -if [ "X$reverse" != "X" ]; then - ssh_sleep=1800 - if [ "X$proxy" != "X" ]; then - # check proxy usage under reverse connection: - if [ "X$use_ssh" = "X" -a "X$use_sshssl" = "X" ]; then - echo "" - if echo "$proxy" | egrep -i "(repeater|vencrypt)://" > /dev/null; then - : - else - echo "*Warning*: SSL -listen and a Web proxy does not make sense." - sleep 2 - fi - elif echo "$proxy" | grep "," > /dev/null; then - : - else - echo "" - echo "*Warning*: -listen and a single proxy/gateway does not make sense." - sleep 2 - fi - - # we now try to PPROXY_LOOP_THYSELF, set this var to disable that. - #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - fi -fi -if [ "X$ssh_cmd" = "X" ]; then - # if no remote ssh cmd, sleep a bit: - ssh_cmd="sleep $ssh_sleep" -fi - -# this should be a host:display: -# -orig="$1" -shift - -dL="-L" -if uname -sr | egrep 'SunOS 5\.[5-8]' > /dev/null; then - dL="-h" -fi - -have_uvnc_dsm_helper_showcert="" -if [ "X$showcert" = "X1" -a "X$SSVNC_USE_S_CLIENT" = "X" -a "X$reverse" = "X" ]; then - if type ultravnc_dsm_helper >/dev/null 2>&1; then - if ultravnc_dsm_helper -help 2>&1 | grep -w showcert >/dev/null; then - have_uvnc_dsm_helper_showcert=1 - fi - fi -fi -have_uvnc_dsm_helper_ipv6="" -if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if type ultravnc_dsm_helper >/dev/null 2>&1; then - if ultravnc_dsm_helper -help 2>&1 | grep -iw ipv6 >/dev/null; then - have_uvnc_dsm_helper_ipv6=1 - fi - fi -fi - -rchk() { - # a kludge to set $RANDOM if we are not bash: - if [ "X$BASH_VERSION" = "X" ]; then - RANDOM=`date +%S``sh -c 'echo $$'``ps -elf 2>&1 | sum 2>&1 | awk '{print $1}'` - fi -} -rchk - -# a portable, but not absolutely safe, tmp file creator -mytmp() { - tf=$1 - if type mktemp > /dev/null 2>&1; then - # if we have mktemp(1), use it: - tf2="$tf.XXXXXX" - tf2=`mktemp "$tf2"` - if [ "X$tf2" != "X" -a -f "$tf2" ]; then - if [ "X$DEBUG_MKTEMP" != "X" ]; then - echo "mytmp-mktemp: $tf2" 1>&2 - fi - echo "$tf2" - return - fi - fi - # fallback to multiple cmds: - rm -rf "$tf" || exit 1 - if [ -d "$tf" ]; then - echo "tmp file $tf still exists as a directory." - exit 1 - elif [ $dL "$tf" ]; then - echo "tmp file $tf still exists as a symlink." - exit 1 - elif [ -f "$tf" ]; then - echo "tmp file $tf still exists." - exit 1 - fi - touch "$tf" || exit 1 - chmod 600 "$tf" || exit 1 - rchk - if [ "X$DEBUG_MKTEMP" != "X" ]; then - echo "mytmp-touch: $tf" 1>&2 - fi - echo "$tf" -} - -# set up special case of ultravnc single click III mode: -if echo "$proxy" | egrep "^sslrepeater://" > /dev/null; then - pstr=`echo "$proxy" | sed -e 's,sslrepeater://,,'` - pstr1=`echo "$pstr" | sed -e 's/+.*$//'` - pstr2=`echo "$pstr" | sed -e 's/^[^+]*+//'` - SSVNC_REPEATER="SCIII=$pstr2"; export SSVNC_REPEATER - orig=$pstr1 - echo - echo "reset: SSVNC_REPEATER=$SSVNC_REPEATER orig=$orig proxy=''" - proxy="" -fi -if echo "$proxy" | egrep "vencrypt://" > /dev/null; then - vtmp="/tmp/ss_handshake${RANDOM}.$$.txt" - vtmp=`mytmp "$vtmp"` - SSVNC_PREDIGESTED_HANDSHAKE="$vtmp" - export SSVNC_PREDIGESTED_HANDSHAKE - if [ "X$SSVNC_USE_OURS" = "X" ]; then - NEED_VENCRYPT_VIEWER_BRIDGE=1 - fi -fi -if [ "X$SSVNC_USE_OURS" = "X" ]; then - VNCVIEWERCMD_EXTRA_OPTS="" -fi - - -# check -ssh and -mycert/-verify conflict: -if [ "X$use_ssh" = "X1" -a "X$use_sshssl" = "X" ]; then - if [ "X$mycert" != "X" -o "X$verify" != "X" ]; then - echo "-mycert and -verify cannot be used in -ssh mode" - exit 1 - fi -fi - -# direct mode Vnc:// means show no warnings. -# direct mode vnc:// will show warnings. -if echo "$orig" | grep '^V[Nn][Cc]://' > /dev/null; then - SSVNC_NO_ENC_WARN=1 - export SSVNC_NO_ENC_WARN - orig=`echo "$orig" | sed -e 's/^...:/vnc:/'` -fi - -# interprest the pseudo URL proto:// strings: -if echo "$orig" | grep '^vnc://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc://,,'` - verify="" - mycert="" - crl="" - use_ssh="" - use_sshssl="" - direct_connect=1 -elif echo "$orig" | grep '^vncs://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncs://,,'` -elif echo "$orig" | grep '^vncssl://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncssl://,,'` -elif echo "$orig" | grep '^vnc+ssl://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc.ssl://,,'` -elif echo "$orig" | grep '^vncssh://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vncssh://,,'` - use_ssh=1 -elif echo "$orig" | grep '^vnc+ssh://' > /dev/null; then - orig=`echo "$orig" | sed -e 's,vnc.ssh://,,'` - use_ssh=1 -fi - -if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - verify="" - mycert="" - crl="" - use_ssh="" - use_sshssl="" - direct_connect=1 - if echo "$SSVNC_ULTRA_DSM" | grep 'noultra:' > /dev/null; then - SSVNC_NO_ULTRA_DSM=1; export SSVNC_NO_ULTRA_DSM - fi -fi - -# rsh mode is an internal/secret thing only I use. -rsh="" -if echo "$orig" | grep '^rsh://' > /dev/null; then - use_ssh=1 - rsh=1 - orig=`echo "$orig" | sed -e 's,rsh://,,'` -elif echo "$orig" | grep '^rsh:' > /dev/null; then - use_ssh=1 - rsh=1 - orig=`echo "$orig" | sed -e 's,rsh:,,'` -fi - -# play around with host:display port: -if echo "$orig" | grep ':[0-9][0-9]*$' > /dev/null; then - : -else - # add or assume :0 if no ':' - if [ "X$reverse" = "X" ]; then - orig="$orig:0" - elif [ "X$orig" = "X" ]; then - orig=":0" - fi -fi - -# extract host and disp number: - -# try to see if it is ipv6 address: -ipv6=0 -if echo "$orig" | grep '\[' > /dev/null; then - # ipv6 [fe80::219:dbff:fee5:3f92%eth1]:5900 - host=`echo "$orig" | sed -e 's/\].*$//' -e 's/\[//'` - disp=`echo "$orig" | sed -e 's/^.*\]://'` - ipv6=1 -elif echo "$orig" | grep ':..*:' > /dev/null; then - # ipv6 fe80::219:dbff:fee5:3f92%eth1:5900 - host=`echo "$orig" | sed -e 's/:[^:]*$//'` - disp=`echo "$orig" | sed -e 's/^.*://'` - ipv6=1 -else - # regular host:port - host=`echo "$orig" | awk -F: '{print $1}'` - disp=`echo "$orig" | awk -F: '{print $2}'` -fi - -if [ "X$reverse" != "X" -a "X$STUNNEL_LISTEN" = "X" -a "X$host" != "X" ]; then - STUNNEL_LISTEN=$host - echo "set STUNNEL_LISTEN=$STUNNEL_LISTEN" -fi - -if [ "X$host" = "X" ]; then - host=$localhost -fi - -if [ "X$SSVNC_IPV6" = "X0" ]; then - # disable checking for it. - ipv6=0 -#elif [ "X$reverse" != "X" -a "X$ipv6" = "X1" ]; then -# ipv6=0 -elif [ "X$ipv6" = "X1" ]; then - : -elif echo "$host" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then - : -else - # regular hostname, can't be sure... - gout="" - if type getent > /dev/null 2>/dev/null; then - gout=`getent hosts "$host" 2>/dev/null` - fi - if echo "$gout" | grep ':.*:' > /dev/null; then - if echo "$gout" | grep '^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' > /dev/null; then - : - else - echo "ipv6: "`echo "$gout" | grep ':.*:' | head -n 1` - ipv6=1 - fi - fi - if [ "X$ipv6" = "X0" ]; then - hout="" - if type host > /dev/null 2>/dev/null; then - host "$host" >/dev/null 2>&1 - host "$host" >/dev/null 2>&1 - hout=`host "$host" 2>/dev/null` - fi - if echo "$hout" | grep -i 'has ipv6 address' > /dev/null; then - if echo "$hout" | grep -i 'has address' > /dev/null; then - : - else - echo "ipv6: "`echo "$hout" | grep -i 'has ipv6 address' | head -n 1` - ipv6=1 - fi - fi - fi - if [ "X$ipv6" = "X0" ]; then - dout="" - if type dig > /dev/null 2>/dev/null; then - dout=`dig -t any "$host" 2>/dev/null` - fi - if echo "$dout" | grep -i "^$host" | grep '[ ]AAAA[ ]' > /dev/null; then - if echo "$dout" | grep -i "^$host" | grep '[ ]A[ ]' > /dev/null; then - : - else - echo "ipv6: "`echo "$dout" | grep -i '[ ]AAAA[ ]' | head -n 1` - ipv6=1 - fi - fi - fi - if [ "X$ipv6" = "X0" ]; then - sout=`env LOOKUP="$host" \ - perl -e ' eval {use Socket}; exit 0 if $@; - eval {use Socket6}; exit 0 if $@; - @res = getaddrinfo($ENV{LOOKUP}, "daytime", AF_UNSPEC, SOCK_STREAM); - $ipv4 = 0; - $ipv6 = 0; - $ip6 = ""; - while (scalar(@res) >= 5) { - ($family, $socktype, $proto, $saddr, $canon, @res) = @res; - $ipv4 = 1 if $family == AF_INET; - $ipv6 = 1 if $family == AF_INET6; - if ($family == AF_INET6 && $ip6 eq "") { - my ($host, $port) = getnameinfo($saddr, NI_NUMERICHOST | NI_NUMERICSERV); - $ip6 = $host; - } - } - if (! $ipv4 && $ipv6) { - print "AF_INET6_ONLY: $ENV{LOOKUP}: $ip6\n"; - } - exit 0; - ' 2>/dev/null` - if echo "$sout" | grep AF_INET6_ONLY > /dev/null; then - echo "$sout" - ipv6=1 - fi - fi -fi -if [ "X$ipv6" = "X1" ]; then - echo "ipv6: addr=$host disp=$disp" -fi -if [ "X$disp" = "X" ]; then - port="" # probably -listen mode. -elif [ $disp -lt 0 ]; then - # negative means use |n| without question: - port=`expr 0 - $disp` -elif [ $disp -lt 200 ]; then - # less than 200 means 5900+n - if [ "X$reverse" = "X" ]; then - port=`expr $disp + 5900` - else - port=`expr $disp + 5500` - fi -else - # otherwise use the number directly, e.g. 443, 2345 - port=$disp -fi - -if [ "X$ipv6" = "X1" -a "X$direct_connect" = "X1" ]; then - if [ "X$proxy" = "X" -a "X$reverse" = "X" ]; then - if [ "X$SSVNC_ULTRA_DSM" != "X" -a "X$have_uvnc_dsm_helper_ipv6" = "X1" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY_DIRECT" != "X" ]; then - : - elif [ "X$SSVNC_USE_OURS" = "X1" ]; then - # requires 1.0.27 and later ssvncviewer binary - : - else - proxy="ipv6://$host:$port" - echo "direct connect: set proxy=$proxy" - fi - fi -fi - -# (possibly) tell the vncviewer to only listen on lo: -if [ "X$reverse" != "X" ]; then - if [ "X$direct_connect" = "X" -o "X$proxy" != "X" -o "X$STUNNEL_LISTEN" != "X" ]; then - VNCVIEWER_LISTEN_LOCALHOST=1 - export VNCVIEWER_LISTEN_LOCALHOST - fi -fi - -# try to find an open listening port via netstat(1): -inuse="" -if uname | grep Linux > /dev/null; then - inuse=`netstat -ant | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*://'` -elif uname | grep SunOS > /dev/null; then - inuse=`netstat -an -f inet -P tcp | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $1}' | sed 's/^.*\.//'` -elif uname | egrep -i 'bsd|darwin' > /dev/null; then - inuse=`netstat -ant -f inet | egrep 'LISTEN|WAIT|ESTABLISH|CLOSE' | awk '{print $4}' | sed 's/^.*\.//'` -# add others... -fi - -# this is a crude attempt for unique ports tags, etc. -date_sec=`date +%S` - -# these are special cases of no vnc, e.g. sleep or xmessage. -# these are for using ssvnc as a general port redirector. -if echo "$VNCVIEWERCMD" | grep '^sleep[ ][ ]*[0-9][0-9]*' > /dev/null; then - if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then - p=`echo "$VNCVIEWERCMD" | awk '{print $3}'` - if [ "X$p" != "X" ]; then - SS_VNCVIEWER_LISTEN_PORT=$p - fi - fi - p2=`echo "$VNCVIEWERCMD" | awk '{print $2}'` - VNCVIEWERCMD="eval sleep $p2; echo Local " -elif echo "$VNCVIEWERCMD" | grep '^xmessage[ ][ ]*[0-9][0-9]*' > /dev/null; then - if [ "X$SS_VNCVIEWER_LISTEN_PORT" = "X" ]; then - p=`echo "$VNCVIEWERCMD" | awk '{print $2}'` - SS_VNCVIEWER_LISTEN_PORT=$p - fi -fi - -# utility to find a free port to listen on. -findfree() { - try0=$1 - try=$try0 - use0="" - - if [ "X$SS_VNCVIEWER_LISTEN_PORT" != "X" ]; then - echo "$SS_VNCVIEWER_LISTEN_PORT" - return - fi - if [ $try -ge 6000 ]; then - fmax=`expr $try + 1000` - else - fmax=6000 - fi - - while [ $try -lt $fmax ] - do - if [ "X$inuse" = "X" ]; then - break - fi - if echo "$inuse" | grep -w $try > /dev/null; then - : - else - use0=$try - break - fi - try=`expr $try + 1` - done - if [ "X$use0" = "X" ]; then - use0=`expr $date_sec + $try0` - fi - - echo $use0 -} - -# utility for exiting; kills some helper processes, -# removes files, etc. -final() { - echo "" - if [ "X$tmp_cfg" != "X" ]; then - rm -f $tmp_cfg - fi - if [ "X$SS_VNCVIEWER_RM" != "X" ]; then - rm -f $SS_VNCVIEWER_RM 2>/dev/null - fi - if [ "X$tcert" != "X" ]; then - rm -f $tcert - fi - if [ "X$pssh" != "X" ]; then - echo "Terminating background ssh process" - echo kill -TERM "$pssh" - kill -TERM "$pssh" 2>/dev/null - sleep 1 - kill -KILL "$pssh" 2>/dev/null - pssh="" - fi - if [ "X$stunnel_pid" != "X" ]; then - echo "Terminating background stunnel process" - echo kill -TERM "$stunnel_pid" - kill -TERM "$stunnel_pid" 2>/dev/null - sleep 1 - kill -KILL "$stunnel_pid" 2>/dev/null - stunnel_pid="" - fi - if [ "X$dsm_pid" != "X" ]; then - echo "Terminating background ultravnc_dsm_helper process" - echo kill -TERM "$dsm_pid" - kill -TERM "$dsm_pid" 2>/dev/null - sleep 1 - kill -KILL "$dsm_pid" 2>/dev/null - stunnel_pid="" - fi - if [ "X$tail_pid" != "X" ]; then - kill -TERM $tail_pid - fi - if [ "X$tail_pid2" != "X" ]; then - kill -TERM $tail_pid2 - fi -} - -if [ "X$reverse" = "X" ]; then - # normal connections try 5930-5999: - if [ "X$showcert" = "X" ]; then - use=`findfree 5930` - else - # move away from normal place for (possibly many) -showcert - pstart=`date +%S` - pstart=`expr 6130 + $pstart + $pstart` - use=`findfree $pstart` - fi - if [ $use -ge 5900 ]; then - N=`expr $use - 5900` - else - N=$use - fi -else - # reverse connections: - p2=`expr $port + 30` - use=`findfree $p2` - if [ $use -ge 5500 ]; then - N=`expr $use - 5500` - else - N=$use - fi -fi - -# this is for my special use of ss_vncip -> vncip viewer. -if echo "$0" | grep vncip > /dev/null; then - VNCVIEWERCMD="$VNCIPCMD" -fi - -if echo "$VNCVIEWERCMD" | egrep -i '^(xmessage|sleep )' > /dev/null; then - : -elif [ "X$VNCVIEWERCMD_EXTRA_OPTS" != "X" ]; then - VNCVIEWERCMD="$VNCVIEWERCMD $VNCVIEWERCMD_EXTRA_OPTS" -fi - -# trick for the undocumented rsh://host:port method. -rsh_setup() { - if echo "$ssh_host" | grep '@' > /dev/null; then - ul=`echo "$ssh_host" | awk -F@ '{print $1}'` - ul="-l $ul" - ssh_host=`echo "$ssh_host" | awk -F@ '{print $2}'` - else - ul="" - fi - ssh_cmd=`echo "$ssh_cmd" | sed -e 's/ -localhost/ /g'` -} - -# trick for the undocumented rsh://host:port method. -rsh_viewer() { - trap "final" 0 2 15 - if [ "X$PORT" = "X" ]; then - exit 1 - elif [ $PORT -ge 5900 ]; then - vdpy=`expr $PORT - 5900` - else - vdpy=":$PORT" - fi - stty sane - echo "$VNCVIEWERCMD" "$@" $ssh_host:$vdpy - echo "" - $VNCVIEWERCMD "$@" $ssh_host:$vdpy - if [ $? != 0 ]; then - sleep 2 - $VNCVIEWERCMD "$@" $ssh_host:$vdpy - fi -} - -check_perl() { - if type "$1" > /dev/null 2>&1; then - : - elif [ ! -x "$1" ]; then - echo "" - echo "*******************************************************" - echo "** Problem finding the Perl command '$1': **" - echo "" - type "perl" - echo "" - echo "** Perhaps you need to install the Perl package. **" - echo "*******************************************************" - echo "" - sleep 5 - fi -} - -# this is the PPROXY tool. used only here for now... -pcode() { - tf=$1 - PPROXY_PROXY=$proxy; export PPROXY_PROXY - PPROXY_DEST="$host:$port"; export PPROXY_DEST - check_perl /usr/bin/perl - - cod='#!/usr/bin/perl - -# A hack to glue stunnel to a Web or SOCKS proxy, UltraVNC repeater for -# client connections. -# Also acts as a VeNCrypt bridge (by redirecting to stunnel.) - -use IO::Socket::INET; - -my $have_inet6 = ""; -eval "use IO::Socket::INET6;"; -$have_inet6 = 1 if $@ eq ""; - -#my $have_sock6 = ""; -#eval "use Socket; use Socket6;"; -#$have_sock6 = 1 if $@ eq ""; - -if (exists $ENV{PPROXY_LOOP_THYSELF}) { - # used for reverse vnc, run a repeating outer loop. - print STDERR "PPROXY_LOOP: $ENV{PPROXY_LOOP_THYSELF}\n"; - my $rm = $ENV{PPROXY_REMOVE}; - my $lp = $ENV{PPROXY_LOOP_THYSELF}; - delete $ENV{PPROXY_REMOVE}; - delete $ENV{PPROXY_LOOP_THYSELF}; - $ENV{PPROXY_LOOP_THYSELF_MASTER} = $$; - my $pid = $$; - my $dbg = 0; - my $c = 0; - use POSIX ":sys_wait_h"; - while (1) { - $pid = fork(); - last if ! defined $pid; - if ($pid eq "0") { - last; - } - $c++; - print STDERR "\nPPROXY_LOOP: pid=$$ child=$pid count=$c\n"; - while (1) { - waitpid(-1, WNOHANG); - fsleep(0.25); - if (! kill 0, $pid) { - print STDERR "PPROXY_LOOP: child=$pid gone.\n"; - last; - } - print STDERR "PPROXY_LOOP: child=$pid alive.\n" if $dbg; - if (! -f $lp) { - print STDERR "PPROXY_LOOP: flag file $lp gone, killing $pid\n"; - kill TERM, $pid; - fsleep(0.1); - wait; - last; - } - print STDERR "PPROXY_LOOP: file exists $lp\n" if $dbg; - } - last if ! -f $lp; - fsleep(0.25); - } - if ($pid ne "0") { - unlink($0) if $rm; - exit 0; - } -} - -if (exists $ENV{PPROXY_SLEEP} && $ENV{PPROXY_SLEEP} > 0) { - print STDERR "PPROXY_PID: $$\n"; - sleep $ENV{PPROXY_SLEEP}; -} - -foreach my $var (qw( - PPROXY_DEST - PPROXY_KILLPID - PPROXY_LISTEN - PPROXY_PROXY - PPROXY_REMOVE - PPROXY_REPEATER - PPROXY_REVERSE - PPROXY_SLEEP - PPROXY_SOCKS - PPROXY_VENCRYPT - PPROXY_VENCRYPT_VIEWER_BRIDGE - )) { - if (0 || $ENV{SS_DEBUG} || $ENV{SSVNC_VENCRYPT_DEBUG}) { - print STDERR "$var: $ENV{$var}\n"; - } -} - -if ($ENV{PPROXY_SOCKS} ne "" && $ENV{PPROXY_PROXY} !~ m,^socks5?://,i) { - if ($ENV{PPROXY_SOCKS} eq "5") { - $ENV{PPROXY_PROXY} = "socks5://$ENV{PPROXY_PROXY}"; - } else { - $ENV{PPROXY_PROXY} = "socks://$ENV{PPROXY_PROXY}"; - } -} - -my $rfbSecTypeAnonTls = 18; -my $rfbSecTypeVencrypt = 19; - -my $rfbVencryptPlain = 256; -my $rfbVencryptTlsNone = 257; -my $rfbVencryptTlsVnc = 258; -my $rfbVencryptTlsPlain = 259; -my $rfbVencryptX509None = 260; -my $rfbVencryptX509Vnc = 261; -my $rfbVencryptX509Plain = 262; - -my $handshake_file = ""; -if (exists $ENV{SSVNC_PREDIGESTED_HANDSHAKE}) { - $handshake_file = $ENV{SSVNC_PREDIGESTED_HANDSHAKE}; -} - -my $have_gettimeofday = 0; -eval "use Time::HiRes;"; -if ($@ eq "") { - $have_gettimeofday = 1; -} -sub gettime { - my $t = "0.0"; - if ($have_gettimeofday) { - $t = Time::HiRes::gettimeofday(); - } - return $t; -} - -my $listen_handle = ""; -my $sock = ""; -my $parent = $$; - -my $initial_data = ""; - -if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { - my ($from, $to) = split(/,/, $ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}); - do_vencrypt_viewer_bridge($from, $to); - exit 0; -} - -my ($first, $second, $third) = split(/,/, $ENV{PPROXY_PROXY}, 3); -my ($mode_1st, $mode_2nd, $mode_3rd) = ("", "", ""); - -($first, $mode_1st) = url_parse($first); - -my ($proxy_host, $proxy_port) = ($first, ""); -if ($proxy_host =~ /^(.*):(\d+)$/) { - $proxy_host = $1; - $proxy_port = $2; -} -my $connect = $ENV{PPROXY_DEST}; - -if ($second ne "") { - ($second, $mode_2nd) = url_parse($second); -} - -if ($third ne "") { - ($third, $mode_3rd) = url_parse($third); -} - - -print STDERR "\n"; -print STDERR "PPROXY v0.4: a tool for Web, SOCKS, and UltraVNC proxies and for\n"; -print STDERR "PPROXY v0.4: IPv6 and VNC VeNCrypt bridging.\n"; -print STDERR "proxy_host: $proxy_host\n"; -print STDERR "proxy_port: $proxy_port\n"; -print STDERR "proxy_connect: $connect\n"; -print STDERR "pproxy_params: $ENV{PPROXY_PROXY}\n"; -print STDERR "pproxy_listen: $ENV{PPROXY_LISTEN}\n"; -print STDERR "pproxy_reverse: $ENV{PPROXY_REVERSE}\n"; -print STDERR "io_socket_inet6: $have_inet6\n"; -print STDERR "\n"; -if (! $have_inet6) { - print STDERR "PPROXY: To enable IPv6 connections, install the IO::Socket::INET6 perl module.\n\n"; -} - -if (1) { - print STDERR "pproxy 1st: $first\t- $mode_1st\n"; - print STDERR "pproxy 2nd: $second\t- $mode_2nd\n"; - print STDERR "pproxy 3rd: $third\t- $mode_3rd\n"; - print STDERR "\n"; -} - -sub pdie { - my $msg = shift; - kill_proxy_pids(); - die "$msg"; -} - -if ($ENV{PPROXY_REVERSE} ne "") { - my ($rhost, $rport) = ($ENV{PPROXY_REVERSE}, ""); - if ($rhost =~ /^(.*):(\d+)$/) { - $rhost = $1; - $rport = $2; - } - $rport = 5900 unless $rport; - my $emsg = ""; - $listen_handle = IO::Socket::INET->new( - PeerAddr => $rhost, - PeerPort => $rport, - Proto => "tcp" - ); - $emsg = $!; - if (! $listen_handle && $have_inet6) { - eval {$listen_handle = IO::Socket::INET6->new( - PeerAddr => $rhost, - PeerPort => $rport, - Proto => "tcp" - );}; - $emsg .= " / $!"; - } - if (! $listen_handle) { - pdie "pproxy: $emsg -- PPROXY_REVERSE\n"; - } - print STDERR "PPROXY_REVERSE: connected to $rhost $rport\n"; - -} elsif ($ENV{PPROXY_LISTEN} ne "") { - my $listen_sock = ""; - my $maxtry = 12; - my $sleep = 5; - my $p2 = ""; - my $emsg = ""; - for (my $i=0; $i < $maxtry; $i++) { - my ($if, $p) = ("", $ENV{PPROXY_LISTEN}); - if ($p =~ /^(.*):(\d+)$/) { - $if = $1; - $p = $2; - } - $p2 = "*:$p"; - if ($if eq "") { - $if = "localhost"; - } - print STDERR "pproxy interface: $if\n"; - - $emsg = ""; - if (($if eq "INADDR_ANY6" || $if eq "::") && $have_inet6) { - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::", - LocalPort => $p, - Proto => "tcp" - );}; - $p2 = ":::$p"; - } elsif ($if =~ /^INADDR_ANY/) { - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalPort => $p, - Proto => "tcp" - ); - } elsif (($if eq "INADDR_LOOPBACK6" || $if eq "::1") && $have_inet6) { - $p2 = "::1:$p"; - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => "::1", - LocalPort => $p, - Proto => "tcp" - );}; - $p2 = "::1:$p"; - } else { - $p2 = "$if:$p"; - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalAddr => $if, - LocalPort => $p, - Proto => "tcp" - ); - $emsg = $!; - - if (! $listen_sock && $have_inet6) { - print STDERR "PPROXY_LISTEN: retry with INET6\n"; - eval {$listen_sock = IO::Socket::INET6->new( - Listen => 2, - ReuseAddr => 1, - Domain => AF_INET6, - LocalAddr => $if, - LocalPort => $p, - Proto => "tcp" - );}; - $emsg .= " / $!"; - } - } - if (! $listen_sock) { - if ($i < $maxtry - 1) { - warn "pproxy: $emsg $!\n"; - warn "Could not listen on port $p2, retrying in $sleep seconds... (Ctrl-C to quit)\n"; - sleep $sleep; - } - } else { - last; - } - } - if (! $listen_sock) { - pdie "pproxy: $emsg -- PPROXY_LISTEN\n"; - } - print STDERR "pproxy: listening on $p2\n"; - my $ip; - ($listen_handle, $ip) = $listen_sock->accept(); - my $err = $!; - close $listen_sock; - if (! $listen_handle) { - pdie "pproxy: $err\n"; - } - - if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { - my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; - if ($sml ne "" && $sml ne "0") { - setpgrp(0, 0); - if (fork()) { - close $viewer_sock; - wait; - exit 0; - } - if (fork()) { - close $viewer_sock; - exit 0; - } - setpgrp(0, 0); - $parent = $$; - } - } -} - -$sock = IO::Socket::INET->new( - PeerAddr => $proxy_host, - PeerPort => $proxy_port, - Proto => "tcp" -); - -my $err = ""; - -if (! $sock && $have_inet6) { - $err = $!; - - print STDERR "pproxy: $!\n"; - - eval {$sock = IO::Socket::INET6->new( - PeerAddr => $proxy_host, - PeerPort => $proxy_port, - Proto => "tcp" - );}; - $err .= " / $!"; -} - -if (! $sock && ($proxy_host =~ /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i || $proxy_host =~ /^::ffff:([\da-f]+:[\da-f]+)$/i)) { - print STDERR "pproxy: $!\n"; - my $ipv4_addr = $1; - if ($ipv4_addr =~ /:/) { - my ($a, $b) = split(/:/, $ipv4_addr); - $a = hex($a); - $b = hex($b); - $ipv4_addr = sprintf("%d.", ($a & 0xff00) >> 8); - $ipv4_addr .= sprintf("%d.", ($a & 0x00ff)); - $ipv4_addr .= sprintf("%d.", ($b & 0xff00) >> 8); - $ipv4_addr .= sprintf("%d", ($b & 0x00ff)); - } - - print STDERR "pproxy: re-trying with ipv4 addr: $ipv4_addr\n"; - - eval {$sock = IO::Socket::INET->new( - PeerAddr => $ipv4_addr, - PeerPort => $proxy_port, - Proto => "tcp" - );}; - $err .= " / $!"; -} - -if (! $sock) { - unlink($0) if $ENV{PPROXY_REMOVE}; - pdie "pproxy: $err\n"; -} - -unlink($0) if $ENV{PPROXY_REMOVE}; - -if ($ENV{PPROXY_PROXY} =~ /^vencrypt:/ && $ENV{PPROXY_VENCRYPT_REVERSE}) { - print STDERR "\nPPROXY: vencrypt+reverse: swapping listen socket with connect socket.\n"; - my $tmp_swap = $sock; - $sock = $listen_handle; - $listen_handle = $tmp_swap; -} - -$cur_proxy = $first; -setmode($mode_1st); - -if ($second ne "") { - connection($second, 1); - - setmode($mode_2nd); - $cur_proxy = $second; - - if ($third ne "") { - connection($third, 2); - setmode($mode_3rd); - $cur_proxy = $third; - connection($connect, 3); - } else { - connection($connect, 2); - } -} else { - connection($connect, 1); -} - -sub kill_proxy_pids() { - if ($ENV{PPROXY_VENCRYPT_VIEWER_BRIDGE}) { - return; - } - if ($ENV{PPROXY_KILLPID}) { - foreach my $p (split(/,/, $ENV{PPROXY_KILLPID})) { - if ($p =~ /^(\+|-)/) { - $p = $parent + $p; - } - print STDERR "kill TERM, $p (PPROXY_KILLPID)\n"; - kill "TERM", $p; - } - } -} - -sub xfer { - my($in, $out) = @_; - $RIN = $WIN = $EIN = ""; - $ROUT = ""; - vec($RIN, fileno($in), 1) = 1; - vec($WIN, fileno($in), 1) = 1; - $EIN = $RIN | $WIN; - - while (1) { - my $nf = 0; - while (! $nf) { - $nf = select($ROUT=$RIN, undef, undef, undef); - } - my $len = sysread($in, $buf, 8192); - if (! defined($len)) { - next if $! =~ /^Interrupted/; - print STDERR "pproxy[$$]: $!\n"; - last; - } elsif ($len == 0) { - print STDERR "pproxy[$$]: Input is EOF.\n"; - last; - } - my $offset = 0; - my $quit = 0; - while ($len) { - my $written = syswrite($out, $buf, $len, $offset); - if (! defined $written) { - print STDERR "pproxy[$$]: Output is EOF. $!\n"; - $quit = 1; - last; - } - $len -= $written; - $offset += $written; - } - last if $quit; - } - close($out); - close($in); - print STDERR "pproxy[$$]: finished xfer.\n"; -} - -sub handler { - print STDERR "pproxy[$$]: got SIGTERM.\n"; - close $listen_handle if $listen_handle; - close $sock if $sock; - exit; -} - -sub xfer_both { - $child = fork; - - if (! defined $child) { - kill_proxy_pids(); - exit 1; - } - - $SIG{TERM} = "handler"; - - if ($child) { - if ($listen_handle) { - print STDERR "pproxy parent[$$] listen_handle -> socket\n"; - xfer($listen_handle, $sock); - } else { - print STDERR "pproxy parent[$$] STDIN -> socket\n"; - xfer(STDIN, $sock); - } - select(undef, undef, undef, 0.25); - if (kill 0, $child) { - select(undef, undef, undef, 0.9); - if (kill 0, $child) { - print STDERR "pproxy[$$]: kill TERM child $child\n"; - kill "TERM", $child; - } else { - print STDERR "pproxy[$$]: child $child gone.\n"; - } - } - } else { - select(undef, undef, undef, 0.05); - if ($listen_handle) { - print STDERR "pproxy child [$$] socket -> listen_handle\n"; - if ($initial_data ne "") { - my $len = length $initial_data; - print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; - syswrite($listen_handle, $initial_data, $len); - } else { - print STDERR "\n"; - } - xfer($sock, $listen_handle); - } else { - print STDERR "pproxy child [$$] socket -> STDOUT\n"; - if ($initial_data ne "") { - my $len = length $initial_data; - print STDERR "pproxy child [$$] sending initial_data, length $len\n\n"; - syswrite(STDOUT, $initial_data, $len); - } else { - print STDERR "\n"; - } - xfer($sock, STDOUT); - } - select(undef, undef, undef, 0.25); - if (kill 0, $parent) { - select(undef, undef, undef, 0.8); - if (kill 0, $parent) { - print STDERR "pproxy[$$]: kill TERM parent $parent\n"; - kill "TERM", $parent; - } else { - print STDERR "pproxy[$$]: parent $parent gone.\n"; - } - } - } - - kill_proxy_pids(); -} - -xfer_both(); - -exit; - -sub fsleep { - select(undef, undef, undef, shift); -} - -sub url_parse { - my $hostport = shift; - my $mode = "http"; - if ($hostport =~ m,^socks4?://(\S*)$,i) { - $mode = "socks4"; - $hostport = $1; - } elsif ($hostport =~ m,^socks5://(\S*)$,i) { - $mode = "socks5"; - $hostport = $1; - } elsif ($hostport =~ m,^https?://(\S*)$,i) { - $mode = "http"; - $hostport = $1; - } elsif ($hostport =~ m,^ipv6://(\S*)$,i) { - $mode = "ipv6"; - $hostport = $1; - } elsif ($hostport =~ m,^repeater://(\S*)\+(\S*)$,i) { - # ultravnc repeater proxy. - $hostport = $1; - $mode = "repeater:$2"; - if ($hostport !~ /:\d+$/) { - $hostport .= ":5900"; - } - } elsif ($hostport =~ m,^vencrypt://(\S*)$,i) { - # vencrypt handshake. - $hostport = $1; - my $m = "connect"; - if ($hostpost =~ /^(\S+)\+(\S+)$/) { - $hostport = $1; - $mode = $2; - } - $mode = "vencrypt:$m"; - if ($hostport !~ /:\d+$/) { - $hostport .= ":5900"; - } - } - return ($hostport, $mode); -} - -sub setmode { - my $mode = shift; - $ENV{PPROXY_REPEATER} = ""; - $ENV{PPROXY_VENCRYPT} = ""; - if ($mode =~ /^socks/) { - if ($mode =~ /^socks5/) { - $ENV{PPROXY_SOCKS} = 5; - } else { - $ENV{PPROXY_SOCKS} = 1; - } - } elsif ($mode =~ /^ipv6/i) { - $ENV{PPROXY_SOCKS} = 0; - } elsif ($mode =~ /^repeater:(.*)/) { - $ENV{PPROXY_REPEATER} = $1; - $ENV{PPROXY_SOCKS} = ""; - } elsif ($mode =~ /^vencrypt:(.*)/) { - $ENV{PPROXY_VENCRYPT} = $1; - $ENV{PPROXY_SOCKS} = ""; - } else { - $ENV{PPROXY_SOCKS} = ""; - } -} - -sub connection { - my ($CONNECT, $w) = @_; - - my $con = ""; - my $msg = ""; - - if ($ENV{PPROXY_SOCKS} eq "5") { - # SOCKS5 - my ($h, $p) = ($CONNECT, ""); - if ($h =~ /^(.*):(\d+)$/) { - $h = $1; - $p = $2; - } - $con .= pack("C", 0x05); - $con .= pack("C", 0x01); - $con .= pack("C", 0x00); - - $msg = "SOCKS5 via $cur_proxy to $h:$p\n\n"; - print STDERR "proxy_request$w: $msg"; - - syswrite($sock, $con, length($con)); - - my ($n1, $n2, $n3, $n4, $n5, $n6); - my ($r1, $r2, $r3, $r4, $r5, $r6); - my ($s1, $s2, $s3, $s4, $s5, $s6); - - $n1 = sysread($sock, $r1, 1); - $n2 = sysread($sock, $r2, 1); - - $s1 = unpack("C", $r1); - $s2 = unpack("C", $r2); - if ($s1 != 0x05 || $s2 != 0x00) { - print STDERR "SOCKS5 fail s1=$s1 s2=$s2 n1=$n1 n2=$n2\n"; - close $sock; - exit(1); - } - - $con = ""; - $con .= pack("C", 0x05); - $con .= pack("C", 0x01); - $con .= pack("C", 0x00); - $con .= pack("C", 0x03); - $con .= pack("C", length($h)); - $con .= $h; - $con .= pack("C", $p >> 8); - $con .= pack("C", $p & 0xff); - - syswrite($sock, $con, length($con)); - - $n1 = sysread($sock, $r1, 1); - $n2 = sysread($sock, $r2, 1); - $n3 = sysread($sock, $r3, 1); - $n4 = sysread($sock, $r4, 1); - $s1 = unpack("C", $r1); - $s2 = unpack("C", $r2); - $s3 = unpack("C", $r3); - $s4 = unpack("C", $r4); - - if ($s4 == 0x1) { - sysread($sock, $r5, 4 + 2); - } elsif ($s4 == 0x3) { - sysread($sock, $r5, 1); - $s5 = unpack("C", $r5); - sysread($sock, $r6, $s5 + 2); - } elsif ($s4 == 0x4) { - sysread($sock, $r5, 16 + 2); - } - - if ($s1 != 0x5 || $s2 != 0x0 || $s3 != 0x0) { - print STDERR "SOCKS5 failed: s1=$s1 s2=$s2 s3=$s3 s4=$s4 n1=$n1 n2=$n2 n3=$n3 n4=$n4\n"; - close $sock; - exit(1); - } - - } elsif ($ENV{PPROXY_SOCKS} eq "1") { - # SOCKS4 SOCKS4a - my ($h, $p) = ($CONNECT, ""); - if ($h =~ /^(.*):(\d+)$/) { - $h = $1; - $p = $2; - } - $con .= pack("C", 0x04); - $con .= pack("C", 0x01); - $con .= pack("n", $p); - - my $SOCKS_4a = 0; - if ($h eq "localhost" || $h eq "127.0.0.1") { - $con .= pack("C", 127); - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 1); - } elsif ($h =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) { - $con .= pack("C", $1); - $con .= pack("C", $2); - $con .= pack("C", $3); - $con .= pack("C", $4); - } else { - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 0); - $con .= pack("C", 3); - $SOCKS_4a = 1; - } - - $con .= "nobody"; - $con .= pack("C", 0); - - $msg = "SOCKS4 via $cur_proxy to $h:$p\n\n"; - if ($SOCKS_4a) { - $con .= $h; - $con .= pack("C", 0); - $msg =~ s/SOCKS4/SOCKS4a/; - } - print STDERR "proxy_request$w: $msg"; - syswrite($sock, $con, length($con)); - - my $ok = 1; - for (my $i = 0; $i < 8; $i++) { - my $c; - sysread($sock, $c, 1); - my $s = unpack("C", $c); - if ($i == 0) { - $ok = 0 if $s != 0x0; - } elsif ($i == 1) { - $ok = 0 if $s != 0x5a; - } - } - if (! $ok) { - print STDERR "SOCKS4 failed.\n"; - close $sock; - exit(1); - } - } elsif ($ENV{PPROXY_SOCKS} eq "0") { - # hack for ipv6 "proxy", nothing to do, assume INET6 call worked. - ; - } elsif ($ENV{PPROXY_REPEATER} ne "") { - my $rep = $ENV{PPROXY_REPEATER}; - print STDERR "repeater: $rep\n"; - $rep .= pack("x") x 250; - syswrite($sock, $rep, 250); - - my $rfb = ""; - - my $ok = 1; - for (my $i = 0; $i < 12; $i++) { - my $c; - last if $ENV{PPROXY_GENERIC_REPEATER}; - sysread($sock, $c, 1); - print STDERR $c; - $rfb .= $c; - } - if ($rfb ne "" && $rfb !~ /^RFB 000\.000/) { - $initial_data = $rfb; - $rfb =~ s/\n//g; - print STDERR "detected non-UltraVNC repeater; forwarding \"$rfb\"\nlength: ", length($initial_data), "\n"; - } - } elsif ($ENV{PPROXY_VENCRYPT} ne "") { - my $vencrypt = $ENV{PPROXY_VENCRYPT}; - vencrypt_dialog($vencrypt); - - } else { - # Web Proxy: - $con = "CONNECT $CONNECT HTTP/1.1\r\n"; - $con .= "Host: $CONNECT\r\n"; - $con .= "Connection: close\r\n\r\n"; - $msg = $con; - - print STDERR "proxy_request$w: via $cur_proxy:\n$msg"; - syswrite($sock, $con, length($con)); - - my $rep = ""; - my $n = 0; - while ($rep !~ /\r\n\r\n/ && $n < 30000) { - my $c; - sysread($sock, $c, 1); - print STDERR $c; - $rep .= $c; - $n++; - } - if ($rep !~ m,HTTP/.* 200,) { - print STDERR "HTTP CONNECT failed.\n"; - close $sock; - exit(1); - } - } -} - -sub vdie { - append_handshake("done\n"); - close $sock; - kill_proxy_pids(); - exit(1); -} - -sub anontls_handshake { - my ($vmode, $db) = @_; - - print STDERR "\nPPROXY: Doing ANONTLS Handshake\n"; - - my $psec = pack("C", $rfbSecTypeAnonTls); - syswrite($sock, $psec, 1); - - append_handshake("done\n"); -} - -sub vencrypt_handshake { - - my ($vmode, $db) = @_; - - print STDERR "\nPPROXY: Doing VeNCrypt Handshake\n"; - - my $psec = pack("C", $rfbSecTypeVencrypt); - - if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { - my $fake = $ENV{SSVNC_TEST_SEC_TYPE}; - print STDERR "PPROXY: sending sec-type: $fake\n"; - $psec = pack("C", $fake); - } - - syswrite($sock, $psec, 1); - - my $vmajor; - my $vminor; - sysread($sock, $vmajor, 1); - sysread($sock, $vminor, 1); - - vdie if $vmajor eq "" || $vminor eq ""; - - $vmajor = unpack("C", $vmajor); - $vminor = unpack("C", $vminor); - print STDERR "server vencrypt version $vmajor.$vminor\n" if $db; - - if (exists $ENV{SSVNC_TEST_SEC_TYPE}) { - print STDERR "PPROXY: continuing on in test mode.\n"; - } else { - vdie if $vmajor ne 0; - vdie if $vminor < 2; - } - - $vmajor = pack("C", 0); - $vminor = pack("C", 2); - append_handshake("subversion=0.2\n"); - - syswrite($sock, $vmajor, 1); - syswrite($sock, $vminor, 1); - - my $result; - sysread($sock, $result, 1); - print STDERR "result empty\n" if $db && $result eq ""; - - vdie if $result eq ""; - $result = unpack("C", $result); - print STDERR "result=$result\n" if $db; - - vdie if $result ne 0; - - my $nsubtypes; - sysread($sock, $nsubtypes, 1); - - vdie if $nsubtypes eq ""; - $nsubtypes = unpack("C", $nsubtypes); - print STDERR "nsubtypes=$nsubtypes\n" if $db; - - my %subtypes; - - for (my $i = 0; $i < $nsubtypes; $i++) { - my $subtype = ""; - sysread($sock, $subtype, 4); - vdie if length($subtype) != 4; - - # XXX fix 64bit. - $subtype = unpack("N", $subtype); - print STDERR "subtype: $subtype\n" if $db; - $subtypes{$subtype} = 1; - append_handshake("sst$i=$subtype\n"); - } - - my $subtype = 0; - if (exists $subtypes{$rfbVencryptX509None}) { - $subtype = $rfbVencryptX509None; - print STDERR "selected rfbVencryptX509None\n" if $db; - } elsif (exists $subtypes{$rfbVencryptX509Vnc}) { - $subtype = $rfbVencryptX509Vnc; - print STDERR "selected rfbVencryptX509Vnc\n" if $db; - } elsif (exists $subtypes{$rfbVencryptX509Plain}) { - $subtype = $rfbVencryptX509Plain; - print STDERR "selected rfbVencryptX509Plain\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsNone}) { - $subtype = $rfbVencryptTlsNone; - print STDERR "selected rfbVencryptTlsNone\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsVnc}) { - $subtype = $rfbVencryptTlsVnc; - print STDERR "selected rfbVencryptTlsVnc\n" if $db; - } elsif (exists $subtypes{$rfbVencryptTlsPlain}) { - $subtype = $rfbVencryptTlsPlain; - print STDERR "selected rfbVencryptTlsPlain\n" if $db; - } - - if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { - my $fake = $ENV{SSVNC_TEST_SEC_SUBTYPE}; - print STDERR "PPROXY: sending sec-subtype: $fake\n"; - $subtype = $fake; - } - - append_handshake("subtype=$subtype\n"); - - my $pst = pack("N", $subtype); - syswrite($sock, $pst, 4); - - if (exists $ENV{SSVNC_TEST_SEC_SUBTYPE}) { - print STDERR "PPROXY: continuing on in test mode.\n"; - } else { - vdie if $subtype == 0; - } - - my $ok; - sysread($sock, $ok, 1); - $ok = unpack("C", $ok); - print STDERR "ok=$ok\n" if $db; - - append_handshake("done\n"); - - vdie if $ok == 0; -} - -sub vencrypt_dialog { - my $vmode = shift; - my $db = 0; - - $db = 1 if exists $ENV{SS_DEBUG}; - $db = 1 if exists $ENV{SSVNC_VENCRYPT_DEBUG}; - - append_handshake("mode=$vmode\n"); - - my $server_rfb = ""; - #syswrite($sock, $rep, 250); - for (my $i = 0; $i < 12; $i++) { - my $c; - sysread($sock, $c, 1); - $server_rfb .= $c; - print STDERR $c; - } - print STDERR "server_rfb: $server_rfb\n" if $db; - append_handshake("server=$server_rfb"); - - my $minor = ""; - if ($server_rfb =~ /^RFB 003\.(\d+)/) { - $minor = $1; - } else { - vdie; - } - my $viewer_rfb = "RFB 003.008\n"; - if ($minor < 7) { - vdie; - } elsif ($minor == 7) { - $viewer_rfb = "RFB 003.007\n"; - } - my $nsec; - my $t1 = gettime(); - my $t0 = gettime(); - - syswrite($sock, $viewer_rfb, 12); - sysread($sock, $nsec, 1); - - $t1 = gettime(); - $t1 = sprintf("%.6f", $t1 - $t0); - - append_handshake("viewer=$viewer_rfb"); - append_handshake("latency=$t1\n"); - - vdie if $nsec eq ""; - - $nsec = unpack("C", $nsec); - - print STDERR "nsec: $nsec\n" if $db; - vdie if $nsec eq 0 || $nsec > 100; - - my %sectypes = (); - - for (my $i = 0; $i < $nsec; $i++) { - my $sec; - sysread($sock, $sec, 1); - vdie if $sec eq ""; - $sec = unpack("C", $sec); - print STDERR "sec: $sec\n" if $db; - $sectypes{$sec} = 1; - } - - if (exists $sectypes{$rfbSecTypeVencrypt}) { - print STDERR "found rfbSecTypeVencrypt\n" if $db; - append_handshake("sectype=$rfbSecTypeVencrypt\n"); - vencrypt_handshake($vmode, $db); - } elsif (exists $sectypes{$rfbSecTypeAnonTls}) { - print STDERR "found rfbSecTypeAnonTls\n" if $db; - append_handshake("sectype=$rfbSecTypeAnonTls\n"); - anontls_handshake($vmode, $db); - } else { - print STDERR "No supported sec-type found\n" if $db; - vdie; - } -} - -sub append_handshake { - my $str = shift; - if ($handshake_file) { - if (open(HSF, ">>$handshake_file")) { - print HSF $str; - close HSF; - } - } -} - -sub do_vencrypt_viewer_bridge { - my ($listen, $connect) = @_; - print STDERR "\npproxy: starting vencrypt_viewer_bridge[$$]: $listen \-> $connect\n"; - my $db = 0; - my $backwards = 0; - if ($listen < 0) { - $backwards = 1; - $listen = -$listen; - } - if ($handshake_file eq "") { - die "pproxy: vencrypt_viewer_bridge[$$]: no SSVNC_PREDIGESTED_HANDSHAKE\n"; - } - my $listen_sock; - my $maxtry = 12; - my $sleep = 5; - for (my $i=0; $i < $maxtry; $i++) { - $listen_sock = IO::Socket::INET->new( - Listen => 2, - ReuseAddr => 1, - LocalAddr => "127.0.0.1", - LocalPort => $listen, - Proto => "tcp" - ); - if (! $listen_sock) { - if ($i < $maxtry - 1) { - warn "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; - warn "Could not listen on port $listen, retrying in $sleep seconds... (Ctrl-C to quit)\n"; - sleep $sleep; - } - } else { - last; - } - } - if (! $listen_sock) { - die "pproxy: vencrypt_viewer_bridge[$$]: $!\n"; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: listening on port $listen\n\n"; - my ($viewer_sock, $ip) = $listen_sock->accept(); - my $err = $!; - close $listen_sock; - if (! $viewer_sock) { - die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; - } - if ($ENV{PPROXY_LOOP_THYSELF_MASTER}) { - my $sml = $ENV{SSVNC_MULTIPLE_LISTEN}; - if ($sml ne "" && $sml ne "0") { - setpgrp(0, 0); - if (fork()) { - close $viewer_sock; - wait; - exit 0; - } - if (fork()) { - close $viewer_sock; - exit 0; - } - setpgrp(0, 0); - $parent = $$; - } - } - print STDERR "vencrypt_viewer_bridge[$$]: viewer_sock $viewer_sock\n" if $db; - - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: connecting to 127.0.0.1:$connect\n"; - my $server_sock = IO::Socket::INET->new( - PeerAddr => "127.0.0.1", - PeerPort => $connect, - Proto => "tcp" - ); - print STDERR "vencrypt_viewer_bridge[$$]: server_sock $server_sock\n" if $db; - if (! $server_sock) { - my $err = $!; - die "pproxy: vencrypt_viewer_bridge[$$]: $err\n"; - } - - if ($backwards) { - print STDERR "vencrypt_viewer_bridge[$$]: reversing roles of viewer and server.\n"; - my $t = $viewer_sock; - $viewer_sock = $server_sock; - $server_sock = $t; - } - - my %hs = (); - my $dt = 0.2; - my $slept = 0.0; - while ($slept < 20.0) { - select(undef, undef, undef, $dt); - $slept += $dt; - if (-f $handshake_file && open(HSF, "<$handshake_file")) { - my $done = 0; - %hs = (); - my $str = ""; - while () { - print STDERR "vencrypt_viewer_bridge[$$]: $_" if $ENV{VENCRYPT_VIEWER_BRIDGE_DEBUG}; - $str .= "vencrypt_viewer_bridge[$$]: $_"; - chomp; - if ($_ eq "done") { - $done = 1; - } else { - my ($k, $v) = split(/=/, $_, 2); - if ($k ne "" && $v ne "") { - $hs{$k} = $v; - } - } - } - close HSF; - if ($done) { - print STDERR "\n" . $str; - last; - } - } - } - if (! exists $hs{server}) { - $hs{server} = "RFB 003.008"; - } - if (! exists $hs{sectype}) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: no sectype.\n"; - } - syswrite($viewer_sock, "$hs{server}\n", length($hs{server}) + 1); - my $viewer_rfb = ""; - for (my $i = 0; $i < 12; $i++) { - my $c; - sysread($viewer_sock, $c, 1); - $viewer_rfb .= $c; - print STDERR $c; - } - my $viewer_major = 3; - my $viewer_minor = 8; - if ($viewer_rfb =~ /RFB (\d+)\.(\d+)/) { - $viewer_major = $1; - $viewer_minor = $2; - } - my $u0 = pack("C", 0); - my $u1 = pack("C", 1); - my $u2 = pack("C", 2); - if ($hs{sectype} == $rfbSecTypeAnonTls) { - unlink($handshake_file); - print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeAnonTls\n"; - if ($viewer_major > 3 || $viewer_minor >= 7) { - ; # setup ok, proceed to xfer. - } else { - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; - my $n; - sysread($server_sock, $n, 1); - $n = unpack("C", $n); - if ($n == 0) { - die "pproxy: vencrypt_viewer_bridge[$$]: nsectypes == $n.\n"; - } - my %types; - for (my $i = 0; $i < $n; $i++) { - my $t; - sysread($server_sock, $t, 1); - $t = unpack("C", $t); - $types{$t} = 1; - } - my $use = 1; # None - if (exists $types{1}) { - $use = 1; # None - } elsif (exists $types{2}) { - $use = 2; # VncAuth - } else { - die "pproxy: vencrypt_viewer_bridge[$$]: no valid sectypes" . join(",", keys %types) . "\n"; - } - - # send 4 bytes sectype to viewer: - # (note this should be MSB, network byte order...) - my $up = pack("C", $use); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $up, 1); - # and tell server the one we selected: - syswrite($server_sock, $up, 1); - if ($use == 1) { - # even None has security result, so read it here and discard it. - my $sr = ""; - sysread($server_sock, $sr, 4); - } - } - } elsif ($hs{sectype} == $rfbSecTypeVencrypt) { - print STDERR "\npproxy: vencrypt_viewer_bridge[$$]: rfbSecTypeVencrypt\n"; - if (! exists $hs{subtype}) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: no subtype.\n"; - } - my $fake_type = "None"; - my $plain = 0; - my $sub_type = $hs{subtype}; - if ($sub_type == $rfbVencryptTlsNone) { - $fake_type = "None"; - } elsif ($sub_type == $rfbVencryptTlsVnc) { - $fake_type = "VncAuth"; - } elsif ($sub_type == $rfbVencryptTlsPlain) { - $fake_type = "None"; - $plain = 1; - } elsif ($sub_type == $rfbVencryptX509None) { - $fake_type = "None"; - } elsif ($sub_type == $rfbVencryptX509Vnc) { - $fake_type = "VncAuth"; - } elsif ($sub_type == $rfbVencryptX509Plain) { - $fake_type = "None"; - $plain = 1; - } - if ($plain) { - if (!open(W, ">$handshake_file")) { - unlink($handshake_file); - die "pproxy: vencrypt_viewer_bridge[$$]: $handshake_file $!\n"; - } - print W <<"END"; - - proc print_out {} { - global user pass env - - if [info exists env(SSVNC_UP_DEBUG)] { - toplevel .b - button .b.b -text "user=\$user pass=\$pass" -command {destroy .b} - pack .b.b - update - tkwait window .b - } - - if [info exists env(SSVNC_UP_FILE)] { - set fh "" - catch {set fh [open \$env(SSVNC_UP_FILE) w]} - if {\$fh != ""} { - puts \$fh user=\$user\\npass=\$pass - flush \$fh - close \$fh - return - } - } - puts stdout user=\$user\\npass=\$pass - flush stdout - } - - proc center_win {w} { - update - set W [winfo screenwidth \$w] - set W [expr \$W + 1] - wm geometry \$w +\$W+0 - update - set x [expr [winfo screenwidth \$w]/2 - [winfo width \$w]/2] - set y [expr [winfo screenheight \$w]/2 - [winfo height \$w]/2] - - wm geometry \$w +\$x+\$y - wm deiconify \$w - update - } - - wm withdraw . - - global env - set up {} - if [info exists env(SSVNC_UNIXPW)] { - set rm 0 - set up \$env(SSVNC_UNIXPW) - if [regexp {^rm:} \$up] { - set rm 1 - regsub {^rm:} \$up {} up - } - if [file exists \$up] { - set fh "" - set f \$up - catch {set fh [open \$up r]} - if {\$fh != ""} { - gets \$fh u - gets \$fh p - close \$fh - set up "\$u@\$p" - } - if {\$rm} { - catch {file delete \$f} - } - } - } elseif [info exists env(SSVNC_VENCRYPT_USERPASS)] { - set up \$env(SSVNC_VENCRYPT_USERPASS) - } - #puts stderr up=\$up - if {\$up != ""} { - if [regexp {@} \$up] { - global user pass - set user \$up - set pass \$up - regsub {@.*\$} \$user "" user - regsub {^[^@]*@} \$pass "" pass - print_out - exit - } - } - - wm title . {VeNCrypt Viewer Bridge User/Pass} - - set user {} - set pass {} - - label .l -text {SSVNC VeNCrypt Viewer Bridge} - - frame .f0 - frame .f0.fL - label .f0.fL.la -text {Username: } - label .f0.fL.lb -text {Password: } - - pack .f0.fL.la .f0.fL.lb -side top - - frame .f0.fR - entry .f0.fR.ea -width 24 -textvariable user - entry .f0.fR.eb -width 24 -textvariable pass -show * - - pack .f0.fR.ea .f0.fR.eb -side top -fill x - - pack .f0.fL -side left - pack .f0.fR -side right -expand 1 -fill x - - button .no -text Cancel -command {destroy .} - button .ok -text Done -command {print_out; destroy .} - - center_win . - pack .l .f0 .no .ok -side top -fill x - update - wm deiconify . - - bind .f0.fR.ea {focus .f0.fR.eb} - bind .f0.fR.eb {print_out; destroy .} - focus .f0.fR.ea - - wm resizable . 1 0 - wm minsize . [winfo reqwidth .] [winfo reqheight .] -END - close W; - - #system("cat $handshake_file"); - my $w = "wish"; - if ($ENV{WISH}) { - $w = $ENV{WISH}; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: prompt VencryptPlain user and passwd.\n"; - my $res = ""; - if (`uname` =~ /Darwin/) { - my $mtmp = `mktemp /tmp/hsup.XXXXXX`; - chomp $mtmp; - system("env SSVNC_UP_FILE=$mtmp $w $handshake_file"); - $res = `cat $mtmp`; - unlink $mtmp; - } else { - $res = `$w $handshake_file`; - } - my $user = ""; - my $pass = ""; - if ($res =~ /user=(\S*)/) { - $user = $1; - } - if ($res =~ /pass=(\S*)/) { - $pass = $1; - } - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: sending VencryptPlain user and passwd.\n"; - my $ulen = pack("C", length($user)); - my $plen = pack("C", length($pass)); - # (note this should be MSB, network byte order...) - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $ulen, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $u0, 1); - syswrite($server_sock, $plen, 1); - syswrite($server_sock, $user, length($user)); - syswrite($server_sock, $pass, length($pass)); - } - unlink($handshake_file); - - my $ft = 0; - if ($fake_type eq "None") { - $ft = 1; - } elsif ($fake_type eq "VncAuth") { - $ft = 2; - } else { - die "pproxy: vencrypt_viewer_bridge[$$]: unknown fake type: $fake_type\n"; - } - my $fp = pack("C", $ft); - if ($viewer_major > 3 || $viewer_minor >= 7) { - syswrite($viewer_sock, $u1, 1); - syswrite($viewer_sock, $fp, 1); - my $cr; - sysread($viewer_sock, $cr, 1); - $cr = unpack("C", $cr); - if ($cr != $ft) { - die "pproxy: vencrypt_viewer_bridge[$$]: client selected wrong type: $cr / $ft\n"; - } - } else { - print STDERR "pproxy: vencrypt_viewer_bridge[$$]: faking RFB version 3.3 to viewer.\n"; - # send 4 bytes sect type to viewer: - # (note this should be MSB, network byte order...) - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $u0, 1); - syswrite($viewer_sock, $fp, 1); - if ($ft == 1) { - # even None has security result, so read it here and discard it. - my $sr = ""; - sysread($server_sock, $sr, 4); - } - } - } - - $listen_handle = $viewer_sock; - $sock = $server_sock; - - xfer_both(); -} -' - # ' - # xpg_echo will expand \n \r, etc. - # try to unset and then test for it. - if type shopt > /dev/null 2>&1; then - shopt -u xpg_echo >/dev/null 2>&1 - fi - v='print STDOUT "abc\n";' - echo "$v" > $tf - chmod 700 $tf - - lc=`wc -l $tf | awk '{print $1}'` - if [ "X$lc" = "X1" ]; then - echo "$cod" > $tf - else - printf "%s" "$cod" > $tf - echo "" >> $tf - fi - # prime perl - perl -e 'use IO::Socket::INET; select(undef, undef, undef, 0.01)' >/dev/null 2>&1 -} - -# make_tcert is no longer invoked via the ssvnc gui (Listen mode). -# make_tcert is for testing only now via -mycert BUILTIN -make_tcert() { - tcert="/tmp/ss_vnc_viewer_tcert${RANDOM}.$$" - tcert=`mytmp "$tcert"` - cat > $tcert < Advanced -> Private SSH KnownHosts file' (or set" - echo "** Warning: SSVNC_KNOWN_HOSTS_FILE directly) to a per-connection known hosts" - echo "** Warning: file. That file holds the 'localhost' cert for this specific" - echo "** Warning: connection. This yields a both secure and convenient solution." - echo "" -} - -space_expand() { - str=`echo "$1" | sed -e 's/%SPACE/ /g' -e 's/%TAB/\t/g'` - echo "$str" -} - -# handle ssh case: -# -if [ "X$use_ssh" = "X1" ]; then - # - # USING SSH - # - ssh_port="22" - ssh_host="$host" - vnc_host="$localhost" - ssh_UKHF="" - localhost_extra="" - # let user override ssh via $SSH - ssh=${SSH:-"ssh -x"} - - sshword=`echo "$ssh" | awk '{print $1}'` - if [ "X$sshword" != "X" ]; then - if [ -x "$sshword" ]; then - : - elif type "$sshword" > /dev/null 2>&1; then - : - else - echo "" - echo "*********************************************************" - echo "** Problem finding the SSH command '$sshword': **" - echo "" - type "$sshword" - echo "" - echo "** Perhaps you need to install the SSH client package. **" - echo "*********************************************************" - echo "" - sleep 5 - fi - fi - - ssh_NHAFL="-o NoHostAuthenticationForLocalhost=yes" - if [ "X$SSVNC_SSH_LOCALHOST_AUTH" = "X1" ]; then - ssh_NHAFL="" - fi - if [ "X$SSVNC_KNOWN_HOSTS_FILE" != "X" ]; then - ssh_NHAFL="" - - ssh_UKHF="-o UserKnownHostsFile=$SSVNC_KNOWN_HOSTS_FILE" - ssh_args="$ssh_args $ssh_UKHF" - if [ ! -f "$SSVNC_KNOWN_HOSTS_FILE" ]; then - touch "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 - fi - chmod 600 "$SSVNC_KNOWN_HOSTS_FILE" >/dev/null 2>&1 - fi - did_ssh_NHAFL="" - - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then - SSVNC_LIM_ACCEPT_PRELOAD="$SSVNC_BASEDIR/$SSVNC_UNAME/$SSVNC_LIM_ACCEPT_PRELOAD" - fi - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ]; then - echo "" - echo "SSVNC_LIM_ACCEPT_PRELOAD=$SSVNC_LIM_ACCEPT_PRELOAD" - fi - - if [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" -a -f "$SSVNC_LIM_ACCEPT_PRELOAD" ]; then - plvar=LD_PRELOAD - if uname | grep Darwin >/dev/null; then - plvar="DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES" - fi - ssh="env $plvar=$SSVNC_LIM_ACCEPT_PRELOAD $ssh" - else - SSVNC_LIM_ACCEPT_PRELOAD="" - fi - - ssh_vencrypt_proxy="" - # We handle vencrypt for SSH+SSL mode. - if echo "$proxy" | grep 'vencrypt://' > /dev/null; then - proxynew="" - for part in `echo "$proxy" | tr ',' ' '` - do - if echo "$part" | egrep -i '^vencrypt://' > /dev/null; then - ssh_vencrypt_proxy=$part - else - if [ "X$proxynew" = "X" ]; then - proxynew="$part" - else - proxynew="$proxynew,$part" - fi - fi - done - proxy=$proxynew - fi - Kecho ssh_vencrypt_proxy=$ssh_vencrypt_proxy - - # note that user must supply http:// for web proxy in SSH and SSH+SSL. - # No xxxx:// implies ssh server+port. - # - if echo "$proxy" | egrep '(http|https|socks|socks4|socks5)://' > /dev/null; then - # Handle Web or SOCKS proxy(ies) for the initial connect. - Kecho host=$host - Kecho port=$port - pproxy="" - sproxy1="" - sproxy_rest="" - for part in `echo "$proxy" | tr ',' ' '` - do - Kecho proxy_part=$part - if [ "X$part" = "X" ]; then - continue - elif echo "$part" | egrep -i '^(http|https|socks|socks4|socks5)://' > /dev/null; then - pproxy="$pproxy,$part" - else - if [ "X$sproxy1" = "X" ]; then - sproxy1="$part" - else - sproxy_rest="$sproxy_rest,$part" - fi - fi - done - pproxy=`echo "$pproxy" | sed -e 's/^,,*//' -e 's/,,*/,/g'` - sproxy_rest=`echo "$sproxy_rest" | sed -e 's/^,,*//' -e 's/,,*/,/g'` - - Kecho pproxy=$pproxy - Kecho sproxy1=$sproxy1 - Kecho sproxy_rest=$sproxy_rest - - sproxy1_host="" - sproxy1_port="" - sproxy1_user="" - - if [ "X$sproxy1" != "X" ]; then - # XXX fix ipv6 ip adder here and below. - sproxy1_host=`echo "$sproxy1" | awk -F: '{print $1}'` - sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` - sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` - if [ "X$sproxy1_host" = "X" ]; then - sproxy1_host=$sproxy1_user - sproxy1_user="" - else - sproxy1_user="${sproxy1_user}@" - fi - sproxy1_port=`echo "$sproxy1" | awk -F: '{print $2}'` - if [ "X$sproxy1_port" = "X" ]; then - sproxy1_port="22" - fi - else - sproxy1_host=`echo "$host" | awk -F: '{print $1}'` - sproxy1_user=`echo "$sproxy1_host" | awk -F@ '{print $1}'` - sproxy1_host=`echo "$sproxy1_host" | awk -F@ '{print $2}'` - if [ "X$sproxy1_host" = "X" ]; then - sproxy1_host=$sproxy1_user - sproxy1_user="" - else - sproxy1_user="${sproxy1_user}@" - fi - sproxy1_port=`echo "$host" | awk -F: '{print $2}'` - if [ "X$sproxy1_port" = "X" ]; then - sproxy1_port="22" - fi - fi - - Kecho sproxy1_host=$sproxy1_host - Kecho sproxy1_port=$sproxy1_port - Kecho sproxy1_user=$sproxy1_user - - ptmp="/tmp/ss_vncviewer_ssh${RANDOM}.$$.pl" - ptmp=`mytmp "$ptmp"` - PPROXY_REMOVE=1; export PPROXY_REMOVE - proxy=$pproxy - port_save=$port - host_save=$host - if [ "X$sproxy1_host" != "X" ]; then - host=$sproxy1_host - fi - if [ "X$sproxy1_port" != "X" ]; then - port=$sproxy1_port - fi - host=`echo "$host" | sed -e 's/^.*@//'` - port=`echo "$port" | sed -e 's/^.*://'` - pcode "$ptmp" - port=$port_save - host=$host_save - - nd=`findfree 6600` - PPROXY_LISTEN=$nd; export PPROXY_LISTEN - # XXX no reverse forever PPROXY_LOOP_THYSELF ... - $ptmp & - sleep 1 - if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then - NHAFL_warning - ssh_args="$ssh_args $ssh_NHAFL" - did_ssh_NHAFL=1 - fi - sleep 1 - if [ "X$sproxy1" = "X" ]; then - u="" - if echo "$host" | grep '@' > /dev/null; then - u=`echo "$host" | sed -e 's/@.*$/@/'` - fi - - proxy="${u}$localhost:$nd" - else - proxy="${sproxy1_user}$localhost:$nd" - fi - localhost_extra=".2" - if [ "X$sproxy_rest" != "X" ]; then - proxy="$proxy,$sproxy_rest" - fi - Kecho proxy=$proxy - fi - - if echo "$proxy" | grep "," > /dev/null; then - - proxy1=`echo "$proxy" | awk -F, '{print $1}'` - proxy2=`echo "$proxy" | awk -F, '{print $2}'` - - # user1@gw1.com:port1,user2@ws2:port2 - ssh_host1=`echo "$proxy1" | awk -F: '{print $1}'` - ssh_port1=`echo "$proxy1" | awk -F: '{print $2}'` - if [ "X$ssh_port1" != "X" ]; then - ssh_port1="-p $ssh_port1" - fi - ssh_host2=`echo "$proxy2" | awk -F: '{print $1}'` - ssh_user2=`echo "$ssh_host2" | awk -F@ '{print $1}'` - ssh_host2=`echo "$ssh_host2" | awk -F@ '{print $2}'` - if [ "X$ssh_host2" = "X" ]; then - ssh_host2=$ssh_user2 - ssh_user2="" - else - ssh_user2="${ssh_user2}@" - fi - ssh_port2=`echo "$proxy2" | awk -F: '{print $2}'` - if [ "X$ssh_port2" = "X" ]; then - ssh_port2="22" - fi - proxport=`findfree 3500` - if [ "X$ssh_NHAFL" != "X" -a "X$did_ssh_NHAFL" != "X1" ]; then - NHAFL_warning - did_ssh_NHAFL=1 - sleep 1 - fi - echo - echo "Running 1st ssh proxy:" - ukhf="" - if [ "X$ssh_UKHF" != "X" ]; then - ukhf="$ssh_UKHF$localhost_extra" - fi - if echo "$ssh_host1" | grep '%' > /dev/null; then - uath=`space_expand "$ssh_host1"` - else - uath="$ssh_host1" - fi - echo "$ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 \"$uath\" \"sleep 30\"" - echo "" - $ssh -f -x $ssh_port1 $targ -e none $ssh_NHAFL $ukhf -L $proxport:$ssh_host2:$ssh_port2 "$uath" "sleep 30" - ssh_args="$ssh_args $ssh_NHAFL" - sleep 1 - stty sane - proxy="${ssh_user2}$localhost:$proxport" - fi - - if [ "X$proxy" != "X" ]; then - ssh_port=`echo "$proxy" | awk -F: '{print $2}'` - if [ "X$ssh_port" = "X" ]; then - ssh_port="22" - fi - ssh_host=`echo "$proxy" | awk -F: '{print $1}'` - vnc_host="$host" - fi - - echo "" - echo "Running ssh:" - sz=`echo "$ssh_cmd" | wc -c` - if [ "$sz" -gt 300 ]; then - info="..." - else - info="$ssh_cmd" - fi - - C="" - if [ "X$SS_VNCVIEWER_USE_C" != "X" ]; then - C="-C" - fi - - getport="" - teeport="" - if echo "$ssh_cmd" | egrep "(PORT=|P=) " > /dev/null; then - getport=1 - if echo "$ssh_cmd" | egrep "P= " > /dev/null; then - teeport=1 - fi - - PORT="" - ssh_cmd=`echo "$ssh_cmd" | sed -e 's/PORT=[ ]*//' -e 's/P=//'` - SSVNC_NO_ENC_WARN=1 - if [ "X$use_sshssl" = "X" ]; then - direct_connect=1 - fi - fi - if [ "X$getport" != "X" ]; then - ssh_redir="-D ${use}" - elif [ "X$reverse" = "X" ]; then - ssh_redir="-L ${use}:${vnc_host}:${port}" - else - ssh_redir="-R ${port}:${vnc_host}:${use}" - fi - pmark=`sh -c 'echo $$'` - - # the -t option actually speeds up typing response via VNC!! - if [ "X$ssh_port" = "X22" ]; then - ssh_port="" - else - ssh_port="-p $ssh_port" - fi - - if echo "$ssh_host" | grep '%' > /dev/null; then - uath=`space_expand "$ssh_host"` - else - uath="$ssh_host" - fi - if [ "X$SS_VNCVIEWER_SSH_ONLY" != "X" ]; then - echo "$ssh -x $ssh_port $targ $C $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x $ssh_port $targ $C $ssh_args "$uath" "$ssh_cmd" - exit $? - - elif [ "X$SS_VNCVIEWER_NO_F" != "X" ]; then - echo "$ssh -x $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" - rc=$? - - elif [ "X$getport" != "X" ]; then - tport=/tmp/ss_vncviewer_tport${RANDOM}.$$ - tport=`mytmp "$tport"` - tport2=/tmp/ss_vncviewer_tport2${RANDOM}.$$ - tport2=`mytmp "$tport2"` - - if [ "X$rsh" != "X1" ]; then - if echo "$ssh_cmd" | grep "sudo " > /dev/null; then - echo "" - echo "Initial ssh with 'sudo id' to prime sudo so hopefully the next one" - echo "will require no password..." - echo "" - targ="-t" - $ssh -x $ssh_port $targ $ssh_args "$uath" "sudo id; tty" - echo "" - fi - echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" > $tport 2> $tport2 - if [ "X$teeport" = "X1" ]; then - tail -f $tport 1>&2 & - tail_pid=$! - tail -f $tport2 1>&2 & - tail_pid2=$! - fi - rc=$? - else - rsh_setup - echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" - echo "" - rsh $ul "$ssh_host" "$ssh_cmd" > $tport & - sleep 1 - rc=0 - fi - - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - - stty sane - i=0 - if type perl > /dev/null 2>&1; then - imax=50 - sleepit="perl -e 'select(undef, undef, undef, 0.20)'" - else - imax=10 - sleepit="sleep 1" - fi - while [ $i -lt $imax ]; do - #echo $sleepit - eval $sleepit - PORT=`grep "^PORT=" $tport | tr '\r' ' ' | head -n 1 | sed -e 's/PORT=//' -e 's/\r//g' -e 's/ *$//'` - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - break - fi - vnss=`sed -e 's/\r//g' $tport $tport2 | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1 | awk '{print $NF}'` - if [ "X$vnss" != "X" ]; then - PORT=`echo "$vnss" | awk -F: '{print $2}'` - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - if [ $PORT -lt 100 ]; then - PORT=`expr $PORT + 5900` - fi - fi - if echo "$PORT" | grep '^[0-9][0-9]*$' > /dev/null; then - vnss=`sed -e 's/\r//g' $tport | egrep -i '^(New.* desktop is|A VNC server is already running).*:[0-9[0-9]*$' | head -n 1` - echo "vncserver string: $vnss" 1>&2 - break - fi - fi - i=`expr $i + 1` - done - - echo "found: PORT='$PORT'" 1>&2 - lh6="" - if [ "X$SSVNC_PORT_IPV6" != "X" ]; then - lh6=1 - elif egrep 'Info: listening on IPv6 only|Info: listening only on IPv6' $tport > /dev/null; then - lh6=1 - fi - if [ "X$lh6" = "X1" ]; then - echo "set SOCKS5 localhost to ::1" 1>&2 - fi - rm -f $tport $tport2 - if [ "X$rsh" = "X1" ]; then - rsh_viewer "$@" - exit $? - fi - PPROXY_SOCKS=5 - if [ "X$SSVNC_SOCKS5" != "X" ]; then - PPROXY_SOCKS=5 - elif [ "X$SSVNC_SOCKS4" != "X" ]; then - PPROXY_SOCKS=1 - fi - export PPROXY_SOCKS - if [ "X$lh6" = "X" ]; then - host="$localhost" - else - host="::1" - fi - port="$PORT" - proxy="$localhost:$use" - - else - if [ "X$rsh" != "X1" ]; then - echo "$ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args \"$uath\" \"$info\"" - echo "" - $ssh -x -f $ssh_port $targ $C $ssh_redir $ssh_args "$uath" "$ssh_cmd" - rc=$? - else - rsh_setup - echo "rsh $ul \"$ssh_host\" \"$ssh_cmd\"" - echo "" - rsh $ul "$ssh_host" "$ssh_cmd" & - sleep 1 - PORT=$port - rsh_viewer "$@" - exit $? - fi - fi - - if [ "$rc" != "0" ]; then - echo "" - echo "ssh to \"$uath\" failed." - exit 1 - fi - stty sane - - c=0 - pssh="" - while [ $c -lt 40 ] - do - p=`expr $pmark + $c` - pout=`ps -p "$p" 2>/dev/null | grep -v '^[ ]*PID' | sed -e 's/-L.*$//' -e 's/-x .*$//'` - if echo "$pout" | grep "ssh" > /dev/null; then - if echo "$pout" | egrep -i 'ssh.*(-add|-agent|-ask|-keygen|-argv0|vnc)' >/dev/null; then - : - elif echo "$pout" | egrep -i 'scp|sshd' >/dev/null; then - : - else - pssh=$p - break - fi - fi - c=`expr $c + 1` - done - if [ "X$getport" != "X" ]; then - : - elif [ "X$SSVNC_LIM_ACCEPT_PRELOAD" != "X" ] ; then - sleep 2 - elif [ "X$ssh_cmd" = "Xsleep $ssh_sleep" ] ; then - #echo T sleep 1 - sleep 1 - elif echo "$ssh_cmd" | grep '^sleep ' >/dev/null; then - #echo T sleep 2 - sleep 2 - else - # let any command get started a bit. - #echo T sleep 5 - sleep 5 - fi - echo "" - #reset - stty sane - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - echo "ssh_pid='$pssh'"; echo - if [ "X$use_sshssl" = "X" -a "X$getport" = "X" ]; then - if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo - fi - echo "Running viewer:" - - trap "final" 0 2 15 - if [ "X$reverse" = "X" ]; then - echo "$VNCVIEWERCMD" "$@" $localhost:$N - echo "" - $VNCVIEWERCMD "$@" $localhost:$N - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" $localhost:$N - fi - fi - else - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - N2=$N - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - N2=`echo "$N2" | sed -e 's/://g'` - if [ $N2 -le 200 ]; then - N2=`expr $N2 + 5500` - fi - fi - echo "$VNCVIEWERCMD" "$@" -listen $N2 - echo "" - $VNCVIEWERCMD "$@" -listen $N2 - fi - - exit $? - else - use2=`findfree 5960` - host0=$host - port0=$port - host=$localhost - port=$use - use=$use2 - N=`expr $use - 5900` - if [ "X$getport" != "X" ]; then - host="$host0" - port="$port0" - else - proxy="" - fi - if [ "X$ssh_vencrypt_proxy" != "X" ]; then - ssh_vencrypt_proxy="vencrypt://$host:$port" - if [ "X$proxy" = "X" ]; then - proxy=$ssh_vencrypt_proxy - else - proxy="$proxy,$ssh_vencrypt_proxy" - fi - Kecho "proxy_now=$proxy" - unset PPROXY_LISTEN - fi - fi -fi - -if [ "X$stunnel_set_here" = "X1" -a "X$showcert" = "X" ]; then - if type $STUNNEL > /dev/null 2>&1; then - : - else - echo "" - echo "***************************************************************" - echo "** Problem finding the Stunnel command '$STUNNEL': **" - echo "" - type $STUNNEL - echo "" - echo "** Perhaps you need to install the stunnel/stunnel4 package. **" - echo "***************************************************************" - echo "" - sleep 5 - fi -fi - -# create the stunnel config file: -if [ "X$verify" != "X" ]; then - if [ -d $verify ]; then - verify="CApath = $verify" - else - verify="CAfile = $verify" - fi - verify="$verify -verify = 2" -fi -if [ "X$SSVNC_STUNNEL_VERIFY3" != "X" ]; then - verify=`echo "$verify" | sed -e 's/verify = 2/verify = 3/'` -fi -if [ "X$mycert" != "X" ]; then - cert="cert = $mycert" -fi -if [ "X$crl" != "X" ]; then - if [ -d $crl ]; then - crl="CRLpath = $crl" - else - crl="CRLfile = $crl" - fi -fi - -if [ "X$showcert" = "X1" ]; then - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - : - elif [ "X$SSVNC_NO_IPV6_PROXY" != "X" ]; then - : - elif [ "X$ipv6" = "X1" -a "X$proxy" = "X" ]; then - proxy="ipv6://$host:$port" - fi -fi - -if [ "X$direct_connect" != "X" -a "X$STUNNEL_LISTEN" != "X" ]; then - proxy=reverse_direct -fi - -ptmp="" -if [ "X$proxy" != "X" ]; then - ptmp="/tmp/ss_vncviewer${RANDOM}.$$.pl" - ptmp=`mytmp "$ptmp"` - PPROXY_REMOVE=1; export PPROXY_REMOVE - pcode "$ptmp" - if [ "X$showcert" != "X1" -a "X$direct_connect" = "X" ]; then - if uname | egrep 'Darwin|SunOS' >/dev/null; then - vout=`echo "$proxy" | grep -i vencrypt` - if [ "X$vout" != "X" -a "X$reverse" = "X1" ]; then - # need to exec for reverse vencrypt - connect="exec = $ptmp" - else - # on mac and solaris we need to listen on socket instead of stdio: - nd=`findfree 6700` - PPROXY_LISTEN=$nd - export PPROXY_LISTEN - if [ "X$reverse" = "X" ]; then - $ptmp & - fi - sleep 2 - host="$localhost" - port="$nd" - connect="connect = $localhost:$nd" - fi - else - # otherwise on unix we can exec it: - connect="exec = $ptmp" - fi - else - connect="exec = $ptmp" - fi -else - connect="connect = $host:$port" -fi - -# handle showcert case: -# -if [ "X$showcert" = "X1" ]; then - if [ "X$proxy" != "X" ]; then - PPROXY_LISTEN=$use - export PPROXY_LISTEN - if [ "X$SS_DEBUG" != "X" ]; then - $ptmp & - else - $ptmp 2>/dev/null & - fi - sleep 1 - more_sleep=1 - if uname | grep Linux > /dev/null; then - if netstat -ant | grep LISTEN | grep "127.0.0.1:$use" > /dev/null; then - more_sleep="" - fi - elif uname | grep SunOS > /dev/null; then - if netstat -an -f inet -P tcp | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then - more_sleep="" - fi - elif uname | egrep -i 'bsd|darwin' > /dev/null; then - if netstat -ant -f inet | grep LISTEN | grep "127.0.0.1.$use" > /dev/null; then - more_sleep="" - fi - fi - if [ "X$more_sleep" = "X1" ]; then - sleep 1 - fi - host="$localhost" - port="$use" - fi - cipher_args="" - if [ "X$ciphers" != "X" ]; then - cipher_args=`echo "$ciphers" | sed -e 's/ciphers=/-cipher /'` - fi - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - : - elif type openssl > /dev/null 2>&1; then - : - else - echo "" - echo "********************************************************" - echo "** Problem finding the OpenSSL command 'openssl': **" - echo "" - type openssl 2>&1 - echo "" - echo "** Perhaps you need to install the 'openssl' package. **" - echo "********************************************************" - echo "" - fi - #echo "openssl s_client $cipher_args -connect $host:$port" - if [ "X$reverse" = "X" ]; then - if type host > /dev/null 2>/dev/null; then - host $host >/dev/null 2>&1 - host $host >/dev/null 2>&1 - fi - timeout=15 - if [ "X$SSVNC_FETCH_TIMEOUT" != "X" ]; then - timeout=$SSVNC_FETCH_TIMEOUT - fi - if [ "X$have_uvnc_dsm_helper_showcert" = "X1" ]; then - if type pkill >/dev/null 2>&1; then - (sleep $timeout; if kill -0 $$; then pkill -TERM -f "ultravnc_dsm_helper.*$host.*$port"; fi) >/dev/null 2>&1 & - fi - ultravnc_dsm_helper showcert $host:$port 2>&1 - else - if type pkill >/dev/null 2>&1; then - (sleep $timeout; if kill -0 $$; then pkill -TERM -f "openssl.*s_client.*$host.*$port"; fi) >/dev/null 2>&1 & - fi - openssl s_client $cipher_args -prexit -connect $host:$port 2>&1 < /dev/null - fi - rc=$? - else - tcert="" - if [ "X$mycert" = "X" ]; then - tcert=`make_tcert` - cert_args="-cert $tcert -CAfile $tcert" - else - cert_args="-cert $mycert -CAfile $mycert" - fi - tmp_out=/tmp/showcert_out${RANDOM}.$$ - tmp_out=`mytmp "$tmp_out"` - tmp_err=/tmp/showcert_err${RANDOM}.$$ - tmp_err=`mytmp "$tmp_err"` - - #echo "openssl s_server $cipher_args $cert_args -accept $port -verify 2 > $tmp_out 2> $tmp_err" 1>&2 - - # assume we have perl: - check_perl perl - - perl -e " - \$p = open(O, \"|openssl s_server $cipher_args $cert_args -accept $port -verify 2 1>$tmp_out 2> $tmp_err\"); - exit 1 unless \$p; - while (1) { - sleep 1; - if (!open(F, \"<$tmp_out\")) { - kill \$p; - exit 1; - } - while () { - if (/RFB 00/) { - fsleep(0.25); - print O \"RFB 000.000\\n\"; - fsleep(1.00); - kill \$p; - fsleep(0.25); - exit 0; - } - } - close F; - } - sub fsleep { - select(undef, undef, undef, shift); - } - "; - - echo "" - cat $tmp_out - echo "" - echo "----2----" - cat $tmp_err - if grep BEGIN.CERTIFICATE $tmp_out >/dev/null; then - rc=0 - else - rc=1 - fi - - rm -f $tmp_out $tmp_err - fi - if [ "X$SSVNC_PREDIGESTED_HANDSHAKE" != "X" ]; then - rm -f $SSVNC_PREDIGESTED_HANDSHAKE - fi - if [ "X$SSVNC_SHOWCERT_EXIT_0" = "X1" ]; then - exit 0 - else - exit $rc - fi -fi - -# handle direct connect case: -# -if [ "X$direct_connect" != "X" ]; then - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - SSVNC_NO_ENC_WARN=1 - echo "" - echo "Using UltraVNC DSM Plugin key for encryption:" - echo "" - ustr=`echo "$SSVNC_ULTRA_DSM" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo " $ustr PORT HOST:PORT" - echo "" - elif [ "X$getport" = "X" ]; then - echo "" - echo "Running viewer for direct connection:" - if echo X"$@" | grep chatonly > /dev/null; then - : - else - echo "" - echo "** WARNING: THERE WILL BE NO SSL OR SSH ENCRYPTION **" - echo "" - fi - fi - x="" - if [ "X$SSVNC_NO_ENC_WARN" != "X" ]; then - if [ "X$getport" = "X" ]; then - sleep 1 - fi - elif type printf > /dev/null 2>&1; then - printf "Are you sure you want to continue? [y]/n " - read x - else - echo -n "Are you sure you want to continue? [y]/n " - read x - fi - if [ "X$x" = "Xn" ]; then - exit 1 - fi - echo "" - if [ "X$ptmp" != "X" ]; then - if [ "X$reverse" = "X" ]; then - PPROXY_LISTEN=$use - export PPROXY_LISTEN - else - if [ "X$proxy" = "Xreverse_direct" ]; then - PPROXY_LISTEN="$STUNNEL_LISTEN:`expr 5500 + $disp`" - PPROXY_DEST="$localhost:$use" - PPROXY_PROXY="ipv6://$localhost:$use" # not always ipv6.. - export PPROXY_LISTEN PPROXY_DEST PPROXY_PROXY - pps=1 - else - PPROXY_REVERSE="$localhost:$use" - export PPROXY_LISTEN - pps=3 - fi - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - pps=2 - fi - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - pps=`expr $pps + $SSVNC_EXTRA_SLEEP` - fi - PPROXY_SLEEP=$pps; export PPROXY_SLEEP; - PPROXY_KILLPID=+1; export PPROXY_KILLPID; - fi - - $ptmp & - - if [ "X$reverse" = "X" ]; then - #sleep 2 - #echo T sleep 1 - sleep 1 - fi - host="$localhost" - disp="$N" - port=`expr $disp + 5900` - fi - if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "T sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP - fi - if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo - fi - if [ "X$reverse" = "X" ]; then - hostdisp="$host:$disp" - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if [ "X$SSVNC_USE_OURS" = "X1" ]; then - hostdisp="exec=$SSVNC_ULTRA_DSM 0 $host:$port" - else - pf=`findfree 5970` - cmd="$SSVNC_ULTRA_DSM -$pf $host:$port" - pf=`expr $pf - 5900` - hostdisp="$localhost:$pf" - ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "Running:" - echo - echo "$ustr &" - echo - $cmd & - dsm_pid=$! - sleep 2 - fi - fi - hostdisp2=`echo "$hostdisp" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "$VNCVIEWERCMD" "$@" "$hostdisp2" - trap "final" 0 2 15 - echo "" - $VNCVIEWERCMD "$@" "$hostdisp" - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" "$hostdisp" - fi - fi - else - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - trap "final" 0 2 15 - if [ "X$SSVNC_ULTRA_DSM" != "X" ]; then - if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then - echo "NOTE: The ultravnc_dsm_helper only runs once. So after the first LISTEN" - echo " ends you must restart the Listening mode. You may also need to" - echo " Press Ctrl-C to stop the viewer and restart for another connection." - echo "" - fi - #SSVNC_LISTEN_ONCE=1; export SSVNC_LISTEN_ONCE - VNCVIEWER_LISTEN_LOCALHOST=1 - export VNCVIEWER_LISTEN_LOCALHOST - dport=`expr 5500 + $disp` - cmd="$SSVNC_ULTRA_DSM $dport $localhost:$use" - ustr=`echo "$cmd" | sed -e 's/pw=[^ ]*/pw=******/g'` - echo "Running:" - echo - echo "$ustr &" - echo - if [ "X$SSVNC_LISTEN_ONCE" = "X1" ]; then - $cmd & - dsm_pid=$! - else - while [ 1 ]; do $cmd; sleep 1; done & - dsm_pid=$! - fi - sleep 2 - disp=$use - if [ $disp -ge 5500 ]; then - disp=`expr $disp - 5500` - fi - fi - disp2=$disp - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - disp2=`echo "$disp2" | sed -e 's/://g'` - if [ $disp2 -le 200 ]; then - disp2=`expr $disp2 + 5500` - fi - fi - echo "$VNCVIEWERCMD" "$@" -listen $disp2 - echo "" - $VNCVIEWERCMD "$@" -listen $disp2 - if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF - fi - fi - exit $? -fi - -tmp_cfg=/tmp/ss_vncviewer${RANDOM}.$$ -tmp_cfg=`mytmp "$tmp_cfg"` - -stunnel_exec="" -if [ "X$SSVNC_USE_OURS" != "X1" ]; then - : -elif echo $STUNNEL_EXTRA_SVC_OPTS | grep '#stunnel-exec' > /dev/null; then - stunnel_exec="#" -fi - -if [ "X$reverse" = "X" ]; then - - if echo "$proxy" | grep "^repeater://" > /dev/null; then - if [ "X$cert" = "XBUILTIN" ]; then - ttcert=`make_tcert` - cert="cert = $ttcert" - fi - # Note for listen mode, an empty cert will cause stunnel to fail. - # The ssvnc gui will have already taken care of this. - fi - - cat > "$tmp_cfg" < /dev/null; then - hloc="$localhost:" - pv=`findfree 5570` - proxy="vencrypt:$pv:$port" - port=$pv - if [ "X$anondh_set" = "X1" ]; then - # not needed for ANONDH in this mode - #ciphers="ciphers = ADH:@STRENGTH" - : - fi - fi - cat > "$tmp_cfg" < /dev/null 2>&1 - $STUNNEL "$tmp_cfg" < /dev/tty > /dev/tty & - stunnel_pid=$! - echo "" - - # pause here to let the user supply a possible passphrase for the - # mycert key: - if [ "X$mycert" != "X" ]; then - nsl=10 - dsl=0 - if [ ! -f $mycert ]; then - dsl=0 - elif grep -i 'Proc-Type.*ENCRYPTED' "$mycert" > /dev/null 2>/dev/null; then - dsl=1 - fi - if [ "X$dsl" = "X1" ]; then - echo "" - echo "(** pausing $nsl secs for possible certificate passphrase dialog **)" - echo "" - sleep $nsl - echo "(** done pausing for passphrase **)" - echo "" - fi - fi - #echo T sleep 1 - sleep 1 - rm -f "$tmp_cfg" -fi - - -echo "" -if [ "X$SSVNC_EXTRA_SLEEP" != "X" ]; then - echo "sleep $SSVNC_EXTRA_SLEEP" - sleep $SSVNC_EXTRA_SLEEP -fi -if [ "X$SSVNC_EXTRA_COMMAND" != "X" ]; then - (sh -c "$SSVNC_EXTRA_COMMAND") & - echo "($SSVNC_EXTRA_COMMAND) &"; echo -fi - -if [ "X$reverse" = "X" ]; then - if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then - port1=`expr 5900 + $N` # stunnel port - port2=`findfree 5970` # bridge port (viewer connects to it.) - N=`expr $port2 - 5900` - env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="$port2,$port1" $ptmp & - sleep 1 - fi - echo "Running viewer:" - vnc_hp=$localhost:$N - if [ "X$stunnel_exec" != "X" ]; then - vnc_hp="exec=$STUNNEL $tmp_cfg" - fi - echo "$VNCVIEWERCMD" "$@" "$vnc_hp" - trap "final" 0 2 15 - echo "" - $VNCVIEWERCMD "$@" "$vnc_hp" - if [ $? != 0 ]; then - echo "vncviewer command failed: $?" - if [ "X$secondtry" = "X1" ]; then - sleep 2 - $VNCVIEWERCMD "$@" "$vnc_hp" - fi - fi -else - echo "Running viewer:" - echo "" - echo "NOTE: Press Ctrl-C to terminate viewer LISTEN mode." - echo "" - trap "final" 0 2 15 - N2=$N - N2_trim=`echo "$N2" | sed -e 's/://g'` - if [ $N2_trim -le 200 ]; then - N2_trim=`expr $N2_trim + 5500` - fi - if [ "X$proxy" != "X" ]; then - if echo "$proxy" | grep -i '^vencrypt:' > /dev/null; then - pstunnel=`echo "$proxy" | awk -F: '{print $2}'` - plisten=`echo "$proxy" | awk -F: '{print $3}'` - IF=INADDR_ANY - if [ "X$STUNNEL_LISTEN" != "X" ]; then - IF=$STUNNEL_LISTEN - fi - PPROXY_VENCRYPT_REVERSE=1; export PPROXY_VENCRYPT_REVERSE - PPROXY_LISTEN="$IF:$plisten"; export PPROXY_LISTEN - PPROXY_PROXY="vencrypt://$localhost:$pstunnel"; export PPROXY_PROXY - PPROXY_DEST="$localhost:$pstunnel"; export PPROXY_DEST - STUNNEL_ONCE=1; export STUNNEL_ONCE - STUNNEL_MAX_CLIENTS=1; export STUNNEL_MAX_CLIENTS - if [ "X$NEED_VENCRYPT_VIEWER_BRIDGE" = "X1" -a "X$ptmp" != "X" ] ; then - port1=`expr 5500 + $N2` - port2=`findfree 5580` - N2=`expr $port2 - 5500` - N2_trim=`echo "$N2" | sed -e 's/://g'` - if [ $N2_trim -le 200 ]; then - N2_trim=`expr $N2_trim + 5500` - fi - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself1.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - PPROXY_LOOP_THYSELF0=$PPROXY_LOOP_THYSELF - fi - env PPROXY_REMOVE=0 PPROXY_SLEEP=0 PPROXY_VENCRYPT_VIEWER_BRIDGE="-$port1,$port2" $ptmp & - sleep 1 - fi - else - PPROXY_REVERSE="$localhost:$port"; export PPROXY_REVERSE - PPROXY_SLEEP=1; export PPROXY_SLEEP; - fi - PPROXY_KILLPID=+1; export PPROXY_KILLPID; - if [ "X$SSVNC_LISTEN_ONCE" != "X1" ]; then - PPROXY_LOOP_THYSELF=`mytmp "/tmp/pproxy_loop_thyself2.${RANDOM}.$$"` - export PPROXY_LOOP_THYSELF - fi - $ptmp & - # Important to have no extra pids generated between here and VNCVIEWERCMD - fi - if [ "X$VNCVIEWER_IS_REALVNC4" = "X1" ]; then - N2=$N2_trim - fi - echo "$VNCVIEWERCMD" "$@" -listen $N2 - echo "" - $VNCVIEWERCMD "$@" -listen $N2 - - if [ "X$PPROXY_LOOP_THYSELF" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF - fi - if [ "X$PPROXY_LOOP_THYSELF0" != "X" ]; then - rm -f $PPROXY_LOOP_THYSELF0 - fi -fi - -sleep 1 diff --git a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch deleted file mode 100644 index bc10f3c..0000000 --- a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-cursor-colors+no-tab-traversal.patch +++ /dev/null @@ -1,111 +0,0 @@ ---- vnc_javasrc.orig/VncCanvas.java 2004-10-10 02:15:54.000000000 -0400 -+++ vnc_javasrc/VncCanvas.java 2010-11-30 21:01:15.000000000 -0500 -@@ -28,13 +28,14 @@ - import java.lang.*; - import java.util.zip.*; - -+import java.util.Collections; - - // - // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. - // - - class VncCanvas extends Canvas -- implements KeyListener, MouseListener, MouseMotionListener { -+ implements KeyListener, MouseListener, MouseMotionListener, MouseWheelListener { - - VncViewer viewer; - RfbProto rfb; -@@ -81,6 +82,20 @@ - cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - -+ // 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); -+ - colors = new Color[256]; - for (int i = 0; i < 256; i++) - colors[i] = new Color(cm8.getRGB(i)); -@@ -169,6 +184,7 @@ - inputEnabled = true; - addMouseListener(this); - addMouseMotionListener(this); -+ addMouseWheelListener(this); - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(true); - } -@@ -177,6 +193,7 @@ - inputEnabled = false; - removeMouseListener(this); - removeMouseMotionListener(this); -+ removeMouseWheelListener(this); - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(false); - } -@@ -1190,6 +1207,9 @@ - public void mouseDragged(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } -+ public void mouseWheelMoved(MouseWheelEvent evt) { -+ processLocalMouseWheelEvent(evt); -+ } - - public void processLocalKeyEvent(KeyEvent evt) { - if (viewer.rfb != null && rfb.inNormalProtocol) { -@@ -1221,6 +1241,19 @@ - evt.consume(); - } - -+ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { -+ if (viewer.rfb != null && rfb.inNormalProtocol) { -+ synchronized(rfb) { -+ try { -+ rfb.writeWheelEvent(evt); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ } -+ rfb.notify(); -+ } -+ } -+ } -+ - public void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (moved) { -@@ -1387,9 +1420,9 @@ - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | -- (pixBuf[i * 4 + 1] & 0xFF) << 16 | -- (pixBuf[i * 4 + 2] & 0xFF) << 8 | -- (pixBuf[i * 4 + 3] & 0xFF); -+ (pixBuf[i * 4 + 2] & 0xFF) << 16 | -+ (pixBuf[i * 4 + 1] & 0xFF) << 8 | -+ (pixBuf[i * 4 + 0] & 0xFF); - } - } else { - result = 0; // Transparent pixel -@@ -1403,9 +1436,9 @@ - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | -- (pixBuf[i * 4 + 1] & 0xFF) << 16 | -- (pixBuf[i * 4 + 2] & 0xFF) << 8 | -- (pixBuf[i * 4 + 3] & 0xFF); -+ (pixBuf[i * 4 + 2] & 0xFF) << 16 | -+ (pixBuf[i * 4 + 1] & 0xFF) << 8 | -+ (pixBuf[i * 4 + 0] & 0xFF); - } - } else { - result = 0; // Transparent pixel diff --git a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch b/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch deleted file mode 100644 index 801234a..0000000 --- a/webclients/ssl/tightvnc-1.3dev7_javasrc-vncviewer-ssl.patch +++ /dev/null @@ -1,2600 +0,0 @@ -diff -Naur vnc_javasrc.orig/Makefile vnc_javasrc/Makefile ---- vnc_javasrc.orig/Makefile 2004-03-04 08:34:25.000000000 -0500 -+++ vnc_javasrc/Makefile 2010-05-18 20:56:26.000000000 -0400 -@@ -4,6 +4,7 @@ - - CP = cp - JC = javac -+JC_ARGS = -target 1.4 -source 1.4 - JAR = jar - ARCHIVE = VncViewer.jar - MANIFEST = MANIFEST.MF -@@ -15,25 +16,29 @@ - DesCipher.class CapabilityInfo.class CapsContainer.class \ - RecordingFrame.class SessionRecorder.class AuthUnixLoginPanel.class \ - SocketFactory.class HTTPConnectSocketFactory.class \ -- HTTPConnectSocket.class ReloginPanel.class -+ HTTPConnectSocket.class ReloginPanel.class \ -+ SSLSocketToMe.class -+ -+SSL_CLASSES = SSLSocketToMe*.class TrustDialog.class - - SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ - OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ - DesCipher.java CapabilityInfo.java CapsContainer.java \ - RecordingFrame.java SessionRecorder.java AuthUnixLoginPanel.java \ - SocketFactory.java HTTPConnectSocketFactory.java \ -- HTTPConnectSocket.java ReloginPanel.java -+ HTTPConnectSocket.java ReloginPanel.java \ -+ SSLSocketToMe.java - - all: $(CLASSES) $(ARCHIVE) - - $(CLASSES): $(SOURCES) -- $(JC) -target 1.1 -O $(SOURCES) -+ $(JC) $(JC_ARGS) -O $(SOURCES) - - $(ARCHIVE): $(CLASSES) $(MANIFEST) -- $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) -+ $(JAR) cfm $(ARCHIVE) $(MANIFEST) $(CLASSES) $(SSL_CLASSES) - - install: $(CLASSES) $(ARCHIVE) -- $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) -+ $(CP) $(CLASSES) $(SSL_CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) - - export:: $(CLASSES) $(ARCHIVE) $(PAGES) - @$(ExportJavaClasses) -diff -Naur vnc_javasrc.orig/RfbProto.java vnc_javasrc/RfbProto.java ---- vnc_javasrc.orig/RfbProto.java 2004-03-04 08:34:25.000000000 -0500 -+++ vnc_javasrc/RfbProto.java 2010-11-30 22:05:12.000000000 -0500 -@@ -199,7 +199,21 @@ - host = h; - port = p; - -- if (viewer.socketFactory == null) { -+ 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 if (viewer.socketFactory == null) { - sock = new Socket(host, port); - } else { - try { -@@ -255,7 +269,7 @@ - || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) - { - throw new Exception("Host " + host + " port " + port + -- " is not an RFB server"); -+ " is not an RFB server: " + b); - } - - serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); -@@ -892,6 +906,38 @@ - final static int ALT_MASK = InputEvent.ALT_MASK; - - -+ void writeWheelEvent(MouseWheelEvent evt) throws IOException { -+ -+ eventBufLen = 0; -+ -+ int x = evt.getX(); -+ int y = evt.getY(); -+ -+ if (x < 0) x = 0; -+ if (y < 0) y = 0; -+ -+ int ptrmask; -+ -+ int clicks = evt.getWheelRotation(); -+ System.out.println("writeWheelEvent: clicks: " + clicks); -+ if (clicks > 0) { -+ ptrmask = 16; -+ } else if (clicks < 0) { -+ ptrmask = 8; -+ } else { -+ return; -+ } -+ -+ eventBuf[eventBufLen++] = (byte) PointerEvent; -+ eventBuf[eventBufLen++] = (byte) ptrmask; -+ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (x & 0xff); -+ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (y & 0xff); -+ -+ os.write(eventBuf, 0, eventBufLen); -+ } -+ - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. -@@ -992,6 +1038,19 @@ - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - - int key; -+ if (viewer.debugKeyboard) { -+ System.out.println("----------------------------------------"); -+ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); -+ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); -+ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); -+ System.out.println("evt.getID: " + evt.getID()); -+ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); -+ System.out.println("evt.isActionKey: " + evt.isActionKey()); -+ System.out.println("evt.isControlDown: " + evt.isControlDown()); -+ System.out.println("evt.getModifiers: " + evt.getModifiers()); -+ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); -+ System.out.println("evt.paramString: " + evt.paramString()); -+ } - if (evt.isActionKey()) { - - // -@@ -1025,6 +1084,13 @@ - return; - } - -+ if(key == 0xffc2 && viewer.mapF5_to_atsign) { -+ if (viewer.debugKeyboard) { -+ System.out.println("Mapping: F5 -> AT "); -+ } -+ key = 0x40; -+ } -+ - } else { - - // -@@ -1036,6 +1102,7 @@ - - key = keyChar; - -+ - if (key < 0x20) { - if (evt.isControlDown()) { - key += 0x60; -@@ -1121,6 +1188,16 @@ - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { -+ if(viewer.forbid_Ctrl_Alt) { -+ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { -+ int orig = newModifiers; -+ newModifiers &= ~ALT_MASK; -+ newModifiers &= ~CTRL_MASK; -+ if (viewer.debugKeyboard) { -+ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); -+ } -+ } -+ } - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - -diff -Naur vnc_javasrc.orig/SSLSocketToMe.java vnc_javasrc/SSLSocketToMe.java ---- vnc_javasrc.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 -+++ vnc_javasrc/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 -@@ -0,0 +1,2067 @@ -+/* -+ * 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, 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. -+ * -+ */ -+ -+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; -+ boolean debug_certs = false; -+ -+ /* sockets */ -+ SSLSocket socket = null; -+ SSLSocketFactory factory; -+ -+ /* fallback for Proxy connection */ -+ boolean proxy_in_use = false; -+ boolean proxy_failure = false; -+ public DataInputStream is = null; -+ public OutputStream os = null; -+ -+ /* strings from user WRT proxy: */ -+ String proxy_auth_string = 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 trustsrv_ctx; -+ SSLContext trusturl_ctx; -+ SSLContext trustone_ctx; -+ -+ /* corresponding trust managers */ -+ TrustManager[] trustAllCerts; -+ TrustManager[] trustSrvCert; -+ TrustManager[] trustUrlCert; -+ TrustManager[] trustOneCert; -+ -+ /* client-side SSL auth key (oneTimeKey=...) */ -+ KeyManager[] mykey = null; -+ -+ boolean user_wants_to_see_cert = true; -+ String cert_fail = null; -+ -+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ -+ java.security.cert.Certificate[] trustallCerts = null; -+ java.security.cert.Certificate[] trustsrvCerts = null; -+ java.security.cert.Certificate[] trusturlCerts = null; -+ -+ /* utility to decode hex oneTimeKey=... and serverCert=... */ -+ 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; -+ -+ debug_certs = v.debugCerts; -+ -+ /* we will first try default factory for certification: */ -+ -+ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); -+ -+ dbg("SSL startup: " + host + " " + port); -+ -+ -+ /* create trust managers to be used if initial handshake fails: */ -+ -+ trustAllCerts = new TrustManager[] { -+ /* -+ * this one accepts everything. Only used if user -+ * has disabled checking (trustAllVncCerts=yes) -+ * or when we grab the cert to show it to them in -+ * a dialog and ask them to manually verify/accept it. -+ */ -+ 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 and stored in -+ * trusturlCerts. -+ */ -+ 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 (URL)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trusturlCerts' */ -+ if (trusturlCerts == null) { -+ throw new CertificateException( -+ "No Trust url Certs array."); -+ } -+ if (trusturlCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust url Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trusturlCerts.length) { -+ throw new CertificateException( -+ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trusturlCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("URL: cert mismatch at i=" + i); -+ dbg("URL: cert mismatch cert" + certs[i]); -+ dbg("URL: cert mismatch url" + trusturlCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("URL: cert info at i=" + i); -+ dbg("URL: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("URL: cert info url" + trusturlCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != URL Cert Chain."); -+ } -+ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustSrvCert = new TrustManager[] { -+ /* -+ * this one accepts cert given to us in the serverCert -+ * Applet Parameter we were started with. It is -+ * currently a fatal error if the VNC Server's cert -+ * doesn't match it. -+ */ -+ 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 (SRV)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustsrvCerts' */ -+ if (trustsrvCerts == null) { -+ throw new CertificateException( -+ "No Trust srv Certs array."); -+ } -+ if (trustsrvCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust srv Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustsrvCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustsrvCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("SRV: cert mismatch at i=" + i); -+ dbg("SRV: cert mismatch cert" + certs[i]); -+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("SRV: cert info at i=" + i); -+ dbg("SRV: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("SRV: cert info srv" + trustsrvCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); -+ } -+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustOneCert = new TrustManager[] { -+ /* -+ * this one accepts only the retrieved server -+ * cert by SSLSocket by this applet we stored in -+ * trustallCerts that user has accepted or applet -+ * parameter trustAllVncCerts=yes is set. This is -+ * for when we reconnect after the user has manually -+ * accepted the trustall cert in the dialog (or set -+ * trustAllVncCerts=yes applet param.) -+ */ -+ 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 (ONE)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustallCerts' */ -+ 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 (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustallCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustallCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("ONE: cert mismatch at i=" + i); -+ dbg("ONE: cert mismatch cert" + certs[i]); -+ dbg("ONE: cert mismatch all" + trustallCerts[i]); -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("ONE: cert info at i=" + i); -+ dbg("ONE: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("ONE: cert info all" + trustallCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != TRUSTALL Cert Chain."); -+ } -+ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ /* -+ * The above TrustManagers are used: -+ * -+ * 1) to retrieve the server cert in case of failure to -+ * display it to the user in a dialog. -+ * 2) to subsequently connect to the server if user agrees. -+ */ -+ -+ /* -+ * build oneTimeKey cert+key if supplied in applet parameter: -+ */ -+ 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); -+ if (debug_certs) { -+ dbg("one time key " + ff); -+ } -+ -+ 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)); -+ if (debug_certs) { -+ dbg("one time cert" + 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(); -+ } -+ -+ /* -+ * build serverCert cert if supplied in applet parameter: -+ */ -+ if (viewer.serverCert != null) { -+ CertificateFactory cf = CertificateFactory.getInstance("X.509"); -+ byte[] cert = hex2bytes(viewer.serverCert); -+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); -+ trustsrvCerts = new Certificate[c.toArray().length]; -+ if (c.size() == 1) { -+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); -+ trustsrvCerts[0] = tmpcert; -+ } else { -+ trustsrvCerts = (Certificate[]) c.toArray(); -+ } -+ } -+ -+ /* the trust loc certs context: */ -+ try { -+ trustloc_ctx = SSLContext.getInstance("SSL"); -+ -+ /* -+ * below is a failed attempt to get jvm's default -+ * trust manager using null (below) makes it so -+ * for HttpsURLConnection the server cannot be -+ * verified (no prompting.) -+ */ -+ if (false) { -+ boolean didit = false; -+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); -+ tmf.init((KeyStore) null); -+ TrustManager [] tml = tmf.getTrustManagers(); -+ for (int i = 0; i < tml.length; i++) { -+ TrustManager tm = tml[i]; -+ if (tm instanceof X509TrustManager) { -+ TrustManager tm1[] = new TrustManager[1]; -+ tm1[0] = tm; -+ trustloc_ctx.init(mykey, tm1, null); -+ didit = true; -+ break; -+ } -+ } -+ if (!didit) { -+ trustloc_ctx.init(mykey, null, null); -+ } -+ } else { -+ /* we have to set trust manager to null */ -+ trustloc_ctx.init(mykey, null, null); -+ } -+ -+ } catch (Exception e) { -+ String msg = "SSL trustloc_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust all certs context: */ -+ 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); -+ } -+ -+ /* the trust url certs context: */ -+ 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); -+ } -+ -+ /* the trust srv certs context: */ -+ try { -+ trustsrv_ctx = SSLContext.getInstance("SSL"); -+ trustsrv_ctx.init(mykey, trustSrvCert, new -+ java.security.SecureRandom()); -+ -+ } catch (Exception e) { -+ String msg = "SSL trustsrv_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust the one cert from server context: */ -+ 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); -+ } -+ } -+ -+ /* -+ * we call this early on to 1) check for a proxy, 2) grab -+ * Browser/JVM accepted HTTPS cert. -+ */ -+ public void check_for_proxy_and_grab_vnc_server_cert() { -+ -+ trusturlCerts = null; -+ proxy_in_use = false; -+ -+ if (viewer.ignoreProxy) { -+ /* applet param says skip it. */ -+ /* the downside is we do not set trusturlCerts for comparison later... */ -+ /* nor do we autodetect x11vnc for GET=1. */ -+ return; -+ } -+ -+ dbg("------------------------------------------------"); -+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); -+ -+ dbg("TRYING HTTPS:"); -+ String ustr = "https://" + host + ":"; -+ if (viewer.httpsPort != null) { -+ ustr += viewer.httpsPort; -+ } else { -+ ustr += port; -+ } -+ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTPS URL connection to host:port */ -+ URL url = new URL(ustr); -+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); -+ -+ if (mykey != null) { -+ /* with oneTimeKey (mykey) we can't use the default SSL context */ -+ if (trustsrvCerts != null) { -+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); -+ } else if (trustloc_ctx != null) { -+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); -+ } -+ } -+ -+ https.setUseCaches(false); -+ https.setRequestMethod("GET"); -+ https.setRequestProperty("Pragma", "No-Cache"); -+ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); -+ https.setDoInput(true); -+ -+ dbg("trying https.connect()"); -+ https.connect(); -+ -+ dbg("trying https.getServerCertificates()"); -+ 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; -+ dbg("An HTTPS proxy is in use. There may be connection problems."); -+ } -+ -+ dbg("trying https.getContent()"); -+ Object output = https.getContent(); -+ dbg("trying https.disconnect()"); -+ https.disconnect(); -+ if (! viewer.GET) { -+ String header = https.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (1), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ -+ } catch(Exception e) { -+ dbg("HttpsURLConnection: " + e.getMessage()); -+ } -+ -+ if (proxy_in_use) { -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } else if (trusturlCerts != null && !viewer.forceProxy) { -+ /* Allow user to require HTTP check? use forceProxy for now. */ -+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } -+ -+ /* -+ * XXX need to remember scenario where this extra check -+ * gives useful info. User's Browser proxy settings? -+ */ -+ dbg("TRYING HTTP:"); -+ ustr = "http://" + host + ":" + port; -+ ustr += viewer.urlPrefix + "/index.vnc"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ -+ 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); -+ -+ dbg("trying http.connect()"); -+ http.connect(); -+ -+ if (http.usingProxy()) { -+ proxy_in_use = true; -+ dbg("An HTTP proxy is in use. There may be connection problems."); -+ } -+ dbg("trying http.getContent()"); -+ Object output = http.getContent(); -+ dbg("trying http.disconnect()"); -+ http.disconnect(); -+ if (! viewer.GET) { -+ String header = http.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (2), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ } catch(Exception e) { -+ dbg("HttpURLConnection: " + e.getMessage()); -+ } -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ } -+ -+ public Socket connectSock() throws IOException { -+ /* -+ * first try a https connection to detect a proxy, and -+ * grab the VNC server cert at the same time: -+ */ -+ check_for_proxy_and_grab_vnc_server_cert(); -+ -+ boolean srv_cert = false; -+ -+ if (trustsrvCerts != null) { -+ /* applet parameter suppled serverCert */ -+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); -+ factory = trustsrv_ctx.getSocketFactory(); -+ srv_cert = true; -+ } else if (viewer.trustAllVncCerts) { -+ /* trust all certs (no checking) */ -+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); -+ factory = trustall_ctx.getSocketFactory(); -+ } else if (trusturlCerts != null) { -+ /* trust certs the Browser/JVM accepted in check_for_proxy... */ -+ dbg("using trusturl_ctx"); -+ factory = trusturl_ctx.getSocketFactory(); -+ } else { -+ /* trust the local defaults */ -+ 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"); -+ } -+ -+ int timeout = 6; -+ if (timeout > 0) { -+ socket = (SSLSocket) factory.createSocket(); -+ InetSocketAddress inetaddr = new InetSocketAddress(host, port); -+ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); -+ socket.connect(inetaddr, timeout * 1000); -+ } else { -+ socket = (SSLSocket) factory.createSocket(host, port); -+ } -+ -+ } catch (Exception esock) { -+ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); -+ } -+ } else { -+ /* n.b. socket is left in error state to cause ex. below. */ -+ } -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The Server Connection Verified OK on 1st try."); -+ -+ java.security.cert.Certificate[] currentTrustedCerts; -+ BrowserCertsDialog bcd; -+ -+ SSLSession sess = socket.getSession(); -+ currentTrustedCerts = sess.getPeerCertificates(); -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-1 keeping socket."); -+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy."); -+ } -+ 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 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.serverCert != null && trustsrvCerts != null) { -+ dbg("viewer.serverCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.trustUrlVncCert) { -+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else { -+ /* have a dialog with the user: */ -+ bcd = new BrowserCertsDialog(serv, host + ":" + port); -+ dbg("browser certs dialog begin."); -+ bcd.queryUser(); -+ dbg("browser certs dialog finished."); -+ -+ if (bcd.showCertDialog) { -+ String msg = "user wants to see cert"; -+ dbg(msg); -+ user_wants_to_see_cert = true; -+ if (cert_fail == null) { -+ cert_fail = "user-view"; -+ } -+ throw new SSLHandshakeException(msg); -+ } else { -+ user_wants_to_see_cert = false; -+ dbg("browser certs dialog: user said yes, accept it"); -+ } -+ } -+ -+ } catch (SSLHandshakeException eh) { -+ dbg("SSLHandshakeException: could not automatically verify Server."); -+ dbg("msg: " + eh.getMessage()); -+ -+ -+ /* send a cleanup string just in case: */ -+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; -+ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!"); -+ } -+ -+ /* reload */ -+ -+ socket = null; -+ -+ String reason = null; -+ -+ if (srv_cert) { -+ /* for serverCert usage we make this a fatal error. */ -+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); -+ /* see below in TrustDialog were we describe this case to user anyway */ -+ } -+ -+ /* -+ * Reconnect, trusting any cert, so we can grab -+ * the cert to show it to the user in a dialog -+ * for him to manually accept. This 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); -+ } -+ -+ if (debug_certs) { -+ dbg("trusturlCerts: " + trusturlCerts); -+ dbg("trustsrvCerts: " + trustsrvCerts); -+ } -+ if (trusturlCerts == null && cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); -+ -+ /* grab the cert: */ -+ try { -+ SSLSession sess = socket.getSession(); -+ trustallCerts = sess.getPeerCertificates(); -+ } catch (Exception e) { -+ throw new Exception("Could not get " + -+ "Peer Certificate"); -+ } -+ if (debug_certs) { -+ dbg("trustallCerts: " + trustallCerts); -+ } -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); -+ } else if (! browser_cert_match()) { -+ /* -+ * close socket now, we will reopen after -+ * dialog if user agrees to use the cert. -+ */ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!"); -+ } -+ socket = null; -+ -+ /* dialog with user to accept cert or not: */ -+ -+ TrustDialog td= new TrustDialog(host, port, -+ trustallCerts); -+ -+ if (cert_fail == null) { -+ ; -+ } else if (cert_fail.equals("user-view")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " You Asked to View the Certificate."; -+ } else if (cert_fail.equals("server-cert-mismatch")) { -+ /* this is now fatal error, see above. */ -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Certificate\n" -+ + " specified in the supplied 'serverCert' Applet Parameter."; -+ } else if (cert_fail.equals("cert-mismatch")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Website's\n" -+ + " HTTPS Certificate (that you previously accepted; either\n" -+ + " manually or automatically via Certificate Authority.)"; -+ } else if (cert_fail.equals("missing-certs")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " Not all Certificates could be obtained to check."; -+ } -+ -+ if (! td.queryUser(reason)) { -+ 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()); -+ } -+ -+ /* reload again: */ -+ -+ if (socket != null) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!!"); -+ } -+ socket = null; -+ } -+ -+ /* -+ * Now connect a 3rd time, using the cert -+ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); -+ -+ } catch (Exception ehand3) { -+ dbg("** Could not TrustAll/TrustOne Verify Server #3."); -+ -+ throw new IOException(ehand3.getMessage()); -+ } -+ } -+ -+ /* we have socket (possibly null) at this point, so proceed: */ -+ -+ /* handle x11vnc GET=1, if applicable: */ -+ 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: " + str); -+ OutputStream os = socket.getOutputStream(); -+ String type = "os"; -+ -+ if (type == "os") { -+ os.write(str.getBytes()); -+ os.flush(); -+ System.out.println("used OutputStream"); -+ } else if (type == "bs") { -+ BufferedOutputStream bs = new BufferedOutputStream(os); -+ bs.write(str.getBytes()); -+ bs.flush(); -+ System.out.println("used BufferedOutputStream"); -+ } else if (type == "ds") { -+ DataOutputStream ds = new DataOutputStream(os); -+ ds.write(str.getBytes()); -+ ds.flush(); -+ System.out.println("used DataOutputStream"); -+ } -+ 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."); -+ dbg(""); -+ -+ /* could be null, let caller handle that. */ -+ return (Socket) socket; -+ } -+ -+ boolean browser_cert_match() { -+ String msg = "Browser URL accept previously accepted cert"; -+ -+ if (user_wants_to_see_cert) { -+ return false; -+ } -+ -+ if (viewer.serverCert != null || trustsrvCerts != null) { -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (trustallCerts != null && trusturlCerts != null) { -+ if (trustallCerts.length == trusturlCerts.length) { -+ boolean ok = true; -+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ -+ for (int i = 0; i < trusturlCerts.length; i++) { -+ if (! trustallCerts[i].equals(trusturlCerts[i])) { -+ dbg("BCM: cert mismatch at i=" + i); -+ dbg("BCM: cert mismatch url" + trusturlCerts[i]); -+ dbg("BCM: cert mismatch all" + trustallCerts[i]); -+ ok = false; -+ } -+ } -+ if (ok) { -+ System.out.println(msg); -+ if (cert_fail == null) { -+ cert_fail = "did-not-fail"; -+ } -+ return true; -+ } else { -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ return false; -+ } -+ } -+ } -+ if (cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ return false; -+ } -+ -+ 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; -+ } -+ -+ /* this will do the proxy CONNECT negotiation and hook us up. */ -+ -+ private void proxy_helper(String proxyHost, int proxyPort) { -+ -+ boolean proxy_auth = false; -+ String proxy_auth_basic_realm = ""; -+ String hp = host + ":" + port; -+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); -+ -+ /* we loop here a few times trying for the password case */ -+ for (int k=0; k < 2; k++) { -+ dbg("proxy_in_use psocket: " + k); -+ -+ if (proxySock != null) { -+ try { -+ proxySock.close(); -+ } catch (Exception e) { -+ dbg("proxy socket is grumpy."); -+ } -+ } -+ -+ proxySock = psocket(proxyHost, proxyPort); -+ if (proxySock == null) { -+ dbg("1-a sadly, returning a null socket"); -+ return; -+ } -+ -+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" -+ + "Host: " + hp + "\r\n"; -+ -+ dbg("requesting via proxy: " + req1); -+ -+ if (proxy_auth) { -+ if (proxy_auth_string == null) { -+ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); -+ pp.queryUser(); -+ proxy_auth_string = pp.getAuth(); -+ } -+ //dbg("auth1: " + proxy_auth_string); -+ -+ String auth2 = Base64Coder.encodeString(proxy_auth_string); -+ //dbg("auth2: " + auth2); -+ -+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; -+ //dbg("req1: " + req1); -+ -+ dbg("added Proxy-Authorization: Basic ... to request"); -+ } -+ req1 += "\r\n"; -+ -+ try { -+ proxy_os.write(req1.getBytes()); -+ String reply = readline(proxy_is); -+ -+ dbg("proxy replied: " + reply.trim()); -+ -+ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { -+ proxy_auth = true; -+ proxySock.close(); -+ } else 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; -+ } -+ } -+ } catch(Exception e) { -+ dbg("some proxy socket problem: " + e.getMessage()); -+ } -+ -+ /* read the rest of the HTTP headers */ -+ while (true) { -+ String line = readline(proxy_is); -+ dbg("proxy line: " + line.trim()); -+ if (proxy_auth) { -+ String uc = line.toLowerCase(); -+ if (uc.indexOf("proxy-authenticate:") == 0) { -+ if (uc.indexOf(" basic ") >= 0) { -+ int idx = uc.indexOf(" realm"); -+ if (idx >= 0) { -+ proxy_auth_basic_realm = uc.substring(idx+1); -+ } -+ } -+ } -+ } -+ if (line.equals("\r\n") || line.equals("\n")) { -+ break; -+ } -+ } -+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { -+ /* we only try once for the non-password case: */ -+ break; -+ } -+ } -+ } -+ -+ 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) { -+ /* sandboxed applet might not be able to read it. */ -+ dbg("props failed: " + e.getMessage()); -+ } -+ if (viewer.proxyHost != null) { -+ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); -+ proxyHost = viewer.proxyHost; -+ if (viewer.proxyPort != null) { -+ proxyPort = gint(viewer.proxyPort); -+ } else { -+ proxyPort = 8080; -+ } -+ -+ } else if (props != null) { -+ dbg("\n---------------\nAll props:"); -+ props.list(System.out); -+ dbg("\n---------------\n\n"); -+ -+ /* scrape throught properties looking for proxy info: */ -+ -+ 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.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; -+ } -+ } -+ -+ 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 (proxyHost != null && proxyPort > 0) { -+ break; -+ } -+ -+ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 -+ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { -+ continue; -+ } -+ if (v2.indexOf("http") < 0) { -+ 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) { -+ j = p.indexOf("http"); -+ 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()); -+ } -+ -+ proxy_helper(proxyHost, proxyPort); -+ if (proxySock == null) { -+ return null; -+ } -+ } 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("proxy socket problem-2: " + 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(String reason) { -+ -+ /* 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"); -+ } -+ if (reason != null) { -+ reason += "\n\n"; -+ } -+ -+ text = "\n" -++ "Unable to verify the identity of\n" -++ "\n" -++ " " + host + ":" + port + "\n" -++ "\n" -++ infostr -++ "\n" -++ "as a trusted VNC server.\n" -++ "\n" -++ reason -++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" -++ "is due to one of the following:\n" -++ "\n" -++ " - Your requesting to View the Certificate before accepting.\n" -++ "\n" -++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" -++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" -++ "\n" -++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" -++ " the Apache Web server has a certificate *different* from the VNC server's.\n" -++ "\n" -++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" -++ " obtained by this applet to compare the VNC Server Certificate against.\n" -++ "\n" -++ " - The VNC Server's Certificate does not match the one specified in the\n" -++ " supplied 'serverCert' Java Applet Parameter.\n" -++ "\n" -++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" -++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" -++ "automatically authenticate this VNC Server.\n" -++ "\n" -++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" -++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" -++ "certificate (except for the Apache portal case above where they don't match.)\n" -++ "\n" -++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" -++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" -++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); -+ dialog.hide(); -+ -+ } else if (evt.getSource() == cancel) { -+ /* Cancel button clicked */ -+ trust_this_session = false; -+ -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+ -+ 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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+class ProxyPasswdDialog implements ActionListener { -+ String guessedHost = null; -+ String guessedPort = null; -+ String guessedUser = null; -+ String guessedPasswd = null; -+ String realm = 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 entry1; -+ TextField entry2; -+ String reply1 = ""; -+ String reply2 = ""; -+ -+ ProxyPasswdDialog (String h, int p, String realm) { -+ guessedHost = h; -+ try { -+ guessedPort = Integer.toString(p); -+ } catch (Exception e) { -+ guessedPort = "8080"; -+ } -+ this.realm = realm; -+ } -+ -+ public void queryUser() { -+ -+ /* create and display the dialog for unverified cert. */ -+ -+ Frame frame = new Frame("Proxy Requires Username and Password"); -+ -+ dialog = new Dialog(frame, true); -+ -+ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); -+ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); -+ entry1 = new TextField(30); -+ entry2 = new TextField(30); -+ entry2.setEchoChar('*'); -+ ok = new Button("OK"); -+ ok.addActionListener(this); -+ -+ dialog.setLayout(new BorderLayout()); -+ dialog.add("North", label); -+ dialog.add("Center", entry1); -+ dialog.add("South", entry2); -+ dialog.add("East", ok); -+ dialog.pack(); -+ dialog.resize(dialog.preferredSize()); -+ -+ dialog.show(); /* block here til OK or Cancel pressed. */ -+ return; -+ } -+ -+ public String getAuth() { -+ return reply1 + ":" + reply2; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == ok) { -+ reply1 = entry1.getText(); -+ reply2 = entry2.getText(); -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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 and/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 += "If the VNC Server connection times out while you are reading this\n"; -+m += "dialog, then restart the connection and try again.\n"; -+m += "\n"; -+m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; -+m += "\n"; -+ -+ TextArea textarea = new TextArea(m, 22, 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. */ -+ System.out.println("done show()"); -+ return; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == yes) { -+ showCertDialog = false; -+ //dialog.dispose(); -+ dialog.hide(); -+ } else if (evt.getSource() == no) { -+ showCertDialog = true; -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ System.out.println("done actionPerformed()"); -+ } -+} -+ -+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 ""; -+ } -+ } -+} -+ -+class Base64Coder { -+ -+ // Mapping table from 6-bit nibbles to Base64 characters. -+ private static char[] map1 = new char[64]; -+ static { -+ int i=0; -+ for (char c='A'; c<='Z'; c++) map1[i++] = c; -+ for (char c='a'; c<='z'; c++) map1[i++] = c; -+ for (char c='0'; c<='9'; c++) map1[i++] = c; -+ map1[i++] = '+'; map1[i++] = '/'; } -+ -+ // Mapping table from Base64 characters to 6-bit nibbles. -+ private static byte[] map2 = new byte[128]; -+ static { -+ for (int i=0; iin. -+ * @return A character array with the Base64 encoded data. -+ */ -+ public static char[] encode (byte[] in, int iLen) { -+ int oDataLen = (iLen*4+2)/3; // output length without padding -+ int oLen = ((iLen+2)/3)*4; // output length including padding -+ char[] out = new char[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++] & 0xff; -+ int i1 = ip < iLen ? in[ip++] & 0xff : 0; -+ int i2 = ip < iLen ? in[ip++] & 0xff : 0; -+ int o0 = i0 >>> 2; -+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); -+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); -+ int o3 = i2 & 0x3F; -+ out[op++] = map1[o0]; -+ out[op++] = map1[o1]; -+ out[op] = op < oDataLen ? map1[o2] : '='; op++; -+ out[op] = op < oDataLen ? map1[o3] : '='; op++; } -+ return out; } -+ -+ /** -+ * Decodes a string from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return A String containing the decoded data. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static String decodeString (String s) { -+ return new String(decode(s)); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (String s) { -+ return decode(s.toCharArray()); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * No blanks or line breaks are allowed within the Base64 encoded data. -+ * @param in a character array containing the Base64 encoded data. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (char[] in) { -+ int iLen = in.length; -+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); -+ while (iLen > 0 && in[iLen-1] == '=') iLen--; -+ int oLen = (iLen*3) / 4; -+ byte[] out = new byte[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++]; -+ int i1 = in[ip++]; -+ int i2 = ip < iLen ? in[ip++] : 'A'; -+ int i3 = ip < iLen ? in[ip++] : 'A'; -+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int b0 = map2[i0]; -+ int b1 = map2[i1]; -+ int b2 = map2[i2]; -+ int b3 = map2[i3]; -+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int o0 = ( b0 <<2) | (b1>>>4); -+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); -+ int o2 = ((b2 & 3)<<6) | b3; -+ out[op++] = (byte)o0; -+ if (op= 2) { -+ proxyPort = new String(pieces[1]); -+ } else { -+ proxyPort = new String("8080"); -+ } -+ } -+ } -+ str = readParameter("proxyHost", false); -+ if (str != null) { -+ proxyHost = new String(str); -+ } -+ str = readParameter("proxyPort", false); -+ if (str != null) { -+ proxyPort = new String(str); -+ } -+ if (proxyHost != null && proxyPort == null) { -+ proxyPort = new String("8080"); -+ } -+ -+ 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; -+ } -+ debugCerts = false; -+ str = readParameter("debugCerts", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugCerts = true; -+ } -+ debugKeyboard = false; -+ str = readParameter("debugKeyboard", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugKeyboard = true; -+ } -+ mapF5_to_atsign = false; -+ str = readParameter("mapF5_to_atsign", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ mapF5_to_atsign = true; -+ } -+ forbid_Ctrl_Alt = false; -+ str = readParameter("forbid_Ctrl_Alt", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ forbid_Ctrl_Alt = true; -+ } - } - - public String readParameter(String name, boolean required) { diff --git a/webclients/ssl/ultra.vnc b/webclients/ssl/ultra.vnc deleted file mode 100644 index 3c57445..0000000 --- a/webclients/ssl/ultra.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/webclients/ssl/ultraproxy.vnc b/webclients/ssl/ultraproxy.vnc deleted file mode 100644 index fd842c4..0000000 --- a/webclients/ssl/ultraproxy.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/webclients/ssl/ultrasigned.vnc b/webclients/ssl/ultrasigned.vnc deleted file mode 100644 index a711655..0000000 --- a/webclients/ssl/ultrasigned.vnc +++ /dev/null @@ -1,28 +0,0 @@ - - - - -$USER's $DESKTOP desktop ($DISPLAY) - - - - - - -$PARAMS - -
-x11vnc site - diff --git a/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch b/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch deleted file mode 100644 index 3309860..0000000 --- a/webclients/ssl/ultravnc-102-JavaViewer-ssl-etc.patch +++ /dev/null @@ -1,5494 +0,0 @@ -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-31 15:40:45.000000000 -0400 -@@ -43,30 +43,36 @@ - viewer = v; - - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); -- disconnectButton = new Button("Disconnect"); -+ if (v.ftpOnly) { -+ disconnectButton = new Button("Quit"); -+ } else { -+ disconnectButton = new Button("Close"); -+ } - disconnectButton.setEnabled(false); - add(disconnectButton); - disconnectButton.addActionListener(this); -- optionsButton = new Button("Options"); -- add(optionsButton); -- optionsButton.addActionListener(this); -- clipboardButton = new Button("Clipboard"); -- clipboardButton.setEnabled(false); -- add(clipboardButton); -- clipboardButton.addActionListener(this); -- if (viewer.rec != null) { -- recordButton = new Button("Record"); -- add(recordButton); -- recordButton.addActionListener(this); -- } -- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); -- ctrlAltDelButton.setEnabled(false); -- add(ctrlAltDelButton); -- ctrlAltDelButton.addActionListener(this); -- refreshButton = new Button("Refresh"); -- refreshButton.setEnabled(false); -- add(refreshButton); -- refreshButton.addActionListener(this); -+ if (!v.ftpOnly) { -+ optionsButton = new Button("Options"); -+ add(optionsButton); -+ optionsButton.addActionListener(this); -+ clipboardButton = new Button("Clipboard"); -+ clipboardButton.setEnabled(false); -+ add(clipboardButton); -+ clipboardButton.addActionListener(this); -+ if (viewer.rec != null) { -+ recordButton = new Button("Record"); -+ add(recordButton); -+ recordButton.addActionListener(this); -+ } -+ ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); -+ ctrlAltDelButton.setEnabled(false); -+ add(ctrlAltDelButton); -+ ctrlAltDelButton.addActionListener(this); -+ refreshButton = new Button("Refresh"); -+ refreshButton.setEnabled(false); -+ add(refreshButton); -+ refreshButton.addActionListener(this); -+ } - ftpButton = new Button("File Transfer"); - ftpButton.setEnabled(false); - add(ftpButton); -@@ -79,9 +85,10 @@ - - public void enableButtons() { - disconnectButton.setEnabled(true); -+ ftpButton.setEnabled(true); -+ if (viewer.ftpOnly) {return;} - clipboardButton.setEnabled(true); - refreshButton.setEnabled(true); -- ftpButton.setEnabled(true); - } - - // -@@ -89,6 +96,9 @@ - // - - public void disableButtonsOnDisconnect() { -+ ftpButton.setEnabled(false); -+ if (viewer.ftpOnly) {return;} -+ - remove(disconnectButton); - disconnectButton = new Button("Hide desktop"); - disconnectButton.setEnabled(true); -@@ -99,7 +109,6 @@ - clipboardButton.setEnabled(false); - ctrlAltDelButton.setEnabled(false); - refreshButton.setEnabled(false); -- ftpButton.setEnabled(false); - - validate(); - } -@@ -110,6 +119,7 @@ - // - - public void enableRemoteAccessControls(boolean enable) { -+ if (viewer.ftpOnly) {return;} - ctrlAltDelButton.setEnabled(enable); - } - -@@ -163,9 +173,19 @@ - } - else if (evt.getSource() == ftpButton) - { -- viewer.ftp.setVisible(!viewer.ftp.isVisible()); -+// begin runge/x11vnc -+ if (viewer.ftpOnly) { -+ viewer.vncFrame.setVisible(false); -+ } -+ viewer.ftp.setSavedLocations(); -+ if (viewer.ftp.isVisible()) { -+ viewer.ftp.doClose(); -+ } else { -+ viewer.ftp.doOpen(); -+ } -+// end runge/x11vnc - 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 2009-01-13 09:48:30.000000000 -0500 -@@ -24,8 +24,17 @@ - import java.io.*; - import java.util.ArrayList; - import java.util.Vector; -+import java.util.Date; - import javax.swing.*; - -+import java.nio.ByteBuffer; -+import java.nio.CharBuffer; -+import java.nio.charset.*; -+ -+// begin runge/x11vnc -+import java.util.Arrays; -+// end runge/x11vnc -+ - - /* - * Created on Feb 25, 2004 -@@ -74,12 +83,31 @@ - public javax.swing.JTextField connectionStatus = null; - public boolean updateDriveList; - private Vector remoteList = null; -+ private Vector remoteListInfo = null; - private Vector localList = null; -+ private Vector localListInfo = null; - private File currentLocalDirectory = null; // Holds the current local Directory - private File currentRemoteDirectory = null; // Holds the current remote Directory - 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; -+ private javax.swing.JButton refreshButton = 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; -+ private int lastRemoteIndex = -1; -+ private int lastLocalIndex = -1; -+ private boolean doingShortcutDir = false; -+ private boolean gotShortcutDir = false; -+ private boolean ignore_events = false; -+// end runge/x11vnc - - // sf@2004 - Separate directories and files for better lisibility - private ArrayList DirsList; -@@ -125,11 +153,61 @@ - - void refreshRemoteLocation() - { -+ -+//System.out.println("refreshRemoteLocation1"); - remoteList.clear(); -+ remoteListInfo.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 -@@ -143,6 +221,9 @@ - int size = str.length(); - String driveType = null; - String[] drive = new String[str.length() / 3]; -+ int idx = 0, C_drive = -1, O_drive = -1; -+ -+System.out.println("ComboBox: Str '" + str + "'"); - - // Loop through the string to create a String[] - for (int i = 0; i < size; i = i + 3) { -@@ -150,26 +231,68 @@ - driveType = str.substring(i + 2, i + 3); - if (driveType.compareTo("f") == 0) - drive[i / 3] += "\\ Floppy"; -- if (driveType.compareTo("l") == 0) -+ if (driveType.compareTo("l") == 0) { - drive[i / 3] += "\\ Local Disk"; -+ if (drive[i/3].substring(0,1).toUpperCase().equals("C")) { -+ C_drive = idx; -+ } else if (O_drive < 0) { -+ O_drive = idx; -+ } -+ } - if (driveType.compareTo("c") == 0) - drive[i / 3] += "\\ CD-ROM"; - if (driveType.compareTo("n") == 0) - drive[i / 3] += "\\ Network"; - - remoteDrivesComboBox.addItem(drive[i / 3]); -+System.out.println("ComboBox: Add " + idx + " '" + drive[i/3] + "'"); -+ idx++; -+ } -+ -+ // runge -+ if (viewer.ftpDropDown != null) { -+ String[] dd = viewer.ftpDropDown.split("\\."); -+ for (int i=0; i < dd.length; i++) { -+ if (!dd[i].equals("")) { -+ String s = dd[i]; -+ if (s.startsWith("TOP_")) { -+ s = s.substring(4); -+ remoteDrivesComboBox.insertItemAt(" [" + s + "]", 0); -+ } else { -+ remoteDrivesComboBox.addItem(" [" + s + "]"); -+ } -+ } -+ } -+ } else { -+ remoteDrivesComboBox.addItem(" [My Documents]"); -+ remoteDrivesComboBox.addItem(" [Desktop]"); -+ remoteDrivesComboBox.addItem(" [Home]"); - } -+ - //sf@ - Select Drive C:as default if possible - boolean bFound = false; -- for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) -- { -- if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) -- { -- remoteDrivesComboBox.setSelectedIndex(i); -+ -+ if (false) { -+ for(int i = 0; i < remoteDrivesComboBox.getItemCount() ; i++) { -+ if(remoteDrivesComboBox.getItemAt(i).toString().substring(0,1).toUpperCase().equals("C")) { -+ remoteDrivesComboBox.setSelectedIndex(i); -+ bFound = true; -+ } -+ } -+ } else { -+ if (C_drive >= 0) { -+ remoteDrivesComboBox.setSelectedIndex(C_drive); -+ bFound = true; -+System.out.println("ComboBox: C_drive index: " + C_drive); -+ } else if (O_drive >= 0) { -+ remoteDrivesComboBox.setSelectedIndex(O_drive); - bFound = true; -+System.out.println("ComboBox: Other_drive index: " + O_drive); - } - } -+ - if (!bFound) remoteDrivesComboBox.setSelectedIndex(0); -+ - updateDriveList = false; - return drive; - } -@@ -185,6 +308,8 @@ - stopButton.setVisible(true); - stopButton.setEnabled(true); - receiveButton.setEnabled(false); -+ viewButton.setEnabled(false); // runge/x11vnc -+ refreshButton.setEnabled(false); - remoteTopButton.setEnabled(false); - sendButton.setEnabled(false); - remoteFileTable.setEnabled(false); -@@ -207,6 +332,8 @@ - stopButton.setVisible(false); - stopButton.setEnabled(false); - receiveButton.setEnabled(true); -+ viewButton.setEnabled(true); // runge/x11vnc -+ refreshButton.setEnabled(true); - remoteTopButton.setEnabled(true); - sendButton.setEnabled(true); - remoteFileTable.setEnabled(true); -@@ -221,10 +348,11 @@ - /* - * Print Directory prints out all the contents of a directory - */ -- void printDirectory(ArrayList a) { -+ void printDirectory(ArrayList a, ArrayList b) { - - for (int i = 0; i < a.size(); i++) { - remoteList.addElement(a.get(i)); -+ remoteListInfo.addElement(b.get(i)); - } - remoteFileTable.setListData(remoteList); - } -@@ -235,10 +363,12 @@ - * @return void - */ - private void initialize() { -+ ignore_events = true; - this.setSize(794, 500); - this.setContentPane(getJContentPane()); -+ ignore_events = false; - updateDriveList = true; -- } -+ } - /** - * This method initializes jContentPane. This is the main content pane - * -@@ -253,6 +383,33 @@ - jContentPane.add(getRemotePanel(), java.awt.BorderLayout.EAST); - jContentPane.add(getLocalPanel(), java.awt.BorderLayout.WEST); - jContentPane.add(getButtonPanel(), java.awt.BorderLayout.CENTER); -+ -+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0); -+ AbstractAction escapeAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ System.out.println("Escape Pressed"); -+ if (viewer.ftpOnly) { -+ System.out.println("exiting..."); -+ System.exit(0); -+ } else { -+ doClose(); -+ } -+ } -+ }; -+ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); -+ jContentPane.getInputMap().put(stroke, "escapeAction"); -+ jContentPane.getActionMap().put("escapeAction", escapeAction); -+ -+ stroke = KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.CTRL_MASK); -+ AbstractAction resetAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ System.out.println("Ctrl-R Pressed"); -+ doReset(); -+ } -+ }; -+ jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "resetAction"); -+ jContentPane.getInputMap().put(stroke, "resetAction"); -+ jContentPane.getActionMap().put("resetAction", resetAction); - } - return jContentPane; - } -@@ -270,6 +427,7 @@ - topPanelLocal.add(getLocalMachineLabel(), java.awt.BorderLayout.CENTER); - topPanelLocal.add(getLocalTopButton(), java.awt.BorderLayout.EAST); - topPanelLocal.setBackground(java.awt.Color.lightGray); -+//System.out.println("getTopPanelLocal"); - } - return topPanelLocal; - } -@@ -288,6 +446,7 @@ - topPanelRemote.add(getRemoteMachineLabel(), java.awt.BorderLayout.CENTER); - topPanelRemote.add(getRemoteTopButton(), java.awt.BorderLayout.EAST); - topPanelRemote.setBackground(java.awt.Color.lightGray); -+//System.out.println("getTopPanelRemote"); - } - return topPanelRemote; - } -@@ -301,6 +460,7 @@ - if (topPanelCenter == null) { - topPanelCenter = new javax.swing.JPanel(); - topPanelCenter.add(getDummyButton(), null); -+//System.out.println("getTopPanelCenter"); - } - return topPanelCenter; - } -@@ -328,6 +488,7 @@ - topPanel.add(getRemoteTopButton(), null); - topPanel.setBackground(java.awt.Color.lightGray); - */ -+//System.out.println("getTopPanel"); - } - return topPanel; - } -@@ -348,6 +509,7 @@ - statusPanel.add(getJProgressBar(), null); - statusPanel.add(getConnectionStatus(), null); - statusPanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getStatusPanel"); - - } - return statusPanel; -@@ -368,6 +530,7 @@ - remotePanel.add(getRemoteScrollPane(), null); - remotePanel.add(getRemoteStatus(), null); - remotePanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getRemotePanel"); - } - return remotePanel; - } -@@ -390,6 +553,7 @@ - localPanel.setComponentOrientation( - java.awt.ComponentOrientation.UNKNOWN); - localPanel.setName("localPanel"); -+//System.out.println("getLocalPanel"); - } - return localPanel; - } -@@ -405,12 +569,15 @@ - buttonPanel = new javax.swing.JPanel(); - buttonPanel.setLayout(null); - buttonPanel.add(getReceiveButton(), null); -+ buttonPanel.add(getRefreshButton(), null); // runge/x11vnc -+ buttonPanel.add(getViewButton(), null); // runge/x11vnc - buttonPanel.add(getNewFolderButton(), null); - buttonPanel.add(getCloseButton(), null); - buttonPanel.add(getDeleteButton(), null); - buttonPanel.add(getSendButton(), null); - buttonPanel.add(getStopButton(), null); - buttonPanel.setBackground(java.awt.Color.lightGray); -+//System.out.println("getButtonPanel"); - } - return buttonPanel; - } -@@ -422,10 +589,11 @@ - 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); -+//System.out.println("getSendButton"); - - } - return sendButton; -@@ -438,7 +606,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 +621,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 +636,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 +644,39 @@ - return newFolderButton; - } - -+// begin runge/x11vnc -+ /** -+ * This method initializes refreshButton -+ * -+ * @return javax.swing.JButton -+ */ -+ private javax.swing.JButton getRefreshButton() { -+ if (refreshButton == null) { -+ refreshButton = new javax.swing.JButton(); -+ refreshButton.setBounds(15, 170, 107, 25); -+ refreshButton.setText("Refresh"); -+ refreshButton.setName("refreshButton"); -+ refreshButton.addActionListener(this); -+ } -+ return refreshButton; -+ } -+ /** -+ * This method initializes viewButton -+ * -+ * @return javax.swing.JButton -+ */ -+ private javax.swing.JButton getViewButton() { -+ if (viewButton == null) { -+ viewButton = new javax.swing.JButton(); -+ viewButton.setBounds(15, 200, 107, 25); -+ viewButton.setText("View File"); -+ viewButton.setName("viewButton"); -+ viewButton.addActionListener(this); -+ } -+ return viewButton; -+ } -+// end runge/x11vnc -+ - /** - * This method initializes stopButton - * -@@ -486,7 +687,7 @@ - if (stopButton == null) - { - stopButton = new javax.swing.JButton(); -- stopButton.setBounds(20, 200, 97, 25); -+ stopButton.setBounds(15, 230, 107, 25); // runge/x11vnc - stopButton.setText("Stop"); - stopButton.setName("stopButton"); - stopButton.addActionListener(this); -@@ -503,8 +704,12 @@ - private javax.swing.JButton getCloseButton() { - if (closeButton == null) { - closeButton = new javax.swing.JButton(); -- closeButton.setBounds(20, 325, 97, 25); -- closeButton.setText("Close"); -+ closeButton.setBounds(15, 325, 107, 25); // runge/x11vnc -+ if (viewer.ftpOnly) { -+ closeButton.setText("Quit"); -+ } else { -+ closeButton.setText("Close"); -+ } - closeButton.setName("closeButton"); - closeButton.addActionListener(this); - } -@@ -551,6 +756,7 @@ - //Select the second entry (e.g. C:\) - // localDrivesComboBox.setSelectedIndex(1); - localDrivesComboBox.addActionListener(this); -+//System.out.println("getLocalDrivesComboBox"); - } - updateDriveList = false; - return localDrivesComboBox; -@@ -567,6 +773,7 @@ - remoteDrivesComboBox.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - remoteDrivesComboBox.addActionListener(this); -+//System.out.println("getRemoteDrivesComboBox"); - - } - return remoteDrivesComboBox; -@@ -587,6 +794,7 @@ - localMachineLabel.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 11)); - localMachineLabel.setEditable(false); -+//System.out.println("getLocalMachineLabel"); - } - return localMachineLabel; - } -@@ -622,6 +830,7 @@ - localTopButton.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); - localTopButton.addActionListener(this); -+//System.out.println("getLocalTopButton"); - } - return localTopButton; - } -@@ -638,6 +847,7 @@ - remoteTopButton.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); - remoteTopButton.addActionListener(this); -+//System.out.println("getRemoteTopButton"); - } - return remoteTopButton; - } -@@ -650,9 +860,24 @@ - private javax.swing.JList getLocalFileTable() { - if (localFileTable == null) { - localList = new Vector(0); -+ localListInfo = new Vector(0); - localFileTable = new JList(localList); -+ MouseMotionListener mlisten = new MouseMotionAdapter() { -+ public void mouseMoved(MouseEvent e) { -+ int index = localFileTable.locationToIndex(e.getPoint()); -+ if (index == lastLocalIndex) { -+ return; -+ } else if (index < 0) { -+ return; -+ } -+ lastLocalIndex = index; -+ connectionStatus.setText((String) localListInfo.get(index)); -+ } -+ }; - localFileTable.addMouseListener(this); -+ localFileTable.addMouseMotionListener(mlisten); - localFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); -+//System.out.println("getLocalFileTable"); - } - return localFileTable; - } -@@ -669,6 +894,7 @@ - localScrollPane.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - localScrollPane.setName("localFileList"); -+//System.out.println("getLocalScrollPane"); - } - return localScrollPane; - } -@@ -680,10 +906,25 @@ - private javax.swing.JList getRemoteFileTable() { - if (remoteFileTable == null) { - remoteList = new Vector(0); -+ remoteListInfo = new Vector(0); - remoteFileTable = new JList(remoteList); -+ MouseMotionListener mlisten = new MouseMotionAdapter() { -+ public void mouseMoved(MouseEvent e) { -+ int index = remoteFileTable.locationToIndex(e.getPoint()); -+ if (index == lastRemoteIndex) { -+ return; -+ } else if (index < 0) { -+ return; -+ } -+ lastRemoteIndex = index; -+ connectionStatus.setText((String) remoteListInfo.get(index)); -+ } -+ }; - remoteFileTable.addMouseListener(this); -+ remoteFileTable.addMouseMotionListener(mlisten); - remoteFileTable.setSelectedValue("C:\\", false); - remoteFileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); -+//System.out.println("getRemoteFileTable"); - - } - return remoteFileTable; -@@ -698,6 +939,7 @@ - remoteScrollPane = new javax.swing.JScrollPane(); - remoteScrollPane.setViewportView(getRemoteFileTable()); - remoteScrollPane.setPreferredSize(new java.awt.Dimension(325, 418)); -+//System.out.println("getRemoteScrollPane"); - } - return remoteScrollPane; - } -@@ -716,6 +958,7 @@ - remoteLocation.setBackground(new Color(255,255,238)); - remoteLocation.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getRemoteLocation"); - } - return remoteLocation; - } -@@ -732,6 +975,7 @@ - localLocation.setBackground( new Color(255,255,238)); - localLocation.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getLocalLocation"); - } - return localLocation; - } -@@ -748,6 +992,7 @@ - localStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - localStatus.setEditable(false); -+//System.out.println("getLocalStatus"); - } - return localStatus; - } -@@ -764,6 +1009,7 @@ - remoteStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); - remoteStatus.setEditable(false); -+//System.out.println("getRemoteStatus"); - } - return remoteStatus; - } -@@ -777,9 +1023,10 @@ - historyComboBox = new javax.swing.JComboBox(); - historyComboBox.setFont( - new java.awt.Font("Dialog", java.awt.Font.BOLD, 10)); -- historyComboBox.insertItemAt(new String("Pulldown to view history ..."),0); -+ historyComboBox.insertItemAt(new String("Pulldown to view history; Press Escape to Close/Quit; Press Ctrl-R to Reset Panel."),0); - historyComboBox.setSelectedIndex(0); - historyComboBox.addActionListener(this); -+//System.out.println("getHistoryComboBox"); - } - return historyComboBox; - } -@@ -791,6 +1038,7 @@ - private javax.swing.JProgressBar getJProgressBar() { - if (jProgressBar == null) { - jProgressBar = new javax.swing.JProgressBar(); -+//System.out.println("getJProgressBar"); - } - return jProgressBar; - } -@@ -806,6 +1054,7 @@ - connectionStatus.setBackground(java.awt.Color.lightGray); - connectionStatus.setFont( - new java.awt.Font("Dialog", java.awt.Font.PLAIN, 10)); -+//System.out.println("getConnectionStatus"); - } - connectionStatus.setEditable(false); - return connectionStatus; -@@ -815,7 +1064,12 @@ - * Implements Action listener. - */ - public void actionPerformed(ActionEvent evt) { -- System.out.println(evt.getSource()); -+// System.out.println(evt.getSource()); -+ -+ if (ignore_events) { -+ System.out.println("ignore_events: " + evt.getSource()); -+ return; -+ } - - if (evt.getSource() == closeButton) - { // Close Button -@@ -829,15 +1083,27 @@ - { - 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); -+ -+ // are these really needed? changeRemoteDrive() does them at the end. -+ if (false) { -+ remoteList.clear(); -+ remoteListInfo.clear(); -+ remoteFileTable.setListData(remoteList); -+ } - } - else if (evt.getSource() == localTopButton) - { -@@ -845,12 +1111,17 @@ - } - else if (evt.getSource() == remoteTopButton) - { -+//System.out.println("remoteTopButton"); // runge/x11vnc - changeRemoteDrive(); - } - else if(evt.getSource() == deleteButton) - { - doDelete(); - } -+ else if(evt.getSource() == refreshButton) -+ { -+ doRefresh(); -+ } - else if(evt.getSource()==newFolderButton) - { - doNewFolder(); -@@ -864,7 +1135,7 @@ - - private void doNewFolder() - { -- String name = JOptionPane.showInputDialog(null,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); -+ String name = JOptionPane.showInputDialog(jContentPane,"Enter new directory name", "Create New Directory", JOptionPane.QUESTION_MESSAGE); - if(selectedTable.equals("remote")) - { - name = remoteLocation.getText()+name; -@@ -880,34 +1151,106 @@ - historyComboBox.setSelectedIndex(0); - } - } -- private void doClose() -+ public void doClose() - { -+ if (viewer.ftpOnly) { -+ viewer.disconnect(); -+ return; -+ } - try { - this.setVisible(false); -- viewer.rfb.writeFramebufferUpdateRequest( -- 0, -- 0, -- viewer.rfb.framebufferWidth, -- viewer.rfb.framebufferHeight, -- true); -+ viewer.rfb.writeFramebufferUpdateRequest(0, 0, viewer.rfb.framebufferWidth, -+ viewer.rfb.framebufferHeight, true); -+ -+ if (false) { -+ this.dispose(); -+ jContentPane = null; -+ } - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } -+ private void unSwing() { -+ jContentPane = null; -+ topPanel = null; -+ topPanelLocal = null; -+ topPanelRemote = null; -+ topPanelCenter = null; -+ statusPanel = null; -+ remotePanel = null; -+ localPanel = null; -+ buttonPanel = null; -+ sendButton = null; -+ receiveButton = null; -+ deleteButton = null; -+ newFolderButton = null; -+ stopButton = null; -+ closeButton = null; -+ dummyButton = null; -+ localDrivesComboBox = null; -+ remoteDrivesComboBox = null; -+ localMachineLabel = null; -+ remoteMachineLabel = null; -+ localTopButton = null; -+ remoteTopButton = null; -+ localScrollPane = null; -+ localFileTable = null; -+ remoteScrollPane = null; -+ remoteFileTable = null; -+ remoteLocation = null; -+ localLocation = null; -+ localStatus = null; -+ remoteStatus = null; -+ historyComboBox = null; -+ jProgressBar = null; -+ connectionStatus = null; -+ viewButton = null; -+ refreshButton = null; -+ } -+ -+ public void doReset() -+ { -+ try { -+ this.setVisible(false); -+ this.dispose(); -+ jContentPane = null; -+ try {Thread.sleep(500);} catch (InterruptedException e) {} -+ viewer.ftp_init(); -+ } catch (Exception e) { -+ // TODO Auto-generated catch block -+ e.printStackTrace(); -+ } -+ } - -+ public void doOpen() -+ { -+ try { -+ this.setVisible(true); -+ if (false) { -+ this.initialize(); -+ } -+ } catch (Exception e) { -+ // TODO Auto-generated catch block -+ e.printStackTrace(); -+ } -+ } - private void doDelete() - { -- System.out.println("Delete Button Pressed"); -+// System.out.println("Delete Button Pressed"); - //Call this method to delete a file at server - if(selectedTable.equals("remote")) - { -- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be deleted - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - -@@ -916,7 +1259,7 @@ - // sf@2004 - Delete prompt - if (remoteList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Remote Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -926,18 +1269,22 @@ - } - else - { -- String sFileName = ((String) this.localFileTable.getSelectedValue()); -+ Object selected = this.localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be deleted - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Deletion is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - // sf@2004 - Delete prompt - if (localList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "Are you sure you want to delete the file \n< " + sFileName + " >\n on Local Machine ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -952,21 +1299,25 @@ - - private void doReceive() - { -- System.out.println("Received Button Pressed"); -+// System.out.println("Received Button Pressed"); - -- String sFileName = ((String) this.remoteFileTable.getSelectedValue()); -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be transfered - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - - // sf@2004 - Overwrite prompt - if (localList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Local Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -979,23 +1330,101 @@ - viewer.rfb.requestRemoteFile(remoteFileName,localDestinationPath); - } - -+// begin runge/x11vnc -+ private void doRefresh() -+ { -+ System.out.println("Refreshing Local and Remote."); -+ refreshLocalLocation(); -+ refreshRemoteLocation(); -+ } -+ -+ 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; -+ } -+ Object selected = this.remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return null; -+ } -+ String sFileName = ((String) selected); -+ -+ 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); -+ System.out.println("remoteFileName: " + remoteFileName); -+if (false) { -+ char[] b = remoteFileName.toCharArray(); -+ for (int n = 0; n < b.length; n++) { -+ System.out.print(Integer.toHexString(b[n]) + " "); -+ } -+ System.out.println(""); -+ for (int n = 0; n < b.length; n++) { -+ System.out.print(b[n]); -+ } -+ System.out.println(""); -+} -+ -+ 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"); -+// System.out.println("Send Button Pressed"); - -- String sFileName = ((String) this.localFileTable.getSelectedValue()); -+ Object selected = this.localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sFileName = ((String) selected); - - // sf@2004 - Directory can't be transfered - if (sFileName.substring(0, 2).equals(" [") && sFileName.substring((sFileName.length() - 1), sFileName.length()).equals("]")) - { -- JOptionPane.showMessageDialog(null, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); -+ JOptionPane.showMessageDialog(jContentPane, (String)"Directory Transfer is not yet available in this version...", "FileTransfer Info", JOptionPane.INFORMATION_MESSAGE); - return; - } - - // sf@2004 - Overwrite prompt - if (remoteList.contains(sFileName)) - { -- int r = JOptionPane.showConfirmDialog(null, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); -+ int r = JOptionPane.showConfirmDialog(jContentPane, "The file < " + sFileName + " >\n already exists on Remote Machine\n Are you sure you want to overwrite it ?", "File Transfer Warning", JOptionPane.YES_NO_OPTION); - if (r == JOptionPane.NO_OPTION) - return; - } -@@ -1013,6 +1442,7 @@ - // - private void doStop() - { -+ System.out.println("** Current Transfer Aborted **"); - viewer.rfb.fAbort = true; - } - /** -@@ -1024,6 +1454,14 @@ - System.out.println("History: " + message); - historyComboBox.insertItemAt(new String(message), 0); - } -+ -+ public void receivedRemoteDirectoryName(String str) { -+ if (doingShortcutDir) { -+ if (str.length() > 1) { -+ remoteLocation.setText(str); -+ } -+ } -+ } - - /** - * This method updates the file table to the current selection of the remoteComboBox -@@ -1034,11 +1472,44 @@ - remoteSelection = null; - - if (!updateDriveList) { -- String drive = remoteDrivesComboBox.getSelectedItem().toString().substring(0,1)+ ":\\"; -- viewer.rfb.readServerDirectory(drive); -- remoteLocation.setText(drive); -+//System.out.println("changeRemoteDrive-A " + drive); // begin runge/x11vnc -+ Object selected = remoteDrivesComboBox.getSelectedItem(); -+ if (selected != null) { -+ String instr = selected.toString(); -+ if (instr != null) { -+System.out.println("changeRemoteDrive: instr='" + instr + "'"); -+ String drive = instr.substring(0,1)+ ":\\"; -+ if (instr.startsWith(" [")) { -+ int idx = instr.lastIndexOf(']'); -+ if (idx > 2) { -+ drive = instr.substring(2, idx); -+ } else { -+ drive = instr.substring(2); -+ } -+ if (drive.equals("Home")) { -+ drive = ""; -+ } -+ drive += "\\"; -+ doingShortcutDir = true; -+ } else { -+ doingShortcutDir = false; -+ drive = saveRemoteHack(drive); -+ } -+ gotShortcutDir = false; -+ viewer.rfb.readServerDirectory(drive); -+ if (!gotShortcutDir) { -+ remoteLocation.setText(drive); -+ } -+ } else { -+System.out.println("changeRemoteDrive: instr null"); -+ } -+ } else { -+System.out.println("changeRemoteDrive: selection null"); -+ } -+//System.out.println("changeRemoteDrive-B " + drive); // end runge/x11vnc - } - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - /** -@@ -1048,6 +1519,7 @@ - private void changeLocalDrive() - { - File currentDrive = new File(localDrivesComboBox.getSelectedItem().toString()); -+System.out.println("changeLocalDrive " + currentDrive.toString()); // runge/x11vnc - if(currentDrive.canRead()) - { - localSelection = null; -@@ -1057,9 +1529,11 @@ - else - { - localList.clear(); -+ localListInfo.clear(); - 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 -@@ -1098,10 +1572,18 @@ - selectedTable = "remote"; - localFileTable.setBackground(new Color(238, 238, 238)); - remoteFileTable.setBackground(new Color(255, 255, 255)); -- String name = (remoteFileTable.getSelectedValue().toString()).substring(1); -+ Object selected = remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String selstr = selected.toString(); -+ if (selstr == null) { -+ return; -+ } -+ String name = selstr.substring(1); - if( !name.substring(0, 2).equals(" [")) - remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); -- -+ - } - - /* -@@ -1115,10 +1597,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 - */ -@@ -1126,20 +1636,29 @@ - String name = null; - String action = null; - String drive = null; -- name = (remoteFileTable.getSelectedValue().toString()).substring(1); -+ Object selected = remoteFileTable.getSelectedValue(); -+ if (selected == null) { -+ return; -+ } -+ String sname = selected.toString(); -+ if (sname == null) { -+ return; -+ } -+ name = sname.substring(1); - - if (name.equals("[..]")) - { - action = "up"; - remoteSelection = null; - drive = remoteLocation.getText().substring(0, remoteLocation.getText().length() - 1); -- // JOptionPane.showMessageDialog(null, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); -+ // JOptionPane.showMessageDialog(jContentPane, (String)drive, "FileTransfer DEBUG", JOptionPane.INFORMATION_MESSAGE); - int index = drive.lastIndexOf("\\"); - drive = drive.substring(0, index + 1); - - remoteLocation.setText(drive); - viewer.rfb.readServerDirectory(drive); - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - else if (!name.substring(0, 2).equals(" [") && !name.substring((name.length() - 1), name.length()).equals("]")) -@@ -1149,6 +1668,7 @@ - remoteSelection = remoteLocation.getText() + name.substring(0, name.length()); - drive = remoteLocation.getText(); - // ?? -+ viewRemote(); // runge/x11vnc - } - else - { -@@ -1159,10 +1679,12 @@ - remoteLocation.setText(drive); - viewer.rfb.readServerDirectory(drive); - remoteList.clear(); -+ remoteListInfo.clear(); - remoteFileTable.setListData(remoteList); - } - //remoteLocation.setText(drive); - } -+ - /** - * Updates the Local File Table based on selection. Called from MouseClicked handler - */ -@@ -1188,6 +1710,7 @@ - else if (currentSelection.isFile()) - { - localSelection = currentSelection.getAbsoluteFile(); -+ viewLocal(); // runge/x11vnc - } - else if (currentSelection.isDirectory()) - { -@@ -1201,13 +1724,22 @@ - * - */ - private String getTrimmedSelection(){ -- String currentSelection = (localFileTable.getSelectedValue().toString()).substring(1); -- if(currentSelection.substring(0,1).equals("[") && -- currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ -- return currentSelection.substring(1,currentSelection.length()-1); -- } else { -- return currentSelection; -- } -+ String currentSelection = ""; -+ Object selected = localFileTable.getSelectedValue(); -+ if (selected == null) { -+ return currentSelection; -+ } -+ String selstr = selected.toString(); -+ if (selstr == null) { -+ return currentSelection; -+ } -+ currentSelection = selstr.substring(1); -+ if(currentSelection.substring(0,1).equals("[") && -+ currentSelection.substring(currentSelection.length()-1,currentSelection.length()).equals("]")){ -+ return currentSelection.substring(1,currentSelection.length()-1); -+ } else { -+ return currentSelection; -+ } - } - - /* -@@ -1241,36 +1773,148 @@ - return null; - } - -+ String timeStr(long t) { -+ Date date = new Date(t); -+ return date.toString(); -+ } -+ String dotPast(double f, int n) { -+ String fs = "" + f; -+ int i = fs.lastIndexOf(".") + n; -+ if (i >= 0) { -+ int len = fs.length(); -+ if (i >= len) { -+ i = len-1; -+ } -+ fs = fs.substring(0, i); -+ } -+ return fs; -+ } -+ String sizeStr(int s) { -+ if (s < 0) { -+ return s + "? B"; -+ } else if (s < 1024) { -+ return s + " B"; -+ } else if (s < 1024 * 1024) { -+ double k = s / 1024.0; -+ String ks = dotPast(k, 3); -+ -+ return s + " (" + ks + " KB)"; -+ } else { -+ double m = s / (1024.0*1024.0); -+ String ms = dotPast(m, 3); -+ return s + " (" + ms + " MB)"; -+ } -+ } -+ -+ int max_char(String text) { -+ int maxc = 0; -+ char chars[] = text.toCharArray(); -+ for (int n = 0; n < chars.length; n++) { -+ if ((int) chars[n] > maxc) { -+ maxc = (int) chars[n]; -+ } -+ } -+ return maxc; -+ } - - /* - * Navigates the local file structure up or down one directory - */ - public void changeLocalDirectory(File dir) - { -- currentLocalDirectory = dir; // Updates Global -+ dir = saveLocalHack(dir); // runge/x11vnc -+ -+ if (dir == null) { -+ connectionStatus.setText("Error changing local directory."); -+ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); -+ historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ - File allFiles[] = dir.listFiles(); // Reads files - String[] contents = dir.list(); - -+ if (contents == null || allFiles == null) { -+ connectionStatus.setText("Error changing local directory."); -+ historyComboBox.insertItemAt(new String("> Error changing local directory."), 0); -+ historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ -+ currentLocalDirectory = dir; // Updates Global -+// 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]); -+ } -+ } else { -+ return; -+ } -+// end runge/x11vnc -+ - localList.clear(); -+ localListInfo.clear(); - localList.addElement(" [..]"); -+ localListInfo.addElement(" [..]"); -+ -+ ArrayList DirInfo = new ArrayList(); -+ ArrayList FilInfo = new ArrayList(); -+ -+ Charset charset = Charset.forName("ISO-8859-1"); -+ CharsetDecoder decoder = charset.newDecoder(); -+ CharsetEncoder encoder = charset.newEncoder(); - - // Populate the Lists - for (int i = 0; i < contents.length; i++) - { -- if (allFiles[i].isDirectory()) -+ String f1 = contents[i]; -+ -+if (false) { -+ -+System.out.println("max_char: " + max_char(f1) + " " + f1); -+ if (max_char(f1) > 255) { -+ try { -+System.out.println("bbuf1"); -+ ByteBuffer bbuf = encoder.encode(CharBuffer.wrap(f1.toCharArray())); -+System.out.println("bbuf2"); -+ CharBuffer cbuf = decoder.decode(bbuf); -+System.out.println("bbuf3"); -+ f1 = cbuf.toString(); -+System.out.println("did bbuf: " + f1); -+ } catch (Exception e) { -+ ; -+ } -+ } -+} -+ -+ String f2 = f1; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ String s = f2 + " \tLastmod: " + timeStr(allFiles[i].lastModified()) + " \t\tSize: " + sizeStr((int) allFiles[i].length()); -+ if (allFiles[i].isDirectory()) { - // localList.addElement("[" + contents[i] + "]"); -- DirsList.add(" [" + contents[i] + "]"); // sf@2004 -- else -- { -+ DirsList.add(" [" + f1 + "]"); // sf@2004 -+ DirInfo.add(s); -+ } else { - // localList.addElement(contents[i]); -- FilesList.add(" " + contents[i]); // sf@2004 -+ FilesList.add(" " + f1); // sf@2004 -+ FilInfo.add(s); - } - } - // sf@2004 -- for (int i = 0; i < DirsList.size(); i++) -+ for (int i = 0; i < DirsList.size(); i++) { - localList.addElement(DirsList.get(i)); -- for (int i = 0; i < FilesList.size(); i++) -+ localListInfo.addElement(DirInfo.get(i)); -+ } -+ for (int i = 0; i < FilesList.size(); i++) { - localList.addElement(FilesList.get(i)); -+ localListInfo.addElement(FilInfo.get(i)); -+ } - - FilesList.clear(); - DirsList.clear(); -@@ -1296,3 +1940,147 @@ - } - - } // @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; -+ TextViewer me; -+ -+ public TextViewer(String s, File f, boolean d) { -+ -+ delete_it = d; -+ file = f; -+ me = this; -+ -+ JScrollPane scrollPane = new JScrollPane(textArea, -+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, -+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); -+ -+ textArea.setEditable(false); -+ textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); -+ -+ KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, InputEvent.SHIFT_MASK); -+ AbstractAction escapeAction = new AbstractAction() { -+ public void actionPerformed(ActionEvent actionEvent) { -+ cleanse(); -+ me.dispose(); -+ } -+ }; -+ textArea.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "escapeAction"); -+ textArea.getInputMap().put(stroke, "escapeAction"); -+ textArea.getActionMap().put("escapeAction", escapeAction); -+ -+ 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/Makefile JavaViewer/Makefile ---- JavaViewer.orig/Makefile 2006-05-29 09:06:32.000000000 -0400 -+++ JavaViewer/Makefile 2010-05-18 20:53:32.000000000 -0400 -@@ -4,6 +4,7 @@ - - CP = cp - JC = javac -+JC_ARGS = -target 1.4 -source 1.4 - JAR = jar - ARCHIVE = VncViewer.jar - PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc -@@ -20,7 +21,7 @@ - all: $(CLASSES) $(ARCHIVE) - - $(CLASSES): $(SOURCES) -- $(JC) -O $(SOURCES) -+ $(JC) $(JC_ARGS) -O $(SOURCES) - - $(ARCHIVE): $(CLASSES) - $(JAR) cf $(ARCHIVE) $(CLASSES) -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 2010-11-30 22:13:58.000000000 -0500 -@@ -31,6 +31,7 @@ - import java.net.Socket; - import java.util.*; - import java.util.zip.*; -+import java.text.DateFormat; - - - class RfbProto { -@@ -86,8 +87,11 @@ - - // sf@2004 - FileTransfer part - ArrayList remoteDirsList; -+ ArrayList remoteDirsListInfo; - ArrayList remoteFilesList; -+ ArrayList remoteFilesListInfo; - ArrayList a; -+ ArrayList b; - boolean fFTInit = true; // sf@2004 - boolean fFTAllowed = true; - boolean fAbort = false; -@@ -199,6 +203,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 +215,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)); -@@ -215,9 +243,12 @@ - osw = new OutputStreamWriter(sock.getOutputStream()); - inDirectory2 = false; - a = new ArrayList(); -+ b = new ArrayList(); - // sf@2004 - remoteDirsList = new ArrayList(); -+ remoteDirsListInfo = new ArrayList(); - remoteFilesList = new ArrayList(); -+ remoteFilesListInfo = new ArrayList(); - - sendFileSource = ""; - } -@@ -420,7 +451,13 @@ - // - - int readServerMessageType() throws IOException { -- int msgType = is.readUnsignedByte(); -+ int msgType; -+ try { -+ msgType = is.readUnsignedByte(); -+ } catch (Exception e) { -+ viewer.disconnect(); -+ return -1; -+ } - - // If the session is being recorded: - if (rec != null) { -@@ -600,6 +637,7 @@ - contentParamT = is.readUnsignedByte(); - contentParamT = contentParamT << 8; - contentParam = contentParam | contentParamT; -+//System.out.println("FTM: contentType " + contentType + " contentParam " + contentParam); - if (contentType == rfbRDrivesList || contentType == rfbDirPacket) - { - readDriveOrDirectory(contentParam); -@@ -610,7 +648,7 @@ - } - else if (contentType == rfbFilePacket) - { -- receiveFileChunk(); -+ receiveFileChunk(); - } - else if (contentType == rfbEndOfFile) - { -@@ -618,6 +656,10 @@ - } - else if (contentType == rfbAbortFileTransfer) - { -+ System.out.println("rfbAbortFileTransfer: fFileReceptionRunning=" -+ + fFileReceptionRunning + " fAbort=" -+ + fAbort + " fFileReceptionError=" -+ + fFileReceptionError); - if (fFileReceptionRunning) - { - endOfReceiveFile(false); // Error -@@ -626,6 +668,11 @@ - { - // sf@2004 - Todo: Add TestPermission - // System.out.println("File Transfer Aborted!"); -+ -+ // runge: seems like we must at least read the remaining -+ // 8 bytes of the header, right? -+ int size = is.readInt(); -+ int length = is.readInt(); - } - - } -@@ -645,6 +692,7 @@ - { - System.out.println("ContentType: " + contentType); - } -+//System.out.println("FTM: done"); - } - - //Refactored from readRfbFileTransferMsg() -@@ -662,6 +710,7 @@ - - //Refactored from readRfbFileTransferMsg() - public void readDriveOrDirectory(int contentParam) throws IOException { -+//System.out.println("RDOD: " + contentParam + " " + inDirectory2); - if (contentParam == rfbADrivesList) - { - readFTPMsgDriveList(); -@@ -688,13 +737,21 @@ - - // Internally used. Write an Rfb message to the server - void writeRfbFileTransferMsg( -- int contentType, -- int contentParam, -- long size, // 0 : compression not supported - 1 : compression supported -- long length, -- String text) throws IOException -+ int contentType, -+ int contentParam, -+ long size, // 0 : compression not supported - 1 : compression supported -+ long length, -+ String text) throws IOException - { - byte b[] = new byte[12]; -+ byte byteArray[]; -+ -+ if (viewer.dsmActive) { -+ // need to send the rfbFileTransfer msg type twice for the plugin... -+ byte b2[] = new byte[1]; -+ b2[0] = (byte) rfbFileTransfer; -+ os.write(b2); -+ } - - b[0] = (byte) rfbFileTransfer; - b[1] = (byte) contentType; -@@ -702,7 +759,7 @@ - - byte by = 0; - long c = 0; -- length++; -+ - c = size & 0xFF000000; - by = (byte) (c >>> 24); - b[4] = by; -@@ -716,6 +773,32 @@ - by = (byte) c; - b[7] = by; - -+ if (text != null) { -+ byte byteArray0[] = text.getBytes(); -+ int maxc = max_char(text); -+ if (maxc > 255) { -+ System.out.println("writeRfbFileTransferMsg: using getBytes(\"UTF-8\")"); -+ byteArray0 = text.getBytes("UTF-8"); -+ } else if (maxc > 127) { -+ System.out.println("writeRfbFileTransferMsg: using getBytes(\"ISO-8859-1\")"); -+ byteArray0 = text.getBytes("ISO-8859-1"); -+ } -+ byteArray = new byte[byteArray0.length + 1]; -+ for (int i = 0; i < byteArray0.length; i++) { -+ byteArray[i] = byteArray0[i]; -+ } -+ byteArray[byteArray.length - 1] = 0; -+System.out.println("writeRfbFileTransferMsg: length: " + length + " -> byteArray.length: " + byteArray.length); -+ -+ // will equal length for ascii, ISO-8859-1, more for UTF-8 -+ length = byteArray.length; -+ -+ //length++; // used to not include null byte at end. -+ } else { -+ String moo = "moo"; -+ byteArray = moo.getBytes(); -+ } -+ - c = length & 0xFF000000; - by = (byte) (c >>> 24); - b[8] = by; -@@ -729,29 +812,91 @@ - by = (byte) c; - b[11] = by; - os.write(b); -+ -+//System.out.println("size: " + size + " length: " + length + " text: " + text); - - - if (text != null) - { -- byte byteArray[] = text.getBytes(); -- byte byteArray2[] = new byte[byteArray.length + 1]; -- for (int i = 0; i < byteArray.length; i++) { -- byteArray2[i] = byteArray[i]; -+ os.write(byteArray); -+ } -+ } -+ -+ int max_char(String text) { -+ int maxc = 0; -+ char chars[] = text.toCharArray(); -+ for (int n = 0; n < chars.length; n++) { -+ if ((int) chars[n] > maxc) { -+ maxc = (int) chars[n]; - } -- byteArray2[byteArray2.length - 1] = 0; -- os.write(byteArray2); - } -- -+ return maxc; - } - -+ String guess_encoding(char[] chars) { -+ boolean saw_high_char = false; -+ -+ for (int i = 0; i < chars.length; i++) { -+ if (chars[i] == '\0') { -+ break; -+ } -+ if (chars[i] >= 128) { -+ saw_high_char = true; -+ break; -+ } -+ } -+ if (!saw_high_char) { -+ return "ASCII"; -+ } -+ char prev = 1; -+ boolean valid_utf8 = true; -+ int n = 0; -+ for (int i = 0; i < chars.length; i++) { -+ if (chars[i] == '\0') { -+ break; -+ } -+ char c = chars[i]; -+ if (prev < 128 && c >= 128) { -+ if (c >> 5 == 0x6) { -+ n = 1; -+ } else if (c >> 4 == 0xe) { -+ n = 2; -+ } else if (c >> 3 == 0x1e) { -+ n = 3; -+ } else if (c >> 2 == 0x3e) { -+ n = 4; -+ } else { -+ valid_utf8 = false; -+ break; -+ } -+ } else { -+ if (n > 0) { -+ if (c < 128) { -+ valid_utf8 = false; -+ break; -+ } -+ n--; -+ } -+ } -+ -+ prev = c; -+ } -+ if (valid_utf8) { -+ return "UTF-8"; -+ } else { -+ return "ISO-8859-1"; -+ } -+ } -+ -+ - //Internally used. Write an rfb message to the server for sending files ONLY - int writeRfbFileTransferMsgForSendFile( -- int contentType, -- int contentParam, -- long size, -- long length, -- String source -- ) throws IOException -+ int contentType, -+ int contentParam, -+ long size, -+ long length, -+ String source -+ ) throws IOException - { - File f = new File(source); - fis = new FileInputStream(f); -@@ -768,50 +913,47 @@ - - while (bytesRead!=-1) - { -- counter += bytesRead; -- myDeflater.setInput(byteBuffer, 0, bytesRead); -- myDeflater.finish(); -- compressedSize = myDeflater.deflate(CompressionBuffer); -- myDeflater.reset(); -- // If the compressed data is larger than the original one, we're dealing with -- // already compressed data -- if (compressedSize > bytesRead) -- fCompress = false; -- this.writeRfbFileTransferMsg( -- contentType, -- contentParam, -- (fCompress ? 1 : 0), -- (fCompress ? compressedSize-1 : bytesRead-1), -- null -- ); -- // Todo: Test write error ! -- os.write( -- fCompress ? CompressionBuffer : byteBuffer, -- 0, -- fCompress ? compressedSize : bytesRead -- ); -- -- // Todo: test read error ! -- bytesRead = fis.read(byteBuffer); -- -- // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); -- viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); -- viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); -- -- if (fAbort == true) -- { -- fAbort = false; -- fError = true; -- break; -- } -- try -- { -- Thread.sleep(5); -- } -- catch(InterruptedException e) -- { -- System.err.println("Interrupted"); -- } -+ counter += bytesRead; -+ myDeflater.setInput(byteBuffer, 0, bytesRead); -+ myDeflater.finish(); -+ compressedSize = myDeflater.deflate(CompressionBuffer); -+ myDeflater.reset(); -+ // If the compressed data is larger than the original one, we're dealing with -+ // already compressed data -+ if (compressedSize > bytesRead) -+ fCompress = false; -+ this.writeRfbFileTransferMsg( -+ contentType, -+ contentParam, -+ (fCompress ? 1 : 0), -+// RUNGE (fCompress ? compressedSize-1 : bytesRead-1), -+ (fCompress ? compressedSize : bytesRead), -+ null -+ ); -+ // Todo: Test write error ! -+ os.write(fCompress ? CompressionBuffer : byteBuffer, 0, fCompress ? compressedSize : bytesRead); -+ -+ // Todo: test read error ! -+ bytesRead = fis.read(byteBuffer); -+ -+ // viewer.ftp.connectionStatus.setText("Sent: "+ counter + " bytes of "+ f.length() + " bytes"); -+ viewer.ftp.jProgressBar.setValue((int)((counter * 100) / f.length())); -+ viewer.ftp.connectionStatus.setText(">>> Sending File: " + source + " - Size: " + f.length() + " bytes - Progress: " + ((counter * 100) / f.length()) + "%"); -+ -+ if (fAbort == true) -+ { -+ fAbort = false; -+ fError = true; -+ break; -+ } -+ try -+ { -+ Thread.sleep(5); -+ } -+ catch(InterruptedException e) -+ { -+ System.err.println("Interrupted"); -+ } - } - - writeRfbFileTransferMsg(fError ? rfbAbortFileTransfer : rfbEndOfFile, 0, 0, 0, null); -@@ -831,24 +973,30 @@ - { - System.out.print((char) is.readUnsignedByte()); - } -+ System.out.println(""); -+ -+ if (size == rfbRErrorCmd || size == -1) { -+ viewer.ftp.enableButtons(); -+ viewer.ftp.connectionStatus.setText("Remote file not available for writing."); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for writing."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ return; -+ } - -- int ret = writeRfbFileTransferMsgForSendFile( -- rfbFilePacket, -- 0, -- 0, -- 0, -- sendFileSource); -+ int ret = writeRfbFileTransferMsgForSendFile(rfbFilePacket, 0, 0, 0, sendFileSource); - - viewer.ftp.refreshRemoteLocation(); - if (ret != 1) - { - viewer.ftp.connectionStatus.setText(" > Error - File NOT sent"); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) + "> was not correctly sent (aborted by user or error)",0); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + sendFileSource) -+ + "> was not correctly sent (aborted or error). Data may still be buffered/in transit. Wait for remote listing...",0); - } - else - { - viewer.ftp.connectionStatus.setText(" > File sent"); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) + "> was sent to Remote Machine",0); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > File: <" + sendFileSource) -+ + "> was sent to Remote Machine. Note: data may still be buffered/in transit. Wait for remote listing...",0); - } - viewer.ftp.historyComboBox.setSelectedIndex(0); - viewer.ftp.enableButtons(); -@@ -907,7 +1055,7 @@ - //Handles acknowledgement that the file has been deleted on the server - void deleteRemoteFileFeedback() throws IOException - { -- is.readInt(); -+ int ret = is.readInt(); - int length = is.readInt(); - String f = ""; - for (int i = 0; i < length; i++) -@@ -916,7 +1064,11 @@ - } - - viewer.ftp.refreshRemoteLocation(); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); -+ if (ret == -1) { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Delete File On Remote Machine: "),0); -+ } else { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Deleted File On Remote Machine: " + f.substring(0, f.length()-1)),0); -+ } - viewer.ftp.historyComboBox.setSelectedIndex(0); - } - -@@ -926,12 +1078,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbCommand, -- rfbCFileDelete, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbCommand, rfbCFileDelete, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -943,7 +1090,7 @@ - // Handles acknowledgement that the directory has been created on the server - void createRemoteDirectoryFeedback() throws IOException - { -- is.readInt(); -+ int ret = is.readInt(); - int length = is.readInt(); - String f=""; - for (int i = 0; i < length; i++) -@@ -951,7 +1098,11 @@ - f += (char)is.readUnsignedByte(); - } - viewer.ftp.refreshRemoteLocation(); -- viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); -+ if (ret == -1) { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR Could not Create Directory on Remote Machine."),0); -+ } else { -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Created Directory on Remote Machine: " + f.substring(0, f.length()-1)),0); -+ } - viewer.ftp.historyComboBox.setSelectedIndex(0); - } - -@@ -961,12 +1112,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbCommand, -- rfbCDirCreate, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbCommand, rfbCDirCreate, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -979,15 +1125,13 @@ - { - try - { -+//System.out.println("requestRemoteFile text: " + text); -+//System.out.println("requestRemoteFile leng: " + text.length()); - String temp = text; - receivePath = localPath; - -- writeRfbFileTransferMsg( -- rfbFileTransferRequest, -- 0, -- 1, // 0 : compression not supported - 1 : compression supported -- temp.length(), -- temp); -+ // 0 : compression not supported - 1 : compression supported -+ writeRfbFileTransferMsg(rfbFileTransferRequest, 0, 1, temp.length(), temp); - } - catch (IOException e) - { -@@ -1004,6 +1148,9 @@ - viewer.ftp.disableButtons(); - int size = is.readInt(); - int length = is.readInt(); -+ -+//System.out.println("receiveFileHeader size: " + size); -+//System.out.println("receiveFileHeader leng: " + length); - - String tempName = ""; - for (int i = 0; i < length; i++) -@@ -1011,6 +1158,15 @@ - tempName += (char) is.readUnsignedByte(); - } - -+ if (size == rfbRErrorCmd || size == -1) { -+ fFileReceptionRunning = false; -+ viewer.ftp.enableButtons(); -+ viewer.ftp.connectionStatus.setText("Remote file not available for reading."); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - Remote file not available for reading."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ return; -+ } -+ - // sf@2004 - Read the high part of file size (not yet in rfbFileTransferMsg for - // backward compatibility reasons...) - int sizeH = is.readInt(); -@@ -1021,7 +1177,16 @@ - fileSize=0; - fileChunkCounter = 0; - String fileName = receivePath; -- fos = new FileOutputStream(fileName); -+ try { -+ fos = new FileOutputStream(fileName); -+ } catch (Exception e) { -+ fFileReceptionRunning = false; -+ writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > ERROR opening Local File: <" + fileName ),0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); -+ viewer.ftp.enableButtons(); -+ return; -+ } - writeRfbFileTransferMsg(rfbFileHeader, 0, 0, 0, null); - } - -@@ -1085,7 +1250,13 @@ - fAbort = false; - fFileReceptionError = true; - writeRfbFileTransferMsg(rfbAbortFileTransfer, 0, 0, 0, null); -- -+ -+ //runge for use with x11vnc/libvncserver, no rfbAbortFileTransfer reply sent. -+ try {Thread.sleep(500);} catch (InterruptedException e) {} -+ viewer.ftp.enableButtons(); -+ viewer.ftp.refreshLocalLocation(); -+ viewer.ftp.connectionStatus.setText(" > Error - File NOT received"); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > Error - File: <" + receivePath + "> not correctly received from Remote Machine (aborted by user or error)") ,0); - } - // sf@2004 - For old FT protocole only - /* -@@ -1104,7 +1275,7 @@ - int length = is.readInt(); - fileSize=0; - fos.close(); -- -+ - viewer.ftp.refreshLocalLocation(); - if (fReceptionOk && !fFileReceptionError) - { -@@ -1132,12 +1303,7 @@ - try - { - String temp = text; -- writeRfbFileTransferMsg( -- rfbDirContentRequest, -- rfbRDirContent, -- 0, -- temp.length(), -- temp); -+ writeRfbFileTransferMsg(rfbDirContentRequest, rfbRDirContent, 0, temp.length(), temp); - } - catch (IOException e) - { -@@ -1197,11 +1363,80 @@ - str += temp; - } - } -+ // runge -+ viewer.ftp.receivedRemoteDirectoryName(str); - // viewer.ftp.changeRemoteDirectory(str); - - } - } - -+ int zogswap(int n) { -+ long l = n; -+ if (l < 0) { -+ l += 0x100000000L; -+ } -+ l = l & 0xFFFFFFFF; -+ l = (l >> 24) | ((l & 0x00ff0000) >> 8) | ((l & 0x0000ff00) << 8) | (l << 24); -+ return (int) l; -+ } -+ -+ int windozeToUnix(int L, int H) { -+ long L2 = zogswap(L); -+ long H2 = zogswap(H); -+ long unix = (H2 << 32) + L2; -+ unix -= 11644473600L * 10000000L; -+ unix /= 10000000L; -+ //System.out.println("unix time: " + unix + " H2: " + H2 + " L2: " + L2); -+ return (int) unix; -+ } -+ -+ String timeStr(int t, int h) { -+ if (h == 0) { -+ // x11vnc/libvncserver unix -+ t = zogswap(t); -+ } else { -+ // ultra (except if h==0 by chance) -+ t = windozeToUnix(t, h); -+ } -+ long tl = (long) t; -+ Date date = new Date(tl * 1000); -+ if (true) { -+ return date.toString(); -+ } else { -+ return DateFormat.getDateTimeInstance().format(date); -+ } -+ } -+ -+ String dotPast(double f, int n) { -+ String fs = "" + f; -+ int i = fs.lastIndexOf(".") + n; -+ if (i >= 0) { -+ int len = fs.length(); -+ if (i >= len) { -+ i = len-1; -+ } -+ fs = fs.substring(0, i); -+ } -+ return fs; -+ } -+ String sizeStr(int s) { -+ s = zogswap(s); -+ if (s < 0) { -+ return s + "? B"; -+ } else if (s < 1024) { -+ return s + " B"; -+ } else if (s < 1024 * 1024) { -+ double k = s / 1024.0; -+ String ks = dotPast(k, 3); -+ -+ return s + " (" + ks + " KB)"; -+ } else { -+ double m = s / (1024.0*1024.0); -+ String ms = dotPast(m, 3); -+ return s + " (" + ms + " MB)"; -+ } -+ } -+ - //Internally used to receive directory content from server - //Here, the server sends one file/directory with it's attributes - void readFTPMsgDirectoryListContent() throws IOException -@@ -1217,17 +1452,32 @@ - dwReserved0, - dwReserved1; - long ftCreationTime, ftLastAccessTime, ftLastWriteTime; -+ int ftCreationTimeL, ftLastAccessTimeL, ftLastWriteTimeL; -+ int ftCreationTimeH, ftLastAccessTimeH, ftLastWriteTimeH; - char cFileName, cAlternateFileName; - int length = 0; - is.readInt(); - length = is.readInt(); -+ -+ char[] chars = new char[4*length]; -+ int char_cnt = 0; -+ for (int i = 0; i < chars.length; i++) { -+ chars[i] = '\0'; -+ } -+ - dwFileAttributes = is.readInt(); - length -= 4; -- ftCreationTime = is.readLong(); -+ //ftCreationTime = is.readLong(); -+ ftCreationTimeL = is.readInt(); -+ ftCreationTimeH = is.readInt(); - length -= 8; -- ftLastAccessTime = is.readLong(); -+ //ftLastAccessTime = is.readLong(); -+ ftLastAccessTimeL = is.readInt(); -+ ftLastAccessTimeH = is.readInt(); - length -= 8; -- ftLastWriteTime = is.readLong(); -+ //ftLastWriteTime = is.readLong(); -+ ftLastWriteTimeL = is.readInt(); -+ ftLastWriteTimeH = is.readInt(); - length -= 8; - nFileSizeHigh = is.readInt(); - length -= 4; -@@ -1239,10 +1489,12 @@ - length -= 4; - cFileName = (char) is.readUnsignedByte(); - length--; -+ chars[char_cnt++] = cFileName; - while (cFileName != '\0') - { - fileName += cFileName; - cFileName = (char) is.readUnsignedByte(); -+ chars[char_cnt++] = cFileName; - length--; - } - cAlternateFileName = (char) is.readByte(); -@@ -1253,7 +1505,28 @@ - cAlternateFileName = (char) is.readUnsignedByte(); - length--; - } -- if (dwFileAttributes == 268435456 -+ String guessed = guess_encoding(chars); -+ if (!guessed.equals("ASCII")) { -+ System.out.println("guess: " + guessed + "\t" + fileName); -+ } -+ if (guessed.equals("UTF-8")) { -+ try { -+ byte[] bytes = new byte[char_cnt-1]; -+ for (int i=0; i < char_cnt-1; i++) { -+ bytes[i] = (byte) chars[i]; -+ } -+ String newstr = new String(bytes, "UTF-8"); -+ fileName = newstr; -+ } catch (Exception e) { -+ System.out.println("failed to convert bytes to UTF-8 based string"); -+ } -+ } -+ for (int i = 0; i < char_cnt; i++) { -+ //System.out.println("char[" + i + "]\t" + (int) chars[i]); -+ } -+ if (fileName.length() <= 0) { -+ ; -+ } else if (dwFileAttributes == 268435456 - || dwFileAttributes == 369098752 - || dwFileAttributes == 285212672 - || dwFileAttributes == 271056896 -@@ -1263,11 +1536,74 @@ - || dwFileAttributes == 369623040) - { - fileName = " [" + fileName + "]"; -- remoteDirsList.add(fileName); // sf@2004 -- } -- else -- { -- remoteFilesList.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; -+ } -+ } -+ //String s = "Lastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " " + fileName; -+ String f2 = fileName; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); -+ //s = fileName + " Lastmod: " + zogswap(ftLastWriteTimeL); -+ if (i >= 0) { -+ remoteDirsList.add(i, fileName); -+ remoteDirsListInfo.add(i, s); -+ } else { -+ remoteDirsList.add(fileName); -+ remoteDirsListInfo.add(s); -+ } -+// end runge/x11vnc -+ } else { -+// 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; -+ } -+ } -+ String f2 = fileName; -+ if (f2.length() < 24) { -+ for (int ik = f2.length(); ik < 24; ik++) { -+ f2 = f2 + " "; -+ } -+ } -+ -+if (false) { -+System.out.println("fileName: " + f2); -+System.out.println("ftLastWriteTimeL: " + ftLastWriteTimeL); -+System.out.println("ftLastWriteTimeH: " + ftLastWriteTimeH); -+System.out.println("nFileSizeLow: " + nFileSizeLow); -+} -+ -+ String s = f2 + " \tLastmod: " + timeStr(ftLastWriteTimeL, ftLastWriteTimeH) + " \t\tSize: " + sizeStr(nFileSizeLow); -+ //s = fileName + " Lastmod: " + ftLastWriteTimeL + "/" + zogswap(ftLastWriteTimeL) + " Size: " + nFileSizeLow + "/" + zogswap(nFileSizeLow); -+ if (i >= 0) { -+ remoteFilesList.add(i, fileName); -+ remoteFilesListInfo.add(i, s); -+ } else { -+ remoteFilesList.add(fileName); -+ remoteFilesListInfo.add(s); -+ } -+// end runge/x11vnc - } - - // a.add(fileName); -@@ -1282,14 +1618,32 @@ - - // sf@2004 - a.clear(); -- for (int i = 0; i < remoteDirsList.size(); i++) -+ b.clear(); -+ for (int i = 0; i < remoteDirsList.size(); i++) { - a.add(remoteDirsList.get(i)); -- for (int i = 0; i < remoteFilesList.size(); i++) -+ b.add(remoteDirsListInfo.get(i)); -+ } -+ for (int i = 0; i < remoteFilesList.size(); i++) { - a.add(remoteFilesList.get(i)); -+ -+ b.add(remoteFilesListInfo.get(i)); -+ } - remoteDirsList.clear(); -+ remoteDirsListInfo.clear(); - remoteFilesList.clear(); -+ remoteFilesListInfo.clear(); - -- viewer.ftp.printDirectory(a); -+// begin runge/x11vnc -+ // Hack for double listing at startup... probably libvncserver bug.. -+ readServerDriveListCnt++; -+ if (readServerDriveListCnt == 2) { -+ if (System.currentTimeMillis() - readServerDriveListTime < 2000) { -+//System.out.println("readServerDriveListCnt skip " + readServerDriveListCnt); -+ return; -+ } -+ } -+// end runge/x11vnc -+ viewer.ftp.printDirectory(a, b); - } - - //Internally used to signify the drive requested is not ready -@@ -1299,6 +1653,8 @@ - System.out.println("Remote Drive unavailable"); - viewer.ftp.connectionStatus.setText(" > WARNING - Remote Drive unavailable (possibly restricted access or media not present)"); - viewer.ftp.remoteStatus.setText("WARNING: Remote Drive unavailable"); -+ viewer.ftp.historyComboBox.insertItemAt(new String(" > WARNING: Remote Drive unavailable."), 0); -+ viewer.ftp.historyComboBox.setSelectedIndex(0); - } - - //Call this method to request the list of drives on the server. -@@ -1306,12 +1662,11 @@ - { - try - { -- viewer.rfb.writeRfbFileTransferMsg( -- RfbProto.rfbDirContentRequest, -- RfbProto.rfbRDrivesList, -- 0, -- 0, -- null); -+ viewer.rfb.writeRfbFileTransferMsg(RfbProto.rfbDirContentRequest, RfbProto.rfbRDrivesList, 0, 0, null); -+// begin runge/x11vnc -+ readServerDriveListCnt = 0; -+ readServerDriveListTime = System.currentTimeMillis(); -+// end runge/x11vnc - } - catch (IOException e) - { -@@ -1355,21 +1710,21 @@ - int h, - boolean incremental) - throws IOException { -- if (!viewer.ftp.isVisible()) { -- byte[] b = new byte[10]; -+ if (!viewer.ftp.isVisible()) { -+ byte[] b = new byte[10]; - -- b[0] = (byte) FramebufferUpdateRequest; -- b[1] = (byte) (incremental ? 1 : 0); -- b[2] = (byte) ((x >> 8) & 0xff); -- b[3] = (byte) (x & 0xff); -- b[4] = (byte) ((y >> 8) & 0xff); -- b[5] = (byte) (y & 0xff); -- b[6] = (byte) ((w >> 8) & 0xff); -- b[7] = (byte) (w & 0xff); -- b[8] = (byte) ((h >> 8) & 0xff); -- b[9] = (byte) (h & 0xff); -+ b[0] = (byte) FramebufferUpdateRequest; -+ b[1] = (byte) (incremental ? 1 : 0); -+ b[2] = (byte) ((x >> 8) & 0xff); -+ b[3] = (byte) (x & 0xff); -+ b[4] = (byte) ((y >> 8) & 0xff); -+ b[5] = (byte) (y & 0xff); -+ b[6] = (byte) ((w >> 8) & 0xff); -+ b[7] = (byte) (w & 0xff); -+ b[8] = (byte) ((h >> 8) & 0xff); -+ b[9] = (byte) (h & 0xff); - -- os.write(b); -+ os.write(b); - } - } - -@@ -1482,7 +1837,13 @@ - b[6] = (byte) ((text.length() >> 8) & 0xff); - b[7] = (byte) (text.length() & 0xff); - -- System.arraycopy(text.getBytes(), 0, b, 8, text.length()); -+ if (false && max_char(text) > 255) { -+ System.arraycopy(text.getBytes("UTF-8"), 0, b, 8, text.length()); -+ } else if (max_char(text) > 127) { -+ System.arraycopy(text.getBytes("ISO-8859-1"), 0, b, 8, text.length()); -+ } else { -+ System.arraycopy(text.getBytes(), 0, b, 8, text.length()); -+ } - - os.write(b); - // } -@@ -1506,6 +1867,37 @@ - final static int META_MASK = InputEvent.META_MASK; - final static int ALT_MASK = InputEvent.ALT_MASK; - -+ void writeWheelEvent(MouseWheelEvent evt) throws IOException { -+ eventBufLen = 0; -+ -+ int x = evt.getX(); -+ int y = evt.getY(); -+ -+ if (x < 0) x = 0; -+ if (y < 0) y = 0; -+ -+ int ptrmask; -+ -+ int clicks = evt.getWheelRotation(); -+ System.out.println("writeWheelEvent: clicks: " + clicks); -+ if (clicks > 0) { -+ ptrmask = 16; -+ } else if (clicks < 0) { -+ ptrmask = 8; -+ } else { -+ return; -+ } -+ -+ eventBuf[eventBufLen++] = (byte) PointerEvent; -+ eventBuf[eventBufLen++] = (byte) ptrmask; -+ eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (x & 0xff); -+ eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); -+ eventBuf[eventBufLen++] = (byte) (y & 0xff); -+ -+ os.write(eventBuf, 0, eventBufLen); -+ } -+ - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. -@@ -1610,6 +2002,21 @@ - - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - -+ if (viewer.debugKeyboard) { -+ System.out.println("----------------------------------------"); -+ System.out.println("evt.getKeyChar: " + evt.getKeyChar()); -+ System.out.println("getKeyText: " + KeyEvent.getKeyText(evt.getKeyCode())); -+ System.out.println("evt.getKeyCode: " + evt.getKeyCode()); -+ System.out.println("evt.getID: " + evt.getID()); -+ System.out.println("evt.getKeyLocation: " + evt.getKeyLocation()); -+ System.out.println("evt.isActionKey: " + evt.isActionKey()); -+ System.out.println("evt.isControlDown: " + evt.isControlDown()); -+ System.out.println("evt.getModifiers: " + evt.getModifiers()); -+ System.out.println("getKeyModifiersText: " + KeyEvent.getKeyModifiersText(evt.getModifiers())); -+ System.out.println("evt.paramString: " + evt.paramString()); -+ } -+ -+ - int key; - if (evt.isActionKey()) { - -@@ -1685,6 +2092,9 @@ - default : - return; - } -+ if (key == 0xffc2 && viewer.mapF5_to_atsign) { -+ key = 0x40; -+ } - - } else { - -@@ -1794,6 +2204,16 @@ - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { -+ if(viewer.forbid_Ctrl_Alt) { -+ if ((newModifiers & CTRL_MASK) != 0 && (newModifiers & ALT_MASK) != 0) { -+ int orig = newModifiers; -+ newModifiers &= ~ALT_MASK; -+ newModifiers &= ~CTRL_MASK; -+ if (viewer.debugKeyboard) { -+ System.out.println("Ctrl+Alt modifiers: " + orig + " -> " + newModifiers); -+ } -+ } -+ } - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - -diff -Naur JavaViewer.orig/SSLSocketToMe.java JavaViewer/SSLSocketToMe.java ---- JavaViewer.orig/SSLSocketToMe.java 1969-12-31 19:00:00.000000000 -0500 -+++ JavaViewer/SSLSocketToMe.java 2010-07-10 19:18:06.000000000 -0400 -@@ -0,0 +1,2067 @@ -+/* -+ * 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, 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. -+ * -+ */ -+ -+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; -+ boolean debug_certs = false; -+ -+ /* sockets */ -+ SSLSocket socket = null; -+ SSLSocketFactory factory; -+ -+ /* fallback for Proxy connection */ -+ boolean proxy_in_use = false; -+ boolean proxy_failure = false; -+ public DataInputStream is = null; -+ public OutputStream os = null; -+ -+ /* strings from user WRT proxy: */ -+ String proxy_auth_string = 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 trustsrv_ctx; -+ SSLContext trusturl_ctx; -+ SSLContext trustone_ctx; -+ -+ /* corresponding trust managers */ -+ TrustManager[] trustAllCerts; -+ TrustManager[] trustSrvCert; -+ TrustManager[] trustUrlCert; -+ TrustManager[] trustOneCert; -+ -+ /* client-side SSL auth key (oneTimeKey=...) */ -+ KeyManager[] mykey = null; -+ -+ boolean user_wants_to_see_cert = true; -+ String cert_fail = null; -+ -+ /* cert(s) we retrieve from Web server, VNC server, or serverCert param: */ -+ java.security.cert.Certificate[] trustallCerts = null; -+ java.security.cert.Certificate[] trustsrvCerts = null; -+ java.security.cert.Certificate[] trusturlCerts = null; -+ -+ /* utility to decode hex oneTimeKey=... and serverCert=... */ -+ 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; -+ -+ debug_certs = v.debugCerts; -+ -+ /* we will first try default factory for certification: */ -+ -+ factory = (SSLSocketFactory) SSLSocketFactory.getDefault(); -+ -+ dbg("SSL startup: " + host + " " + port); -+ -+ -+ /* create trust managers to be used if initial handshake fails: */ -+ -+ trustAllCerts = new TrustManager[] { -+ /* -+ * this one accepts everything. Only used if user -+ * has disabled checking (trustAllVncCerts=yes) -+ * or when we grab the cert to show it to them in -+ * a dialog and ask them to manually verify/accept it. -+ */ -+ 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 and stored in -+ * trusturlCerts. -+ */ -+ 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 (URL)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trusturlCerts' */ -+ if (trusturlCerts == null) { -+ throw new CertificateException( -+ "No Trust url Certs array."); -+ } -+ if (trusturlCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust url Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trusturlCerts.length) { -+ throw new CertificateException( -+ "certs.length != trusturlCerts.length " + certs.length + " " + trusturlCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trusturlCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("URL: cert mismatch at i=" + i); -+ dbg("URL: cert mismatch cert" + certs[i]); -+ dbg("URL: cert mismatch url" + trusturlCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("URL: cert info at i=" + i); -+ dbg("URL: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("URL: cert info url" + trusturlCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != URL Cert Chain."); -+ } -+ dbg("URL: trusturlCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustSrvCert = new TrustManager[] { -+ /* -+ * this one accepts cert given to us in the serverCert -+ * Applet Parameter we were started with. It is -+ * currently a fatal error if the VNC Server's cert -+ * doesn't match it. -+ */ -+ 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 (SRV)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustsrvCerts' */ -+ if (trustsrvCerts == null) { -+ throw new CertificateException( -+ "No Trust srv Certs array."); -+ } -+ if (trustsrvCerts.length < 1) { -+ throw new CertificateException( -+ "No Trust srv Certs."); -+ } -+ if (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustsrvCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustsrvCerts.length " + certs.length + " " + trustsrvCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustsrvCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("SRV: cert mismatch at i=" + i); -+ dbg("SRV: cert mismatch cert" + certs[i]); -+ dbg("SRV: cert mismatch srv" + trustsrvCerts[i]); -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("SRV: cert info at i=" + i); -+ dbg("SRV: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("SRV: cert info srv" + trustsrvCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != serverCert Applet Parameter Cert Chain."); -+ } -+ dbg("SRV: trustsrvCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ trustOneCert = new TrustManager[] { -+ /* -+ * this one accepts only the retrieved server -+ * cert by SSLSocket by this applet we stored in -+ * trustallCerts that user has accepted or applet -+ * parameter trustAllVncCerts=yes is set. This is -+ * for when we reconnect after the user has manually -+ * accepted the trustall cert in the dialog (or set -+ * trustAllVncCerts=yes applet param.) -+ */ -+ 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 (ONE)"); -+ } -+ public void checkServerTrusted( -+ java.security.cert.X509Certificate[] certs, -+ String authType) throws CertificateException { -+ /* we want to check 'certs' against 'trustallCerts' */ -+ 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 (certs == null) { -+ throw new CertificateException( -+ "No this-certs array."); -+ } -+ if (certs.length < 1) { -+ throw new CertificateException( -+ "No this-certs Certs."); -+ } -+ if (certs.length != trustallCerts.length) { -+ throw new CertificateException( -+ "certs.length != trustallCerts.length " + certs.length + " " + trustallCerts.length); -+ } -+ boolean ok = true; -+ for (int i = 0; i < certs.length; i++) { -+ if (! trustallCerts[i].equals(certs[i])) { -+ ok = false; -+ dbg("ONE: cert mismatch at i=" + i); -+ dbg("ONE: cert mismatch cert" + certs[i]); -+ dbg("ONE: cert mismatch all" + trustallCerts[i]); -+ } -+ if (debug_certs) { -+ dbg("\n***********************************************"); -+ dbg("ONE: cert info at i=" + i); -+ dbg("ONE: cert info cert" + certs[i]); -+ dbg("==============================================="); -+ dbg("ONE: cert info all" + trustallCerts[i]); -+ dbg("***********************************************"); -+ } -+ } -+ if (!ok) { -+ throw new CertificateException( -+ "Server Cert Chain != TRUSTALL Cert Chain."); -+ } -+ dbg("ONE: trustallCerts[i] matches certs[i] i=0:" + (certs.length-1)); -+ } -+ } -+ }; -+ -+ /* -+ * The above TrustManagers are used: -+ * -+ * 1) to retrieve the server cert in case of failure to -+ * display it to the user in a dialog. -+ * 2) to subsequently connect to the server if user agrees. -+ */ -+ -+ /* -+ * build oneTimeKey cert+key if supplied in applet parameter: -+ */ -+ 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); -+ if (debug_certs) { -+ dbg("one time key " + ff); -+ } -+ -+ 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)); -+ if (debug_certs) { -+ dbg("one time cert" + 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(); -+ } -+ -+ /* -+ * build serverCert cert if supplied in applet parameter: -+ */ -+ if (viewer.serverCert != null) { -+ CertificateFactory cf = CertificateFactory.getInstance("X.509"); -+ byte[] cert = hex2bytes(viewer.serverCert); -+ Collection c = cf.generateCertificates(new ByteArrayInputStream(cert)); -+ trustsrvCerts = new Certificate[c.toArray().length]; -+ if (c.size() == 1) { -+ Certificate tmpcert = cf.generateCertificate(new ByteArrayInputStream(cert)); -+ trustsrvCerts[0] = tmpcert; -+ } else { -+ trustsrvCerts = (Certificate[]) c.toArray(); -+ } -+ } -+ -+ /* the trust loc certs context: */ -+ try { -+ trustloc_ctx = SSLContext.getInstance("SSL"); -+ -+ /* -+ * below is a failed attempt to get jvm's default -+ * trust manager using null (below) makes it so -+ * for HttpsURLConnection the server cannot be -+ * verified (no prompting.) -+ */ -+ if (false) { -+ boolean didit = false; -+ TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); -+ tmf.init((KeyStore) null); -+ TrustManager [] tml = tmf.getTrustManagers(); -+ for (int i = 0; i < tml.length; i++) { -+ TrustManager tm = tml[i]; -+ if (tm instanceof X509TrustManager) { -+ TrustManager tm1[] = new TrustManager[1]; -+ tm1[0] = tm; -+ trustloc_ctx.init(mykey, tm1, null); -+ didit = true; -+ break; -+ } -+ } -+ if (!didit) { -+ trustloc_ctx.init(mykey, null, null); -+ } -+ } else { -+ /* we have to set trust manager to null */ -+ trustloc_ctx.init(mykey, null, null); -+ } -+ -+ } catch (Exception e) { -+ String msg = "SSL trustloc_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust all certs context: */ -+ 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); -+ } -+ -+ /* the trust url certs context: */ -+ 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); -+ } -+ -+ /* the trust srv certs context: */ -+ try { -+ trustsrv_ctx = SSLContext.getInstance("SSL"); -+ trustsrv_ctx.init(mykey, trustSrvCert, new -+ java.security.SecureRandom()); -+ -+ } catch (Exception e) { -+ String msg = "SSL trustsrv_ctx FAILED."; -+ dbg(msg); -+ throw new Exception(msg); -+ } -+ -+ /* the trust the one cert from server context: */ -+ 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); -+ } -+ } -+ -+ /* -+ * we call this early on to 1) check for a proxy, 2) grab -+ * Browser/JVM accepted HTTPS cert. -+ */ -+ public void check_for_proxy_and_grab_vnc_server_cert() { -+ -+ trusturlCerts = null; -+ proxy_in_use = false; -+ -+ if (viewer.ignoreProxy) { -+ /* applet param says skip it. */ -+ /* the downside is we do not set trusturlCerts for comparison later... */ -+ /* nor do we autodetect x11vnc for GET=1. */ -+ return; -+ } -+ -+ dbg("------------------------------------------------"); -+ dbg("Into check_for_proxy_and_grab_vnc_server_cert():"); -+ -+ dbg("TRYING HTTPS:"); -+ String ustr = "https://" + host + ":"; -+ if (viewer.httpsPort != null) { -+ ustr += viewer.httpsPort; -+ } else { -+ ustr += port; -+ } -+ ustr += viewer.urlPrefix + "/check.https.proxy.connection"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTPS URL connection to host:port */ -+ URL url = new URL(ustr); -+ HttpsURLConnection https = (HttpsURLConnection) url.openConnection(); -+ -+ if (mykey != null) { -+ /* with oneTimeKey (mykey) we can't use the default SSL context */ -+ if (trustsrvCerts != null) { -+ dbg("passing trustsrv_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustsrv_ctx.getSocketFactory()); -+ } else if (trustloc_ctx != null) { -+ dbg("passing trustloc_ctx to HttpsURLConnection to provide client cert."); -+ https.setSSLSocketFactory(trustloc_ctx.getSocketFactory()); -+ } -+ } -+ -+ https.setUseCaches(false); -+ https.setRequestMethod("GET"); -+ https.setRequestProperty("Pragma", "No-Cache"); -+ https.setRequestProperty("Proxy-Connection", "Keep-Alive"); -+ https.setDoInput(true); -+ -+ dbg("trying https.connect()"); -+ https.connect(); -+ -+ dbg("trying https.getServerCertificates()"); -+ 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; -+ dbg("An HTTPS proxy is in use. There may be connection problems."); -+ } -+ -+ dbg("trying https.getContent()"); -+ Object output = https.getContent(); -+ dbg("trying https.disconnect()"); -+ https.disconnect(); -+ if (! viewer.GET) { -+ String header = https.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (1), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ -+ } catch(Exception e) { -+ dbg("HttpsURLConnection: " + e.getMessage()); -+ } -+ -+ if (proxy_in_use) { -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } else if (trusturlCerts != null && !viewer.forceProxy) { -+ /* Allow user to require HTTP check? use forceProxy for now. */ -+ dbg("SKIPPING HTTP PROXY CHECK: got trusturlCerts, assuming proxy info is correct."); -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ return; -+ } -+ -+ /* -+ * XXX need to remember scenario where this extra check -+ * gives useful info. User's Browser proxy settings? -+ */ -+ dbg("TRYING HTTP:"); -+ ustr = "http://" + host + ":" + port; -+ ustr += viewer.urlPrefix + "/index.vnc"; -+ dbg("ustr is: " + ustr); -+ -+ try { -+ /* prepare for an HTTP URL connection to the same host:port (but not httpsPort) */ -+ 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); -+ -+ dbg("trying http.connect()"); -+ http.connect(); -+ -+ if (http.usingProxy()) { -+ proxy_in_use = true; -+ dbg("An HTTP proxy is in use. There may be connection problems."); -+ } -+ dbg("trying http.getContent()"); -+ Object output = http.getContent(); -+ dbg("trying http.disconnect()"); -+ http.disconnect(); -+ if (! viewer.GET) { -+ String header = http.getHeaderField("VNC-Server"); -+ if (header != null && header.startsWith("x11vnc")) { -+ dbg("detected x11vnc server (2), setting GET=1"); -+ viewer.GET = true; -+ } -+ } -+ } catch(Exception e) { -+ dbg("HttpURLConnection: " + e.getMessage()); -+ } -+ dbg("exit check_for_proxy_and_grab_vnc_server_cert():"); -+ dbg("------------------------------------------------"); -+ } -+ -+ public Socket connectSock() throws IOException { -+ /* -+ * first try a https connection to detect a proxy, and -+ * grab the VNC server cert at the same time: -+ */ -+ check_for_proxy_and_grab_vnc_server_cert(); -+ -+ boolean srv_cert = false; -+ -+ if (trustsrvCerts != null) { -+ /* applet parameter suppled serverCert */ -+ dbg("viewer.trustSrvCert-0 using trustsrv_ctx"); -+ factory = trustsrv_ctx.getSocketFactory(); -+ srv_cert = true; -+ } else if (viewer.trustAllVncCerts) { -+ /* trust all certs (no checking) */ -+ dbg("viewer.trustAllVncCerts-0 using trustall_ctx"); -+ factory = trustall_ctx.getSocketFactory(); -+ } else if (trusturlCerts != null) { -+ /* trust certs the Browser/JVM accepted in check_for_proxy... */ -+ dbg("using trusturl_ctx"); -+ factory = trusturl_ctx.getSocketFactory(); -+ } else { -+ /* trust the local defaults */ -+ 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"); -+ } -+ -+ int timeout = 6; -+ if (timeout > 0) { -+ socket = (SSLSocket) factory.createSocket(); -+ InetSocketAddress inetaddr = new InetSocketAddress(host, port); -+ dbg("Using timeout of " + timeout + " secs to: " + host + ":" + port); -+ socket.connect(inetaddr, timeout * 1000); -+ } else { -+ socket = (SSLSocket) factory.createSocket(host, port); -+ } -+ -+ } catch (Exception esock) { -+ dbg("socket error: " + 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("proxy_socket error: " + e.getMessage()); -+ } -+ } else { -+ /* n.b. socket is left in error state to cause ex. below. */ -+ } -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The Server Connection Verified OK on 1st try."); -+ -+ java.security.cert.Certificate[] currentTrustedCerts; -+ BrowserCertsDialog bcd; -+ -+ SSLSession sess = socket.getSession(); -+ currentTrustedCerts = sess.getPeerCertificates(); -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-1 keeping socket."); -+ } else if (currentTrustedCerts == null || currentTrustedCerts.length < 1) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy."); -+ } -+ 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 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.serverCert != null && trustsrvCerts != null) { -+ dbg("viewer.serverCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else if (viewer.trustUrlVncCert) { -+ dbg("viewer.trustUrlVncCert-1 skipping browser certs dialog"); -+ user_wants_to_see_cert = false; -+ } else { -+ /* have a dialog with the user: */ -+ bcd = new BrowserCertsDialog(serv, host + ":" + port); -+ dbg("browser certs dialog begin."); -+ bcd.queryUser(); -+ dbg("browser certs dialog finished."); -+ -+ if (bcd.showCertDialog) { -+ String msg = "user wants to see cert"; -+ dbg(msg); -+ user_wants_to_see_cert = true; -+ if (cert_fail == null) { -+ cert_fail = "user-view"; -+ } -+ throw new SSLHandshakeException(msg); -+ } else { -+ user_wants_to_see_cert = false; -+ dbg("browser certs dialog: user said yes, accept it"); -+ } -+ } -+ -+ } catch (SSLHandshakeException eh) { -+ dbg("SSLHandshakeException: could not automatically verify Server."); -+ dbg("msg: " + eh.getMessage()); -+ -+ -+ /* send a cleanup string just in case: */ -+ String getoutstr = "GET /index.vnc HTTP/1.0\r\nConnection: close\r\n\r\n"; -+ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!"); -+ } -+ -+ /* reload */ -+ -+ socket = null; -+ -+ String reason = null; -+ -+ if (srv_cert) { -+ /* for serverCert usage we make this a fatal error. */ -+ throw new IOException("Fatal: VNC Server's Cert does not match Applet Parameter 'serverCert=...'"); -+ /* see below in TrustDialog were we describe this case to user anyway */ -+ } -+ -+ /* -+ * Reconnect, trusting any cert, so we can grab -+ * the cert to show it to the user in a dialog -+ * for him to manually accept. This 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); -+ } -+ -+ if (debug_certs) { -+ dbg("trusturlCerts: " + trusturlCerts); -+ dbg("trustsrvCerts: " + trustsrvCerts); -+ } -+ if (trusturlCerts == null && cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ -+ try { -+ socket.startHandshake(); -+ -+ dbg("The TrustAll Server Cert-grab Connection (trivially) Verified OK."); -+ -+ /* grab the cert: */ -+ try { -+ SSLSession sess = socket.getSession(); -+ trustallCerts = sess.getPeerCertificates(); -+ } catch (Exception e) { -+ throw new Exception("Could not get " + -+ "Peer Certificate"); -+ } -+ if (debug_certs) { -+ dbg("trustallCerts: " + trustallCerts); -+ } -+ -+ if (viewer.trustAllVncCerts) { -+ dbg("viewer.trustAllVncCerts-3. skipping dialog, trusting everything."); -+ } else if (! browser_cert_match()) { -+ /* -+ * close socket now, we will reopen after -+ * dialog if user agrees to use the cert. -+ */ -+ try { -+ OutputStream os = socket.getOutputStream(); -+ os.write(getoutstr.getBytes()); -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!"); -+ } -+ socket = null; -+ -+ /* dialog with user to accept cert or not: */ -+ -+ TrustDialog td= new TrustDialog(host, port, -+ trustallCerts); -+ -+ if (cert_fail == null) { -+ ; -+ } else if (cert_fail.equals("user-view")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " You Asked to View the Certificate."; -+ } else if (cert_fail.equals("server-cert-mismatch")) { -+ /* this is now fatal error, see above. */ -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Certificate\n" -+ + " specified in the supplied 'serverCert' Applet Parameter."; -+ } else if (cert_fail.equals("cert-mismatch")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " The VNC Server's Certificate does not match the Website's\n" -+ + " HTTPS Certificate (that you previously accepted; either\n" -+ + " manually or automatically via Certificate Authority.)"; -+ } else if (cert_fail.equals("missing-certs")) { -+ reason = "Reason for this Dialog:\n\n" -+ + " Not all Certificates could be obtained to check."; -+ } -+ -+ if (! td.queryUser(reason)) { -+ 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()); -+ } -+ -+ /* reload again: */ -+ -+ if (socket != null) { -+ try { -+ socket.close(); -+ } catch (Exception e) { -+ dbg("socket is grumpy!!!"); -+ } -+ socket = null; -+ } -+ -+ /* -+ * Now connect a 3rd time, using the cert -+ * retrieved during connection 2 (sadly, 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/TrustOne Server Connection Verified #3."); -+ -+ } catch (Exception ehand3) { -+ dbg("** Could not TrustAll/TrustOne Verify Server #3."); -+ -+ throw new IOException(ehand3.getMessage()); -+ } -+ } -+ -+ /* we have socket (possibly null) at this point, so proceed: */ -+ -+ /* handle x11vnc GET=1, if applicable: */ -+ 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: " + str); -+ OutputStream os = socket.getOutputStream(); -+ String type = "os"; -+ -+ if (type == "os") { -+ os.write(str.getBytes()); -+ os.flush(); -+ System.out.println("used OutputStream"); -+ } else if (type == "bs") { -+ BufferedOutputStream bs = new BufferedOutputStream(os); -+ bs.write(str.getBytes()); -+ bs.flush(); -+ System.out.println("used BufferedOutputStream"); -+ } else if (type == "ds") { -+ DataOutputStream ds = new DataOutputStream(os); -+ ds.write(str.getBytes()); -+ ds.flush(); -+ System.out.println("used DataOutputStream"); -+ } -+ 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."); -+ dbg(""); -+ -+ /* could be null, let caller handle that. */ -+ return (Socket) socket; -+ } -+ -+ boolean browser_cert_match() { -+ String msg = "Browser URL accept previously accepted cert"; -+ -+ if (user_wants_to_see_cert) { -+ return false; -+ } -+ -+ if (viewer.serverCert != null || trustsrvCerts != null) { -+ if (cert_fail == null) { -+ cert_fail = "server-cert-mismatch"; -+ } -+ } -+ if (trustallCerts != null && trusturlCerts != null) { -+ if (trustallCerts.length == trusturlCerts.length) { -+ boolean ok = true; -+ /* check toath trustallCerts (socket) equals trusturlCerts (browser) */ -+ for (int i = 0; i < trusturlCerts.length; i++) { -+ if (! trustallCerts[i].equals(trusturlCerts[i])) { -+ dbg("BCM: cert mismatch at i=" + i); -+ dbg("BCM: cert mismatch url" + trusturlCerts[i]); -+ dbg("BCM: cert mismatch all" + trustallCerts[i]); -+ ok = false; -+ } -+ } -+ if (ok) { -+ System.out.println(msg); -+ if (cert_fail == null) { -+ cert_fail = "did-not-fail"; -+ } -+ return true; -+ } else { -+ if (cert_fail == null) { -+ cert_fail = "cert-mismatch"; -+ } -+ return false; -+ } -+ } -+ } -+ if (cert_fail == null) { -+ cert_fail = "missing-certs"; -+ } -+ return false; -+ } -+ -+ 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; -+ } -+ -+ /* this will do the proxy CONNECT negotiation and hook us up. */ -+ -+ private void proxy_helper(String proxyHost, int proxyPort) { -+ -+ boolean proxy_auth = false; -+ String proxy_auth_basic_realm = ""; -+ String hp = host + ":" + port; -+ dbg("proxy_helper: " + proxyHost + ":" + proxyPort + " hp: " + hp); -+ -+ /* we loop here a few times trying for the password case */ -+ for (int k=0; k < 2; k++) { -+ dbg("proxy_in_use psocket: " + k); -+ -+ if (proxySock != null) { -+ try { -+ proxySock.close(); -+ } catch (Exception e) { -+ dbg("proxy socket is grumpy."); -+ } -+ } -+ -+ proxySock = psocket(proxyHost, proxyPort); -+ if (proxySock == null) { -+ dbg("1-a sadly, returning a null socket"); -+ return; -+ } -+ -+ String req1 = "CONNECT " + hp + " HTTP/1.1\r\n" -+ + "Host: " + hp + "\r\n"; -+ -+ dbg("requesting via proxy: " + req1); -+ -+ if (proxy_auth) { -+ if (proxy_auth_string == null) { -+ ProxyPasswdDialog pp = new ProxyPasswdDialog(proxyHost, proxyPort, proxy_auth_basic_realm); -+ pp.queryUser(); -+ proxy_auth_string = pp.getAuth(); -+ } -+ //dbg("auth1: " + proxy_auth_string); -+ -+ String auth2 = Base64Coder.encodeString(proxy_auth_string); -+ //dbg("auth2: " + auth2); -+ -+ req1 += "Proxy-Authorization: Basic " + auth2 + "\r\n"; -+ //dbg("req1: " + req1); -+ -+ dbg("added Proxy-Authorization: Basic ... to request"); -+ } -+ req1 += "\r\n"; -+ -+ try { -+ proxy_os.write(req1.getBytes()); -+ String reply = readline(proxy_is); -+ -+ dbg("proxy replied: " + reply.trim()); -+ -+ if (reply.indexOf("HTTP/1.") == 0 && reply.indexOf(" 407 ") > 0) { -+ proxy_auth = true; -+ proxySock.close(); -+ } else 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; -+ } -+ } -+ } catch(Exception e) { -+ dbg("some proxy socket problem: " + e.getMessage()); -+ } -+ -+ /* read the rest of the HTTP headers */ -+ while (true) { -+ String line = readline(proxy_is); -+ dbg("proxy line: " + line.trim()); -+ if (proxy_auth) { -+ String uc = line.toLowerCase(); -+ if (uc.indexOf("proxy-authenticate:") == 0) { -+ if (uc.indexOf(" basic ") >= 0) { -+ int idx = uc.indexOf(" realm"); -+ if (idx >= 0) { -+ proxy_auth_basic_realm = uc.substring(idx+1); -+ } -+ } -+ } -+ } -+ if (line.equals("\r\n") || line.equals("\n")) { -+ break; -+ } -+ } -+ if (!proxy_auth || proxy_auth_basic_realm.equals("")) { -+ /* we only try once for the non-password case: */ -+ break; -+ } -+ } -+ } -+ -+ 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) { -+ /* sandboxed applet might not be able to read it. */ -+ dbg("props failed: " + e.getMessage()); -+ } -+ if (viewer.proxyHost != null) { -+ dbg("Using supplied proxy " + viewer.proxyHost + " " + viewer.proxyPort + " applet parameters."); -+ proxyHost = viewer.proxyHost; -+ if (viewer.proxyPort != null) { -+ proxyPort = gint(viewer.proxyPort); -+ } else { -+ proxyPort = 8080; -+ } -+ -+ } else if (props != null) { -+ dbg("\n---------------\nAll props:"); -+ props.list(System.out); -+ dbg("\n---------------\n\n"); -+ -+ /* scrape throught properties looking for proxy info: */ -+ -+ 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.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; -+ } -+ } -+ -+ 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 (proxyHost != null && proxyPort > 0) { -+ break; -+ } -+ -+ // look for something like: javaplugin.proxy.config.list = http=10.0.2.1:8082 -+ if (s2.indexOf("proxy") < 0 && v2.indexOf("proxy") < 0) { -+ continue; -+ } -+ if (v2.indexOf("http") < 0) { -+ 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) { -+ j = p.indexOf("http"); -+ 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()); -+ } -+ -+ proxy_helper(proxyHost, proxyPort); -+ if (proxySock == null) { -+ return null; -+ } -+ } 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("proxy socket problem-2: " + 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(String reason) { -+ -+ /* 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"); -+ } -+ if (reason != null) { -+ reason += "\n\n"; -+ } -+ -+ text = "\n" -++ "Unable to verify the identity of\n" -++ "\n" -++ " " + host + ":" + port + "\n" -++ "\n" -++ infostr -++ "\n" -++ "as a trusted VNC server.\n" -++ "\n" -++ reason -++ "In General not being able to verify the VNC Server and/or your seeing this Dialog\n" -++ "is due to one of the following:\n" -++ "\n" -++ " - Your requesting to View the Certificate before accepting.\n" -++ "\n" -++ " - The VNC server is using a Self-Signed Certificate or a Certificate\n" -++ " Authority not recognized by your Web Browser or Java Plugin runtime.\n" -++ "\n" -++ " - The use of an Apache SSL portal scheme employing CONNECT proxying AND\n" -++ " the Apache Web server has a certificate *different* from the VNC server's.\n" -++ "\n" -++ " - No previously accepted Certificate (via Web Broswer/Java Plugin) could be\n" -++ " obtained by this applet to compare the VNC Server Certificate against.\n" -++ "\n" -++ " - The VNC Server's Certificate does not match the one specified in the\n" -++ " supplied 'serverCert' Java Applet Parameter.\n" -++ "\n" -++ " - A Man-In-The-Middle attack impersonating as the VNC server that 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 Certificate\n" -++ "Authority certificate) you can configure your Web Browser and Java Plugin to\n" -++ "automatically authenticate this VNC Server.\n" -++ "\n" -++ "If you do so, then you will only have to click \"Yes\" when this VNC Viewer\n" -++ "applet asks you whether to trust your Browser/Java Plugin's acceptance of the\n" -++ "certificate (except for the Apache portal case above where they don't match.)\n" -++ "\n" -++ "You can also set the applet parameter 'trustUrlVncCert=yes' to automatically\n" -++ "accept certificates already accepted/trusted by your Web Browser/Java Plugin,\n" -++ "and thereby see no dialog from this VNC Viewer applet.\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, 38, 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(); -+ dialog.hide(); -+ -+ } else if (evt.getSource() == cancel) { -+ /* Cancel button clicked */ -+ trust_this_session = false; -+ -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+ -+ 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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+class ProxyPasswdDialog implements ActionListener { -+ String guessedHost = null; -+ String guessedPort = null; -+ String guessedUser = null; -+ String guessedPasswd = null; -+ String realm = 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 entry1; -+ TextField entry2; -+ String reply1 = ""; -+ String reply2 = ""; -+ -+ ProxyPasswdDialog (String h, int p, String realm) { -+ guessedHost = h; -+ try { -+ guessedPort = Integer.toString(p); -+ } catch (Exception e) { -+ guessedPort = "8080"; -+ } -+ this.realm = realm; -+ } -+ -+ public void queryUser() { -+ -+ /* create and display the dialog for unverified cert. */ -+ -+ Frame frame = new Frame("Proxy Requires Username and Password"); -+ -+ dialog = new Dialog(frame, true); -+ -+ //Label label = new Label("Please Enter your Web Proxy Username in the top Entry and Password in the bottom Entry", Label.CENTER); -+ TextArea label = new TextArea("Please Enter your Web Proxy\nUsername in the Top Entry and\nPassword in the Bottom Entry,\nand then press OK.", 4, 20, TextArea.SCROLLBARS_NONE); -+ entry1 = new TextField(30); -+ entry2 = new TextField(30); -+ entry2.setEchoChar('*'); -+ ok = new Button("OK"); -+ ok.addActionListener(this); -+ -+ dialog.setLayout(new BorderLayout()); -+ dialog.add("North", label); -+ dialog.add("Center", entry1); -+ dialog.add("South", entry2); -+ dialog.add("East", ok); -+ dialog.pack(); -+ dialog.resize(dialog.preferredSize()); -+ -+ dialog.show(); /* block here til OK or Cancel pressed. */ -+ return; -+ } -+ -+ public String getAuth() { -+ return reply1 + ":" + reply2; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == ok) { -+ reply1 = entry1.getText(); -+ reply2 = entry2.getText(); -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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(); -+ dialog.hide(); -+ } -+ } -+} -+ -+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 and/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 += "If the VNC Server connection times out while you are reading this\n"; -+m += "dialog, then restart the connection and try again.\n"; -+m += "\n"; -+m += "Should this VNC Viewer applet now connect to the above VNC server?\n"; -+m += "\n"; -+ -+ TextArea textarea = new TextArea(m, 22, 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. */ -+ System.out.println("done show()"); -+ return; -+ } -+ -+ public synchronized void actionPerformed(ActionEvent evt) { -+ System.out.println(evt.getActionCommand()); -+ if (evt.getSource() == yes) { -+ showCertDialog = false; -+ //dialog.dispose(); -+ dialog.hide(); -+ } else if (evt.getSource() == no) { -+ showCertDialog = true; -+ //dialog.dispose(); -+ dialog.hide(); -+ } -+ System.out.println("done actionPerformed()"); -+ } -+} -+ -+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 ""; -+ } -+ } -+} -+ -+class Base64Coder { -+ -+ // Mapping table from 6-bit nibbles to Base64 characters. -+ private static char[] map1 = new char[64]; -+ static { -+ int i=0; -+ for (char c='A'; c<='Z'; c++) map1[i++] = c; -+ for (char c='a'; c<='z'; c++) map1[i++] = c; -+ for (char c='0'; c<='9'; c++) map1[i++] = c; -+ map1[i++] = '+'; map1[i++] = '/'; } -+ -+ // Mapping table from Base64 characters to 6-bit nibbles. -+ private static byte[] map2 = new byte[128]; -+ static { -+ for (int i=0; iin. -+ * @return A character array with the Base64 encoded data. -+ */ -+ public static char[] encode (byte[] in, int iLen) { -+ int oDataLen = (iLen*4+2)/3; // output length without padding -+ int oLen = ((iLen+2)/3)*4; // output length including padding -+ char[] out = new char[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++] & 0xff; -+ int i1 = ip < iLen ? in[ip++] & 0xff : 0; -+ int i2 = ip < iLen ? in[ip++] & 0xff : 0; -+ int o0 = i0 >>> 2; -+ int o1 = ((i0 & 3) << 4) | (i1 >>> 4); -+ int o2 = ((i1 & 0xf) << 2) | (i2 >>> 6); -+ int o3 = i2 & 0x3F; -+ out[op++] = map1[o0]; -+ out[op++] = map1[o1]; -+ out[op] = op < oDataLen ? map1[o2] : '='; op++; -+ out[op] = op < oDataLen ? map1[o3] : '='; op++; } -+ return out; } -+ -+ /** -+ * Decodes a string from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return A String containing the decoded data. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static String decodeString (String s) { -+ return new String(decode(s)); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * @param s a Base64 String to be decoded. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (String s) { -+ return decode(s.toCharArray()); } -+ -+ /** -+ * Decodes a byte array from Base64 format. -+ * No blanks or line breaks are allowed within the Base64 encoded data. -+ * @param in a character array containing the Base64 encoded data. -+ * @return An array containing the decoded data bytes. -+ * @throws IllegalArgumentException if the input is not valid Base64 encoded data. -+ */ -+ public static byte[] decode (char[] in) { -+ int iLen = in.length; -+ if (iLen%4 != 0) throw new IllegalArgumentException ("Length of Base64 encoded input string is not a multiple of 4."); -+ while (iLen > 0 && in[iLen-1] == '=') iLen--; -+ int oLen = (iLen*3) / 4; -+ byte[] out = new byte[oLen]; -+ int ip = 0; -+ int op = 0; -+ while (ip < iLen) { -+ int i0 = in[ip++]; -+ int i1 = in[ip++]; -+ int i2 = ip < iLen ? in[ip++] : 'A'; -+ int i3 = ip < iLen ? in[ip++] : 'A'; -+ if (i0 > 127 || i1 > 127 || i2 > 127 || i3 > 127) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int b0 = map2[i0]; -+ int b1 = map2[i1]; -+ int b2 = map2[i2]; -+ int b3 = map2[i3]; -+ if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) -+ throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); -+ int o0 = ( b0 <<2) | (b1>>>4); -+ int o1 = ((b1 & 0xf)<<4) | (b2>>>2); -+ int o2 = ((b2 & 3)<<6) | b3; -+ out[op++] = (byte)o0; -+ if (op 0) - { - viewer.options.oldEightBitColors = viewer.options.eightBitColors; -@@ -237,6 +265,9 @@ - } - else - { -+// begin runge/x11vnc -+ viewer.options.oldEightBitColors = viewer.options.eightBitColors; -+// end runge/x11vnc - rfb.writeSetPixelFormat( - 32, - 24, -@@ -376,12 +407,14 @@ - // Start/stop session recording if necessary. - viewer.checkRecordingStatus(); - -- rfb.writeFramebufferUpdateRequest( -- 0, -- 0, -- rfb.framebufferWidth, -- rfb.framebufferHeight, -- false); -+ if (!viewer.graftFtp) { -+ rfb.writeFramebufferUpdateRequest( -+ 0, -+ 0, -+ rfb.framebufferWidth, -+ rfb.framebufferHeight, -+ false); -+ } - - // - // main dispatch loop -@@ -390,6 +423,9 @@ - while (true) { - // Read message type from the server. - int msgType = rfb.readServerMessageType(); -+ if (viewer.ftpOnly && msgType != RfbProto.rfbFileTransfer) { -+ System.out.println("msgType:" + msgType); -+ } - - // Process the message depending on its type. - switch (msgType) { -@@ -1332,6 +1368,9 @@ - public void mouseDragged(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } -+ public void mouseWheelMoved(MouseWheelEvent evt) { -+ processLocalMouseWheelEvent(evt); -+ } - - public void processLocalKeyEvent(KeyEvent evt) { - if (viewer.rfb != null && rfb.inNormalProtocol) { -@@ -1367,6 +1406,19 @@ - evt.consume(); - } - -+ public void processLocalMouseWheelEvent(MouseWheelEvent evt) { -+ if (viewer.rfb != null && rfb.inNormalProtocol) { -+ synchronized(rfb) { -+ try { -+ rfb.writeWheelEvent(evt); -+ } catch (Exception e) { -+ e.printStackTrace(); -+ } -+ rfb.notify(); -+ } -+ } -+ } -+ - public void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (moved) { -@@ -1532,9 +1584,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 +1622,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 2010-03-27 18:00:28.000000000 -0400 -@@ -41,6 +41,7 @@ - import java.io.*; - import java.net.*; - import javax.swing.*; -+import java.util.Date; - - public class VncViewer extends java.applet.Applet - implements java.lang.Runnable, WindowListener { -@@ -80,11 +81,11 @@ - GridBagLayout gridbag; - ButtonPanel buttonPanel; - AuthPanel authenticator; -- VncCanvas vc; -+ VncCanvas vc = null; - OptionsFrame options; - ClipboardFrame clipboard; - RecordingFrame rec; -- FTPFrame ftp; // KMC: FTP Frame declaration -+ FTPFrame ftp = null; // KMC: FTP Frame declaration - - // Control session recording. - Object recordingSync; -@@ -96,7 +97,7 @@ - - // Variables read from parameter values. - String host; -- int port; -+ int port, vncserverport; - String passwordParam; - String encPasswordParam; - boolean showControls; -@@ -115,28 +116,75 @@ - int i; - // mslogon support 2 end - -+// begin runge/x11vnc -+boolean disableSSL; -+boolean GET; -+String CONNECT; -+String urlPrefix; -+String httpsPort; -+String oneTimeKey; -+String serverCert; -+String ftpDropDown; -+String proxyHost; -+String proxyPort; -+boolean forceProxy; -+boolean ignoreProxy; -+boolean trustAllVncCerts; -+boolean trustUrlVncCert; -+boolean debugCerts; -+boolean debugKeyboard; -+boolean mapF5_to_atsign; -+boolean forbid_Ctrl_Alt; -+ -+boolean ignoreMSLogonCheck; -+boolean delayAuthPanel; -+boolean ftpOnly; -+boolean graftFtp; -+boolean dsmActive; -+ -+boolean gotAuth; -+int authGot; -+// end runge/x11vnc -+ -+ - // - // init() - // - -+public void ftp_init() { -+ boolean show = false; -+ if (ftp != null) { -+ show = true; -+ } -+ ftp = null; -+ -+ ftp = new FTPFrame(this); // KMC: FTPFrame creation -+ -+ if (show) { -+ ftp.doOpen(); -+ rfb.readServerDriveList(); -+ } -+} -+ - public void init() { - - readParameters(); - - if (inSeparateFrame) { -- vncFrame = new Frame("Ultr@VNC"); -- if (!inAnApplet) { -- vncFrame.add("Center", this); -- } -- vncContainer = vncFrame; -+ vncFrame = new Frame("Ultr@VNC"); -+ if (!inAnApplet) { -+ vncFrame.add("Center", this); -+ } -+ vncContainer = vncFrame; - } else { -- vncContainer = this; -+ vncContainer = this; - } - - recordingSync = new Object(); - - options = new OptionsFrame(this); - clipboard = new ClipboardFrame(this); -+ - // authenticator = new AuthPanel(false); // mslogon support : go to connectAndAuthenticate() - if (RecordingFrame.checkSecurity()) - rec = new RecordingFrame(this); -@@ -147,10 +195,11 @@ - cursorUpdatesDef = null; - eightBitColorsDef = null; - -- if (inSeparateFrame) -+ if (inSeparateFrame && vncFrame != null) - vncFrame.addWindowListener(this); - -- ftp = new FTPFrame(this); // KMC: FTPFrame creation -+ ftp_init(); -+ - rfbThread = new Thread(this); - rfbThread.start(); - } -@@ -186,6 +235,30 @@ - gbc.weightx = 1.0; - gbc.weighty = 1.0; - -+ if (ftpOnly) { -+ if (showControls) { -+ buttonPanel.enableButtons(); -+ } -+ ActionListener taskPerformer = new ActionListener() { -+ public void actionPerformed(ActionEvent evt) { -+ vncFrame.setVisible(false); -+ ftp.setSavedLocations(); -+ if (ftp.isVisible()) { -+ ftp.doClose(); -+ } else { -+ ftp.doOpen(); -+ } -+ rfb.readServerDriveList(); -+ } -+ }; -+ Timer t = new Timer(300, taskPerformer); -+ t.setRepeats(false); -+ t.start(); -+ -+ vc.processNormalProtocol(); -+ return; -+ } -+ - // Add ScrollPanel to applet mode - - // Create a panel which itself is resizeable and can hold -@@ -286,6 +359,24 @@ - - void connectAndAuthenticate() throws Exception { - -+ if (graftFtp) { -+ rfb = new RfbProto(host, port, this); -+ rfb.desktopName = "ftponly"; -+ rfb.framebufferWidth = 12; -+ rfb.framebufferHeight = 12; -+ rfb.bitsPerPixel = 32; -+ rfb.depth = 24; -+ rfb.trueColour = true; -+ rfb.redMax = 255; -+ rfb.greenMax = 255; -+ rfb.blueMax = 255; -+ rfb.redShift = 16; -+ rfb.greenShift = 8; -+ rfb.blueShift = 0; -+ rfb.inNormalProtocol = true; -+ return; -+ } -+ - // If "ENCPASSWORD" parameter is set, decrypt the password into - // the passwordParam string. - -@@ -336,7 +427,22 @@ - // - - -- prologueDetectAuthProtocol() ; -+// begin runge/x11vnc -+ gotAuth = false; -+ if (delayAuthPanel) { -+ if (tryAuthenticate(null, null)) { -+ if (inSeparateFrame) { -+ vncFrame.pack(); -+ vncFrame.show(); -+ } -+ return; -+ } -+ } -+// prologueDetectAuthProtocol() ; -+ if (ignoreMSLogonCheck == false) { -+ prologueDetectAuthProtocol() ; -+ } -+// end runge/x11vnc - - authenticator = new AuthPanel(mslogon); - -@@ -371,6 +477,7 @@ - //mslogon support end - } - -+ int tries = 0; - while (true) { - // Wait for user entering a password, or a username and a password - synchronized(authenticator) { -@@ -390,6 +497,13 @@ - break; - //mslogon support end - -+// begin runge/x11vnc -+ gotAuth = false; -+ if (++tries > 2) { -+ throw new Exception("Incorrect password entered " + tries + " times."); -+ } -+// end runge/x11vnc -+ - // Retry on authentication failure. - authenticator.retry(); - } -@@ -405,9 +519,11 @@ - - void prologueDetectAuthProtocol() throws Exception { - -- rfb = new RfbProto(host, port, this); -+ if (!gotAuth) { -+ rfb = new RfbProto(host, port, this); - -- rfb.readVersionMsg(); -+ rfb.readVersionMsg(); -+ } - - System.out.println("RFB server supports protocol version " + - rfb.serverMajor + "." + rfb.serverMinor); -@@ -431,16 +547,36 @@ - - boolean tryAuthenticate(String us, String pw) throws Exception { - -- rfb = new RfbProto(host, port, this); -+ int authScheme; - -- rfb.readVersionMsg(); -+ if (!gotAuth) { -+ rfb = new RfbProto(host, port, this); - -- System.out.println("RFB server supports protocol version " + -- rfb.serverMajor + "." + rfb.serverMinor); -+ rfb.readVersionMsg(); - -- rfb.writeVersionMsg(); -+ System.out.println("RFB server supports protocol version: " + -+ rfb.serverMajor + "." + rfb.serverMinor); - -- int authScheme = rfb.readAuthScheme(); -+ rfb.writeVersionMsg(); -+ -+ authScheme = rfb.readAuthScheme(); -+ -+ gotAuth = true; -+ authGot = authScheme; -+ } else { -+ authScheme = authGot; -+ } -+// begin runge/x11vnc -+ if (delayAuthPanel && pw == null) { -+ if (authScheme == RfbProto.NoAuth) { -+ System.out.println("No authentication needed"); -+ return true; -+ } else { -+ return false; -+ } -+ } -+System.out.println("as: " + authScheme); -+// end runge/x11vnc - - switch (authScheme) { - -@@ -629,6 +765,10 @@ - - void doProtocolInitialisation() throws IOException { - -+ if (graftFtp) { -+ return; -+ } -+ - rfb.writeClientInit(); - - rfb.readServerInit(); -@@ -774,9 +914,28 @@ - fatalError("HOST parameter not specified"); - } - } -+ Date d = new Date(); -+ System.out.println("-\nSSL VNC Java Applet starting. " + d); - -- String str = readParameter("PORT", true); -- port = Integer.parseInt(str); -+ port = 0; -+ String str = readParameter("PORT", false); -+ if (str != null) { -+ port = Integer.parseInt(str); -+ } -+ // When there is a proxy VNCSERVERPORT may be inaccessible (inside firewall). -+ vncserverport = 0; -+ str = readParameter("VNCSERVERPORT", false); -+ if (str != null) { -+ vncserverport = Integer.parseInt(str); -+ } -+ if (port == 0 && vncserverport == 0) { -+ fatalError("Neither PORT nor VNCSERVERPORT parameters specified"); -+ } -+ if (port == 0) { -+ // Nevertheless, fall back to vncserverport if we have to. -+ System.out.println("using vncserverport: '" + vncserverport + "' for PORT."); -+ port = vncserverport; -+ } - - if (inAnApplet) { - str = readParameter("Open New Window", false); -@@ -804,6 +963,158 @@ - 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 + "'"); -+ -+ ftpDropDown = readParameter("ftpDropDown", false); -+ if (ftpDropDown != null) { -+ ftpDropDown = ftpDropDown.replaceAll("%2F", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("%2f", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("_2F_", "/"); -+ ftpDropDown = ftpDropDown.replaceAll("%20", " "); -+ System.out.println("ftpDropDown: '" + ftpDropDown + "'"); -+ } -+ -+ -+ oneTimeKey = readParameter("oneTimeKey", false); -+ if (oneTimeKey != null) { -+ System.out.println("oneTimeKey is set."); -+ } -+ -+ serverCert = readParameter("serverCert", false); -+ if (serverCert != null) { -+ System.out.println("serverCert is set."); -+ } -+ -+ forceProxy = false; -+ proxyHost = null; -+ proxyPort = null; -+ str = readParameter("forceProxy", false); -+ if (str != null) { -+ if (str.equalsIgnoreCase("Yes")) { -+ forceProxy = true; -+ } else if (str.equalsIgnoreCase("No")) { -+ forceProxy = false; -+ } else { -+ forceProxy = true; -+ String[] pieces = str.split(" "); -+ proxyHost = new String(pieces[0]); -+ if (pieces.length >= 2) { -+ proxyPort = new String(pieces[1]); -+ } else { -+ proxyPort = new String("8080"); -+ } -+ } -+ } -+ str = readParameter("proxyHost", false); -+ if (str != null) { -+ proxyHost = new String(str); -+ } -+ str = readParameter("proxyPort", false); -+ if (str != null) { -+ proxyPort = new String(str); -+ } -+ if (proxyHost != null && proxyPort == null) { -+ proxyPort = new String("8080"); -+ } -+ -+ 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; -+ } -+ debugCerts = false; -+ str = readParameter("debugCerts", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugCerts = true; -+ } -+ debugKeyboard = false; -+ str = readParameter("debugKeyboard", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ debugKeyboard = true; -+ } -+ mapF5_to_atsign = false; -+ str = readParameter("mapF5_to_atsign", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ mapF5_to_atsign = true; -+ } -+ forbid_Ctrl_Alt = false; -+ str = readParameter("forbid_Ctrl_Alt", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ forbid_Ctrl_Alt = true; -+ } -+ ignoreMSLogonCheck = false; -+ str = readParameter("ignoreMSLogonCheck", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ ignoreMSLogonCheck = true; -+ } -+ ftpOnly = false; -+ str = readParameter("ftpOnly", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ ftpOnly = true; -+ } -+ graftFtp = false; -+ str = readParameter("graftFtp", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ graftFtp = true; -+ } -+ dsmActive = false; -+ str = readParameter("dsmActive", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ dsmActive = true; -+ } -+ delayAuthPanel = false; -+ str = readParameter("delayAuthPanel", false); -+ if (str != null && str.equalsIgnoreCase("Yes")) { -+ delayAuthPanel = true; -+ } -+// end runge/x11vnc - } - - public String readParameter(String name, boolean required) { -- cgit v1.2.3