Commit edbd5ab8 authored by Christian Beier's avatar Christian Beier

Add noVNC HTML5 client connect possibility to our http server.

Pure JavaScript, no Java plugin required anymore! (But a recent browser...)
parent bffd9ee3
......@@ -13,6 +13,12 @@ $USER's $DESKTOP desktop ($DISPLAY)
<param name=PORT value=$PORT>
<param name="Open New Window" value=yes>
</APPLET>
<BR>
<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
<br/>
<br/>
If the above Java applet does not work, you can also try the new JavaScript-only <a href="http://novnc.com"<noVNC</a> viewer. You will need a HTML5-capable browser though.
<a href="novnc/vnc_auto.html?host=$HOST&port=$PORT&true_color=1">Click here to connect using noVNC.</a>
<br/>
<br/>
<br/>
<A href="http://libvncserver.sf.net/">LibVNCServer/libVNCClient Homepage</A>
</HTML>
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
Some portions of noVNC are copyright to their individual authors.
Please refer to the individual source files and/or to the noVNC commit
history: https://github.com/kanaka/noVNC/commits/master
noVNC is licensed under the LGPL (GNU Lesser General Public License)
version 3 with the following exceptions (all LGPL-3 compatible):
include/input.js : LGPL-2 or any later version
include/base64.js : Dual GPL-2 or LGPL-2.1
include/des.js : Various BSD style licenses
include/web-socket-js/ : New BSD license. Source code at
http://github.com/gimite/web-socket-js
include/Orbitron* : SIL Open Font License 1.1
(Copyright 2009 Matt McInerney)
images/ : Creative Commons Attribution-ShareAlike
http://creativecommons.org/licenses/by-sa/3.0/
The license texts are included at:
docs/LICENSE.LGPL-3 and
docs/LICENSE.GPL-3
docs/LICENSE.OFL-1.1
Or alternatively the license texts may be found here:
http://www.gnu.org/licenses/lgpl.html and
http://www.gnu.org/licenses/gpl.html
http://scripts.sil.org/OFL
## noVNC: HTML5 VNC Client
### Description
noVNC is a VNC client implemented using HTML5 technologies,
specifically Canvas and WebSockets (supports 'wss://' encryption).
noVNC is licensed under the
[LGPLv3](http://www.gnu.org/licenses/lgpl.html).
Special thanks to [Sentry Data Systems](http://www.sentryds.com) for
sponsoring ongoing development of this project (and for employing me).
There are many companies/projects that have integrated noVNC into
their products including: [Sentry Data Systems](http://www.sentryds.com), [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr), [Archipel](http://archipelproject.org), [openQRM](http://www.openqrm.com/), [OpenNode](http://www.opennodecloud.com/), [OpenStack](http://www.openstack.org), [Broadway (HTML5 GDK/GTK+ backend)](http://blogs.gnome.org/alexl/2011/03/15/gtk-html-backend-update/), [OpenNebula](http://opennebula.org/), [CloudSigma](http://www.cloudsigma.com/), [Zentyal (formerly eBox)](http://www.zentyal.org/), and [SlapOS](http://www.slapos.org). See [this wiki page](https://github.com/kanaka/noVNC/wiki/ProjectsCompanies-using-noVNC) for more info and links.
Notable commits, announcements and news are posted to
@<a href="http://www.twitter.com/noVNC">noVNC</a>
### Screenshots
Running in Chrome before and after connecting:
<img src="http://kanaka.github.com/noVNC/img/noVNC-5.png" width=400>&nbsp;<img src="http://kanaka.github.com/noVNC/img/noVNC-7.jpg" width=400>
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
### Browser Requirements
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
Safari, Opera 11+, Internet Explorer 9+, etc.
* HTML5 WebSockets: For browsers that do not have builtin
WebSockets support, the project includes
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in
WebSocket support.
* Fast Javascript Engine: noVNC avoids using new Javascript
functionality so it will run on older browsers, but decode and
rendering happen in Javascript, so a slow Javascript engine will
mean noVNC is painfully slow.
* I maintain a more detailed browser compatibility list <a
href="https://github.com/kanaka/noVNC/wiki/Browser-support">here</a>.
### Server Requirements
Unless you are using a VNC server with support for WebSockets
connections (only my [fork of libvncserver](http://github.com/kanaka/libvncserver)
currently), you need to use a WebSockets to TCP socket proxy. There is
a python proxy included ('websockify'). One advantage of using the
proxy is that it has builtin support for SSL/TLS encryption (i.e.
"wss://").
There a few reasons why a proxy is required:
1. WebSockets is not a pure socket protocol. There is an initial HTTP
like handshake to allow easy hand-off by web servers and allow
some origin policy exchange. Also, each WebSockets frame begins
with 0 ('\x00') and ends with 255 ('\xff').
2. Javascript itself does not have the ability to handle pure byte
arrays. The python proxy encodes the data as base64 so that the
Javascript client can decode the data as an integer array.
### Quick Start
* Use the launch script to start a mini-webserver and the WebSockets
proxy (websockify). The `--vnc` option is used to specify the location of
a running VNC server:
`./utils/launch.sh --vnc localhost:5901`
* Point your browser to the cut-and-paste URL that is output by the
launch script. Enter a password if the VNC server has one
configured. Hit the Connect button and enjoy!
### Other Pages
* [Advanced Usage](https://github.com/kanaka/noVNC/wiki/Advanced-usage). Generating an SSL
certificate, starting a VNC server, advanced websockify usage, etc.
* [Integrating noVNC](https://github.com/kanaka/noVNC/wiki/Integration) into existing projects.
* [Troubleshooting noVNC](https://github.com/kanaka/noVNC/wiki/Troubleshooting) problems.
images/favicon.ico
\ No newline at end of file
body {
margin:0;
padding:0;
font-family: Helvetica;
/*Background image with light grey curve.*/
background-color:#494949;
background-repeat:no-repeat;
background-position:right bottom;
height:100%;
}
html {
height:100%;
}
#noVNC_controls ul {
list-style: none;
margin: 0px;
padding: 0px;
}
#noVNC_controls li {
padding-bottom:8px;
}
#noVNC_host {
width:150px;
}
#noVNC_port {
width: 80px;
}
#noVNC_password {
width: 150px;
}
#noVNC_encrypt {
}
#noVNC_connectTimeout {
width: 30px;
}
#noVNC_path {
width: 100px;
}
#noVNC_connect_button {
width: 110px;
float:right;
}
#noVNC_view_drag_button {
display: none;
}
#sendCtrlAltDelButton {
display: none;
}
#noVNC_mobile_buttons {
display: none;
}
.noVNC-buttons-left {
float: left;
padding-left:10px;
padding-top:4px;
}
.noVNC-buttons-right {
float:right;
right: 0px;
padding-right:10px;
padding-top:4px;
}
#noVNC_status_bar {
margin-top: 0px;
padding: 0px;
}
#noVNC_status_bar div {
font-size: 12px;
padding-top: 4px;
width:100%;
}
#noVNC_status {
height:20px;
text-align: center;
}
#noVNC_settings_menu {
margin: 3px;
text-align: left;
}
#noVNC_settings_menu ul {
list-style: none;
margin: 0px;
padding: 0px;
}
#noVNC_apply {
float:right;
}
.noVNC_status_normal {
background: #eee;
}
.noVNC_status_error {
background: #f44;
}
.noVNC_status_warn {
background: #ff4;
}
/* Do not set width/height for VNC_screen or VNC_canvas or incorrect
* scaling will occur. Canvas resizes to remote VNC settings */
#noVNC_screen_pad {
margin: 0px;
padding: 0px;
height: 44px;
}
#noVNC_screen {
text-align: center;
display: table;
width:100%;
height:100%;
background-color:#313131;
border-bottom-right-radius: 800px 600px;
/*border-top-left-radius: 800px 600px;*/
}
#noVNC_container, #noVNC_canvas {
margin: 0px;
padding: 0px;
}
#noVNC_canvas {
left: 0px;
}
#VNC_clipboard_clear_button {
float:right;
}
#VNC_clipboard_text {
font-size: 11px;
}
#noVNC_clipboard_clear_button {
float:right;
}
/*Bubble contents divs*/
#noVNC_settings {
display:none;
margin-top:77px;
right:20px;
position:fixed;
}
#noVNC_controls {
margin-top:77px;
right:12px;
position:fixed;
}
#noVNC_controls.top:after {
right:15px;
}
#noVNC_clipboard {
display:none;
margin-top:77px;
right:30px;
position:fixed;
}
#noVNC_clipboard.top:after {
right:85px;
}
#keyboardinput {
width:1px;
height:1px;
background-color:#fff;
color:#fff;
border:0;
position: relative;
left: -40px;
z-index: -1;
}
.noVNC_status_warn {
background-color:yellow;
}
/*
* Advanced Styling
*/
/* Control bar */
#noVNC-control-bar {
position:fixed;
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
display:block;
height:44px;
left:0;
top:0;
width:100%;
z-index:200;
}
.noVNC_status_button {
padding: 4px 4px;
vertical-align: middle;
border:1px solid #869dbc;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
}
.noVNC_status_button_selected {
padding: 4px 4px;
vertical-align: middle;
border:1px solid #4366a9;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
background: #779ced; /* Old browsers */
background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
}
/*Settings Bubble*/
.triangle-right {
position:relative;
padding:15px;
margin:1em 0 3em;
color:#fff;
background:#fff; /* default background for browsers without gradient support */
/* css3 */
/*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698));
background:-moz-linear-gradient(#2e88c4, #075698);
background:-o-linear-gradient(#2e88c4, #075698);
background:linear-gradient(#2e88c4, #075698);*/
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
color:#000;
border:2px solid #E0E0E0;
}
.triangle-right.top:after {
border-color: transparent #E0E0E0;
border-width: 20px 20px 0 0;
bottom: auto;
left: auto;
right: 50px;
top: -20px;
}
.triangle-right:after {
content:"";
position:absolute;
bottom:-20px; /* value = - border-top-width - border-bottom-width */
left:50px; /* controls horizontal position */
border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */
border-style:solid;
border-color:#E0E0E0 transparent;
/* reduce the damage in FF3.0 */
display:block;
width:0;
}
.triangle-right.top:after {
top:-40px; /* value = - border-top-width - border-bottom-width */
right:50px; /* controls horizontal position */
bottom:auto;
left:auto;
border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */
border-color:transparent #E0E0E0;
}
/*Default noVNC logo.*/
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */
@font-face {
font-family: 'Orbitron';
font-style: normal;
font-weight: 700;
src: local('?'), url('Orbitron700.woff') format('woff'),
url('Orbitron700.ttf') format('truetype');
}
#noVNC_logo {
margin-top: 170px;
margin-left: 10px;
color:yellow;
text-align:left;
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
line-height:90%;
text-shadow:
5px 5px 0 #000,
-1px -1px 0 #000,
1px -1px 0 #000,
-1px 1px 0 #000,
1px 1px 0 #000;
}
#noVNC_logo span{
color:green;
}
/* ----------------------------------------
* Media sizing
* ----------------------------------------
*/
.noVNC_status_button {
font-size: 12px;
}
#noVNC_clipboard_text {
width: 500px;
}
#noVNC_logo {
font-size: 180px;
}
@media screen and (min-width: 481px) and (max-width: 640px) {
.noVNC_status_button {
font-size: 10px;
}
#noVNC_clipboard_text {
width: 410px;
}
#noVNC_logo {
font-size: 150px;
}
}
@media screen and (min-width: 321px) and (max-width: 480px) {
.noVNC_status_button {
font-size: 10px;
}
#noVNC_clipboard_text {
width: 250px;
}
#noVNC_logo {
font-size: 110px;
}
}
@media screen and (max-width: 320px) {
.noVNC_status_button {
font-size: 9px;
}
#noVNC_clipboard_text {
width: 220px;
}
#noVNC_logo {
font-size: 90px;
}
}
/*
* Modified from:
* http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956
*/
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XML-RPC Client component.
*
* The Initial Developer of the Original Code is
* Digital Creations 2, Inc.
* Portions created by the Initial Developer are Copyright (C) 2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Martijn Pieters <mj@digicool.com> (original author)
* Samuel Sieb <samuel@sieb.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*jslint white: false, bitwise: false, plusplus: false */
/*global console */
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
base64Pad : '=',
encode: function (data) {
"use strict";
var result = '',
chrTable = Base64.toBase64Table.split(''),
pad = Base64.base64Pad,
length = data.length,
i;
// Convert every three bytes to 4 ascii characters.
for (i = 0; i < (length - 2); i += 3) {
result += chrTable[data[i] >> 2];
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
result += chrTable[data[i+2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
if (length%3) {
i = length - (length%3);
result += chrTable[data[i] >> 2];
if ((length%3) === 2) {
result += chrTable[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
result += chrTable[(data[i+1] & 0x0f) << 2];
result += pad;
} else {
result += chrTable[(data[i] & 0x03) << 4];
result += pad + pad;
}
}
return result;
},
/* Convert Base64 data to a string */
toBinaryTable : [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var binTable = Base64.toBinaryTable,
pad = Base64.base64Pad,
result, result_length, idx, i, c, padding,
leftbits = 0, // number of bits decoded, but yet to be appended
leftdata = 0, // bits decoded, but yet to be appended
data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length%4)/1.5);
result = new Array(result_length);
// Convert one by one.
for (idx = 0, i = offset; i < data.length; i++) {
c = binTable[data.charCodeAt(i) & 0x7f];
padding = (data.charAt(i) === pad);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character '" + data.charCodeAt(i) + "'");
continue;
}
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding) {
result[idx++] = (leftdata >> leftbits) & 0xff;
}
leftdata &= (1 << leftbits) - 1;
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
throw {name: 'Base64-Error',
message: 'Corrupted base64 string'};
}
return result;
}
}; /* End of Base64 namespace */
#keyboardinput {
background-color:#000;
}
#noVNC-control-bar {
background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.triangle-right {
border:2px solid #fff;
background:#000;
color:#fff;
}
.noVNC_status_button {
font-size: 12px;
vertical-align: middle;
border:1px solid #4c4c4c;
background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.noVNC_status_button_selected {
background: #9dd53a; /* Old browsers */
background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */
}
#noVNC-control-bar {
background-color:#04073d;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(10,15,79)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(10,15,79) 54%,
rgb(4,7,61) 50%
);
}
.triangle-right {
border:2px solid #fff;
background:#04073d;
color:#fff;
}
#keyboardinput {
background-color:#04073d;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.LGPL-3)
*/
"use strict";
/*jslint browser: true, white: false */
/*global Util, VNC_frame_data, finish */
var rfb, mode, test_state, frame_idx, frame_length,
iteration, iterations, istart_time,
// Pre-declarations for jslint
send_array, next_iteration, queue_next_packet, do_packet;
// Override send_array
send_array = function (arr) {
// Stub out send_array
};
next_iteration = function () {
if (iteration === 0) {
frame_length = VNC_frame_data.length;
test_state = 'running';
} else {
rfb.disconnect();
}
if (test_state !== 'running') { return; }
iteration += 1;
if (iteration > iterations) {
finish();
return;
}
frame_idx = 0;
istart_time = (new Date()).getTime();
rfb.connect('test', 0, "bogus");
queue_next_packet();
};
queue_next_packet = function () {
var frame, foffset, toffset, delay;
if (test_state !== 'running') { return; }
frame = VNC_frame_data[frame_idx];
while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) {
//Util.Debug("Send frame " + frame_idx);
frame_idx += 1;
frame = VNC_frame_data[frame_idx];
}
if (frame === 'EOF') {
Util.Debug("Finished, found EOF");
next_iteration();
return;
}
if (frame_idx >= frame_length) {
Util.Debug("Finished, no more frames");
next_iteration();
return;
}
if (mode === 'realtime') {
foffset = frame.slice(1, frame.indexOf('{', 1));
toffset = (new Date()).getTime() - istart_time;
delay = foffset - toffset;
if (delay < 1) {
delay = 1;
}
setTimeout(do_packet, delay);
} else {
setTimeout(do_packet, 1);
}
};
do_packet = function () {
//Util.Debug("Processing frame: " + frame_idx);
var frame = VNC_frame_data[frame_idx];
rfb.recv_message({'data' : frame.slice(frame.indexOf('{', 1) + 1)});
frame_idx += 1;
queue_next_packet();
};
This diff is collapsed.
This diff is collapsed.
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
"use strict";
/*jslint bitwise: false, white: false */
/*global window, console, document, navigator, ActiveXObject */
// Globals defined here
var Util = {};
/*
* Make arrays quack
*/
Array.prototype.push8 = function (num) {
this.push(num & 0xFF);
};
Array.prototype.push16 = function (num) {
this.push((num >> 8) & 0xFF,
(num ) & 0xFF );
};
Array.prototype.push32 = function (num) {
this.push((num >> 24) & 0xFF,
(num >> 16) & 0xFF,
(num >> 8) & 0xFF,
(num ) & 0xFF );
};
/*
* ------------------------------------------------------
* Namespaced in Util
* ------------------------------------------------------
*/
/*
* Logging/debug routines
*/
Util._log_level = 'warn';
Util.init_logging = function (level) {
if (typeof level === 'undefined') {
level = Util._log_level;
} else {
Util._log_level = level;
}
if (typeof window.console === "undefined") {
if (typeof window.opera !== "undefined") {
window.console = {
'log' : window.opera.postError,
'warn' : window.opera.postError,
'error': window.opera.postError };
} else {
window.console = {
'log' : function(m) {},
'warn' : function(m) {},
'error': function(m) {}};
}
}
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
switch (level) {
case 'debug': Util.Debug = function (msg) { console.log(msg); };
case 'info': Util.Info = function (msg) { console.log(msg); };
case 'warn': Util.Warn = function (msg) { console.warn(msg); };
case 'error': Util.Error = function (msg) { console.error(msg); };
case 'none':
break;
default:
throw("invalid logging type '" + level + "'");
}
};
Util.get_logging = function () {
return Util._log_level;
};
// Initialize logging level
Util.init_logging();
// Set configuration default for Crockford style function namespaces
Util.conf_default = function(cfg, api, defaults, v, mode, type, defval, desc) {
var getter, setter;
// Default getter function
getter = function (idx) {
if ((type in {'arr':1, 'array':1}) &&
(typeof idx !== 'undefined')) {
return cfg[v][idx];
} else {
return cfg[v];
}
};
// Default setter function
setter = function (val, idx) {
if (type in {'boolean':1, 'bool':1}) {
if ((!val) || (val in {'0':1, 'no':1, 'false':1})) {
val = false;
} else {
val = true;
}
} else if (type in {'integer':1, 'int':1}) {
val = parseInt(val, 10);
} else if (type === 'func') {
if (!val) {
val = function () {};
}
}
if (typeof idx !== 'undefined') {
cfg[v][idx] = val;
} else {
cfg[v] = val;
}
};
// Set the description
api[v + '_description'] = desc;
// Set the getter function
if (typeof api['get_' + v] === 'undefined') {
api['get_' + v] = getter;
}
// Set the setter function with extra sanity checks
if (typeof api['set_' + v] === 'undefined') {
api['set_' + v] = function (val, idx) {
if (mode in {'RO':1, 'ro':1}) {
throw(v + " is read-only");
} else if ((mode in {'WO':1, 'wo':1}) &&
(typeof cfg[v] !== 'undefined')) {
throw(v + " can only be set once");
}
setter(val, idx);
};
}
// Set the default value
if (typeof defaults[v] !== 'undefined') {
defval = defaults[v];
} else if ((type in {'arr':1, 'array':1}) &&
(! (defval instanceof Array))) {
defval = [];
}
// Coerce existing setting to the right type
//Util.Debug("v: " + v + ", defval: " + defval + ", defaults[v]: " + defaults[v]);
setter(defval);
};
// Set group of configuration defaults
Util.conf_defaults = function(cfg, api, defaults, arr) {
var i;
for (i = 0; i < arr.length; i++) {
Util.conf_default(cfg, api, defaults, arr[i][0], arr[i][1],
arr[i][2], arr[i][3], arr[i][4]);
}
}
/*
* Cross-browser routines
*/
// Get DOM element position on page
Util.getPosition = function (obj) {
var x = 0, y = 0;
if (obj.offsetParent) {
do {
x += obj.offsetLeft;
y += obj.offsetTop;
obj = obj.offsetParent;
} while (obj);
}
return {'x': x, 'y': y};
};
// Get mouse event position in DOM element
Util.getEventPosition = function (e, obj, scale) {
var evt, docX, docY, pos;
//if (!e) evt = window.event;
evt = (e ? e : window.event);
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
if (evt.pageX || evt.pageY) {
docX = evt.pageX;
docY = evt.pageY;
} else if (evt.clientX || evt.clientY) {
docX = evt.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft;
docY = evt.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
pos = Util.getPosition(obj);
if (typeof scale === "undefined") {
scale = 1;
}
return {'x': (docX - pos.x) / scale, 'y': (docY - pos.y) / scale};
};
// Event registration. Based on: http://www.scottandrew.com/weblog/articles/cbs-events
Util.addEvent = function (obj, evType, fn){
if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else if (obj.addEventListener){
obj.addEventListener(evType, fn, false);
return true;
} else {
throw("Handler could not be attached");
}
};
Util.removeEvent = function(obj, evType, fn){
if (obj.detachEvent){
var r = obj.detachEvent("on"+evType, fn);
return r;
} else if (obj.removeEventListener){
obj.removeEventListener(evType, fn, false);
return true;
} else {
throw("Handler could not be removed");
}
};
Util.stopEvent = function(e) {
if (e.stopPropagation) { e.stopPropagation(); }
else { e.cancelBubble = true; }
if (e.preventDefault) { e.preventDefault(); }
else { e.returnValue = false; }
};
// Set browser engine versions. Based on mootools.
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
Util.Engine = {
'presto': (function() {
return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
'trident': (function() {
return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4); }()),
'webkit': (function() {
try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
//'webkit': (function() {
// return ((typeof navigator.taintEnabled !== "unknown") && navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); }()),
'gecko': (function() {
return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19 : 18); }())
};
if (Util.Engine.webkit) {
// Extract actual webkit version if available
Util.Engine.webkit = (function(v) {
var re = new RegExp('WebKit/([0-9\.]*) ');
v = (navigator.userAgent.match(re) || ['', v])[1];
return parseFloat(v, 10);
})(Util.Engine.webkit);
}
Util.Flash = (function(){
var v, version;
try {
v = navigator.plugins['Shockwave Flash'].description;
} catch(err1) {
try {
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
} catch(err2) {
v = '0 r0';
}
}
version = v.match(/\d+/g);
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
}());
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*jslint evil: true */
/*global window, document, INCLUDE_URI */
/*
* Load supporting scripts
*/
function get_INCLUDE_URI() {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
}
(function () {
"use strict";
var extra = "", start, end;
start = "<script src='" + get_INCLUDE_URI();
end = "'><\/script>";
// Uncomment to activate firebug lite
//extra += "<script src='http://getfirebug.com/releases/lite/1.2/" +
// "firebug-lite-compressed.js'><\/script>";
extra += start + "util.js" + end;
extra += start + "webutil.js" + end;
extra += start + "base64.js" + end;
extra += start + "websock.js" + end;
extra += start + "des.js" + end;
extra += start + "input.js" + end;
extra += start + "display.js" + end;
extra += start + "rfb.js" + end;
document.write(extra);
}());
* How to try
Assuming you have Web server (e.g. Apache) running at http://example.com/ .
- Download web_socket.rb from:
http://github.com/gimite/web-socket-ruby/tree/master
- Run sample Web Socket server (echo server) in example.com with: (#1)
$ ruby web-socket-ruby/samples/echo_server.rb example.com 10081
- If your server already provides socket policy file at port 843, modify the file to allow access to port 10081. Otherwise you can skip this step. See below for details.
- Publish the web-socket-js directory with your Web server (e.g. put it in ~/public_html).
- Change ws://localhost:10081 to ws://example.com:10081 in sample.html.
- Open sample.html in your browser.
- After "onopen" is shown, input something, click [Send] and confirm echo back.
#1: First argument of echo_server.rb means that it accepts Web Socket connection from HTML pages in example.com.
* Troubleshooting
If it doesn't work, try these:
1. Try Chrome and Firefox 3.x.
- It doesn't work on Chrome:
-- It's likely an issue of your code or the server. Debug your code as usual e.g. using console.log.
- It works on Chrome but it doesn't work on Firefox:
-- It's likely an issue of web-socket-js specific configuration (e.g. 3 and 4 below).
- It works on both Chrome and Firefox, but it doesn't work on your browser:
-- Check "Supported environment" section below. Your browser may not be supported by web-socket-js.
2. Add this line before your code:
WEB_SOCKET_DEBUG = true;
and use Developer Tools (Chrome/Safari) or Firebug (Firefox) to see if console.log outputs any errors.
3. Make sure you do NOT open your HTML page as local file e.g. file:///.../sample.html. web-socket-js doesn't work on local file. Open it via Web server e.g. http:///.../sample.html.
4. If you are NOT using web-socket-ruby as your WebSocket server, you need to place Flash socket policy file on your server. See "Flash socket policy file" section below for details.
5. Check if sample.html bundled with web-socket-js works.
6. Make sure the port used for WebSocket (10081 in example above) is not blocked by your server/client's firewall.
7. Install debugger version of Flash Player available here to see Flash errors:
http://www.adobe.com/support/flashplayer/downloads.html
* Supported environments
It should work on:
- Google Chrome 4 or later (just uses native implementation)
- Firefox 3.x, Internet Explorer 8 + Flash Player 9 or later
It may or may not work on other browsers such as Safari, Opera or IE 6. Patch for these browsers are appreciated, but I will not work on fixing issues specific to these browsers by myself.
* Flash socket policy file
This implementation uses Flash's socket, which means that your server must provide Flash socket policy file to declare the server accepts connections from Flash.
If you use web-socket-ruby available at
http://github.com/gimite/web-socket-ruby/tree/master
, you don't need anything special, because web-socket-ruby handles Flash socket policy file request. But if you already provide socket policy file at port 843, you need to modify the file to allow access to Web Socket port, because it precedes what web-socket-ruby provides.
If you use other Web Socket server implementation, you need to provide socket policy file yourself. See
http://www.lightsphere.com/dev/articles/flash_socket_policy.html
for details and sample script to run socket policy file server. node.js implementation is available here:
http://github.com/LearnBoost/Socket.IO-node/blob/master/lib/socket.io/transports/flashsocket.js
Actually, it's still better to provide socket policy file at port 843 even if you use web-socket-ruby. Flash always try to connect to port 843 first, so providing the file at port 843 makes startup faster.
* Cookie considerations
Cookie is sent if Web Socket host is the same as the origin of JavaScript. Otherwise it is not sent, because I don't know way to send right Cookie (which is Cookie of the host of Web Socket, I heard).
Note that it's technically possible that client sends arbitrary string as Cookie and any other headers (by modifying this library for example) once you place Flash socket policy file in your server. So don't trust Cookie and other headers if you allow connection from untrusted origin.
* Proxy considerations
The WebSocket spec (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) specifies instructions for User Agents to support proxied connections by implementing the HTTP CONNECT method.
The AS3 Socket class doesn't implement this mechanism, which renders it useless for the scenarios where the user trying to open a socket is behind a proxy.
The class RFC2817Socket (by Christian Cantrell) effectively lets us implement this, as long as the proxy settings are known and provided by the interface that instantiates the WebSocket. As such, if you want to support proxied conncetions, you'll have to supply this information to the WebSocket constructor when Flash is being used. One way to go about it would be to ask the user for proxy settings information if the initial connection fails.
* How to host HTML file and SWF file in different domains
By default, HTML file and SWF file must be in the same domain. You can follow steps below to allow hosting them in different domain.
WARNING: If you use the method below, HTML files in ANY domains can send arbitrary TCP data to your WebSocket server, regardless of configuration in Flash socket policy file. Arbitrary TCP data means that they can even fake request headers including Origin and Cookie.
- Unzip WebSocketMainInsecure.zip to extract WebSocketMainInsecure.swf.
- Put WebSocketMainInsecure.swf on your server, instead of WebSocketMain.swf.
- In JavaScript, set WEB_SOCKET_SWF_LOCATION to URL of your WebSocketMainInsecure.swf.
* How to build WebSocketMain.swf
Install Flex 4 SDK:
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4
$ cd flash-src
$ ./build.sh
* License
New BSD License.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2011 Joel Martin
* Licensed under LGPL-3 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
"use strict";
/*jslint bitwise: false, white: false */
/*global window, document */
// Globals defined here
var WebUtil = {}, $D;
/*
* Simple DOM selector by ID
*/
if (!window.$D) {
$D = function (id) {
if (document.getElementById) {
return document.getElementById(id);
} else if (document.all) {
return document.all[id];
} else if (document.layers) {
return document.layers[id];
}
return undefined;
};
}
/*
* ------------------------------------------------------
* Namespaced in WebUtil
* ------------------------------------------------------
*/
// init log level reading the logging HTTP param
WebUtil.init_logging = function() {
Util._log_level = (document.location.href.match(
/logging=([A-Za-z0-9\._\-]*)/) ||
['', Util._log_level])[1];
Util.init_logging()
}
WebUtil.init_logging();
WebUtil.dirObj = function (obj, depth, parent) {
var i, msg = "", val = "";
if (! depth) { depth=2; }
if (! parent) { parent= ""; }
// Print the properties of the passed-in object
for (i in obj) {
if ((depth > 1) && (typeof obj[i] === "object")) {
// Recurse attributes that are objects
msg += WebUtil.dirObj(obj[i], depth-1, parent + "." + i);
} else {
//val = new String(obj[i]).replace("\n", " ");
if (typeof(obj[i]) === "undefined") {
val = "undefined";
} else {
val = obj[i].toString().replace("\n", " ");
}
if (val.length > 30) {
val = val.substr(0,30) + "...";
}
msg += parent + "." + i + ": " + val + "\n";
}
}
return msg;
};
// Read a query string variable
WebUtil.getQueryVar = function(name, defVal) {
var re = new RegExp('[?][^#]*' + name + '=([^&#]*)');
if (typeof defVal === 'undefined') { defVal = null; }
return (document.location.href.match(re) || ['',defVal])[1];
};
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/
// No days means only for this browser session
WebUtil.createCookie = function(name,value,days) {
var date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
expires = "; expires="+date.toGMTString();
}
else {
expires = "";
}
document.cookie = name+"="+value+expires+"; path=/";
};
WebUtil.readCookie = function(name, defaultValue) {
var i, c, nameEQ = name + "=", ca = document.cookie.split(';');
for(i=0; i < ca.length; i += 1) {
c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1,c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length,c.length); }
}
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};
WebUtil.eraseCookie = function(name) {
WebUtil.createCookie(name,"",-1);
};
/*
* Alternate stylesheet selection
*/
WebUtil.getStylesheets = function() { var i, links, sheets = [];
links = document.getElementsByTagName("link");
for (i = 0; i < links.length; i += 1) {
if (links[i].title &&
links[i].rel.toUpperCase().indexOf("STYLESHEET") > -1) {
sheets.push(links[i]);
}
}
return sheets;
};
// No sheet means try and use value from cookie, null sheet used to
// clear all alternates.
WebUtil.selectStylesheet = function(sheet) {
var i, link, sheets = WebUtil.getStylesheets();
if (typeof sheet === 'undefined') {
sheet = 'default';
}
for (i=0; i < sheets.length; i += 1) {
link = sheets[i];
if (link.title === sheet) {
Util.Debug("Using stylesheet " + sheet);
link.disabled = false;
} else {
//Util.Debug("Skipping stylesheet " + link.title);
link.disabled = true;
}
}
return sheet;
};
This diff is collapsed.
This diff is collapsed.
......@@ -346,12 +346,6 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen)
return;
}
if (strchr(fname+1, '/') != NULL) {
rfbErr("httpd: asking for file in other directory\n");
rfbWriteExact(&cl, NOT_FOUND_STR, strlen(NOT_FOUND_STR));
httpCloseSock(rfbScreen);
return;
}
getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen);
rfbLog("httpd: get '%s' for %s\n", fname+1,
......@@ -447,6 +441,10 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen)
sprintf(str, "%d", rfbScreen->port);
rfbWriteExact(&cl, str, strlen(str));
} else if (compareAndSkip(&ptr, "$HOST")) {
rfbWriteExact(&cl, rfbScreen->thisHost, strlen(rfbScreen->thisHost));
} else if (compareAndSkip(&ptr, "$DESKTOP")) {
rfbWriteExact(&cl, rfbScreen->desktopName, strlen(rfbScreen->desktopName));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment