Merge branch 'master' of git@github.com:kanaka/noVNC
This commit is contained in:
commit
02a7dd2c29
17
README.md
17
README.md
|
@ -24,6 +24,23 @@ Running in Chrome before and after connecting:
|
||||||
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">here</a>.
|
||||||
|
|
||||||
|
|
||||||
|
### Projects/Companies using noVNC
|
||||||
|
|
||||||
|
* [Sentry Data Systems](http://www.sentryds.com): uses noVNC in the
|
||||||
|
[Datanex Cloud Computing Platform](http://www.sentryds.com/products/datanex/).
|
||||||
|
|
||||||
|
* [Ganeti Web Manager](http://code.osuosl.org/projects/ganeti-webmgr):
|
||||||
|
Feature [#1935](http://code.osuosl.org/issues/1935).
|
||||||
|
|
||||||
|
* [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).
|
||||||
|
|
||||||
|
|
||||||
### Browser Requirements
|
### Browser Requirements
|
||||||
|
|
||||||
* HTML5 Canvas: Except for Internet Explorer, most
|
* HTML5 Canvas: Except for Internet Explorer, most
|
||||||
|
|
|
@ -35,6 +35,10 @@ Cursor appearance/style (for Cursor pseudo-encoding):
|
||||||
https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
|
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:
|
Related projects:
|
||||||
|
|
||||||
guacamole: http://guacamole.sourceforge.net/
|
guacamole: http://guacamole.sourceforge.net/
|
||||||
|
|
|
@ -109,6 +109,7 @@ function constructor() {
|
||||||
if (! conf.ctx) { conf.ctx = c.getContext('2d'); }
|
if (! conf.ctx) { conf.ctx = c.getContext('2d'); }
|
||||||
ctx = conf.ctx;
|
ctx = conf.ctx;
|
||||||
|
|
||||||
|
Util.Debug("User Agent: " + navigator.userAgent);
|
||||||
if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); }
|
if (UE.gecko) { Util.Debug("Browser: gecko " + UE.gecko); }
|
||||||
if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); }
|
if (UE.webkit) { Util.Debug("Browser: webkit " + UE.webkit); }
|
||||||
if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); }
|
if (UE.trident) { Util.Debug("Browser: trident " + UE.trident); }
|
||||||
|
@ -216,7 +217,7 @@ function constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Translate DOM key down/up event to keysym value */
|
/* Translate DOM key down/up event to keysym value */
|
||||||
function getKeysym(e) {
|
that.getKeysym = function(e) {
|
||||||
var evt, keysym;
|
var evt, keysym;
|
||||||
evt = (e ? e : window.event);
|
evt = (e ? e : window.event);
|
||||||
|
|
||||||
|
@ -362,24 +363,24 @@ function onMouseMove(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(e) {
|
function onKeyDown(e) {
|
||||||
//Util.Debug("keydown: " + getKeysym(e));
|
//Util.Debug("keydown: " + that.getKeysym(e));
|
||||||
if (! conf.focused) {
|
if (! conf.focused) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c_keyPress) {
|
if (c_keyPress) {
|
||||||
c_keyPress(getKeysym(e), 1);
|
c_keyPress(that.getKeysym(e), 1);
|
||||||
}
|
}
|
||||||
Util.stopEvent(e);
|
Util.stopEvent(e);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onKeyUp(e) {
|
function onKeyUp(e) {
|
||||||
//Util.Debug("keyup: " + getKeysym(e));
|
//Util.Debug("keyup: " + that.getKeysym(e));
|
||||||
if (! conf.focused) {
|
if (! conf.focused) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (c_keyPress) {
|
if (c_keyPress) {
|
||||||
c_keyPress(getKeysym(e), 0);
|
c_keyPress(that.getKeysym(e), 0);
|
||||||
}
|
}
|
||||||
Util.stopEvent(e);
|
Util.stopEvent(e);
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -254,7 +254,8 @@ function constructor() {
|
||||||
Util.Info("Using native WebSockets");
|
Util.Info("Using native WebSockets");
|
||||||
updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
|
updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
|
||||||
} else {
|
} else {
|
||||||
Util.Warn("Using web-socket-js flash bridge");
|
Util.Warn("Using web-socket-js bridge. Flash version: " +
|
||||||
|
Util.Flash.version);
|
||||||
if ((! Util.Flash) ||
|
if ((! Util.Flash) ||
|
||||||
(Util.Flash.version < 9)) {
|
(Util.Flash.version < 9)) {
|
||||||
updateState('fatal', "WebSockets or Adobe Flash is required");
|
updateState('fatal', "WebSockets or Adobe Flash is required");
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
<html>
|
||||||
|
<head><title>Input Test</title></head>
|
||||||
|
<body>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
Canvas:<br>
|
||||||
|
<canvas id="canvas" width="640" height="20"
|
||||||
|
style="border-style: dotted; border-width: 1px;">
|
||||||
|
Canvas not supported.
|
||||||
|
</canvas>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
Results:<br>
|
||||||
|
<textarea id="messages" style="font-size: 9;" cols=80 rows=25></textarea>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<script type='text/javascript'
|
||||||
|
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||||
|
-->
|
||||||
|
<script src="include/util.js"></script>
|
||||||
|
<script src="include/webutil.js"></script>
|
||||||
|
<script src="include/base64.js"></script>
|
||||||
|
<script src="include/canvas.js"></script>
|
||||||
|
<script>
|
||||||
|
var msg_cnt = 0;
|
||||||
|
var width = 400, height = 200;
|
||||||
|
var canvas;
|
||||||
|
|
||||||
|
function message(str) {
|
||||||
|
console.log(str);
|
||||||
|
msg_cnt++;
|
||||||
|
cell = $D('messages');
|
||||||
|
cell.innerHTML += msg_cnt + ": " + str + "\n";
|
||||||
|
cell.scrollTop = cell.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyDown(evt) {
|
||||||
|
var e = (evt ? evt : window.event);
|
||||||
|
msg = "Dn: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
||||||
|
message(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyUp(evt) {
|
||||||
|
var e = (evt ? evt : window.event);
|
||||||
|
msg = "Up: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
||||||
|
message(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyPress(evt) {
|
||||||
|
var e = (evt ? evt : window.event);
|
||||||
|
msg = "Pr: key:" + e.keyCode + " char:" + e.charCode + " which:" + e.which + " ksym:" + canvas.getKeysym(evt) + " alt:" + e.altKey + " shift:" + e.shiftKey + " ctrl:" + e.ctrlKey;
|
||||||
|
message(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onload = function() {
|
||||||
|
var c = $D('canvas');
|
||||||
|
canvas = new Canvas({'target' : c});
|
||||||
|
canvas.resize(width, height, true);
|
||||||
|
//canvas.start(keyPress);
|
||||||
|
Util.addEvent(document, 'keydown', keyDown);
|
||||||
|
Util.addEvent(document, 'keyup', keyUp);
|
||||||
|
Util.addEvent(document, 'keypress', keyPress);
|
||||||
|
message("Canvas initialized");
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
|
@ -187,6 +187,7 @@ int ws_socket_free(ws_ctx_t *ctx) {
|
||||||
ctx->ssl_ctx = NULL;
|
ctx->ssl_ctx = NULL;
|
||||||
}
|
}
|
||||||
if (ctx->sockfd) {
|
if (ctx->sockfd) {
|
||||||
|
shutdown(ctx->sockfd, SHUT_RDWR);
|
||||||
close(ctx->sockfd);
|
close(ctx->sockfd);
|
||||||
ctx->sockfd = 0;
|
ctx->sockfd = 0;
|
||||||
}
|
}
|
||||||
|
@ -350,26 +351,30 @@ ws_ctx_t *do_handshake(int sock) {
|
||||||
handshake[len] = 0;
|
handshake[len] = 0;
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
handler_msg("ignoring empty handshake\n");
|
handler_msg("ignoring empty handshake\n");
|
||||||
close(sock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if (bcmp(handshake, "<policy-file-request/>", 22) == 0) {
|
} else if (bcmp(handshake, "<policy-file-request/>", 22) == 0) {
|
||||||
len = recv(sock, handshake, 1024, 0);
|
len = recv(sock, handshake, 1024, 0);
|
||||||
handshake[len] = 0;
|
handshake[len] = 0;
|
||||||
handler_msg("sending flash policy response\n");
|
handler_msg("sending flash policy response\n");
|
||||||
send(sock, policy_response, sizeof(policy_response), 0);
|
send(sock, policy_response, sizeof(policy_response), 0);
|
||||||
close(sock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
} else if ((bcmp(handshake, "\x16", 1) == 0) ||
|
} else if ((bcmp(handshake, "\x16", 1) == 0) ||
|
||||||
(bcmp(handshake, "\x80", 1) == 0)) {
|
(bcmp(handshake, "\x80", 1) == 0)) {
|
||||||
// SSL
|
// SSL
|
||||||
if (! settings.cert) { return NULL; }
|
if (!settings.cert) {
|
||||||
|
handler_msg("SSL connection but no cert specified\n");
|
||||||
|
return NULL;
|
||||||
|
} else if (access(settings.cert, R_OK) != 0) {
|
||||||
|
handler_msg("SSL connection but '%s' not found\n",
|
||||||
|
settings.cert);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
ws_ctx = ws_socket_ssl(sock, settings.cert, settings.key);
|
ws_ctx = ws_socket_ssl(sock, settings.cert, settings.key);
|
||||||
if (! ws_ctx) { return NULL; }
|
if (! ws_ctx) { return NULL; }
|
||||||
scheme = "wss";
|
scheme = "wss";
|
||||||
handler_msg("using SSL socket\n");
|
handler_msg("using SSL socket\n");
|
||||||
} else if (settings.ssl_only) {
|
} else if (settings.ssl_only) {
|
||||||
handler_msg("non-SSL connection disallowed\n");
|
handler_msg("non-SSL connection disallowed\n");
|
||||||
close(sock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
ws_ctx = ws_socket(sock);
|
ws_ctx = ws_socket(sock);
|
||||||
|
@ -380,14 +385,12 @@ ws_ctx_t *do_handshake(int sock) {
|
||||||
len = ws_recv(ws_ctx, handshake, 4096);
|
len = ws_recv(ws_ctx, handshake, 4096);
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
handler_emsg("Client closed during handshake\n");
|
handler_emsg("Client closed during handshake\n");
|
||||||
close(sock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
handshake[len] = 0;
|
handshake[len] = 0;
|
||||||
|
|
||||||
if (!parse_handshake(handshake, &headers)) {
|
if (!parse_handshake(handshake, &headers)) {
|
||||||
handler_emsg("Invalid WS request\n");
|
handler_emsg("Invalid WS request\n");
|
||||||
close(sock);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -524,8 +527,7 @@ void start_server() {
|
||||||
if (pid == 0) { // handler process
|
if (pid == 0) { // handler process
|
||||||
ws_ctx = do_handshake(csock);
|
ws_ctx = do_handshake(csock);
|
||||||
if (ws_ctx == NULL) {
|
if (ws_ctx == NULL) {
|
||||||
close(csock);
|
handler_msg("No connection after handshake\n");
|
||||||
handler_msg("No connection after handshake");
|
|
||||||
break; // Child process exits
|
break; // Child process exits
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,13 +535,22 @@ void start_server() {
|
||||||
if (pipe_error) {
|
if (pipe_error) {
|
||||||
handler_emsg("Closing due to SIGPIPE\n");
|
handler_emsg("Closing due to SIGPIPE\n");
|
||||||
}
|
}
|
||||||
close(csock);
|
|
||||||
handler_msg("handler exit\n");
|
|
||||||
break; // Child process exits
|
break; // Child process exits
|
||||||
} else { // parent process
|
} else { // parent process
|
||||||
settings.handler_id += 1;
|
settings.handler_id += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (pid == 0) {
|
||||||
|
if (ws_ctx) {
|
||||||
|
ws_socket_free(ws_ctx);
|
||||||
|
} else {
|
||||||
|
shutdown(csock, SHUT_RDWR);
|
||||||
|
close(csock);
|
||||||
|
}
|
||||||
|
handler_msg("handler exit\n");
|
||||||
|
} else {
|
||||||
|
handler_msg("wsproxy exit\n");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,11 @@ def do_handshake(sock):
|
||||||
sock.close()
|
sock.close()
|
||||||
return False
|
return False
|
||||||
elif handshake[0] in ("\x16", "\x80"):
|
elif handshake[0] in ("\x16", "\x80"):
|
||||||
|
if not os.path.exists(settings['cert']):
|
||||||
|
handler_msg("SSL connection but '%s' not found"
|
||||||
|
% settings['cert'])
|
||||||
|
sock.close()
|
||||||
|
return False
|
||||||
retsock = ssl.wrap_socket(
|
retsock = ssl.wrap_socket(
|
||||||
sock,
|
sock,
|
||||||
server_side=True,
|
server_side=True,
|
||||||
|
|
|
@ -257,6 +257,10 @@ int main(int argc, char *argv[])
|
||||||
};
|
};
|
||||||
|
|
||||||
settings.cert = realpath("self.pem", NULL);
|
settings.cert = realpath("self.pem", NULL);
|
||||||
|
if (!settings.cert) {
|
||||||
|
/* Make sure it's always set to something */
|
||||||
|
settings.cert = "self.pem";
|
||||||
|
}
|
||||||
settings.key = "";
|
settings.key = "";
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -326,9 +330,11 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl_only) {
|
if (ssl_only) {
|
||||||
if (!settings.cert || !access(settings.cert, R_OK)) {
|
if (!access(settings.cert, R_OK)) {
|
||||||
usage("SSL only and cert file not found\n");
|
usage("SSL only and cert file '%s' not found\n", settings.cert);
|
||||||
}
|
}
|
||||||
|
} else if (access(settings.cert, R_OK) != 0) {
|
||||||
|
fprintf(stderr, "Warning: '%s' not found\n", settings.cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf(" verbose: %d\n", settings.verbose);
|
//printf(" verbose: %d\n", settings.verbose);
|
||||||
|
|
|
@ -162,6 +162,8 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
if options.ssl_only and not os.path.exists(options.cert):
|
if options.ssl_only and not os.path.exists(options.cert):
|
||||||
parser.error("SSL only and %s not found" % options.cert)
|
parser.error("SSL only and %s not found" % options.cert)
|
||||||
|
elif not os.path.exists(options.cert):
|
||||||
|
print "Warning: %s not found" % options.cert
|
||||||
|
|
||||||
settings['verbose'] = options.verbose
|
settings['verbose'] = options.verbose
|
||||||
settings['listen_host'] = host
|
settings['listen_host'] = host
|
||||||
|
|
Loading…
Reference in New Issue