diff --git a/.gitignore b/.gitignore index 6c1a93c..1a9d961 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ *.pyc *.o -wsproxy -tests/data_*.js +*.so diff --git a/LICENSE.txt b/LICENSE.txt index a2eb8eb..c8242fe 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,10 +1,8 @@ -noVNC is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and +websockify is licensed under the LGPL version 3 (see docs/LICENSE.GPL-3 and docs/LICENSE.LGPL-3) with the following exceptions: include/base64.js : Dual GPL-2 or LGPL-2.1 - incluee/des.js : Various BSD style licenses - include/web-socket-js/ : New BSD license. Source code at http://github.com/gimite/web-socket-js diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7dc1bc4 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +TARGETS=rebind.so +CFLAGS += -fPIC + +all: $(TARGETS) + +rebind.so: rebind.o + $(CC) $(LDFLAGS) $^ -shared -fPIC -ldl -o $@ + +clean: + rm -f rebind.o rebind.so + diff --git a/README.md b/README.md index 3e1eb60..20f8e79 100644 --- a/README.md +++ b/README.md @@ -1,207 +1,165 @@ -## noVNC: HTML5 VNC Client +## websockify: WebSockets support for any application/server + +websockify was formerly named `wsproxy` and was part of the +[noVNC](https://github.com/kanaka/noVNC) project. + +At the most basic level, websockify just translates WebSockets traffic +to normal socket traffic. websockify accepts the WebSockets handshake, +parses it, and then begins forwarding traffic between the client and +the target in both directions. WebSockets payload data is UTF-8 +encoded so in order to transport binary data it must use an encoding +that can be encapsulated within UTF-8. websockify uses base64 to encode +all traffic to and from the client. Also, WebSockets traffic starts +with '\0' (0) and ends with '\xff' (255). Some buffering is done in +case the data from the client is not a full WebSockets frame (i.e. +does not end in 255). + + +### Additional websockify features + +These are not necessary for the basic operation. + +* Daemonizing: When the `-D` option is specified, websockify runs + in the background as a daemon process. + +* SSL (the wss:// WebSockets URI): This is detected automatically by + websockify by sniffing the first byte sent from the client and then + wrapping the socket if the data starts with '\x16' or '\x80' + (indicating SSL). + +* Flash security policy: websockify detects flash security policy + requests (again by sniffing the first packet) and answers with an + appropriate flash security policy response (and then closes the + port). This means no separate flash security policy server is needed + for supporting the flash WebSockets fallback emulator. + +* Session recording: This feature that allows recording of the traffic + sent and received from the client to a file using the `--record` + option. + +* Mini-webserver: websockify can detect and respond to normal web + requests on the same port as the WebSockets proxy and Flash security + policy. This functionality is activate with the `--web DIR` option + where DIR is the root of the web directory to serve. + +* Wrap a program: see the "Wrap a Program" section below. + + +### Implementations of websockify + +The primary implementation of websockify is in python. There are two +other implementations of websockify in C, and Node (node.js) in the +`other` directory. + +Here is the feature support matrix for the the websockify +implementations: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ProgramLanguageMultiprocessDaemonizeSSL/wssFlash Policy ServerSession RecordWeb ServerProgram Wrap
websockifypythonyesyesyes 1yesyesyesyes
other/websockifyCyesyesyesyesnonono
other/websockify.jsNode (node.js)yesnononononono
-### Description +* Note 1: to use SSL/wss with python 2.5 or older, see the following + section on *Building the Python ssl module*. -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). +### Wrap a Program -Notable commits, announcements and news are posted to -@noVNC +In addition to proxying from a source address to a target address +(which may be on a different system), websockify has the ability to +launch a program on the local system and proxy WebSockets traffic to +a normal TCP port owned/bound by the program. +The is accomplished with a small LD_PRELOAD library (`rebind.so`) +which intercepts bind() system calls by the program. The specified +port is moved to a new localhost/loopback free high port. websockify +then proxies WebSockets traffic directed to the original port to the +new (moved) port of the program. -### Screenshots +The program wrap mode is invoked by replacing the target with `--` +followed by the program command line to wrap. -Running in Chrome before and after connecting: + `./websockify 2023 -- PROGRAM ARGS` -  +The `--wrap-mode` option can be used to indicate what action to take +when the wrapped program exits or daemonizes. -See more screenshots here. +Here is an example of using websockify to wrap the vncserver command +(which backgrounds itself) for use with +[noVNC](https://github.com/kanaka/noVNC): + `./websockify 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1` -### Projects/Companies using noVNC +Here is an example of wrapping telnetd (from krb5-telnetd).telnetd +exits after the connection closes so the wrap mode is set to respawn +the command: -* [Sentry Data Systems](http://www.sentryds.com): uses noVNC in the - [Datanex Cloud Computing Platform](http://www.sentryds.com/products/datanex/). + `sudo ./websockify 2023 --wrap-mode=respawn -- telnetd -debug 2023` -* [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr): - Feature [#1935](http://code.osuosl.org/issues/1935). +The `wstelnet.html` page demonstrates a simple WebSockets based telnet +client. -* [Archipel](http://archipelproject.org): - [Video demo](http://antoinemercadal.fr/archipelblog/wp-content/themes/ArchipelWPTemplate/video_youtube.php?title=VNC%20Demonstration&id=te_bzW574Zo) -* [openQRM](http://www.openqrm.com/): VNC plugin available - by request. Probably included in [version - 4.8](http://www.openqrm.com/?q=node/15). [Video - demo](http://www.openqrm-enterprise.com/news/details/article/remote-vm-console-plugin-available.html). +### Building the Python ssl module (for python 2.5 and older) +* Install the build dependencies. On Ubuntu use this command: -### Browser Requirements + `sudo aptitude install python-dev bluetooth-dev` -* HTML5 Canvas: Except for Internet Explorer, most - browsers have had Canvas support for quite some time. Internet - Explorer 9 will have Canvas support (finally). +* Download, build the ssl module and symlink to it: -* HTML5 WebSockets: For browsers that do not have builtin - WebSockets support, the project includes - web-socket-js, - a WebSockets emulator using Adobe Flash. + `cd websockify/` -* 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. + `wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz` -* I maintain a more detailed list of browser compatibility here. + `tar xvzf ssl-1.15.tar.gz` + `cd ssl-1.15` -### Server Requirements + `make` -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 ('wsproxy'). One advantage of using the proxy -is that it has builtin support for SSL/TLS encryption (i.e. "wss://"). + `cd ../` -There a few reasons why a proxy is required: + `ln -sf ssl-1.15/build/lib.linux-*/ssl ssl` - 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. 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! - - -### Advanced usage - -* To encrypt the traffic using the WebSocket 'wss://' URI scheme you - need to generate a certificate for the proxy to load. By default the - proxy loads a certificate file name `self.pem` but the `--cert=CERT` - option can override the file name. You can generate a self-signed - certificate using openssl. When asked for the common name, use the - hostname of the server where the proxy will be running: - - `openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem` - -* `tightvnc` provide a nice startup script that can be used to run - a separate X desktop that is served by VNC. To install and run the - server under Ubuntu you would do something like this: - - `sudo apt-get install tightvncserver` - - `vncserver :1` - - The VNC server will run in the background. The port that it runs - on is the display number + 5900 (i.e. 5901 in the case above). - -* `x11vnc` can be used to share your current X desktop. Note that if - you run noVNC on the X desktop you are connecting to via VNC you - will get a neat hall of mirrors effect, but the the client and - server will fight over the mouse. - - `sudo apt-get install x11vnc` - - `x11vnc -forever -display :0` - - Without the `-forever` option, x11vnc will exit after the first - disconnect. The `-display` option indicates the exiting X display to - share. The port that it runs on is the display number + 5900 (i.e. - 5900 in the case above). - -* To run the python proxy directly without using launch script (to - pass additional options for example): - - `./utils/wsproxy.py source_port target_addr:target_port` - - `./utils/wsproxy.py 8787 localhost:5901` - -* To activate the mini-webserver in wsproxy.py use the `--web DIR` - option: - - `./utils/wsproxy.py --web ./ 8787 localhost:5901` - - -* Point your web browser at http://localhost:8787/vnc.html. On the - page enter the location where the proxy is running (localhost and - 8787) and the password that the vnc server is using (if any). Hit - the Connect button. - -* If you are using python 2.3 or 2.4 and you want wsproxy to support - 'wss://' (TLS) then see the - [wsproxy README](http://github.com/kanaka/noVNC/blob/master/utils/README.md) - for instructions on building the ssl module. - - -### Integration - -The client is designed to be easily integrated with existing web -structure and style. - -At a minimum you must include the `vnc.js` and `ui.js` scripts and -call UI.load(). For example: - - - - - - -
Loading
- - - - -See `vnc.html` and `vnc_auto.html` for examples. The file -`include/plain.css` has a list of stylable elements. - -The `vnc.js` also includes other scripts within the `include` -sub-directory. The `VNC_uri_prefix` variable can be use override the -URL path to the `include` sub-directory. - - -### Troubleshooting - -You will need console logging support in the browser. Recent Chrome -and Opera versions have built in support. Firefox has a nice extension -called "firebug" that gives console logging support. - -First, load the noVNC page with `logging=debug` added to the query string. -For example `vnc.html?logging=debug`. - -Then, activate the console logger in your browser. With Chrome it can -be activate using Ctrl+Shift+J and then switching to the "Console" -tab. With firefox+firebug, it can be activated using Ctrl+F12. - -Now reproduce the problem. The console log output will give more -information about what is going wrong and where in the code the -problem is located. - -If you file a issue/bug, it is very helpful for me to have the last -page of console output leading up the problem in the issue report. -Other helpful issue/bug information: browser version, OS version, -noVNC git version, and VNC server name/version. diff --git a/docs/TODO b/docs/TODO index c64cace..1166e5e 100644 --- a/docs/TODO +++ b/docs/TODO @@ -1,35 +1 @@ -Short Term: - -- Keyboard layout/internationalization support - - convert keyCode into proper charCode - -- Test on IE 9 preview 3. - -- Status bar menu/buttons: - - Explanatory hover text over buttons - - - Configuration menu: - - Tunable: speed vs. bandwidth selection - - Tunable: CPU use versus latency. - - Scaling - - - Keyboard menu: - - Ctrl Lock, Alt Lock, SysRq Lock - - Highlight menu icon when keys are locked - - - Clipboard button -> popup: - - text, clear and send buttons - - wstelnet: support CSI L and CSI M - -Medium Term: - -- VNC performance and regression playback suite. - -- Viewport support - -- Touchscreen testing/support. - -- wswrapper: - - epoll_* support - diff --git a/docs/browsers.md b/docs/browsers.md deleted file mode 100644 index ad52fd3..0000000 --- a/docs/browsers.md +++ /dev/null @@ -1,126 +0,0 @@ -## noVNC: Browser Support - -### Ubuntu Karmic (9.10) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BrowserStatusPerformance/Notes
Chrome 7.0.510.0BrokenWebKit render bug (see note 3)
Chrome 5.0.375.29ExcellentVery fast. Native WebSockets.
Firefox 4.0 Beta 6ExcellentFast. Native WebSockets. SSL cert hassle (see note 2)
Firefox 3.6.1GoodSlowed by web-socket-js overhead. Local cursor causes segfault.
Arora 0.10.1FairSlow due to broken putImageData and web-socket-js.
Opera 10.60Poorweb-socket-js problems, mouse/keyboard issues (see note 1)
Konqueror 4.3.2Brokenweb-socket-js never loads
- - -### Ubuntu Jaunty (9.04) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
BrowserStatusPerformance/Notes
Chrome 5.0.375.29ExcellentVery fast. Native WebSockets.
Firefox 3.5GoodSlowed by web-socket-js overhead.
Firefox 3.0.17FairWorks fine but is slow.
Arora 0.5FairSlow due to broken putImageData and web-socket-js.
Opera 10.60Poorweb-socket-js problems, mouse/keyboard issues (see note 1)
Konqueror 4.2.2Brokenweb-socket-js never loads
- - -### Windows XP - - - - - - - - - - - - - - - - - - - - - - - -
BrowserStatusPerformance/Notes
Chrome 5.0.375.99ExcellentVery fast. Native WebSockets.
Safari 5.0ExcellentFast. Native WebSockets.
Firefox 3.0.19GoodSome overhead from web-socket-js.
IE 6, 7, 8Non-starterNo basic Canvas support. Javascript painfully slow.
- - -* Note 1: Opera interacts poorly with web-socket-js. After two - disconnects the browser tab or Flash often hang. Although Javascript - is faster than Firefox 3.5, the high variability of web-socket-js - performance results in overall performance being lower. Middle mouse - clicks and keyboard events need some work to work properly under - Opera. Also, Opera does not have support for setting the cursor - style url to a data URI scheme, so cursor pseudo-encoding is - disabled. - -* Note 2: Firefox 4.0 Beta does not provide a direct way to accept - SSL certificates via WebSockets. You can work around this by - navigating directly to the WebSockets port using 'https://' and - accepting the certificate. Then return to noVNC and connect - normally. - -* Note 3: Browsers using WebKit build 66396 through 68867 - (Chrome/Chromium build 57968 through 61278) have a Canvas rendering - bug. The WebKit bug is #46319. - The noVNC bug is #28. - - diff --git a/docs/links b/docs/links deleted file mode 100644 index 8cb4da1..0000000 --- a/docs/links +++ /dev/null @@ -1,70 +0,0 @@ -New tight PNG protocol: - http://wiki.qemu.org/VNC_Tight_PNG - http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling - -RFB protocol and extensions: - http://tigervnc.org/cgi-bin/rfbproto - -Canvas Browser Compatibility: - http://philip.html5.org/tests/canvas/suite/tests/results.html - -WebSockets API standard: - http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket - http://dev.w3.org/html5/websockets/ - http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt - -Browser Keyboard Events detailed: - http://unixpapa.com/js/key.html - -ActionScript (Flash) WebSocket implementation: - http://github.com/gimite/web-socket-js - -ActionScript (Flash) crypto/TLS library: - http://code.google.com/p/as3crypto - http://github.com/lyokato/as3crypto_patched - -TLS Protocol: - http://en.wikipedia.org/wiki/Transport_Layer_Security - -Generate self-signed certificate: - http://docs.python.org/dev/library/ssl.html#certificates - -Cursor appearance/style (for Cursor pseudo-encoding): - http://en.wikipedia.org/wiki/ICO_(file_format) - http://www.daubnet.com/en/file-format-cur - https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property - - -RDP Protocol specification: - http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx - - -Related projects: - - guacamole: http://guacamole.sourceforge.net/ - - - Web client, but Java servlet does pre-processing - - jsvnc: http://code.google.com/p/jsvnc/ - - - No releases - - webvnc: http://code.google.com/p/webvnc/ - - - Jetty web server gateway, no updates since April 2008. - - RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html - - - Java applet - - Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/ - - - Adobe Flash implementation - - FVNC: http://osflash.org/fvnc - - - Adbove Flash implementation - - CanVNC: http://canvnc.sourceforge.net/ - - - HTML client with REST to VNC python proxy. Mostly vapor. diff --git a/docs/rfb_notes b/docs/rfb_notes deleted file mode 100644 index 643e16c..0000000 --- a/docs/rfb_notes +++ /dev/null @@ -1,147 +0,0 @@ -5.1.1 ProtocolVersion: 12, 12 bytes - - - Sent by server, max supported - 12 ascii - "RFB 003.008\n" - - Response by client, version to use - 12 ascii - "RFB 003.003\n" - -5.1.2 Authentication: >=4, [16, 4] bytes - - - Sent by server - CARD32 - authentication-scheme - 0 - connection failed - CARD32 - length - length - reason - 1 - no authentication - - 2 - VNC authentication - 16 CARD8 - challenge (random bytes) - - - Response by client (if VNC authentication) - 16 CARD8 - client encrypts the challenge with DES, using user - password as key, sends resulting 16 byte response - - - Response by server (if VNC authentication) - CARD32 - 0 - OK - 1 - failed - 2 - too-many - -5.1.3 ClientInitialisation: 1 byte - - Sent by client - CARD8 - shared-flag, 0 exclusive, non-zero shared - -5.1.4 ServerInitialisation: >=24 bytes - - Sent by server - CARD16 - framebuffer-width - CARD16 - framebuffer-height - 16 byte PIXEL_FORMAT - server-pixel-format - CARD8 - bits-per-pixel - CARD8 - depth - CARD8 - big-endian-flag, non-zero is big endian - CARD8 - true-color-flag, non-zero then next 6 apply - CARD16 - red-max - CARD16 - green-max - CARD16 - blue-max - CARD8 - red-shift - CARD8 - green-shift - CARD8 - blue-shift - 3 bytes - padding - CARD32 - name-length - - CARD8[length] - name-string - - - -Client to Server Messages: - -5.2.1 SetPixelFormat: 20 bytes - CARD8: 0 - message-type - ... - -5.2.2 FixColourMapEntries: >=6 bytes - CARD8: 1 - message-type - ... - -5.2.3 SetEncodings: >=8 bytes - CARD8: 2 - message-type - CARD8 - padding - CARD16 - numer-of-encodings - - CARD32 - encoding-type in preference order - 0 - raw - 1 - copy-rectangle - 2 - RRE - 4 - CoRRE - 5 - hextile - -5.2.4 FramebufferUpdateRequest (10 bytes) - CARD8: 3 - message-type - CARD8 - incremental (0 for full-update, non-zero for incremental) - CARD16 - x-position - CARD16 - y-position - CARD16 - width - CARD16 - height - - -5.2.5 KeyEvent: 8 bytes - CARD8: 4 - message-type - CARD8 - down-flag - 2 bytes - padding - CARD32 - key (X-Windows keysym values) - -5.2.6 PointerEvent: 6 bytes - CARD8: 5 - message-type - CARD8 - button-mask - CARD16 - x-position - CARD16 - y-position - -5.2.7 ClientCutText: >=9 bytes - CARD8: 6 - message-type - ... - - -Server to Client Messages: - -5.3.1 FramebufferUpdate - CARD8: 0 - message-type - 1 byte - padding - CARD16 - number-of-rectangles - - CARD16 - x-position - CARD16 - y-position - CARD16 - width - CARD16 - height - CARD16 - encoding-type: - 0 - raw - 1 - copy rectangle - 2 - RRE - 4 - CoRRE - 5 - hextile - - raw: - - width x height pixel values - - copy rectangle: - CARD16 - src-x-position - CARD16 - src-y-position - - RRE: - CARD32 - N number-of-subrectangles - Nxd bytes - background-pixel-value (d bits-per-pixel) - - ... - -5.3.2 SetColourMapEntries (no support) - CARD8: 1 - message-type - ... - -5.3.3 Bell - CARD8: 2 - message-type - -5.3.4 ServerCutText - CARD8: 3 - message-type - - - - - diff --git a/docs/rfbproto-3.3.pdf b/docs/rfbproto-3.3.pdf deleted file mode 100644 index 56b8764..0000000 Binary files a/docs/rfbproto-3.3.pdf and /dev/null differ diff --git a/docs/rfbproto-3.7.pdf b/docs/rfbproto-3.7.pdf deleted file mode 100644 index 1ef5462..0000000 Binary files a/docs/rfbproto-3.7.pdf and /dev/null differ diff --git a/docs/rfbproto-3.8.pdf b/docs/rfbproto-3.8.pdf deleted file mode 100644 index 8f0730f..0000000 Binary files a/docs/rfbproto-3.8.pdf and /dev/null differ diff --git a/include/VT100.js b/include/VT100.js new file mode 100644 index 0000000..6a325e1 --- /dev/null +++ b/include/VT100.js @@ -0,0 +1,919 @@ +// VT100.js -- a text terminal emulator in JavaScript with a ncurses-like +// interface and a POSIX-like interface. (The POSIX-like calls are +// implemented on top of the ncurses-like calls, not the other way round.) +// +// Released under the GNU LGPL v2.1, by Frank Bi +// +// 2007-08-12 - refresh(): +// - factor out colour code to html_colours_() +// - fix handling of A_REVERSE | A_DIM +// - simplify initial
output code +// - fix underlining colour +// - fix attron() not to turn off attributes +// - decouple A_STANDOUT and A_BOLD +// 2007-08-11 - getch() now calls refresh() +// 2007-08-06 - Safari compat fix -- turn '\r' into '\n' for onkeypress +// 2007-08-05 - Opera compat fixes for onkeypress +// 2007-07-30 - IE compat fixes: +// - change key handling code +// - add
...
  so that 1st and last lines align +// 2007-07-28 - change wrapping behaviour -- writing at the right edge no +// longer causes the cursor to immediately wrap around +// - add ... to output to make A_STANDOUT stand out more +// - add handling of backspace, tab, return keys +// - fix doc. of VT100() constructor +// - change from GPL to LGPL +// 2007-07-09 - initial release +// +// class VT100 +// A_NORMAL, A_UNDERLINE, A_REVERSE, A_BLINK, A_DIM, A_BOLD, A_STANDOUT +// =class constants= +// Attribute constants. +// VT100(wd, ht, scr_id) =constructor= +// Creates a virtual terminal with width `wd', and +// height `ht'. The terminal will be displayed between +//
...
tags which have element ID `scr_id'. +// addch(ch [, attr]) +// Writes out the character `ch'. If `attr' is given, +// it specifies the attributes for the character, +// otherwise the current attributes are used. +// addstr(stuff) Writes out the string `stuff' using the current +// attributes. +// attroff(mode) Turns off any current options given in mode. +// attron(mode) Turns on any options given in mode. +// attrset(mode) Sets the current options to mode. +// bkgdset(attr) Sets the background attributes to attr. +// clear() Clears the terminal using the background attributes, +// and homes the cursor. +// clrtobol() Clears the portion of the terminal from the cursor +// to the bottom. +// clrtoeol() Clears the portion of the current line after the +// cursor. +// curs_set(vis [, grab]) +// If `vis' is 0, makes the cursor invisible; otherwise +// make it visible. If `grab' is given and true, starts +// capturing keyboard events (for `getch()'); if given +// and false, stops capturing events. +// echo() Causes key strokes to be automatically echoed on the +// terminal. +// erase() Same as `clear()'. +// getch(isr) Arranges to call `isr' when a key stroke is +// received. The received character and the terminal +// object are passed as arguments to `isr'. +// getmaxyx() Returns an associative array with the maximum row +// (`y') and column (`x') numbers for the terminal. +// getyx() Returns an associative array with the current row +// (`y') and column (`x') of the cursor. +// move(r, c) Moves the cursor to row `r', column `c'. +// noecho() Stops automatically echoing key strokes. +// refresh() Updates the display. +// scroll() Scrolls the terminal up one line. +// standend() Same as `attrset(VT100.A_NORMAL)'. +// standout() Same as `attron(VT100.A_STANDOUT)'. +// write(stuff) Writes `stuff' to the terminal and immediately +// updates the display; (some) escape sequences are +// interpreted and acted on. + +// constructor +function VT100(wd, ht, scr_id) +{ + var r; + var c; + var scr = document.getElementById(scr_id); + this.wd_ = wd; + this.ht_ = ht; + this.scrolled_ = 0; + this.bkgd_ = { + mode: VT100.A_NORMAL, + fg: VT100.COLOR_WHITE, + bg: VT100.COLOR_BLACK + }; + this.c_attr_ = { + mode: VT100.A_NORMAL, + fg: VT100.COLOR_WHITE, + bg: VT100.COLOR_BLACK + }; + this.text_ = new Array(ht); + this.attr_ = new Array(ht); + for (r = 0; r < ht; ++r) { + this.text_[r] = new Array(wd); + this.attr_[r] = new Array(wd); + } + this.scr_ = scr; + this.cursor_vis_ = true; + this.grab_events_ = false; + this.getch_isr_ = undefined; + this.key_buf_ = []; + this.echo_ = true; + this.esc_state_ = 0; + // Internal debug setting. + this.debug_ = 0; + this.clear(); + this.refresh(); +} + +// public constants -- colours and colour pairs +VT100.COLOR_BLACK = 0; +VT100.COLOR_BLUE = 1; +VT100.COLOR_GREEN = 2; +VT100.COLOR_CYAN = 3; +VT100.COLOR_RED = 4; +VT100.COLOR_MAGENTA = 5; +VT100.COLOR_YELLOW = 6; +VT100.COLOR_WHITE = 7; +VT100.COLOR_PAIRS = 256; +VT100.COLORS = 8; +// public constants -- attributes +VT100.A_NORMAL = 0; +VT100.A_UNDERLINE = 1; +VT100.A_REVERSE = 2; +VT100.A_BLINK = 4; +VT100.A_DIM = 8; +VT100.A_BOLD = 16; +VT100.A_STANDOUT = 32; +VT100.A_PROTECT = VT100.A_INVIS = 0; // ? +// other public constants +VT100.TABSIZE = 8; +// private constants +VT100.ATTR_FLAGS_ = VT100.A_UNDERLINE | VT100.A_REVERSE | VT100.A_BLINK | + VT100.A_DIM | VT100.A_BOLD | VT100.A_STANDOUT | + VT100.A_PROTECT | VT100.A_INVIS; +VT100.COLOR_SHIFT_ = 6; +VT100.browser_ie_ = (navigator.appName.indexOf("Microsoft") != -1); +VT100.browser_opera_ = (navigator.appName.indexOf("Opera") != -1); +// class variables +VT100.the_vt_ = undefined; + +// class methods + +// this is actually an event handler +VT100.handle_onkeypress_ = function VT100_handle_onkeypress(event) +{ + var vt = VT100.the_vt_, ch; + if (vt === undefined) + return true; + if (VT100.browser_ie_ || VT100.browser_opera_) { + ch = event.keyCode; + if (ch == 13) + ch = 10; + else if (ch > 255 || (ch < 32 && ch != 8)) + return true; + ch = String.fromCharCode(ch); + } else { + ch = event.charCode; + //dump("ch: " + ch + "\n"); + //dump("ctrl?: " + event.ctrlKey + "\n"); + vt.debug("onkeypress:: keyCode: " + event.keyCode + ", ch: " + event.charCode); + if (ch) { + if (ch > 255) + return true; + if (event.ctrlKey && event.shiftKey) { + // Don't send the copy/paste commands. + var charStr = String.fromCharCode(ch); + if (charStr == 'C' || charStr == 'V') { + return false; + } + } + if (event.ctrlKey) { + ch = String.fromCharCode(ch - 96); + } else { + ch = String.fromCharCode(ch); + if (ch == '\r') + ch = '\n'; + } + } else { + switch (event.keyCode) { + case event.DOM_VK_BACK_SPACE: + ch = '\b'; + break; + case event.DOM_VK_TAB: + ch = '\t'; + // Stop tab from moving to another element. + event.preventDefault(); + break; + case event.DOM_VK_RETURN: + case event.DOM_VK_ENTER: + ch = '\n'; + break; + case event.DOM_VK_UP: + ch = '\x1b[A'; + break; + case event.DOM_VK_DOWN: + ch = '\x1b[B'; + break; + case event.DOM_VK_RIGHT: + ch = '\x1b[C'; + break; + case event.DOM_VK_LEFT: + ch = '\x1b[D'; + break; + case event.DOM_VK_DELETE: + ch = '\x1b[3~'; + break; + case event.DOM_VK_HOME: + ch = '\x1b[H'; + break; + case event.DOM_VK_ESCAPE: + ch = '\x1bc'; + break; + default: + return true; + } + } + } + vt.key_buf_.push(ch); + setTimeout(VT100.go_getch_, 0); + return false; +} + +// this is actually an event handler +VT100.handle_onkeydown_ = function VT100_handle_onkeydown() +{ + var vt = VT100.the_vt_, ch; + switch (event.keyCode) { + case 8: + ch = '\b'; break; + default: + return true; + } + vt.key_buf_.push(ch); + setTimeout(VT100.go_getch_, 0); + return false; +} + +VT100.go_getch_ = function VT100_go_getch() +{ + var vt = VT100.the_vt_; + if (vt === undefined) + return; + var isr = vt.getch_isr_; + vt.getch_isr_ = undefined; + if (isr === undefined) + return; + var ch = vt.key_buf_.shift(); + if (ch === undefined) { + vt.getch_isr_ = isr; + return; + } + if (vt.echo_) + vt.addch(ch); + isr(ch, vt); +} + +// object methods + +VT100.prototype.may_scroll_ = function() +{ + var ht = this.ht_, cr = this.row_; + while (cr >= ht) { + this.scroll(); + --cr; + } + this.row_ = cr; +} + +VT100.prototype.html_colours_ = function(attr) +{ + var fg, bg, co0, co1; + fg = attr.fg; + bg = attr.bg; + switch (attr.mode & (VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD)) { + case 0: + case VT100.A_DIM | VT100.A_BOLD: + co0 = '00'; co1 = 'c0'; + break; + case VT100.A_BOLD: + co0 = '00'; co1 = 'ff'; + break; + case VT100.A_DIM: + if (fg == VT100.COLOR_BLACK) + co0 = '40'; + else + co0 = '00'; + co1 = '40'; + break; + case VT100.A_REVERSE: + case VT100.A_REVERSE | VT100.A_DIM | VT100.A_BOLD: + co0 = 'c0'; co1 = '40'; + break; + case VT100.A_REVERSE | VT100.A_BOLD: + co0 = 'c0'; co1 = '00'; + break; + default: + if (fg == VT100.COLOR_BLACK) + co0 = '80'; + else + co0 = 'c0'; + co1 = 'c0'; + } + return { + f: '#' + (fg & 4 ? co1 : co0) + + (fg & 2 ? co1 : co0) + + (fg & 1 ? co1 : co0), + b: '#' + (bg & 4 ? co1 : co0) + + (bg & 2 ? co1 : co0) + + (bg & 1 ? co1 : co0) + }; +} + +VT100.prototype.addch = function(ch, attr) +{ + var cc = this.col_; + this.debug("addch:: ch: " + ch + ", attr: " + attr); + switch (ch) { + case '\b': + if (cc != 0) + --cc; + break; + case '\n': + ++this.row_; + cc = 0; + this.clrtoeol(); + this.may_scroll_(); + break; + case '\r': + this.may_scroll_(); + cc = 0; + break; + case '\t': + this.may_scroll_(); + cc += VT100.TABSIZE - cc % VT100.TABSIZE; + if (cc >= this.wd_) { + ++this.row_; + cc -= this.wd_; + } + break; + default: + if (attr === undefined) + attr = this._cloneAttr(this.c_attr_); + if (cc >= this.wd_) { + ++this.row_; + cc = 0; + } + this.may_scroll_(); + this.text_[this.row_][cc] = ch; + this.attr_[this.row_][cc] = attr; + ++cc; + } + this.col_ = cc; +} + +VT100.prototype.addstr = function(stuff) +{ + for (var i = 0; i < stuff.length; ++i) + this.addch(stuff.charAt(i)); +} + +VT100.prototype._cloneAttr = function VT100_cloneAttr(a) +{ + return { + mode: a.mode, + fg: a.fg, + bg: a.bg + }; +} + +VT100.prototype.attroff = function(a) +{ + //dump("attroff: " + a + "\n"); + a &= VT100.ATTR_FLAGS_; + this.c_attr_.mode &= ~a; +} + +VT100.prototype.attron = function(a) +{ + //dump("attron: " + a + "\n"); + a &= VT100.ATTR_FLAGS_; + this.c_attr_.mode |= a; +} + +VT100.prototype.attrset = function(a) +{ + //dump("attrset: " + a + "\n"); + this.c_attr_.mode = a; +} + +VT100.prototype.fgset = function(fg) +{ + //dump("fgset: " + fg + "\n"); + this.c_attr_.fg = fg; +} + +VT100.prototype.bgset = function(bg) +{ + //dump("bgset: " + bg + "\n"); + if (bg !== 0) { + this.warn("bgset: " + bg + "\n"); + } + this.c_attr_.bg = bg; +} + +VT100.prototype.bkgdset = function(a) +{ + this.bkgd_ = a; +} + +VT100.prototype.clear = function() +{ + this.debug("clear"); + this.row_ = this.col_ = 0; + this.scrolled_ = 0; + for (r = 0; r < this.ht_; ++r) { + for (c = 0; c < this.wd_; ++c) { + this.text_[r][c] = ' '; + this.attr_[r][c] = this._cloneAttr(this.bkgd_); + } + } +} + +VT100.prototype.clrtobot = function() +{ + this.debug("clrtobot, row: " + this.row_); + var ht = this.ht_; + var wd = this.wd_; + this.clrtoeol(); + for (var r = this.row_ + 1; r < ht; ++r) { + for (var c = 0; c < wd; ++c) { + this.text_[r][c] = ' '; + this.attr_[r][c] = this.bkgd_; + } + } +} + +VT100.prototype.clrtoeol = function() +{ + this.debug("clrtoeol, col: " + this.col_); + var r = this.row_; + if (r >= this.ht_) + return; + for (var c = this.col_; c < this.wd_; ++c) { + this.text_[r][c] = ' '; + this.attr_[r][c] = this.bkgd_; + } +} + +VT100.prototype.clearpos = function(row, col) +{ + this.debug("clearpos (" + row + ", " + col + ")"); + if (row < 0 || row >= this.ht_) + return; + if (col < 0 || col >= this.wd_) + return; + this.text_[row][col] = ' '; + this.attr_[row][col] = this.bkgd_; +} + +VT100.prototype.curs_set = function(vis, grab, eventist) +{ + this.debug("curs_set:: vis: " + vis + ", grab: " + grab); + if (vis !== undefined) + this.cursor_vis_ = (vis > 0); + if (eventist === undefined) + eventist = window; + if (grab === true || grab === false) { + if (grab === this.grab_events_) + return; + if (grab) { + this.grab_events_ = true; + VT100.the_vt_ = this; + eventist.addEventListener("keypress", VT100.handle_onkeypress_, false); + if (VT100.browser_ie_) + document.onkeydown = VT100.handle_onkeydown_; + } else { + eventist.removeEventListener("keypress", VT100.handle_onkeypress_, false); + if (VT100.browser_ie_) + document.onkeydown = VT100.handle_onkeydown_; + this.grab_events_ = false; + VT100.the_vt_ = undefined; + } + } +} + +VT100.prototype.echo = function() +{ + this.debug("echo on"); + this.echo_ = true; +} + +VT100.prototype.erase = VT100.prototype.clear; + +VT100.prototype.getch = function(isr) +{ + this.debug("getch"); + this.refresh(); + this.getch_isr_ = isr; + setTimeout(VT100.go_getch_, 0); +} + +VT100.prototype.getmaxyx = function() +{ + return { y: this.ht_ - 1, x: this.wd_ - 1 }; +} + +VT100.prototype.getyx = function() +{ + return { y: this.row_, x: this.col_ }; +} + +VT100.prototype.move = function(r, c) +{ + this.debug("move: (" + r + ", " + c + ")"); + if (r < 0) + r = 0; + else if (r >= this.ht_) + r = this.ht_ - 1; + if (c < 0) + c = 0; + else if (c >= this.wd_) + c = this.wd_ - 1; + this.row_ = r; + this.col_ = c; +} + +VT100.prototype.noecho = function() +{ + this.debug("echo off"); + this.echo_ = false; +} + +VT100.prototype.refresh = function() +{ + this.debug("refresh"); + var r, c, stuff = "", start_tag = "", end_tag = "", at = -1, n_at, ch, + pair, cr, cc, ht, wd, cv, added_end_tag; + ht = this.ht_; + wd = this.wd_; + cr = this.row_; + cc = this.col_; + cv = this.cursor_vis_; + var innerHTML = this.scr_.innerHTML; + if (cc >= wd) + cc = wd - 1; + for (r = 0; r < ht; ++r) { + if (r > 0) { + stuff += '\n'; + } + for (c = 0; c < wd; ++c) { + added_end_tag = false; + n_at = this.attr_[r][c]; + if (cv && r == cr && c == cc) { + // Draw the cursor here. + n_at = this._cloneAttr(n_at); + n_at.mode ^= VT100.A_REVERSE; + } + // If the attributes changed, make a new span. + if (n_at.mode != at.mode || n_at.fg != at.fg || n_at.bg != at.bg) { + if (c > 0) { + stuff += end_tag; + } + start_tag = ""; + end_tag = ""; + if (n_at.mode & VT100.A_BLINK) { + start_tag = ""; + end_tag = "" + end_tag; + } + if (n_at.mode & VT100.A_STANDOUT) + n_at.mode |= VT100.A_BOLD; + pair = this.html_colours_(n_at); + start_tag += ''; + stuff += start_tag; + end_tag = "" + end_tag; + at = n_at; + added_end_tag = true; + } else if (c == 0) { + stuff += start_tag; + } + ch = this.text_[r][c]; + switch (ch) { + case '&': + stuff += '&'; break; + case '<': + stuff += '<'; break; + case '>': + stuff += '>'; break; + case ' ': + //stuff += ' '; break; + stuff += ' '; break; + default: + stuff += ch; + } + } + if (!added_end_tag) + stuff += end_tag; + } + this.scr_.innerHTML = "" + stuff + "\n"; +} + +VT100.prototype.scroll = function() +{ + this.scrolled_ += 1; + this.debug("scrolled: " + this.scrolled_); + var n_text = this.text_[0], n_attr = this.attr_[0], + ht = this.ht_, wd = this.wd_; + for (var r = 1; r < ht; ++r) { + this.text_[r - 1] = this.text_[r]; + this.attr_[r - 1] = this.attr_[r]; + } + this.text_[ht - 1] = n_text; + this.attr_[ht - 1] = n_attr; + for (var c = 0; c < wd; ++c) { + n_text[c] = ' '; + n_attr[c] = this.bkgd_; + } +} + +VT100.prototype.standend = function() +{ + //this.debug("standend"); + this.attrset(0); +} + +VT100.prototype.standout = function() +{ + //this.debug("standout"); + this.attron(VT100.A_STANDOUT); +} + +VT100.prototype.write = function(stuff) +{ + var ch, x, r, c, i, j, yx, myx; + for (i = 0; i < stuff.length; ++i) { + ch = stuff.charAt(i); + if (ch == '\x0D') { + this.debug("write:: ch: " + ch.charCodeAt(0) + ", '\\x0D'"); + } else { + this.debug("write:: ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'"); + } + //dump("ch: " + ch.charCodeAt(0) + ", '" + (ch == '\x1b' ? "ESC" : ch) + "'\n"); + switch (ch) { + case '\x00': + case '\x7f': + case '\x07': /* bell, ignore it */ + this.debug("write:: ignoring bell character: " + ch); + continue; + case '\a': + case '\b': + case '\t': + case '\r': + this.addch(ch); + continue; + case '\n': + case '\v': + case '\f': // what a mess + yx = this.getyx(); + myx = this.getmaxyx(); + if (yx.y >= myx.y) { + this.scroll(); + this.move(myx.y, 0); + } else + this.move(yx.y + 1, 0); + continue; + case '\x18': + case '\x1a': + this.esc_state_ = 0; + this.debug("write:: set escape state: 0"); + continue; + case '\x1b': + this.esc_state_ = 1; + this.debug("write:: set escape state: 1"); + continue; + case '\x9b': + this.esc_state_ = 2; + this.debug("write:: set escape state: 2"); + continue; + } + // not a recognized control character + switch (this.esc_state_) { + case 0: // not in escape sequence + this.addch(ch); + break; + case 1: // just saw ESC + switch (ch) { + case '[': + this.esc_state_ = 2; + this.debug("write:: set escape state: 2"); + break; + case '=': + /* Set keypade mode (ignored) */ + this.debug("write:: set keypade mode: ignored"); + this.esc_state_ = 0; + break; + case '>': + /* Reset keypade mode (ignored) */ + this.debug("write:: reset keypade mode: ignored"); + this.esc_state_ = 0; + break; + case 'H': + /* Set tab at cursor column (ignored) */ + this.debug("write:: set tab cursor column: ignored"); + this.esc_state_ = 0; + break; + } + break; + case 2: // just saw CSI + switch (ch) { + case 'K': + /* Erase in Line */ + this.esc_state_ = 0; + this.clrtoeol(); + continue; + case 'H': + /* Move to (0,0). */ + this.esc_state_ = 0; + this.move(0, 0); + continue; + case 'J': + /* Clear to the bottom. */ + this.esc_state_ = 0; + this.clrtobot(); + continue; + case '?': + /* Special VT100 mode handling. */ + this.esc_state_ = 5; + this.debug("write:: special vt100 mode"); + continue; + } + // Drop through to next case. + this.csi_parms_ = [0]; + this.debug("write:: set escape state: 3"); + this.esc_state_ = 3; + case 3: // saw CSI and parameters + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + x = this.csi_parms_.pop(); + this.csi_parms_.push(x * 10 + ch * 1); + this.debug("csi_parms_: " + this.csi_parms_); + continue; + case ';': + if (this.csi_parms_.length < 17) + this.csi_parms_.push(0); + continue; + } + this.esc_state_ = 0; + switch (ch) { + case 'A': + // Cursor Up [{COUNT}A + this.move(this.row_ - Math.max(1, this.csi_parms_[0]), + this.col_); + break; + case 'B': + // Cursor Down [{COUNT}B + this.move(this.row_ + Math.max(1, this.csi_parms_[0]), + this.col_); + break; + case 'C': + // Cursor Forward [{COUNT}C + this.move(this.row_, + this.col_ + Math.max(1, this.csi_parms_[0])); + break; + case 'c': + this.warn("write:: got TERM query"); + break; + case 'D': + // Cursor Backward [{COUNT}D + this.move(this.row_, + this.col_ - Math.max(1, this.csi_parms_[0])); + break; + case 'f': + case 'H': + // Cursor Home [{ROW};{COLUMN}H + this.csi_parms_.push(0); + this.move(this.csi_parms_[0] - 1, + this.csi_parms_[1] - 1); + break; + case 'J': + switch (this.csi_parms_[0]) { + case 0: + this.clrtobot(); + break; + case 2: + this.clear(); + this.move(0, 0); + } + break; + case 'm': + for (j=0; j[? + // Expect a number - the reset type + this.csi_parms_ = [ch]; + this.esc_state_ = 6; + break; + case 6: // Reset mode handling, saw [?1 + // Expect a letter - the mode target, example: + // [?1l : cursor key mode = cursor + // [?1h : save current screen, create new empty + // screen and position at 0,0 + // [?5l : White on blk + // XXX: Ignored for now. + //dump("Saw reset mode: [?" + this.csi_parms_[0] + ch + "\n"); + this.esc_state_ = 0; + this.debug("write:: set escape state: 0"); + break; + } + } + this.refresh(); +} + +VT100.prototype.debug = function(message) { + if (this.debug_) { + dump(message + "\n"); + } +} + +VT100.prototype.warn = function(message) { + dump(message + "\n"); +} diff --git a/include/black.css b/include/black.css deleted file mode 100644 index 4cc195e..0000000 --- a/include/black.css +++ /dev/null @@ -1,122 +0,0 @@ -body { - margin: 0; - font-size: 13px; - color: #111; - font-family: "Helvetica"; -} - -#VNC_controls { - background: #111; - line-height: 1em; - color: #FFF; - overflow: hidden; - padding: 4px 24px; -} - -#VNC_controls ul { - list-style:none; - list-style-position: outside; - margin: 0; - padding: 0; -} -#VNC_controls li { - margin-right: 15px; - padding: 2px 0; - float: left; -} -#VNC_controls li input[type=text], -#VNC_controls li input[type=password] { - border: 2px solid #333; -} - -#VNC_host { - width: 100; -} -#VNC_port { - width: 50; -} -#VNC_password { - width: 80; -} -#VNC_encrypt { -} -#VNC_connect_button { - width: 100px; -} - -#VNC_status_bar td { - padding: 0px; - margin: 0px; -} -#VNC_status_bar div { - font-size: 12px; - font-weight: bold; - text-align: center; - margin: 0px; - padding: 1em; -} -.VNC_status_button { - font-size: 10px; - margin: 0px; - padding: 0px; -} -#VNC_status { - text-align: center; -} -#VNC_settings_menu { - display: none; - position: absolute; - width: 12em; - border: 1px solid #888; - background-color: #f0f2f6; - padding: 5px; margin: 3px; - z-index: 100; opacity: 1; - text-align: left; white-space: normal; -} -#VNC_settings_menu ul { - list-style: none; - margin: 0; - padding: 0; -} - -.VNC_buttons_right { - text-align: right; -} -.VNC_buttons_left { - text-align: left; -} -.VNC_status_normal { - background: #111; - color: #fff; -} -.VNC_status_error { - background: #111; - color: #f44; -} -.VNC_status_warn { - background: #111; - color: #ff4; -} - -#VNC_screen { - -webkit-border-radius: 10px; - -moz-border-radius: 10px; - border-radius: 10px; - background: #111; - padding: 20px; - margin: 0 auto; - color: #FFF; - margin-top: 20px; - text-align: center; - - /* This causes the width of the outer div(#screen) honor the size of the inner (#vnc) div */ - display: table; - table-layout: auto; -} -#VNC_canvas { - background: #111; - margin: 0 auto; -} -#VNC_clipboard { - display: none; -} diff --git a/include/canvas.js b/include/canvas.js deleted file mode 100644 index 0e15a65..0000000 --- a/include/canvas.js +++ /dev/null @@ -1,783 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2010 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -"use strict"; -/*jslint browser: true, white: false, bitwise: false */ -/*global window, Util, Base64 */ - -Canvas = function(conf) { - -conf = conf || {}; // Configuration -var that = {}, // Public API interface - - // Private Canvas namespace variables - c_forceCanvas = false, - - c_width = 0, - c_height = 0, - - c_prevStyle = "", - - c_keyPress = null, - c_mouseButton = null, - c_mouseMove = null, - - c_webkit_bug = false, - c_flush_timer = null; - -// Configuration settings -function cdef(v, type, defval, desc) { - Util.conf_default(conf, that, v, type, defval, desc); } - -// Capability settings, default can be overridden -cdef('prefer_js', 'raw', null, 'Prefer Javascript over canvas methods'); -cdef('cursor_uri', 'raw', null, 'Can we render cursor using data URI'); - -cdef('target', 'dom', null, 'Canvas element for VNC viewport'); -cdef('focusContainer', 'dom', document, 'DOM element that traps keyboard input'); -cdef('true_color', 'bool', true, 'Request true color pixel data'); -cdef('focused', 'bool', true, 'Capture and send key strokes'); -cdef('colourMap', 'raw', [], 'Colour map array (not true color)'); -cdef('scale', 'float', 1, 'VNC viewport scale factor'); - -cdef('render_mode', 'str', '', 'Canvas rendering mode (read-only)'); - -// Override some specific getters/setters -that.set_prefer_js = function(val) { - if (val && c_forceCanvas) { - Util.Warn("Preferring Javascript to Canvas ops is not supported"); - return false; - } - conf.prefer_js = val; - return true; -}; - -that.get_colourMap = function(idx) { - if (typeof idx === 'undefined') { - return conf.colourMap; - } else { - return conf.colourMap[idx]; - } -}; - -that.set_colourMap = function(val, idx) { - if (typeof idx === 'undefined') { - conf.colourMap = val; - } else { - conf.colourMap[idx] = val; - } -}; - -that.set_render_mode = function () { throw("render_mode is read-only"); }; - -// Add some other getters/setters -that.get_width = function() { - return c_width; -}; -that.get_height = function() { - return c_height; -}; - - - -// -// Private functions -// - -// Create the public API interface -function constructor() { - Util.Debug(">> Canvas.init"); - - var c, ctx, func, origfunc, imgTest, tval, 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 (! conf.ctx) { conf.ctx = c.getContext('2d'); } - ctx = conf.ctx; - - 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(); - - /* - * Determine browser Canvas feature support - * and select fastest rendering methods - */ - tval = 0; - try { - imgTest = ctx.getImageData(0, 0, 1,1); - imgTest.data[0] = 123; - imgTest.data[3] = 255; - ctx.putImageData(imgTest, 0, 0); - tval = ctx.getImageData(0, 0, 1, 1).data[0]; - if (tval === 123) { - has_imageData = true; - } - } catch (exc1) {} - - if (has_imageData) { - Util.Info("Canvas supports imageData"); - c_forceCanvas = false; - if (ctx.createImageData) { - // If it's there, it's faster - Util.Info("Using Canvas createImageData"); - conf.render_mode = "createImageData rendering"; - that.imageData = that.imageDataCreate; - } else if (ctx.getImageData) { - // I think this is mostly just Opera - Util.Info("Using Canvas getImageData"); - conf.render_mode = "getImageData rendering"; - that.imageData = that.imageDataGet; - } - Util.Info("Prefering javascript operations"); - if (conf.prefer_js === null) { - conf.prefer_js = true; - } - that.rgbxImage = that.rgbxImageData; - that.cmapImage = that.cmapImageData; - } else { - Util.Warn("Canvas lacks imageData, using fillRect (slow)"); - conf.render_mode = "fillRect rendering (slow)"; - c_forceCanvas = true; - conf.prefer_js = false; - that.rgbxImage = that.rgbxImageFill; - that.cmapImage = that.cmapImageFill; - } - - if (UE.webkit && UE.webkit >= 534.7 && UE.webkit <= 534.9) { - // Workaround WebKit canvas rendering bug #46319 - conf.render_mode += ", webkit bug workaround"; - Util.Debug("Working around WebKit bug #46319"); - c_webkit_bug = true; - for (func in {"fillRect":1, "copyImage":1, "rgbxImage":1, - "cmapImage":1, "blitStringImage":1}) { - that[func] = (function() { - var myfunc = that[func]; // Save original function - //Util.Debug("Wrapping " + func); - return function() { - myfunc.apply(this, arguments); - if (!c_flush_timer) { - c_flush_timer = setTimeout(that.flush, 100); - } - }; - })(); - } - } - - /* - * 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; - that.changeCursor(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; - } - - conf.focused = true; - - Util.Debug("<< Canvas.init"); - return that ; -} - -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); - bmask = 1 << evt.button; - //Util.Debug('mouse ' + pos.x + "," + pos.y + " down: " + down + " bmask: " + bmask); - if (c_mouseButton) { - c_mouseButton(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; - 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 (c_mouseButton) { - c_mouseButton(pos.x, pos.y, 1, bmask); - c_mouseButton(pos.x, pos.y, 0, bmask); - } - Util.stopEvent(e); - return false; -} - -function onMouseMove(e) { - var evt, pos; - 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 (c_mouseMove) { - c_mouseMove(pos.x, pos.y); - } -} - -function onKeyDown(e) { - //Util.Debug("keydown: " + getKeysym(e)); - if (! conf.focused) { - return true; - } - if (c_keyPress) { - c_keyPress(getKeysym(e), 1, e.ctrlKey, e.shiftKey, e.altKey); - } - Util.stopEvent(e); - return false; -} - -function onKeyUp(e) { - //Util.Debug("keyup: " + getKeysym(e)); - if (! conf.focused) { - return true; - } - if (c_keyPress) { - c_keyPress(getKeysym(e), 0, e.ctrlKey, e.shiftKey, e.altKey); - } - 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 < c_width) && (pos.y < c_height)) { - //Util.Debug("mouse event disabled"); - Util.stopEvent(e); - return false; - } - //Util.Debug("mouse event not disabled"); - return true; -} - -// -// Public API interface functions -// - -that.getContext = function () { - return conf.ctx; -}; - -that.start = function(keyPressFunc, mouseButtonFunc, mouseMoveFunc) { - var c; - Util.Debug(">> Canvas.start"); - - c = conf.target; - c_keyPress = keyPressFunc || null; - c_mouseButton = mouseButtonFunc || null; - c_mouseMove = mouseMoveFunc || null; - - Util.addEvent(conf.focusContainer, 'keydown', onKeyDown); - Util.addEvent(conf.focusContainer, 'keyup', onKeyUp); - 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(conf.focusContainer, 'click', onMouseDisable); - Util.addEvent(conf.focusContainer.body, 'contextmenu', onMouseDisable); - - Util.Debug("<< Canvas.start"); -}; - -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 (conf.scale === factor) { - //Util.Debug("Canvas 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)"; -}; - -that.resize = function(width, height, true_color) { - var c = conf.target; - - if (typeof true_color !== "undefined") { - conf.true_color = true_color; - } - c_prevStyle = ""; - - c.width = width; - c.height = height; - - c_width = c.offsetWidth; - c_height = c.offsetHeight; - - that.rescale(conf.scale); -}; - -that.clear = function() { - that.resize(640, 20); - conf.ctx.clearRect(0, 0, c_width, c_height); -}; - -that.stop = function() { - var c = conf.target; - Util.removeEvent(conf.focusContainer, 'keydown', onKeyDown); - Util.removeEvent(conf.focusContainer, 'keyup', onKeyUp); - 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(conf.focusContainer, 'click', onMouseDisable); - Util.removeEvent(conf.focusContainer.body, 'contextmenu', onMouseDisable); - - // Turn off cursor rendering - if (conf.cursor_uri) { - c.style.cursor = "default"; - } -}; - -that.flush = function() { - var old_val; - //Util.Debug(">> flush"); - // Force canvas redraw (for webkit bug #46319 workaround) - old_val = conf.target.style.marginRight; - conf.target.style.marginRight = "1px"; - c_flush_timer = null; - setTimeout(function () { - conf.target.style.marginRight = old_val; - }, 1); -}; - -that.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) { - conf.ctx.fillStyle = newStyle; - c_prevStyle = newStyle; - } -}; - -that.fillRect = function(x, y, width, height, color) { - that.setFillColor(color); - conf.ctx.fillRect(x, y, width, height); -}; - -that.copyImage = function(old_x, old_y, new_x, new_y, width, height) { - conf.ctx.drawImage(conf.target, old_x, old_y, width, height, - new_x, new_y, width, height); -}; - -/* - * Tile rendering functions optimized for rendering engines. - * - * - In Chrome/webkit, Javascript image data array manipulations are - * faster than direct Canvas fillStyle, fillRect rendering. In - * gecko, Javascript array handling is much slower. - */ -that.getTile = function(x, y, width, height, color) { - var img, data = [], p, rgb, red, green, blue, j, i; - img = {'x': x, 'y': y, 'width': width, 'height': height, - 'data': 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; - } - } else { - that.fillRect(x, y, width, height, color); - } - return img; -}; - -that.setSubTile = function(img, x, y, w, h, color) { - var data, p, rgb, red, green, blue, width, j, i, xend, yend; - if (conf.prefer_js) { - data = img.data; - width = img.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; - } - } - } else { - that.fillRect(img.x + x, img.y + y, w, h, color); - } -}; - -that.putTile = function(img) { - if (conf.prefer_js) { - that.rgbxImage(img.x, img.y, img.width, img.height, img.data, 0); - } else { - // No-op, under gecko already done by setSubTile - } -}; - -that.imageDataGet = function(width, height) { - return conf.ctx.getImageData(0, 0, width, height); -}; -that.imageDataCreate = function(width, height) { - return conf.ctx.createImageData(width, height); -}; - -that.rgbxImageData = function(x, y, width, height, arr, offset) { - var img, i, j, data; - img = that.imageData(width, height); - data = img.data; - for (i=0, j=offset; i < (width * height * 4); i=i+4, j=j+4) { - data[i + 0] = arr[j + 0]; - data[i + 1] = arr[j + 1]; - data[i + 2] = arr[j + 2]; - data[i + 3] = 255; // Set Alpha - } - conf.ctx.putImageData(img, x, y); -}; - -// really slow fallback if we don't have imageData -that.rgbxImageFill = function(x, y, width, height, arr, offset) { - var i, j, sx = 0, sy = 0; - for (i=0, j=offset; i < (width * height); i+=1, j+=4) { - that.fillRect(x+sx, y+sy, 1, 1, [arr[j+0], arr[j+1], arr[j+2]]); - sx += 1; - if ((sx % width) === 0) { - sx = 0; - sy += 1; - } - } -}; - -that.cmapImageData = function(x, y, width, height, arr, offset) { - var img, i, j, data, rgb, cmap; - img = that.imageData(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 + 0] = rgb[0]; - data[i + 1] = rgb[1]; - data[i + 2] = rgb[2]; - data[i + 3] = 255; // Set Alpha - } - conf.ctx.putImageData(img, x, y); -}; - -that.cmapImageFill = function(x, y, width, height, arr, offset) { - var i, j, sx = 0, sy = 0, cmap; - cmap = conf.colourMap; - for (i=0, j=offset; i < (width * height); i+=1, j+=1) { - that.fillRect(x+sx, y+sy, 1, 1, [arr[j]]); - sx += 1; - if ((sx % width) === 0) { - sx = 0; - sy += 1; - } - } -}; - - -that.blitImage = function(x, y, width, height, arr, offset) { - if (conf.true_color) { - that.rgbxImage(x, y, width, height, arr, offset); - } else { - that.cmapImage(x, y, width, height, arr, offset); - } -}; - -that.blitStringImage = function(str, x, y) { - var img = new Image(); - img.onload = function () { conf.ctx.drawImage(img, x, y); }; - img.src = str; -}; - -that.changeCursor = function(pixels, mask, hotx, hoty, w, h) { - var cur = [], cmap, rgb, IHDRsz, ANDsz, XORsz, url, idx, alpha, x, y; - //Util.Debug(">> changeCursor, x: " + hotx + ", y: " + hoty + ", w: " + w + ", h: " + h); - - if (conf.cursor_uri === false) { - Util.Warn("changeCursor called but no cursor data URI support"); - return; - } - - // 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 ); - }; - - cmap = conf.colourMap; - IHDRsz = 40; - ANDsz = w * h * 4; - XORsz = Math.ceil( (w * h) / 8.0 ); - - // Main header - cur.push16le(0); // Reserved - cur.push16le(2); // .CUR type - cur.push16le(1); // Number of images, 1 for non-animated ico - - // Cursor #1 header - cur.push(w); // width - cur.push(h); // height - cur.push(0); // colors, 0 -> true-color - cur.push(0); // reserved - cur.push16le(hotx); // hotspot x coordinate - cur.push16le(hoty); // hotspot y coordinate - cur.push32le(IHDRsz + XORsz + ANDsz); // cursor data byte size - cur.push32le(22); // offset of cursor data in the file - - // Cursor #1 InfoHeader - cur.push32le(IHDRsz); // Infoheader size - cur.push32le(w); // Cursor width - cur.push32le(h*2); // XOR+AND height - cur.push16le(1); // number of planes - cur.push16le(32); // bits per pixel - cur.push32le(0); // Type of compression - cur.push32le(XORsz + ANDsz); // Size of Image - cur.push32le(0); - cur.push32le(0); - cur.push32le(0); - cur.push32le(0); - - // XOR/color data - 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 (conf.true_color) { - idx = ((w * y) + x) * 4; - cur.push(pixels[idx + 2]); // blue - cur.push(pixels[idx + 1]); // green - cur.push(pixels[idx + 0]); // red - cur.push(alpha); // red - } else { - 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 - } - } - } - - // AND/bitmask data (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); - conf.target.style.cursor = "url(" + url + ") " + hotx + " " + hoty + ", default"; - //Util.Debug("<< changeCursor, cur.length: " + cur.length); -}; - - - -return constructor(); // Return the public API interface - -} // End of Canvas() - - -/* Translate DOM key down/up event to keysym value */ -function getKeysym(e) { - var evt, keysym; - evt = (e ? e : window.event); - - /* Remap modifier and special keys */ - switch ( evt.keyCode ) { - case 8 : keysym = 0xFF08; break; // BACKSPACE - case 9 : keysym = 0xFF09; break; // TAB - case 13 : keysym = 0xFF0D; break; // ENTER - case 27 : keysym = 0xFF1B; break; // ESCAPE - case 45 : keysym = 0xFF63; break; // INSERT - 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 37 : keysym = 0xFF51; break; // LEFT - case 38 : keysym = 0xFF52; break; // UP - case 39 : keysym = 0xFF53; break; // RIGHT - case 40 : keysym = 0xFF54; break; // DOWN - 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 - 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) - default : keysym = evt.keyCode; break; - } - - /* 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) - if (Util.Engine.gecko) { - 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; - } - - return keysym; -} - diff --git a/include/des.js b/include/des.js deleted file mode 100644 index 1f95285..0000000 --- a/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/include/keysym.js b/include/keysym.js new file mode 100644 index 0000000..b24ac88 --- /dev/null +++ b/include/keysym.js @@ -0,0 +1,99 @@ +/* + * from noVNC: HTML5 VNC client + * Copyright (C) 2010 Joel Martin + * Licensed under LGPL-3 (see LICENSE.txt) + */ + +/* Translate DOM key down/up event to keysym value */ +function getKeysym(e) { + var evt, keysym; + evt = (e ? e : window.event); + + /* Remap modifier and special keys */ + switch ( evt.keyCode ) { + case 8 : keysym = 0xFF08; break; // BACKSPACE + case 9 : keysym = 0xFF09; break; // TAB + case 13 : keysym = 0xFF0D; break; // ENTER + case 27 : keysym = 0xFF1B; break; // ESCAPE + case 45 : keysym = 0xFF63; break; // INSERT + 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 37 : keysym = 0xFF51; break; // LEFT + case 38 : keysym = 0xFF52; break; // UP + case 39 : keysym = 0xFF53; break; // RIGHT + case 40 : keysym = 0xFF54; break; // DOWN + 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 + 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) + default : keysym = evt.keyCode; break; + } + + /* 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) + if (Util.Engine.gecko) { + 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; + } + + return keysym; +} + diff --git a/include/plain.css b/include/plain.css deleted file mode 100644 index d8ee942..0000000 --- a/include/plain.css +++ /dev/null @@ -1,97 +0,0 @@ -#VNC_controls { - overflow: hidden; -} -#VNC_controls ul { - list-style: none; - margin: 0; - padding: 0; -} -#VNC_controls li { - float: left; - margin-right: 15px; -} - -#VNC_host { - width: 100; -} -#VNC_port { - width: 50; -} -#VNC_password { - width: 80; -} -#VNC_encrypt { -} -#VNC_connectTimeout { - width: 30; -} -#VNC_connect_button { - width: 110px; -} - -#VNC_status_bar td { - margin-top: 15px; - padding: 0px; - margin: 0px; -} -#VNC_status_bar div { - font-size: 12px; - margin: 0px; - padding: 0px; -} -.VNC_status_button { - font-size: 10px; - margin: 0px; - padding: 0px; -} -#VNC_status { - text-align: center; -} -#VNC_settings_menu { - display: none; - position: absolute; - width: 12em; - border: 1px solid #888; - background-color: #f0f2f6; - padding: 5px; margin: 3px; - z-index: 100; opacity: 1; - text-align: left; white-space: normal; -} -#VNC_settings_menu ul { - list-style: none; - margin: 0; - padding: 0; -} - -.VNC_buttons_right { - text-align: right; -} -.VNC_buttons_left { - text-align: left; -} -.VNC_status_normal { - background: #eee; -} -.VNC_status_error { - background: #f44; -} -.VNC_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 */ -#VNC_screen { - text-align: center; - display: table; -} -#VNC_canvas { - background: #eee; -} - -#VNC_clipboard_clear_button { -} -#VNC_clipboard_text { - font-size: 9; -} - diff --git a/include/playback.js b/include/playback.js deleted file mode 100644 index 4a8106b..0000000 --- a/include/playback.js +++ /dev/null @@ -1,90 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2010 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/include/rfb.js b/include/rfb.js deleted file mode 100644 index 6adccd4..0000000 --- a/include/rfb.js +++ /dev/null @@ -1,1622 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2010 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -"use strict"; -/*jslint white: false, browser: true, bitwise: false, plusplus: false */ -/*global window, WebSocket, Util, Canvas, VNC_native_ws, Base64, DES */ - - -function RFB(conf) { - -conf = conf || {}; // Configuration -var that = {}, // Public API interface - - // Pre-declare private functions used before definitions (jslint) - init_vars, updateState, init_msg, normal_msg, recv_message, - framebufferUpdate, print_stats, - - pixelFormat, clientEncodings, fbUpdateRequest, - keyEvent, pointerEvent, clientCutText, - - extract_data_uri, scan_tight_imgQ, - - send_array, checkEvents, // Overridable for testing - - - // - // Private RFB namespace variables - // - rfb_host = '', - rfb_port = 5900, - rfb_password = '', - - 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, // Web Socket object - canvas = null, // Canvas object - sendTimer = null, // Send Queue check timer - connTimer = null, // connection timer - disconnTimer = null, // disconnection timer - msgTimer = null, // queued handle_message timer - - // Receive and send queues - rQ = [], // Receive Queue - rQi = 0, // Receive Queue Index - rQmax = 100000, // Max size before compacting - sQ = [], // Send Queue - - // 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 = 100, - 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, - - /* Mouse state */ - mouse_buttonMask = 0, - mouse_arr = []; - - -// -// Configuration settings -// -function cdef(v, type, defval, desc) { - Util.conf_default(conf, that, v, type, defval, desc); } - -cdef('target', 'str', null, 'VNC viewport rendering Canvas'); -cdef('focusContainer', 'dom', document, 'Area that traps keyboard input'); - -cdef('encrypt', 'bool', false, 'Use TLS/SSL/wss encryption'); -cdef('true_color', 'bool', true, 'Request true color pixel data'); -cdef('local_cursor', 'bool', false, 'Request locally rendered cursor'); -cdef('shared', 'bool', true, 'Request shared mode'); - -cdef('connectTimeout', 'int', 2, 'Time (s) to wait for connection'); -cdef('disconnectTimeout', 'int', 3, 'Time (s) to wait for disconnection'); -cdef('check_rate', 'int', 217, 'Timing (ms) of send/receive check'); -cdef('fbu_req_rate', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests'); - -cdef('updateState', - 'func', function() { Util.Debug("updateState stub"); }, - 'callback: state update'); -cdef('clipboardReceive', - 'func', function() { Util.Debug("clipboardReceive stub"); }, - 'callback: clipboard contents received'); - - -// Override/add some specific getters/setters -that.set_local_cursor = function(cursor) { - if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) { - conf.local_cursor = false; - } else { - if (canvas.get_cursor_uri()) { - conf.local_cursor = true; - } else { - Util.Warn("Browser does not support local cursor"); - } - } -}; - -that.get_canvas = function() { - return canvas; -}; - - - - -// -// Private functions -// - -// -// Receive Queue functions -// -function rQlen() { - return rQ.length - rQi; -} - -function rQshift16() { - return (rQ[rQi++] << 8) + - (rQ[rQi++] ); -} -function rQshift32() { - return (rQ[rQi++] << 24) + - (rQ[rQi++] << 16) + - (rQ[rQi++] << 8) + - (rQ[rQi++] ); -} -function rQshiftStr(len) { - var arr = rQ.slice(rQi, rQi + len); - rQi += len; - return arr.map(function (num) { - return String.fromCharCode(num); } ).join(''); - -} -function rQshiftBytes(len) { - rQi += len; - return rQ.slice(rQi-len, rQi); -} - -// Check to see if we must wait for 'num' bytes (default to FBU.bytes) -// to be available in the receive queue. Return true if we need to -// wait (and possibly print a debug message), otherwise false. -function rQwait(msg, num, goback) { - if (typeof num !== 'number') { num = FBU.bytes; } - var rQlen = rQ.length - rQi; // Skip rQlen() function call - if (rQlen < num) { - if (goback) { - if (rQi < goback) { - throw("rQwait cannot backup " + goback + " bytes"); - } - rQi -= goback; - } - //Util.Debug(" waiting for " + (num-rQlen) + - // " " + msg + " byte(s)"); - return true; // true means need more data - } - return false; -} - - -// -// Setup routines -// - -// Create the public API interface and initialize -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 canvas - try { - canvas = new Canvas({'target': conf.target, - 'focusContainer': conf.focusContainer}); - } catch (exc) { - Util.Error("Canvas exception: " + exc); - updateState('fatal', "No working Canvas"); - } - rmode = canvas.get_render_mode(); - - init_vars(); - - /* Check web-socket-js if no builtin WebSocket support */ - if (VNC_native_ws) { - 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 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 init_ws() { - Util.Debug(">> RFB.init_ws"); - - var uri = ""; - if (conf.encrypt) { - uri = "wss://"; - } else { - uri = "ws://"; - } - uri += rfb_host + ":" + rfb_port + "/"; - Util.Info("connecting to " + uri); - ws = new WebSocket(uri); - - ws.onmessage = recv_message; - ws.onopen = function(e) { - Util.Debug(">> WebSocket.onopen"); - if (rfb_state === "connect") { - updateState('ProtocolVersion', "Starting VNC handshake"); - } else { - fail("Got unexpected WebSockets connection"); - } - Util.Debug("<< WebSocket.onopen"); - }; - ws.onclose = function(e) { - Util.Debug(">> WebSocket.onclose"); - 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'); - } - Util.Debug("<< WebSocket.onclose"); - }; - ws.onerror = function(e) { - Util.Debug(">> WebSocket.onerror"); - fail("WebSocket error"); - Util.Debug("<< WebSocket.onerror"); - }; - - Util.Debug("<< RFB.init_ws"); -} - -init_vars = function() { - /* Reset state */ - rQ = []; - rQi = 0; - sQ = []; - 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 (var i=0; i < encodings.length; i+=1) { - encStats[encodings[i][1]][0] = 0; - } -}; - -// Print statistics -print_stats = function() { - var i, encName, 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 -// - - -/* - * Running states: - * disconnected - idle state - * normal - connected - * - * Page states: - * loaded - page load, equivalent to disconnected - * connect - starting initialization - * disconnect - starting disconnect - * failed - abnormal transition to disconnected - * fatal - failed to load page, or fatal error - * - * VNC initialization states: - * ProtocolVersion - * Security - * Authentication - * password - waiting for password, not part of RFB - * SecurityResult - * ClientInitialization - not triggered by server message - * ServerInitialization - */ -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 (canvas && canvas.getContext()) { - canvas.stop(); - if (Util.get_logging() !== 'debug') { - canvas.clear(); - } - } - - if (ws) { - if ((ws.readyState === WebSocket.OPEN) || - (ws.readyState === WebSocket.CONNECTING)) { - Util.Info("Closing WebSocket connection"); - ws.close(); - } - ws.onmessage = function (e) { return; }; - } - } - - 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(); - init_ws(); - - // 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); - } else { - conf.updateState(that, state, oldstate, statusMsg); - } -}; -function fail(msg) { - updateState('failed', msg); - return false; -} - -function encode_message() { - /* base64 encode */ - return Base64.encode(sQ); -} - -function decode_message(data) { - //Util.Debug(">> decode_message: " + data); - /* base64 decode */ - rQ = rQ.concat(Base64.decode(data, 0)); - //Util.Debug(">> decode_message, rQ: " + rQ); -} - -function handle_message() { - //Util.Debug("rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + rQlen() + ")"); - if (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() && 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"); - } - } - // Compact the queue - if (rQ.length > rQmax) { - //Util.Debug("Compacting receive queue"); - rQ = rQ.slice(rQi); - rQi = 0; - } - break; - default: - init_msg(); - break; - } -} - -recv_message = function(e) { - //Util.Debug(">> recv_message: " + e.data.length); - - try { - decode_message(e.data); - if (rQlen() > 0) { - handle_message(); - } else { - Util.Debug("Ignoring empty message"); - } - } catch (exc) { - if (typeof exc.stack !== 'undefined') { - Util.Warn("recv_message, caught exception: " + exc.stack); - } else if (typeof exc.description !== 'undefined') { - Util.Warn("recv_message, caught exception: " + exc.description); - } else { - Util.Warn("recv_message, caught exception:" + exc); - } - if (typeof exc.name !== 'undefined') { - fail(exc.name + ": " + exc.message); - } else { - fail(exc); - } - } - //Util.Debug("<< recv_message"); -}; - -// overridable for testing -send_array = function(arr) { - //Util.Debug(">> send_array: " + arr); - sQ = sQ.concat(arr); - if (ws.bufferedAmount === 0) { - //Util.Debug("arr: " + arr); - //Util.Debug("sQ: " + sQ); - ws.send(encode_message(sQ)); - sQ = []; - } else { - Util.Debug("Delaying send"); - } -}; - -function send_string(str) { - //Util.Debug(">> send_string: " + str); - send_array(str.split('').map( - function (chr) { return chr.charCodeAt(0); } ) ); -} - -function genDES(password, challenge) { - var i, passwd = [], des; - 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_array(mouse_arr.concat(fbUpdateRequest(1))); - send_array(mouse_arr); - setTimeout(function() { - send_array(fbUpdateRequest(1)); - }, 50); - - mouse_arr = []; - return true; - } else { - return false; - } -} - -// overridable for testing -checkEvents = function() { - var now; - if (rfb_state === 'normal') { - if (! flushClient()) { - now = new Date().getTime(); - if (now > last_req_time + conf.fbu_req_rate) { - last_req_time = now; - send_array(fbUpdateRequest(1)); - } - } - } - setTimeout(checkEvents, conf.check_rate); -}; - -function keyPress(keysym, down) { - var arr; - arr = keyEvent(keysym, down); - arr = arr.concat(fbUpdateRequest(1)); - send_array(arr); -} - -function mouseButton(x, y, down, bmask) { - if (down) { - mouse_buttonMask |= bmask; - } else { - mouse_buttonMask ^= bmask; - } - mouse_arr = mouse_arr.concat( pointerEvent(x, y) ); - flushClient(); -} - -function mouseMove(x, y) { - //Util.Debug('>> mouseMove ' + x + "," + y); - mouse_arr = mouse_arr.concat( pointerEvent(x, 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, true_color, name_length; - - //Util.Debug("rQ (" + rQlen() + ") " + rQ); - switch (rfb_state) { - - case 'ProtocolVersion' : - if (rQlen() < 12) { - return fail("Incomplete protocol version"); - } - sversion = 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. - if (ws.bufferedAmount === 0) { - if (sQ) { - ws.send(encode_message(sQ)); - sQ = []; - } - } else { - Util.Debug("Delaying send"); - } - }, 50); - } - - cversion = "00" + parseInt(rfb_version,10) + - ".00" + ((rfb_version * 10) % 10); - 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 = rQ[rQi++]; - if (rQwait("security type", num_types, 1)) { return false; } - if (num_types === 0) { - strlen = rQshift32(); - reason = rQshiftStr(strlen); - return fail("Security failure: " + reason); - } - rfb_auth_scheme = 0; - types = 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); - } - - send_array([rfb_auth_scheme]); - } else { - // Server decides - if (rQwait("security scheme", 4)) { return false; } - rfb_auth_scheme = 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 (rQwait("auth reason", 4)) { return false; } - strlen = rQshift32(); - reason = rQshiftStr(strlen); - return fail("Auth failure: " + reason); - case 1: // no authentication - if (rfb_version >= 3.8) { - updateState('SecurityResult'); - return; - } else { - // Fall through to ClientInitialisation - } - break; - case 2: // VNC authentication - if (rfb_password.length === 0) { - updateState('password', "Password Required"); - return; - } - if (rQwait("auth challenge", 16)) { return false; } - challenge = 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"); - send_array(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 (rQwait("VNC auth response ", 4)) { return false; } - switch (rQshift32()) { - case 0: // OK - // Fall through to ClientInitialisation - break; - case 1: // failed - if (rfb_version >= 3.8) { - length = rQshift32(); - if (rQwait("SecurityResult reason", length, 8)) { - return false; - } - reason = 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' : - send_array([conf.shared ? 1 : 0]); // ClientInitialisation - updateState('ServerInitialisation', "Authentication OK"); - break; - - case 'ServerInitialisation' : - if (rQwait("server initialization", 24)) { return false; } - - /* Screen size */ - fb_width = rQshift16(); - fb_height = rQshift16(); - - /* PIXEL_FORMAT */ - bpp = rQ[rQi++]; - depth = rQ[rQi++]; - big_endian = rQ[rQi++]; - true_color = rQ[rQi++]; - - Util.Info("Screen: " + fb_width + "x" + fb_height + - ", bpp: " + bpp + ", depth: " + depth + - ", big_endian: " + big_endian + - ", true_color: " + true_color); - - /* Connection name/title */ - rQshiftStr(12); - name_length = rQshift32(); - fb_name = rQshiftStr(name_length); - - canvas.resize(fb_width, fb_height, conf.true_color); - canvas.start(keyPress, mouseButton, mouseMove); - - 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(fbUpdateRequest(0)); - timing.fbu_rt_start = (new Date()).getTime(); - send_array(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, - c, first_colour, num_colours, red, green, blue; - - if (FBU.rects > 0) { - msg_type = 0; - } else { - msg_type = rQ[rQi++]; - } - switch (msg_type) { - case 0: // FramebufferUpdate - ret = framebufferUpdate(); // false means need more data - break; - case 1: // SetColourMapEntries - Util.Debug("SetColourMapEntries"); - rQi++; // Padding - first_colour = rQshift16(); // First colour - num_colours = rQshift16(); - for (c=0; c < num_colours; c+=1) { - red = rQshift16(); - //Util.Debug("red before: " + red); - red = parseInt(red / 256, 10); - //Util.Debug("red after: " + red); - green = parseInt(rQshift16() / 256, 10); - blue = parseInt(rQshift16() / 256, 10); - canvas.set_colourMap([red, green, blue], first_colour + c); - } - Util.Info("Registered " + num_colours + " colourMap entries"); - //Util.Debug("colourMap: " + canvas.get_colourMap()); - break; - case 2: // Bell - Util.Warn("Bell (unsupported)"); - break; - case 3: // ServerCutText - Util.Debug("ServerCutText"); - if (rQwait("ServerCutText header", 7, 1)) { return false; } - rQshiftBytes(3); // Padding - length = rQshift32(); - if (rQwait("ServerCutText", length, 8)) { return false; } - - conf.clipboardReceive(that, rQshiftStr(length)); - break; - default: - fail("Disconnected: illegal server message type " + msg_type); - Util.Debug("rQ.slice(0,30):" + rQ.slice(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: rQ.slice(0,20): " + rQ.slice(0,20)); - if (rQwait("FBU header", 3)) { - if (rQi === 0) { - rQ.unshift(0); // FBU msg_type - } else { - rQi -= 1; - } - return false; - } - rQi++; - FBU.rects = 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 (rQwait("FBU")) { return false; } - if (FBU.bytes === 0) { - if (rQwait("rect header", 12)) { return false; } - /* New FramebufferUpdate */ - - hdr = 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); - - 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 += ", rQlen(): " + 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 - } - } - return true; // We finished this FBU -}; - -// -// FramebufferUpdate encodings -// - -encHandlers.RAW = function display_raw() { - //Util.Debug(">> display_raw (" + 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 (rQwait("RAW")) { return false; } - cur_y = FBU.y + (FBU.height - FBU.lines); - cur_height = Math.min(FBU.lines, - Math.floor(rQlen()/(FBU.width * fb_Bpp))); - canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height, rQ, rQi); - 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 (" + rQlen() + " bytes)"); - return true; -}; - -encHandlers.COPYRECT = function display_copy_rect() { - //Util.Debug(">> display_copy_rect"); - - var old_x, old_y; - - if (rQwait("COPYRECT", 4)) { return false; } - old_x = rQshift16(); - old_y = rQshift16(); - canvas.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 (" + rQlen() + " bytes)"); - var color, x, y, width, height, chunk; - - if (FBU.subrects === 0) { - if (rQwait("RRE", 4+fb_Bpp)) { return false; } - FBU.subrects = rQshift32(); - color = rQshiftBytes(fb_Bpp); // Background - canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); - } - while ((FBU.subrects > 0) && (rQlen() >= (fb_Bpp + 8))) { - color = rQshiftBytes(fb_Bpp); - x = rQshift16(); - y = rQshift16(); - width = rQshift16(); - height = rQshift16(); - canvas.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, tile, color, cur_tile, - tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh; - - 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, rQlen() at least 1 */ - while (FBU.tiles > 0) { - FBU.bytes = 1; - if (rQwait("HEXTILE subencoding")) { return false; } - //Util.Debug(" 2 rQ length: " + rQlen() + " rQ[rQi]: " + rQ[rQi] + " rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + ", FBU.rects: " + FBU.rects + ", FBU.tiles: " + FBU.tiles); - subencoding = rQ[rQi]; // Peek - if (subencoding > 30) { // Raw - fail("Disconnected: illegal hextile subencoding " + subencoding); - //Util.Debug("rQ.slice(0,30):" + rQ.slice(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 (rQwait("hextile subrects header")) { 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 + - ", rQlen():" + rQlen() + ", FBU.bytes:" + FBU.bytes + - " last:" + rQ.slice(FBU.bytes-10, FBU.bytes) + - " next:" + rQ.slice(FBU.bytes-1, FBU.bytes+10)); - */ - if (rQwait("hextile")) { 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 { - canvas.fillRect(x, y, w, h, FBU.background); - } - } else if (FBU.subencoding & 0x01) { // Raw - canvas.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; - } - - tile = canvas.getTile(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; - - canvas.setSubTile(tile, sx, sy, sw, sh, color); - } - } - canvas.putTile(tile); - } - //rQshiftBytes(FBU.bytes); - 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 rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + rQlen() + ")"); - - FBU.bytes = 1; // compression-control byte - if (rQwait("TIGHT compression-control")) { return false; } - - // Get 'compact length' header and data size - getCLength = function (arr, offset) { - var header = 1, data = 0; - data += arr[offset + 0] & 0x7f; - if (arr[offset + 0] & 0x80) { - header += 1; - data += (arr[offset + 1] & 0x7f) << 7; - if (arr[offset + 1] & 0x80) { - header += 1; - data += arr[offset + 2] << 14; - } - } - return [header, data]; - }; - - ctl = rQ[rQi]; - 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 (rQwait("TIGHT " + cmode)) { return false; } - - //Util.Debug(" rQ.slice(0,20): " + rQ.slice(0,20) + " (" + rQlen() + ")"); - //Util.Debug(" cmode: " + cmode); - - // Determine FBU.bytes - switch (cmode) { - case "fill": - rQi++; // shift off ctl - color = rQshiftBytes(fb_depth); - canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color); - break; - case "jpeg": - case "png": - clength = getCLength(rQ, rQi+1); - FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data - if (rQwait("TIGHT " + cmode)) { return false; } - - // We have everything, render it - //Util.Debug(" png, rQlen(): " + rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]); - rQshiftBytes(1 + clength[0]); // shift off ctl + compact length - img = new Image(); - img.onload = scan_tight_imgQ; - FBU.imgQ.push([img, FBU.x, FBU.y]); - img.src = "data:image/" + cmode + - extract_data_uri(rQshiftBytes(clength[1])); - img = null; - break; - } - FBU.bytes = 0; - FBU.rects -= 1; - //Util.Debug(" ending rQ.slice(rQi,rQi+20): " + rQ.slice(rQi,rQi+20) + " (" + 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 img, imgQ, ctx; - ctx = canvas.getContext(); - if (rfb_state === 'normal') { - imgQ = FBU.imgQ; - while ((imgQ.length > 0) && (imgQ[0][0].complete)) { - img = imgQ.shift(); - ctx.drawImage(img[0], img[1], img[2]); - } - setTimeout(scan_tight_imgQ, scan_imgQ_rate); - } -}; - -encHandlers.DesktopSize = function set_desktopsize() { - Util.Debug(">> set_desktopsize"); - fb_width = FBU.width; - fb_height = FBU.height; - canvas.clear(); - canvas.resize(fb_width, fb_height); - timing.fbu_rt_start = (new Date()).getTime(); - // Send a new non-incremental request - send_array(fbUpdateRequest(0)); - - 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 (rQwait("cursor encoding")) { return false; } - - //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h); - - canvas.changeCursor(rQshiftBytes(pixelslength), - 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 (!x) { x = 0; } - if (!y) { y = 0; } - if (!xw) { xw = fb_width; } - if (!yw) { 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; -}; - -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) { - //Util.Debug(">> connect"); - - rfb_host = host; - rfb_port = port; - rfb_password = (password !== undefined) ? password : ""; - - 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(fbUpdateRequest(1)); - send_array(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(fbUpdateRequest(1)); - send_array(arr); -}; - -that.clipboardPasteFrom = function(text) { - if (rfb_state !== "normal") { return; } - //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "..."); - send_array(clientCutText(text)); - //Util.Debug("<< clipboardPasteFrom"); -}; - -that.testMode = function(override_send_array) { - // Overridable internal functions for testing - test_mode = true; - send_array = override_send_array; - that.recv_message = recv_message; // Expose it - - 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/include/ui.js b/include/ui.js deleted file mode 100644 index 217b8b1..0000000 --- a/include/ui.js +++ /dev/null @@ -1,420 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2010 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ -"use strict"; -/*jslint white: false */ -/*global $, Util, RFB, Canvas, VNC_uri_prefix, Element, Fx */ - -var UI = { - -settingsOpen : false, - -// Render default UI and initialize settings menu -load: function(target) { - var html = '', i, sheet, sheets, llevels; - - /* Populate the 'target' DOM element with default UI */ - if (!target) { - target = $D('vnc'); - } else if (typeof target === 'string') { - target = $D(target); - } - - if ((!document.createElement('canvas').getContext) && - window.ActiveXObject) { - // Suggest Chrome frame for Internet Explorer users - html += '
'; - html += ' You are using a version of Internet Explorer '; - html += ' that does not have HTML5 Canvas support. '; - html += ' To use noVNC you must use a browser with HTML5 '; - html += ' Canvas support or install '; - html += ' '; - html += ' Google Chrome Frame.'; - html += '
'; - target.innerHTML = html; - return; - } - - html += '
'; - html += '
    '; - html += '
  • Host:
  • '; - html += '
  • Port:
  • '; - html += '
  • Password: ' + sheets[i].title + ''; - } - html += ' Style
  • '; - - // Logging selection dropdown - html += '
  • Logging
  • '; - - html += '
    '; - html += '
  • > settingsDisabled"); - $D('VNC_encrypt').disabled = disabled; - $D('VNC_true_color').disabled = disabled; - if (rfb && rfb.get_canvas() && rfb.get_canvas().get_cursor_uri()) { - $D('VNC_cursor').disabled = disabled; - } else { - UI.updateSetting('cursor', false); - $D('VNC_cursor').disabled = true; - } - $D('VNC_shared').disabled = disabled; - $D('VNC_connectTimeout').disabled = disabled; - //Util.Debug("<< settingsDisabled"); -}, - -// Save/apply settings when 'Apply' button is pressed -settingsApply: function() { - //Util.Debug(">> settingsApply"); - UI.saveSetting('encrypt'); - UI.saveSetting('true_color'); - if (UI.rfb.get_canvas().get_cursor_uri()) { - UI.saveSetting('cursor'); - } - UI.saveSetting('shared'); - UI.saveSetting('connectTimeout'); - UI.saveSetting('stylesheet'); - UI.saveSetting('logging'); - - // Settings with immediate (non-connected related) effect - WebUtil.selectStylesheet(UI.getSetting('stylesheet')); - WebUtil.init_logging(UI.getSetting('logging')); - - //Util.Debug("<< settingsApply"); -}, - - - -setPassword: function() { - UI.rfb.sendPassword($D('VNC_password').value); - return false; -}, - -sendCtrlAltDel: function() { - UI.rfb.sendCtrlAltDel(); -}, - -updateState: function(rfb, state, oldstate, msg) { - var s, sb, c, cad, klass; - s = $D('VNC_status'); - sb = $D('VNC_status_bar'); - c = $D('VNC_connect_button'); - cad = $D('sendCtrlAltDelButton'); - switch (state) { - case 'failed': - case 'fatal': - c.disabled = true; - cad.disabled = true; - UI.settingsDisabled(true, rfb); - klass = "VNC_status_error"; - break; - case 'normal': - c.value = "Disconnect"; - c.onclick = UI.disconnect; - c.disabled = false; - cad.disabled = false; - UI.settingsDisabled(true, rfb); - klass = "VNC_status_normal"; - break; - case 'disconnected': - case 'loaded': - c.value = "Connect"; - c.onclick = UI.connect; - - c.disabled = false; - cad.disabled = true; - UI.settingsDisabled(false, rfb); - klass = "VNC_status_normal"; - break; - case 'password': - c.value = "Send Password"; - c.onclick = UI.setPassword; - - c.disabled = false; - cad.disabled = true; - UI.settingsDisabled(true, rfb); - klass = "VNC_status_warn"; - break; - default: - c.disabled = true; - cad.disabled = true; - UI.settingsDisabled(true, rfb); - klass = "VNC_status_warn"; - break; - } - - if (typeof(msg) !== 'undefined') { - s.setAttribute("class", klass); - sb.setAttribute("class", klass); - s.innerHTML = msg; - } - -}, - -clipReceive: function(rfb, text) { - Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "..."); - $D('VNC_clipboard_text').value = text; - Util.Debug("<< UI.clipReceive"); -}, - - -connect: function() { - var host, port, password; - - UI.closeSettingsMenu(); - - host = $D('VNC_host').value; - port = $D('VNC_port').value; - password = $D('VNC_password').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); -}, - -disconnect: function() { - UI.closeSettingsMenu(); - - UI.rfb.disconnect(); -}, - -canvasBlur: function() { - UI.rfb.get_canvas().set_focused(false); -}, - -canvasFocus: function() { - UI.rfb.get_canvas().set_focused(true); -}, - -clipClear: function() { - $D('VNC_clipboard_text').value = ""; - UI.rfb.clipboardPasteFrom(""); -}, - -clipSend: function() { - var text = $D('VNC_clipboard_text').value; - Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "..."); - UI.rfb.clipboardPasteFrom(text); - Util.Debug("<< UI.clipSend"); -} - -}; diff --git a/include/util.js b/include/util.js index af83b8a..e719c58 100644 --- a/include/util.js +++ b/include/util.js @@ -1,5 +1,5 @@ /* - * noVNC: HTML5 VNC client + * from noVNC: HTML5 VNC client * Copyright (C) 2010 Joel Martin * Licensed under LGPL-3 (see LICENSE.txt) * diff --git a/include/vnc.js b/include/vnc.js deleted file mode 100644 index a11f28d..0000000 --- a/include/vnc.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * noVNC: HTML5 VNC client - * Copyright (C) 2010 Joel Martin - * Licensed under LGPL-3 (see LICENSE.txt) - * - * See README.md for usage and integration instructions. - */ - -"use strict"; -/*jslint evil: true */ -/*global window, document, VNC_uri_prefix */ - -// Globals defined here -var VNC_native_ws, WEB_SOCKET_SWF_LOCATION; - -/* - * Load supporting scripts - */ -function get_VNC_uri_prefix() { - return (typeof VNC_uri_prefix !== "undefined") ? VNC_uri_prefix : "include/"; -} - -(function () { - var extra = "", start, end; - - start = " - - - - - - - Iterations:   - - Width:   - Height:   - -   - -

    - - Canvas (should see three squares and two happy faces):
    - - Canvas not supported. - - -
    - Results:
    - - - - - diff --git a/tests/cursor.html b/tests/cursor.html deleted file mode 100644 index 0fdd7a8..0000000 --- a/tests/cursor.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - Cursor Change test - - - - - - - - -

    Roll over the buttons to test cursors

    -
    - - - -
    -
    -
    - Debug:
    - -
    -
    - - Canvas not supported. - - - - - diff --git a/tests/face.png b/tests/face.png deleted file mode 100644 index 74c30d8..0000000 Binary files a/tests/face.png and /dev/null differ diff --git a/tests/face.png.js b/tests/face.png.js deleted file mode 100644 index e0b5d6c..0000000 --- a/tests/face.png.js +++ /dev/null @@ -1 +0,0 @@ -var face64 = 'iVBORw0KGgoAAAANSUhEUgAAACMAAAAjCAIAAACRuyQOAAAAA3NCSVQICAjb4U/gAAAAGXRFWHRTb2Z0d2FyZQBnbm9tZS1zY3JlZW5zaG907wO/PgAACJJJREFUSIm1lltsXMUdxr8558zZq9d3OxebJDYhJLhNIAmUWyFKIBUtVaGqSgtUlIJKeahoEahgIZU+VC0oQiVVC60obckDgVIp3KRCQkmhhIhA4oY4wjg2ufmS9drec/bc5vbvw9prJwq85dP/YWfP7Pfb/8w3s8v6339l2fkrbMvGuZQ2mkUTA0bpc4qpyjrX3dTkAATQ5z0WUrqcAwjL/eXirmBqj0yKSTTBwNxMM0+15JuurG/dlClcOH/yWcVEaVBKUR3Eidizr2946Nhr/9q5b//BsudZzDLG5DK4sDt3443XrFm34bkX9x4ZPimkWNBa/+MfrB84+O7rbxz4+JPQD8liljY6n8t9uWfld2/++vp1F3ct6cikU2eSnvr7P7e99OqC9vaTJ0ccMtl8loyJ4igKwzAIK0GglersWv7sM08VCrk4joY/O/rLXz3mTYzmcnnXdZXWcRzHURwEQRCEHUuXdS/vnp4qP/CT2zdvuAKAQwCB4kRse+m1LY//Wojkscd/57opKUQUJ8wyzFaOq7OGGGPcdZ/f/sKbu3YT0YZrr3JT7pq1l3qeH4SBqgRETBljDKXSqXyh/i9PP/W/Q31btz59zVXrUpxb1dYsixUK+c7Fi59/YUdz2yInnbXcLHfTtpu23ZRlu4ZZiRBTp8Z37HjlhW1/evnFZ9/a+VZdLsecFOMpx83ydJanc24q67iuFOr48NC1G6+fKBY7zutIElFNBAC4nN99602XXLzutjvvETqAlcqktVQin0QiLsRxEAUBaRVUfBh1QfcigmzIuw0NTe2LOjNlL07iOArDwA88z0unGWNTk5P1dfkf3XH3BT2r9b23zZKIAHxr81f/uGpF/8G+Fau+VPbKp8ZHpqdKSRiEYWiMMVopJSuVyl+f3UpIQKL34btvvf2BxuZWN5Umo7TWFiNDDHCampob6utLpRKz7Hvv+E5jfR5ELCkNShFXOytOTH7vjrsOfXJ0wcLFF63sXr1mfXtbS6FQB4BZyGYzX7l0TWtrvWVpUGxUMFEa2bv3Q9+bNCaECX2/NFEc3bd/4r19/tR0uLC98c+/3/LVy9fWzhNq56m1pfEPvabnut2OI8EvBMAYAxhgAWz3u3tuvuWeRx/56aYNa0Hy3fc/euiRZx596IZvbF5Dpgw9CdMI0waqaMrEScPgvtdWXH5JzdzC7NElIPQH3GyTk+4ABCgCEpAkMgRGcLb/49WGxqYtTzwNaJDa/tJ7DU1tW558GaYCEwESYGAWwEidTOcWM8tElcGauTP/ivDGd7V3fxv6JGCBIpBDjIMxgIM5B/YfjMJwfGwEMIA40DcQhcn46DGAzX7p6gIwBhj5WUvH8vLYG+nu8+d6qimY2lPXup70GFEEE9baAhRIj5w8cfz4MSESkJw3FLOfnrvSCETqs3xTd2Vyd+1Na/4MmRRt3gBTgfGJKkQhTAQTwgQgv2tpR8X3Vq5YCiiC7lrSXPG9lRe0AmZ2hQxo5jXpspNqEElxPmlOIi5ZThYUgBKYKRgPxgMFMAGM/+D9P2xuLPQ+dBcoAYkHf/bN5sZM74M3gHS1acBUi0gZ4zk8J5NyzdzBGSIJkoANCqsrwgBAg+zN1605Mfw6IIkiUHL9xouODzwBE4ACkKrGBNBkBEgSKSIz39gxRkuRVAduulHLCZtZoARkzybTAFU2m7GjBBSDkmoRJYCc3U5lSBgjAFeJae4Wauan9WSnWlU0aqdtUAXElAicVDNIgfHZaJkZU0pAESgmCJAACUCApJIBKCITg+VVMuWm2+btEwFE1coVLvOKe2HVE8UwUd/OXi0nQZXZ8kH+7HIFoIgoqvKqzWkV9L2zy5jQ6Ig5nX5pOFd/Vc3cmv9zW9eyYfzITmY1giKiMJNtCiYPw1RgPBh/psiHqcAEZAJQBFMlxaDEnyqmc3mjY2NCiy+bHB3Kt2w8I+UzxTPLlAzjygCz6kFBx6qNg/ue84p9M7AZRoWoQhSAqumfacsrnRg6uH9Rd4/RFWafl1RGjLJ5ZknNnIXjh+PQB0BEQkqv9L4sb1t59cMU74GVKxcnhg5sdzN1jQtX5grtqVyj46ZtywIJrUOZeCKYCLxTU+PHkzhZ2vO1XH5MRIfcwvcHP9qRafp5XfN6l3PGGIA5ktJaJEJINXnkvmWrNza0rSBxEFYbnE6veGRq9IPQO54Ep5QItRYAs22Hu1k315QtdDYsuCzf1KHDt0XlbTu3ySuVRo6MNnc/6XLHTbmObc+QotAHIJUSQiSJTKLR4Nh9Pdc+kM44JA+D5RhfBud8ZjeD5WHVMVYHqwAYmGkyUyRPqPDfMnhTxcNW+jKpGj/94NX8eVtTmYWpFHddlzsOABaOzZGkkImQUsrI/1iVfrPq6vszuSyJD0EasGEVmN0KlgXLgYGMT6qkkwEthrQuG53Y2U0icT79YIfb2pup6+Gcp1zOXV4j9VdJxhghpJBSSCmEjL0+XXqsa+0tTYvWQ/aTHJrZW9JEkowwJjYmMjo0OmR8uZ1eNz12+Nih/zgtv0gXVrsur1Jcl1uWNUsK/GoQldZSSCGllEpIGYcndOm36Vyqa/VNmboFRh4ldZR02ZhpMhJwCGnmLGZ8SewXj/bvTkLDW3pT2UUu55w7Lufc5dVNAsCCsf4o8Gqpr8KkUlIqpZRUKim/Y/y/pVLZ1s5V+Zbl3C3Ybp5Iq2RKxhP+xFBxZFAmwi7cmaq/kjuO4zicO9xx5mPOQqrGvYZRWmulldYqGlLBf3X8EfQkSR8A43WMN1nuWid3hZPpcmzbdmzHtmuwarjnkw5FldNIczyljDZKa62NNpoM1QSA1WQx27Jt23Js27It7pzJmLthz/7/nzHOOThcImPoNBIIAMNpJMtiNcBZDZ3PfVIjgtkWsy3riyZ9AaFGMlozhuqCnDsxxv4PC7uS+QV5eeoAAAAASUVORK5CYII='; diff --git a/tests/input.html b/tests/input.html deleted file mode 100644 index 7f47a2b..0000000 --- a/tests/input.html +++ /dev/null @@ -1,62 +0,0 @@ - - Input Test - -

    - - Canvas:
    - - Canvas not supported. - - -
    - Results:
    - - - - - - - - - - diff --git a/tests/keyboard.html b/tests/keyboard.html deleted file mode 100644 index 2d6df05..0000000 --- a/tests/keyboard.html +++ /dev/null @@ -1,67 +0,0 @@ - - Input Test - -

    - - Canvas:
    - - Canvas not supported. - - -
    - Results:
    - - - - - - - - - - diff --git a/tests/vnc_perf.html b/tests/vnc_perf.html deleted file mode 100644 index 1fde5dd..0000000 --- a/tests/vnc_perf.html +++ /dev/null @@ -1,197 +0,0 @@ - - - VNC Performance Benchmark - - - - - Passes:   - -   - -

    - - Results:
    - - -

    - -
    -
    - - -
    Loading
    -
    - - Canvas not supported. - -
    - - - - - - - - - - - diff --git a/tests/vnc_playback.html b/tests/vnc_playback.html deleted file mode 100644 index 3a1f49a..0000000 --- a/tests/vnc_playback.html +++ /dev/null @@ -1,128 +0,0 @@ - - - VNC Playback - - - - - Iterations:   - Perftest:  - Realtime:   - -   - -

    - - Results:
    - - -

    - -
    -
    - - -
    Loading
    -
    - - Canvas not supported. - -
    - - - - - - - - - - diff --git a/tests/wsecho.html b/tests/wsecho.html deleted file mode 120000 index 28c636a..0000000 --- a/tests/wsecho.html +++ /dev/null @@ -1 +0,0 @@ -../utils/wsecho.html \ No newline at end of file diff --git a/tests/wsecho.html b/tests/wsecho.html new file mode 100644 index 0000000..9e3c6d6 --- /dev/null +++ b/tests/wsecho.html @@ -0,0 +1,176 @@ + + + + WebSockets Echo Test + + + + + + + + + + + + Host:   + Port:   + Encrypt:   +   + + +
    + Log:
    + + + + + + + diff --git a/utils/wsecho.py b/tests/wsecho.py similarity index 97% rename from utils/wsecho.py rename to tests/wsecho.py index 15e2ef7..3787f3c 100755 --- a/utils/wsecho.py +++ b/tests/wsecho.py @@ -11,7 +11,8 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates ''' -import sys, socket, select +import os, sys, socket, select +sys.path.insert(0,os.path.dirname(__file__) + "/../") from websocket import WebSocketServer class WebSocketEcho(WebSocketServer): diff --git a/tests/wsencoding.py b/tests/wsencoding.py index c44b934..4960376 100755 --- a/tests/wsencoding.py +++ b/tests/wsencoding.py @@ -12,7 +12,7 @@ from base64 import b64encode, b64decode from codecs import utf_8_encode, utf_8_decode from select import select -sys.path.insert(0,os.path.dirname(__file__) + "/../utils/") +sys.path.insert(0,os.path.dirname(__file__) + "/../") from websocket import * buffer_size = 65536 diff --git a/tests/wstest.html b/tests/wstest.html deleted file mode 120000 index 13d6ff0..0000000 --- a/tests/wstest.html +++ /dev/null @@ -1 +0,0 @@ -../utils/wstest.html \ No newline at end of file diff --git a/tests/wstest.html b/tests/wstest.html new file mode 100644 index 0000000..c56c4c7 --- /dev/null +++ b/tests/wstest.html @@ -0,0 +1,252 @@ + + + + WebSockets Test + + + + + + + + + + + + Host:   + Port:   + Encrypt:   + Send Delay (ms):   +   + +

    + + + + + + + + + + + +
    Packets sent:
    0
    Good Packets Received:
    0
    Errors (Bad Packets Received:)
    0
    + +
    + Errors:
    + + + + + + + diff --git a/tests/wstest.py b/tests/wstest.py deleted file mode 120000 index 2c5b2b3..0000000 --- a/tests/wstest.py +++ /dev/null @@ -1 +0,0 @@ -../utils/wstest.py \ No newline at end of file diff --git a/tests/wstest.py b/tests/wstest.py new file mode 100755 index 0000000..3177252 --- /dev/null +++ b/tests/wstest.py @@ -0,0 +1,171 @@ +#!/usr/bin/python + +''' +WebSocket server-side load test program. Sends and receives traffic +that has a random payload (length and content) that is checksummed and +given a sequence number. Any errors are reported and counted. +''' + +import sys, os, socket, ssl, time, traceback +import random, time +from select import select + +sys.path.insert(0,os.path.dirname(__file__) + "/../") +from websocket import WebSocketServer + + +class WebSocketTest(WebSocketServer): + + buffer_size = 65536 + max_packet_size = 10000 + recv_cnt = 0 + send_cnt = 0 + + def __init__(self, *args, **kwargs): + self.errors = 0 + self.delay = kwargs.pop('delay') + + print "Prepopulating random array" + self.rand_array = [] + for i in range(0, self.max_packet_size): + self.rand_array.append(random.randint(0, 9)) + + WebSocketServer.__init__(self, *args, **kwargs) + + def new_client(self, client): + self.send_cnt = 0 + self.recv_cnt = 0 + + try: + self.responder(client) + except: + print "accumulated errors:", self.errors + self.errors = 0 + raise + + def responder(self, client): + cqueue = [] + cpartial = "" + socks = [client] + last_send = time.time() * 1000 + + while True: + ins, outs, excepts = select(socks, socks, socks, 1) + if excepts: raise Exception("Socket exception") + + if client in ins: + buf = client.recv(self.buffer_size) + if len(buf) == 0: + raise self.EClose("Client closed") + #print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf)) + if buf[-1] == '\xff': + if cpartial: + err = self.check(cpartial + buf) + cpartial = "" + else: + err = self.check(buf) + if err: + self.traffic("}") + self.errors = self.errors + 1 + print err + else: + self.traffic(">") + else: + self.traffic(".>") + cpartial = cpartial + buf + + now = time.time() * 1000 + if client in outs and now > (last_send + self.delay): + last_send = now + #print "Client send: %s" % repr(cqueue[0]) + client.send(self.generate()) + self.traffic("<") + + def generate(self): + length = random.randint(10, self.max_packet_size) + numlist = self.rand_array[self.max_packet_size-length:] + # Error in length + #numlist.append(5) + chksum = sum(numlist) + # Error in checksum + #numlist[0] = 5 + nums = "".join( [str(n) for n in numlist] ) + data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums) + self.send_cnt += 1 + + return WebSocketServer.encode(data) + + + def check(self, buf): + try: + data_list = WebSocketServer.decode(buf) + except: + print "\n" + repr(buf) + "" + return "Failed to decode" + + err = "" + for data in data_list: + if data.count('$') > 1: + raise Exception("Multiple parts within single packet") + if len(data) == 0: + self.traffic("_") + continue + + if data[0] != "^": + err += "buf did not start with '^'\n" + continue + + try: + cnt, length, chksum, nums = data[1:-1].split(':') + cnt = int(cnt) + length = int(length) + chksum = int(chksum) + except: + print "\n" + repr(data) + "" + err += "Invalid data format\n" + continue + + if self.recv_cnt != cnt: + err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt) + self.recv_cnt = cnt + 1 + continue + + self.recv_cnt += 1 + + if len(nums) != length: + err += "Expected length %d but got %d\n" % (length, len(nums)) + continue + + inv = nums.translate(None, "0123456789") + if inv: + err += "Invalid characters found: %s\n" % inv + continue + + real_chksum = 0 + for num in nums: + real_chksum += int(num) + + if real_chksum != chksum: + err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum) + return err + + +if __name__ == '__main__': + try: + if len(sys.argv) < 2: raise + listen_port = int(sys.argv[1]) + if len(sys.argv) == 3: + delay = int(sys.argv[2]) + else: + delay = 10 + except: + print "Usage: %s [delay_ms]" % sys.argv[0] + sys.exit(1) + + server = WebSocketTest( + listen_port=listen_port, + verbose=True, + cert='self.pem', + web='.', + delay=delay) + server.start_server() diff --git a/utils/README.md b/utils/README.md deleted file mode 100644 index 7aeebe6..0000000 --- a/utils/README.md +++ /dev/null @@ -1,163 +0,0 @@ -## WebSockets Proxy - - -### wsproxy - -At the most basic level, wsproxy just translates WebSockets traffic -to normal socket traffic. wsproxy accepts the WebSockets handshake, -parses it, and then begins forwarding traffic between the client and -the target in both directions. WebSockets payload data is UTF-8 -encoded so in order to transport binary data it must use an encoding -that can be encapsulated within UTF-8. wsproxy uses base64 to encode -all traffic to and from the client. Also, WebSockets traffic starts -with '\0' (0) and ends with '\xff' (255). Some buffering is done in -case the data from the client is not a full WebSockets frame (i.e. -does not end in 255). - - -#### Additional wsproxy features - -These are not necessary for the basic operation. - -* Daemonizing: When the `-D` option is specified, wsproxy runs - in the background as a daemon process. - -* SSL (the wss:// WebSockets URI): This is detected automatically by - wsproxy by sniffing the first byte sent from the client and then - wrapping the socket if the data starts with '\x16' or '\x80' - (indicating SSL). - -* Flash security policy: wsproxy detects flash security policy - requests (again by sniffing the first packet) and answers with an - appropriate flash security policy response (and then closes the - port). This means no separate flash security policy server is needed - for supporting the flash WebSockets fallback emulator. - -* Session recording: This feature that allows recording of the traffic - sent and received from the client to a file using the `--record` - option. - -* Mini-webserver: wsproxy can detect and respond to normal web - requests on the same port as the WebSockets proxy and Flash security - policy. This functionality is activate with the `--web DIR` option - where DIR is the root of the web directory to serve. - -* Wrap a program: see the "Wrap a Program" section below. - - -#### Implementations of wsproxy - -There are three implementations of wsproxy: python, C, and Node -(node.js). wswrapper is only implemented in C. - -Here is the feature support matrix for the the wsproxy -implementations: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    ProgramLanguageMultiprocessDaemonizeSSL/wssFlash Policy ServerSession RecordWeb ServerProgram Wrap
    wsproxy.pypythonyesyesyes 1yesyesyesyes
    wsproxyCyesyesyesyesnonono
    wsproxy.jsNode (node.js)yesnononononono
    - - -* Note 1: to use SSL/wss with python 2.5 or older, see the following - section on *Building the Python ssl module*. - - -### Wrap a Program - -In addition to proxying from a source address to a target address -(which may be on a different system), wsproxy has the ability to -launch a program on the local system and proxy WebSockets traffic to -a normal TCP port owned/bound by the program. - -The is accomplished with a small LD_PRELOAD library (`rebind.so`) -which intercepts bind() system calls by the program. The specified -port is moved to a new localhost/loopback free high port. wsproxy -then proxies WebSockets traffic directed to the original port to the -new (moved) port of the program. - -The program wrap mode is invoked by replacing the target with `--` -followed by the program command line to wrap. - - `./utils/wsproxy.py 2023 -- PROGRAM ARGS` - -The `--wrap-mode` option can be used to indicate what action to take -when the wrapped program exits or daemonizes. - -Here is an example of using wsproxy to wrap the vncserver command -(which backgrounds itself): - - `./utils/wsproxy.py 5901 --wrap-mode=ignore -- vncserver -geometry 1024x768 :1` - -Here is an example of wrapping telnetd (from krb5-telnetd).telnetd -exits after the connection closes so the wrap mode is set to respawn -the command: - - `sudo ./utils/wsproxy.py 2023 --wrap-mode=respawn -- telnetd -debug 2023` - -The `utils/wstelnet.html` page demonstrates a simple WebSockets based -telnet client. - - -### Building the Python ssl module (for python 2.5 and older) - -* Install the build dependencies. On Ubuntu use this command: - - `sudo aptitude install python-dev bluetooth-dev` - -* Download, build the ssl module and symlink to it: - - `cd noVNC/utils` - - `wget http://pypi.python.org/packages/source/s/ssl/ssl-1.15.tar.gz` - - `tar xvzf ssl-1.15.tar.gz` - - `cd ssl-1.15` - - `make` - - `cd ../` - - `ln -sf ssl-1.15/build/lib.linux-*/ssl ssl` - diff --git a/utils/VT100.js b/utils/VT100.js deleted file mode 120000 index 4178564..0000000 --- a/utils/VT100.js +++ /dev/null @@ -1 +0,0 @@ -VT100-orig.js \ No newline at end of file diff --git a/utils/include b/utils/include deleted file mode 120000 index f5030fe..0000000 --- a/utils/include +++ /dev/null @@ -1 +0,0 @@ -../include \ No newline at end of file diff --git a/utils/web.py b/utils/web.py deleted file mode 100755 index 6b7670b..0000000 --- a/utils/web.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/python -''' -A super simple HTTP/HTTPS webserver for python. Automatically detect - -You can make a cert/key with openssl using: -openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem -as taken from http://docs.python.org/dev/library/ssl.html#certificates - -''' - -import traceback, sys -import socket -import ssl -#import http.server as server # python 3.X -import SimpleHTTPServer as server # python 2.X - -def do_request(connstream, from_addr): - x = object() - server.SimpleHTTPRequestHandler(connstream, from_addr, x) - connstream.close() - -def serve(): - bindsocket = socket.socket() - bindsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - #bindsocket.bind(('localhost', PORT)) - bindsocket.bind(('', PORT)) - bindsocket.listen(5) - - print("serving on port", PORT) - - while True: - try: - newsocket, from_addr = bindsocket.accept() - peek = newsocket.recv(1024, socket.MSG_PEEK) - if peek.startswith("\x16"): - connstream = ssl.wrap_socket( - newsocket, - server_side=True, - certfile='self.pem', - ssl_version=ssl.PROTOCOL_TLSv1) - else: - connstream = newsocket - - do_request(connstream, from_addr) - - except Exception: - traceback.print_exc() - -try: - PORT = int(sys.argv[1]) -except: - print "%s port" % sys.argv[0] - sys.exit(2) - -serve() diff --git a/utils/wsecho.html b/utils/wsecho.html deleted file mode 100644 index 9e3c6d6..0000000 --- a/utils/wsecho.html +++ /dev/null @@ -1,176 +0,0 @@ - - - - WebSockets Echo Test - - - - - - - - - - - - Host:   - Port:   - Encrypt:   -   - - -
    - Log:
    - - - - - - - diff --git a/utils/wstest.html b/utils/wstest.html deleted file mode 100644 index c56c4c7..0000000 --- a/utils/wstest.html +++ /dev/null @@ -1,252 +0,0 @@ - - - - WebSockets Test - - - - - - - - - - - - Host:   - Port:   - Encrypt:   - Send Delay (ms):   -   - -

    - - - - - - - - - - - -
    Packets sent:
    0
    Good Packets Received:
    0
    Errors (Bad Packets Received:)
    0
    - -
    - Errors:
    - - - - - - - diff --git a/utils/wstest.py b/utils/wstest.py deleted file mode 100755 index 9442b04..0000000 --- a/utils/wstest.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/python - -''' -WebSocket server-side load test program. Sends and receives traffic -that has a random payload (length and content) that is checksummed and -given a sequence number. Any errors are reported and counted. -''' - -import sys, os, socket, ssl, time, traceback -import random, time -from select import select - -sys.path.insert(0,os.path.dirname(__file__) + "/../utils/") -from websocket import WebSocketServer - - -class WebSocketTest(WebSocketServer): - - buffer_size = 65536 - max_packet_size = 10000 - recv_cnt = 0 - send_cnt = 0 - - def __init__(self, *args, **kwargs): - self.errors = 0 - self.delay = kwargs.pop('delay') - - print "Prepopulating random array" - self.rand_array = [] - for i in range(0, self.max_packet_size): - self.rand_array.append(random.randint(0, 9)) - - WebSocketServer.__init__(self, *args, **kwargs) - - def new_client(self, client): - self.send_cnt = 0 - self.recv_cnt = 0 - - try: - self.responder(client) - except: - print "accumulated errors:", self.errors - self.errors = 0 - raise - - def responder(self, client): - cqueue = [] - cpartial = "" - socks = [client] - last_send = time.time() * 1000 - - while True: - ins, outs, excepts = select(socks, socks, socks, 1) - if excepts: raise Exception("Socket exception") - - if client in ins: - buf = client.recv(self.buffer_size) - if len(buf) == 0: - raise self.EClose("Client closed") - #print "Client recv: %s (%d)" % (repr(buf[1:-1]), len(buf)) - if buf[-1] == '\xff': - if cpartial: - err = self.check(cpartial + buf) - cpartial = "" - else: - err = self.check(buf) - if err: - self.traffic("}") - self.errors = self.errors + 1 - print err - else: - self.traffic(">") - else: - self.traffic(".>") - cpartial = cpartial + buf - - now = time.time() * 1000 - if client in outs and now > (last_send + self.delay): - last_send = now - #print "Client send: %s" % repr(cqueue[0]) - client.send(self.generate()) - self.traffic("<") - - def generate(self): - length = random.randint(10, self.max_packet_size) - numlist = self.rand_array[self.max_packet_size-length:] - # Error in length - #numlist.append(5) - chksum = sum(numlist) - # Error in checksum - #numlist[0] = 5 - nums = "".join( [str(n) for n in numlist] ) - data = "^%d:%d:%d:%s$" % (self.send_cnt, length, chksum, nums) - self.send_cnt += 1 - - return WebSocketServer.encode(data) - - - def check(self, buf): - try: - data_list = WebSocketServer.decode(buf) - except: - print "\n" + repr(buf) + "" - return "Failed to decode" - - err = "" - for data in data_list: - if data.count('$') > 1: - raise Exception("Multiple parts within single packet") - if len(data) == 0: - self.traffic("_") - continue - - if data[0] != "^": - err += "buf did not start with '^'\n" - continue - - try: - cnt, length, chksum, nums = data[1:-1].split(':') - cnt = int(cnt) - length = int(length) - chksum = int(chksum) - except: - print "\n" + repr(data) + "" - err += "Invalid data format\n" - continue - - if self.recv_cnt != cnt: - err += "Expected count %d but got %d\n" % (self.recv_cnt, cnt) - self.recv_cnt = cnt + 1 - continue - - self.recv_cnt += 1 - - if len(nums) != length: - err += "Expected length %d but got %d\n" % (length, len(nums)) - continue - - inv = nums.translate(None, "0123456789") - if inv: - err += "Invalid characters found: %s\n" % inv - continue - - real_chksum = 0 - for num in nums: - real_chksum += int(num) - - if real_chksum != chksum: - err += "Expected checksum %d but real chksum is %d\n" % (chksum, real_chksum) - return err - - -if __name__ == '__main__': - try: - if len(sys.argv) < 2: raise - listen_port = int(sys.argv[1]) - if len(sys.argv) == 3: - delay = int(sys.argv[2]) - else: - delay = 10 - except: - print "Usage: %s [delay_ms]" % sys.argv[0] - sys.exit(1) - - server = WebSocketTest( - listen_port=listen_port, - verbose=True, - cert='self.pem', - web='.', - delay=delay) - server.start_server() diff --git a/vnc.html b/vnc.html deleted file mode 100644 index de6b427..0000000 --- a/vnc.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - noVNC - - - - - - - - - -
    Loading
    - - - - - diff --git a/vnc_auto.html b/vnc_auto.html deleted file mode 100644 index 1e9e64a..0000000 --- a/vnc_auto.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - noVNC - - - - - - - -
    -
    - - - -
    Loading
    - -
    -
    - - Canvas not supported. - -
    - - - - - - diff --git a/utils/websocket.py b/websocket.py similarity index 88% rename from utils/websocket.py rename to websocket.py index 4520508..d92b0ff 100755 --- a/utils/websocket.py +++ b/websocket.py @@ -23,7 +23,7 @@ except: from urlparse import urlsplit from cgi import parse_qsl -class WebSocketServer(): +class WebSocketServer(object): """ WebSockets server class. Must be sub-classed with new_client method definition. @@ -345,49 +345,51 @@ Connection: Upgrade\r while True: try: - csock = startsock = None - pid = err = 0 - try: - self.poll() + csock = startsock = None + pid = err = 0 - ready = select.select([lsock], [], [], 1)[0]; - if lsock in ready: - startsock, address = lsock.accept() + try: + self.poll() + + ready = select.select([lsock], [], [], 1)[0]; + if lsock in ready: + startsock, address = lsock.accept() + else: + continue + except Exception, exc: + if hasattr(exc, 'errno'): + err = exc.errno + else: + err = exc[0] + if err == errno.EINTR: + self.vmsg("Ignoring interrupted syscall") + continue + else: + raise + + self.vmsg('%s: forking handler' % address[0]) + pid = os.fork() + + if pid == 0: + # handler process + csock = self.do_handshake(startsock, address) + self.new_client(csock) else: - continue + # parent process + self.handler_id += 1 + + except self.EClose, exc: + # Connection was not a WebSockets connection + if exc.args[0]: + self.msg("%s: %s" % (address[0], exc.args[0])) + except KeyboardInterrupt, exc: + pass except Exception, exc: - if hasattr(exc, 'errno'): - err = exc.errno - elif type(exc) == select.error: - err = exc[0] - if err == errno.EINTR: - self.vmsg("Ignoring interrupted syscall()") - continue - else: - raise + self.msg("handler exception: %s" % str(exc)) + if self.verbose: + self.msg(traceback.format_exc()) - self.vmsg('%s: forking handler' % address[0]) - pid = os.fork() - - if pid == 0: - # handler process - csock = self.do_handshake(startsock, address) - self.new_client(csock) - else: - # parent process - self.handler_id += 1 - - except self.EClose, exc: - # Connection was not a WebSockets connection - if exc.args[0]: - self.msg("%s: %s" % (address[0], exc.args[0])) - except KeyboardInterrupt, exc: - pass - except Exception, exc: - self.msg("handler exception: %s" % str(exc)) - if self.verbose: - self.msg(traceback.format_exc()) finally: if csock and csock != startsock: csock.close() diff --git a/utils/wsproxy.py b/websockify similarity index 100% rename from utils/wsproxy.py rename to websockify diff --git a/utils/wstelnet.html b/wstelnet.html similarity index 95% rename from utils/wstelnet.html rename to wstelnet.html index 01e987b..f257a2b 100644 --- a/utils/wstelnet.html +++ b/wstelnet.html @@ -5,9 +5,9 @@ - - - + + +