rfb.js 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2010 Joel Martin
  4. * Licensed under LGPL-3 (see LICENSE.txt)
  5. *
  6. * See README.md for usage and integration instructions.
  7. */
  8. /*jslint white: false, browser: true, bitwise: false, plusplus: false */
  9. /*global window, Util, Canvas, Keyboard, Mouse, Websock, Websock_native, Base64, DES, noVNC_logo */
  10. function RFB(conf) {
  11. "use strict";
  12. conf = conf || {}; // Configuration
  13. var that = {}, // Public API interface
  14. // Pre-declare private functions used before definitions (jslint)
  15. init_vars, updateState, fail, handle_message,
  16. init_msg, normal_msg, framebufferUpdate, print_stats,
  17. pixelFormat, clientEncodings, fbUpdateRequest,
  18. keyEvent, pointerEvent, clientCutText,
  19. extract_data_uri, scan_tight_imgQ,
  20. keyPress, mouseButton, mouseMove,
  21. checkEvents, // Overridable for testing
  22. //
  23. // Private RFB namespace variables
  24. //
  25. rfb_host = '',
  26. rfb_port = 5900,
  27. rfb_password = '',
  28. rfb_state = 'disconnected',
  29. rfb_version = 0,
  30. rfb_max_version= 3.8,
  31. rfb_auth_scheme= '',
  32. // In preference order
  33. encodings = [
  34. ['COPYRECT', 0x01 ],
  35. ['TIGHT_PNG', -260 ],
  36. ['HEXTILE', 0x05 ],
  37. ['RRE', 0x02 ],
  38. ['RAW', 0x00 ],
  39. ['DesktopSize', -223 ],
  40. ['Cursor', -239 ],
  41. // Psuedo-encoding settings
  42. ['JPEG_quality_lo', -32 ],
  43. //['JPEG_quality_hi', -23 ],
  44. ['compress_lo', -255 ]
  45. //['compress_hi', -247 ]
  46. ],
  47. encHandlers = {},
  48. encNames = {},
  49. encStats = {}, // [rectCnt, rectCntTot]
  50. ws = null, // Websock object
  51. canvas = null, // Canvas object
  52. keyboard = null, // Keyboard input handler object
  53. mouse = null, // Mouse input handler object
  54. sendTimer = null, // Send Queue check timer
  55. connTimer = null, // connection timer
  56. disconnTimer = null, // disconnection timer
  57. msgTimer = null, // queued handle_message timer
  58. // Frame buffer update state
  59. FBU = {
  60. rects : 0,
  61. subrects : 0, // RRE
  62. lines : 0, // RAW
  63. tiles : 0, // HEXTILE
  64. bytes : 0,
  65. x : 0,
  66. y : 0,
  67. width : 0,
  68. height : 0,
  69. encoding : 0,
  70. subencoding : -1,
  71. background : null,
  72. imgQ : [] // TIGHT_PNG image queue
  73. },
  74. fb_Bpp = 4,
  75. fb_depth = 3,
  76. fb_width = 0,
  77. fb_height = 0,
  78. fb_name = "",
  79. scan_imgQ_rate = 100,
  80. last_req_time = 0,
  81. rre_chunk_sz = 100,
  82. timing = {
  83. last_fbu : 0,
  84. fbu_total : 0,
  85. fbu_total_cnt : 0,
  86. full_fbu_total : 0,
  87. full_fbu_cnt : 0,
  88. fbu_rt_start : 0,
  89. fbu_rt_total : 0,
  90. fbu_rt_cnt : 0
  91. },
  92. test_mode = false,
  93. /* Mouse state */
  94. mouse_buttonMask = 0,
  95. mouse_arr = [];
  96. //
  97. // Configuration settings
  98. //
  99. function cdef(v, type, defval, desc) {
  100. Util.conf_default(conf, that, v, type, defval, desc); }
  101. cdef('target', 'str', null, 'VNC viewport rendering Canvas');
  102. cdef('focusContainer', 'dom', document, 'Area that traps keyboard input');
  103. cdef('encrypt', 'bool', false, 'Use TLS/SSL/wss encryption');
  104. cdef('true_color', 'bool', true, 'Request true color pixel data');
  105. cdef('local_cursor', 'bool', false, 'Request locally rendered cursor');
  106. cdef('shared', 'bool', true, 'Request shared mode');
  107. if (Websock_native) {
  108. cdef('connectTimeout', 'int', 2, 'Time (s) to wait for connection');
  109. } else {
  110. cdef('connectTimeout', 'int', 5, 'Time (s) to wait for connection');
  111. }
  112. cdef('disconnectTimeout', 'int', 3, 'Time (s) to wait for disconnection');
  113. cdef('check_rate', 'int', 217, 'Timing (ms) of send/receive check');
  114. cdef('fbu_req_rate', 'int', 1413, 'Timing (ms) of frameBufferUpdate requests');
  115. cdef('updateState',
  116. 'func', function() { Util.Debug("updateState stub"); },
  117. 'callback: state update');
  118. cdef('clipboardReceive',
  119. 'func', function() { Util.Debug("clipboardReceive stub"); },
  120. 'callback: clipboard contents received');
  121. // Override/add some specific getters/setters
  122. that.set_local_cursor = function(cursor) {
  123. if ((!cursor) || (cursor in {'0':1, 'no':1, 'false':1})) {
  124. conf.local_cursor = false;
  125. } else {
  126. if (canvas.get_cursor_uri()) {
  127. conf.local_cursor = true;
  128. } else {
  129. Util.Warn("Browser does not support local cursor");
  130. }
  131. }
  132. };
  133. that.get_canvas = function() {
  134. return canvas;
  135. };
  136. //
  137. // Setup routines
  138. //
  139. // Create the public API interface and initialize
  140. function constructor() {
  141. var i, rmode;
  142. Util.Debug(">> RFB.constructor");
  143. // Create lookup tables based encoding number
  144. for (i=0; i < encodings.length; i+=1) {
  145. encHandlers[encodings[i][1]] = encHandlers[encodings[i][0]];
  146. encNames[encodings[i][1]] = encodings[i][0];
  147. encStats[encodings[i][1]] = [0, 0];
  148. }
  149. // Initialize canvas, mouse and keyboard
  150. try {
  151. canvas = new Canvas({'target': conf.target});
  152. keyboard = new Keyboard({'target': conf.focusContainer,
  153. 'keyPress': keyPress});
  154. mouse = new Mouse({'target': conf.target,
  155. 'mouseButton': mouseButton,
  156. 'mouseMove': mouseMove});
  157. } catch (exc) {
  158. Util.Error("Canvas exception: " + exc);
  159. updateState('fatal', "No working Canvas");
  160. }
  161. rmode = canvas.get_render_mode();
  162. init_vars();
  163. /* Check web-socket-js if no builtin WebSocket support */
  164. if (Websock_native) {
  165. Util.Info("Using native WebSockets");
  166. updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
  167. } else {
  168. Util.Warn("Using web-socket-js bridge. Flash version: " +
  169. Util.Flash.version);
  170. if ((! Util.Flash) ||
  171. (Util.Flash.version < 9)) {
  172. updateState('fatal', "WebSockets or <a href='http://get.adobe.com/flashplayer'>Adobe Flash<\/a> is required");
  173. } else if (document.location.href.substr(0, 7) === "file://") {
  174. updateState('fatal',
  175. "'file://' URL is incompatible with Adobe Flash");
  176. } else {
  177. updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
  178. }
  179. }
  180. Util.Debug("<< RFB.constructor");
  181. return that; // Return the public API interface
  182. }
  183. function connect() {
  184. Util.Debug(">> RFB.connect");
  185. var uri = "";
  186. if (conf.encrypt) {
  187. uri = "wss://";
  188. } else {
  189. uri = "ws://";
  190. }
  191. uri += rfb_host + ":" + rfb_port + "/";
  192. Util.Info("connecting to " + uri);
  193. ws.open(uri);
  194. Util.Debug("<< RFB.connect");
  195. }
  196. init_vars = function() {
  197. var i;
  198. /* Reset state */
  199. ws = new Websock();
  200. ws.init();
  201. ws.on('message', handle_message);
  202. ws.on('open', function() {
  203. if (rfb_state === "connect") {
  204. updateState('ProtocolVersion', "Starting VNC handshake");
  205. } else {
  206. fail("Got unexpected WebSockets connection");
  207. }
  208. });
  209. ws.on('close', function() {
  210. if (rfb_state === 'disconnect') {
  211. updateState('disconnected', 'VNC disconnected');
  212. } else if (rfb_state === 'ProtocolVersion') {
  213. fail('Failed to connect to server');
  214. } else if (rfb_state in {'failed':1, 'disconnected':1}) {
  215. Util.Error("Received onclose while disconnected");
  216. } else {
  217. fail('Server disconnected');
  218. }
  219. });
  220. ws.on('error', function(e) {
  221. fail("WebSock error: " + e);
  222. });
  223. FBU.rects = 0;
  224. FBU.subrects = 0; // RRE and HEXTILE
  225. FBU.lines = 0; // RAW
  226. FBU.tiles = 0; // HEXTILE
  227. FBU.imgQ = []; // TIGHT_PNG image queue
  228. mouse_buttonMask = 0;
  229. mouse_arr = [];
  230. // Clear the per connection encoding stats
  231. for (i=0; i < encodings.length; i+=1) {
  232. encStats[encodings[i][1]][0] = 0;
  233. }
  234. };
  235. // Print statistics
  236. print_stats = function() {
  237. var i, s;
  238. Util.Info("Encoding stats for this connection:");
  239. for (i=0; i < encodings.length; i+=1) {
  240. s = encStats[encodings[i][1]];
  241. if ((s[0] + s[1]) > 0) {
  242. Util.Info(" " + encodings[i][0] + ": " +
  243. s[0] + " rects");
  244. }
  245. }
  246. Util.Info("Encoding stats since page load:");
  247. for (i=0; i < encodings.length; i+=1) {
  248. s = encStats[encodings[i][1]];
  249. if ((s[0] + s[1]) > 0) {
  250. Util.Info(" " + encodings[i][0] + ": " +
  251. s[1] + " rects");
  252. }
  253. }
  254. };
  255. //
  256. // Utility routines
  257. //
  258. /*
  259. * Running states:
  260. * disconnected - idle state
  261. * normal - connected
  262. *
  263. * Page states:
  264. * loaded - page load, equivalent to disconnected
  265. * connect - starting initialization
  266. * disconnect - starting disconnect
  267. * failed - abnormal transition to disconnected
  268. * fatal - failed to load page, or fatal error
  269. *
  270. * VNC initialization states:
  271. * ProtocolVersion
  272. * Security
  273. * Authentication
  274. * password - waiting for password, not part of RFB
  275. * SecurityResult
  276. * ClientInitialization - not triggered by server message
  277. * ServerInitialization
  278. */
  279. updateState = function(state, statusMsg) {
  280. var func, cmsg, oldstate = rfb_state;
  281. if (state === oldstate) {
  282. /* Already here, ignore */
  283. Util.Debug("Already in state '" + state + "', ignoring.");
  284. return;
  285. }
  286. /*
  287. * These are disconnected states. A previous connect may
  288. * asynchronously cause a connection so make sure we are closed.
  289. */
  290. if (state in {'disconnected':1, 'loaded':1, 'connect':1,
  291. 'disconnect':1, 'failed':1, 'fatal':1}) {
  292. if (sendTimer) {
  293. clearInterval(sendTimer);
  294. sendTimer = null;
  295. }
  296. if (msgTimer) {
  297. clearInterval(msgTimer);
  298. msgTimer = null;
  299. }
  300. if (canvas && canvas.getContext()) {
  301. keyboard.ungrab();
  302. mouse.ungrab();
  303. canvas.defaultCursor();
  304. if (Util.get_logging() !== 'debug') {
  305. canvas.clear();
  306. }
  307. if ((Util.get_logging() !== 'debug') ||
  308. (state === 'loaded')) {
  309. // Show noVNC logo on load and when disconnected if
  310. // debug is off
  311. if (typeof noVNC_logo !== 'undefined' && noVNC_logo) {
  312. canvas.resize(noVNC_logo.width, noVNC_logo.height);
  313. canvas.blitStringImage(noVNC_logo.data, 0, 0);
  314. }
  315. }
  316. }
  317. ws.close();
  318. }
  319. if (oldstate === 'fatal') {
  320. Util.Error("Fatal error, cannot continue");
  321. }
  322. if ((state === 'failed') || (state === 'fatal')) {
  323. func = Util.Error;
  324. } else {
  325. func = Util.Warn;
  326. }
  327. if ((oldstate === 'failed') && (state === 'disconnected')) {
  328. // Do disconnect action, but stay in failed state.
  329. rfb_state = 'failed';
  330. } else {
  331. rfb_state = state;
  332. }
  333. cmsg = typeof(statusMsg) !== 'undefined' ? (" Msg: " + statusMsg) : "";
  334. func("New state '" + rfb_state + "', was '" + oldstate + "'." + cmsg);
  335. if (connTimer && (rfb_state !== 'connect')) {
  336. Util.Debug("Clearing connect timer");
  337. clearInterval(connTimer);
  338. connTimer = null;
  339. }
  340. if (disconnTimer && (rfb_state !== 'disconnect')) {
  341. Util.Debug("Clearing disconnect timer");
  342. clearInterval(disconnTimer);
  343. disconnTimer = null;
  344. }
  345. switch (state) {
  346. case 'normal':
  347. if ((oldstate === 'disconnected') || (oldstate === 'failed')) {
  348. Util.Error("Invalid transition from 'disconnected' or 'failed' to 'normal'");
  349. }
  350. break;
  351. case 'connect':
  352. connTimer = setTimeout(function () {
  353. fail("Connect timeout");
  354. }, conf.connectTimeout * 1000);
  355. init_vars();
  356. connect();
  357. // WebSocket.onopen transitions to 'ProtocolVersion'
  358. break;
  359. case 'disconnect':
  360. if (! test_mode) {
  361. disconnTimer = setTimeout(function () {
  362. fail("Disconnect timeout");
  363. }, conf.disconnectTimeout * 1000);
  364. }
  365. print_stats();
  366. // WebSocket.onclose transitions to 'disconnected'
  367. break;
  368. case 'failed':
  369. if (oldstate === 'disconnected') {
  370. Util.Error("Invalid transition from 'disconnected' to 'failed'");
  371. }
  372. if (oldstate === 'normal') {
  373. Util.Error("Error while connected.");
  374. }
  375. if (oldstate === 'init') {
  376. Util.Error("Error while initializing.");
  377. }
  378. // Make sure we transition to disconnected
  379. setTimeout(function() { updateState('disconnected'); }, 50);
  380. break;
  381. default:
  382. // No state change action to take
  383. }
  384. if ((oldstate === 'failed') && (state === 'disconnected')) {
  385. // Leave the failed message
  386. conf.updateState(that, state, oldstate);
  387. } else {
  388. conf.updateState(that, state, oldstate, statusMsg);
  389. }
  390. };
  391. fail = function(msg) {
  392. updateState('failed', msg);
  393. return false;
  394. };
  395. handle_message = function() {
  396. //Util.Debug(">> handle_message ws.rQlen(): " + ws.rQlen());
  397. //Util.Debug("ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  398. if (ws.rQlen() === 0) {
  399. Util.Warn("handle_message called on empty receive queue");
  400. return;
  401. }
  402. switch (rfb_state) {
  403. case 'disconnected':
  404. case 'failed':
  405. Util.Error("Got data while disconnected");
  406. break;
  407. case 'normal':
  408. if (normal_msg() && ws.rQlen() > 0) {
  409. // true means we can continue processing
  410. // Give other events a chance to run
  411. if (msgTimer === null) {
  412. Util.Debug("More data to process, creating timer");
  413. msgTimer = setTimeout(function () {
  414. msgTimer = null;
  415. handle_message();
  416. }, 10);
  417. } else {
  418. Util.Debug("More data to process, existing timer");
  419. }
  420. }
  421. break;
  422. default:
  423. init_msg();
  424. break;
  425. }
  426. };
  427. function genDES(password, challenge) {
  428. var i, passwd = [];
  429. for (i=0; i < password.length; i += 1) {
  430. passwd.push(password.charCodeAt(i));
  431. }
  432. return (new DES(passwd)).encrypt(challenge);
  433. }
  434. function flushClient() {
  435. if (mouse_arr.length > 0) {
  436. //send(mouse_arr.concat(fbUpdateRequest(1)));
  437. ws.send(mouse_arr);
  438. setTimeout(function() {
  439. ws.send(fbUpdateRequest(1));
  440. }, 50);
  441. mouse_arr = [];
  442. return true;
  443. } else {
  444. return false;
  445. }
  446. }
  447. // overridable for testing
  448. checkEvents = function() {
  449. var now;
  450. if (rfb_state === 'normal') {
  451. if (! flushClient()) {
  452. now = new Date().getTime();
  453. if (now > last_req_time + conf.fbu_req_rate) {
  454. last_req_time = now;
  455. ws.send(fbUpdateRequest(1));
  456. }
  457. }
  458. }
  459. setTimeout(checkEvents, conf.check_rate);
  460. };
  461. keyPress = function(keysym, down) {
  462. var arr;
  463. arr = keyEvent(keysym, down);
  464. arr = arr.concat(fbUpdateRequest(1));
  465. ws.send(arr);
  466. };
  467. mouseButton = function(x, y, down, bmask) {
  468. if (down) {
  469. mouse_buttonMask |= bmask;
  470. } else {
  471. mouse_buttonMask ^= bmask;
  472. }
  473. mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
  474. flushClient();
  475. };
  476. mouseMove = function(x, y) {
  477. //Util.Debug('>> mouseMove ' + x + "," + y);
  478. mouse_arr = mouse_arr.concat( pointerEvent(x, y) );
  479. };
  480. //
  481. // Server message handlers
  482. //
  483. // RFB/VNC initialisation message handler
  484. init_msg = function() {
  485. //Util.Debug(">> init_msg [rfb_state '" + rfb_state + "']");
  486. var strlen, reason, length, sversion, cversion,
  487. i, types, num_types, challenge, response, bpp, depth,
  488. big_endian, true_color, name_length;
  489. //Util.Debug("ws.rQ (" + ws.rQlen() + ") " + ws.rQslice(0));
  490. switch (rfb_state) {
  491. case 'ProtocolVersion' :
  492. if (ws.rQlen() < 12) {
  493. return fail("Incomplete protocol version");
  494. }
  495. sversion = ws.rQshiftStr(12).substr(4,7);
  496. Util.Info("Server ProtocolVersion: " + sversion);
  497. switch (sversion) {
  498. case "003.003": rfb_version = 3.3; break;
  499. case "003.006": rfb_version = 3.3; break; // UltraVNC
  500. case "003.007": rfb_version = 3.7; break;
  501. case "003.008": rfb_version = 3.8; break;
  502. default:
  503. return fail("Invalid server version " + sversion);
  504. }
  505. if (rfb_version > rfb_max_version) {
  506. rfb_version = rfb_max_version;
  507. }
  508. if (! test_mode) {
  509. sendTimer = setInterval(function() {
  510. // Send updates either at a rate of one update
  511. // every 50ms, or whatever slower rate the network
  512. // can handle.
  513. ws.flush();
  514. }, 50);
  515. }
  516. cversion = "00" + parseInt(rfb_version,10) +
  517. ".00" + ((rfb_version * 10) % 10);
  518. ws.send_string("RFB " + cversion + "\n");
  519. updateState('Security', "Sent ProtocolVersion: " + cversion);
  520. break;
  521. case 'Security' :
  522. if (rfb_version >= 3.7) {
  523. // Server sends supported list, client decides
  524. num_types = ws.rQshift8();
  525. if (ws.rQwait("security type", num_types, 1)) { return false; }
  526. if (num_types === 0) {
  527. strlen = ws.rQshift32();
  528. reason = ws.rQshiftStr(strlen);
  529. return fail("Security failure: " + reason);
  530. }
  531. rfb_auth_scheme = 0;
  532. types = ws.rQshiftBytes(num_types);
  533. Util.Debug("Server security types: " + types);
  534. for (i=0; i < types.length; i+=1) {
  535. if ((types[i] > rfb_auth_scheme) && (types[i] < 3)) {
  536. rfb_auth_scheme = types[i];
  537. }
  538. }
  539. if (rfb_auth_scheme === 0) {
  540. return fail("Unsupported security types: " + types);
  541. }
  542. ws.send([rfb_auth_scheme]);
  543. } else {
  544. // Server decides
  545. if (ws.rQwait("security scheme", 4)) { return false; }
  546. rfb_auth_scheme = ws.rQshift32();
  547. }
  548. updateState('Authentication',
  549. "Authenticating using scheme: " + rfb_auth_scheme);
  550. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  551. break;
  552. // Triggered by fallthough, not by server message
  553. case 'Authentication' :
  554. //Util.Debug("Security auth scheme: " + rfb_auth_scheme);
  555. switch (rfb_auth_scheme) {
  556. case 0: // connection failed
  557. if (ws.rQwait("auth reason", 4)) { return false; }
  558. strlen = ws.rQshift32();
  559. reason = ws.rQshiftStr(strlen);
  560. return fail("Auth failure: " + reason);
  561. case 1: // no authentication
  562. if (rfb_version >= 3.8) {
  563. updateState('SecurityResult');
  564. return;
  565. }
  566. // Fall through to ClientInitialisation
  567. break;
  568. case 2: // VNC authentication
  569. if (rfb_password.length === 0) {
  570. updateState('password', "Password Required");
  571. return;
  572. }
  573. if (ws.rQwait("auth challenge", 16)) { return false; }
  574. challenge = ws.rQshiftBytes(16);
  575. //Util.Debug("Password: " + rfb_password);
  576. //Util.Debug("Challenge: " + challenge +
  577. // " (" + challenge.length + ")");
  578. response = genDES(rfb_password, challenge);
  579. //Util.Debug("Response: " + response +
  580. // " (" + response.length + ")");
  581. //Util.Debug("Sending DES encrypted auth response");
  582. ws.send(response);
  583. updateState('SecurityResult');
  584. return;
  585. default:
  586. fail("Unsupported auth scheme: " + rfb_auth_scheme);
  587. return;
  588. }
  589. updateState('ClientInitialisation', "No auth required");
  590. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  591. break;
  592. case 'SecurityResult' :
  593. if (ws.rQwait("VNC auth response ", 4)) { return false; }
  594. switch (ws.rQshift32()) {
  595. case 0: // OK
  596. // Fall through to ClientInitialisation
  597. break;
  598. case 1: // failed
  599. if (rfb_version >= 3.8) {
  600. length = ws.rQshift32();
  601. if (ws.rQwait("SecurityResult reason", length, 8)) {
  602. return false;
  603. }
  604. reason = ws.rQshiftStr(length);
  605. fail(reason);
  606. } else {
  607. fail("Authentication failed");
  608. }
  609. return;
  610. case 2: // too-many
  611. return fail("Too many auth attempts");
  612. }
  613. updateState('ClientInitialisation', "Authentication OK");
  614. init_msg(); // Recursive fallthrough (workaround JSLint complaint)
  615. break;
  616. // Triggered by fallthough, not by server message
  617. case 'ClientInitialisation' :
  618. ws.send([conf.shared ? 1 : 0]); // ClientInitialisation
  619. updateState('ServerInitialisation', "Authentication OK");
  620. break;
  621. case 'ServerInitialisation' :
  622. if (ws.rQwait("server initialization", 24)) { return false; }
  623. /* Screen size */
  624. fb_width = ws.rQshift16();
  625. fb_height = ws.rQshift16();
  626. /* PIXEL_FORMAT */
  627. bpp = ws.rQshift8();
  628. depth = ws.rQshift8();
  629. big_endian = ws.rQshift8();
  630. true_color = ws.rQshift8();
  631. Util.Info("Screen: " + fb_width + "x" + fb_height +
  632. ", bpp: " + bpp + ", depth: " + depth +
  633. ", big_endian: " + big_endian +
  634. ", true_color: " + true_color);
  635. /* Connection name/title */
  636. ws.rQshiftStr(12);
  637. name_length = ws.rQshift32();
  638. fb_name = ws.rQshiftStr(name_length);
  639. canvas.resize(fb_width, fb_height, conf.true_color);
  640. keyboard.grab();
  641. mouse.grab();
  642. if (conf.true_color) {
  643. fb_Bpp = 4;
  644. fb_depth = 3;
  645. } else {
  646. fb_Bpp = 1;
  647. fb_depth = 1;
  648. }
  649. response = pixelFormat();
  650. response = response.concat(clientEncodings());
  651. response = response.concat(fbUpdateRequest(0));
  652. timing.fbu_rt_start = (new Date()).getTime();
  653. ws.send(response);
  654. /* Start pushing/polling */
  655. setTimeout(checkEvents, conf.check_rate);
  656. setTimeout(scan_tight_imgQ, scan_imgQ_rate);
  657. if (conf.encrypt) {
  658. updateState('normal', "Connected (encrypted) to: " + fb_name);
  659. } else {
  660. updateState('normal', "Connected (unencrypted) to: " + fb_name);
  661. }
  662. break;
  663. }
  664. //Util.Debug("<< init_msg");
  665. };
  666. /* Normal RFB/VNC server message handler */
  667. normal_msg = function() {
  668. //Util.Debug(">> normal_msg");
  669. var ret = true, msg_type, length,
  670. c, first_colour, num_colours, red, green, blue;
  671. if (FBU.rects > 0) {
  672. msg_type = 0;
  673. } else {
  674. msg_type = ws.rQshift8();
  675. }
  676. switch (msg_type) {
  677. case 0: // FramebufferUpdate
  678. ret = framebufferUpdate(); // false means need more data
  679. break;
  680. case 1: // SetColourMapEntries
  681. Util.Debug("SetColourMapEntries");
  682. ws.rQshift8(); // Padding
  683. first_colour = ws.rQshift16(); // First colour
  684. num_colours = ws.rQshift16();
  685. for (c=0; c < num_colours; c+=1) {
  686. red = ws.rQshift16();
  687. //Util.Debug("red before: " + red);
  688. red = parseInt(red / 256, 10);
  689. //Util.Debug("red after: " + red);
  690. green = parseInt(ws.rQshift16() / 256, 10);
  691. blue = parseInt(ws.rQshift16() / 256, 10);
  692. canvas.set_colourMap([red, green, blue], first_colour + c);
  693. }
  694. Util.Info("Registered " + num_colours + " colourMap entries");
  695. //Util.Debug("colourMap: " + canvas.get_colourMap());
  696. break;
  697. case 2: // Bell
  698. Util.Warn("Bell (unsupported)");
  699. break;
  700. case 3: // ServerCutText
  701. Util.Debug("ServerCutText");
  702. if (ws.rQwait("ServerCutText header", 7, 1)) { return false; }
  703. ws.rQshiftBytes(3); // Padding
  704. length = ws.rQshift32();
  705. if (ws.rQwait("ServerCutText", length, 8)) { return false; }
  706. conf.clipboardReceive(that, ws.rQshiftStr(length));
  707. break;
  708. default:
  709. fail("Disconnected: illegal server message type " + msg_type);
  710. Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
  711. break;
  712. }
  713. //Util.Debug("<< normal_msg");
  714. return ret;
  715. };
  716. framebufferUpdate = function() {
  717. var now, hdr, fbu_rt_diff, ret = true;
  718. if (FBU.rects === 0) {
  719. //Util.Debug("New FBU: ws.rQslice(0,20): " + ws.rQslice(0,20));
  720. if (ws.rQwait("FBU header", 3)) {
  721. ws.rQunshift8(0); // FBU msg_type
  722. return false;
  723. }
  724. ws.rQshift8(); // padding
  725. FBU.rects = ws.rQshift16();
  726. //Util.Debug("FramebufferUpdate, rects:" + FBU.rects);
  727. FBU.bytes = 0;
  728. timing.cur_fbu = 0;
  729. if (timing.fbu_rt_start > 0) {
  730. now = (new Date()).getTime();
  731. Util.Info("First FBU latency: " + (now - timing.fbu_rt_start));
  732. }
  733. }
  734. while (FBU.rects > 0) {
  735. if (rfb_state !== "normal") {
  736. return false;
  737. }
  738. if (ws.rQwait("FBU", FBU.bytes)) { return false; }
  739. if (FBU.bytes === 0) {
  740. if (ws.rQwait("rect header", 12)) { return false; }
  741. /* New FramebufferUpdate */
  742. hdr = ws.rQshiftBytes(12);
  743. FBU.x = (hdr[0] << 8) + hdr[1];
  744. FBU.y = (hdr[2] << 8) + hdr[3];
  745. FBU.width = (hdr[4] << 8) + hdr[5];
  746. FBU.height = (hdr[6] << 8) + hdr[7];
  747. FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
  748. (hdr[10] << 8) + hdr[11], 10);
  749. if (encNames[FBU.encoding]) {
  750. // Debug:
  751. /*
  752. var msg = "FramebufferUpdate rects:" + FBU.rects;
  753. msg += " x: " + FBU.x + " y: " + FBU.y;
  754. msg += " width: " + FBU.width + " height: " + FBU.height;
  755. msg += " encoding:" + FBU.encoding;
  756. msg += "(" + encNames[FBU.encoding] + ")";
  757. msg += ", ws.rQlen(): " + ws.rQlen();
  758. Util.Debug(msg);
  759. */
  760. } else {
  761. fail("Disconnected: unsupported encoding " +
  762. FBU.encoding);
  763. return false;
  764. }
  765. }
  766. timing.last_fbu = (new Date()).getTime();
  767. ret = encHandlers[FBU.encoding]();
  768. now = (new Date()).getTime();
  769. timing.cur_fbu += (now - timing.last_fbu);
  770. if (ret) {
  771. encStats[FBU.encoding][0] += 1;
  772. encStats[FBU.encoding][1] += 1;
  773. }
  774. if (FBU.rects === 0) {
  775. if (((FBU.width === fb_width) &&
  776. (FBU.height === fb_height)) ||
  777. (timing.fbu_rt_start > 0)) {
  778. timing.full_fbu_total += timing.cur_fbu;
  779. timing.full_fbu_cnt += 1;
  780. Util.Info("Timing of full FBU, cur: " +
  781. timing.cur_fbu + ", total: " +
  782. timing.full_fbu_total + ", cnt: " +
  783. timing.full_fbu_cnt + ", avg: " +
  784. (timing.full_fbu_total /
  785. timing.full_fbu_cnt));
  786. }
  787. if (timing.fbu_rt_start > 0) {
  788. fbu_rt_diff = now - timing.fbu_rt_start;
  789. timing.fbu_rt_total += fbu_rt_diff;
  790. timing.fbu_rt_cnt += 1;
  791. Util.Info("full FBU round-trip, cur: " +
  792. fbu_rt_diff + ", total: " +
  793. timing.fbu_rt_total + ", cnt: " +
  794. timing.fbu_rt_cnt + ", avg: " +
  795. (timing.fbu_rt_total /
  796. timing.fbu_rt_cnt));
  797. timing.fbu_rt_start = 0;
  798. }
  799. }
  800. if (! ret) {
  801. return ret; // false ret means need more data
  802. }
  803. }
  804. return true; // We finished this FBU
  805. };
  806. //
  807. // FramebufferUpdate encodings
  808. //
  809. encHandlers.RAW = function display_raw() {
  810. //Util.Debug(">> display_raw (" + ws.rQlen() + " bytes)");
  811. var cur_y, cur_height;
  812. if (FBU.lines === 0) {
  813. FBU.lines = FBU.height;
  814. }
  815. FBU.bytes = FBU.width * fb_Bpp; // At least a line
  816. if (ws.rQwait("RAW", FBU.bytes)) { return false; }
  817. cur_y = FBU.y + (FBU.height - FBU.lines);
  818. cur_height = Math.min(FBU.lines,
  819. Math.floor(ws.rQlen()/(FBU.width * fb_Bpp)));
  820. canvas.blitImage(FBU.x, cur_y, FBU.width, cur_height,
  821. ws.get_rQ(), ws.get_rQi());
  822. ws.rQshiftBytes(FBU.width * cur_height * fb_Bpp);
  823. FBU.lines -= cur_height;
  824. if (FBU.lines > 0) {
  825. FBU.bytes = FBU.width * fb_Bpp; // At least another line
  826. } else {
  827. FBU.rects -= 1;
  828. FBU.bytes = 0;
  829. }
  830. //Util.Debug("<< display_raw (" + ws.rQlen() + " bytes)");
  831. return true;
  832. };
  833. encHandlers.COPYRECT = function display_copy_rect() {
  834. //Util.Debug(">> display_copy_rect");
  835. var old_x, old_y;
  836. if (ws.rQwait("COPYRECT", 4)) { return false; }
  837. old_x = ws.rQshift16();
  838. old_y = ws.rQshift16();
  839. canvas.copyImage(old_x, old_y, FBU.x, FBU.y, FBU.width, FBU.height);
  840. FBU.rects -= 1;
  841. FBU.bytes = 0;
  842. return true;
  843. };
  844. encHandlers.RRE = function display_rre() {
  845. //Util.Debug(">> display_rre (" + ws.rQlen() + " bytes)");
  846. var color, x, y, width, height, chunk;
  847. if (FBU.subrects === 0) {
  848. if (ws.rQwait("RRE", 4+fb_Bpp)) { return false; }
  849. FBU.subrects = ws.rQshift32();
  850. color = ws.rQshiftBytes(fb_Bpp); // Background
  851. canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
  852. }
  853. while ((FBU.subrects > 0) && (ws.rQlen() >= (fb_Bpp + 8))) {
  854. color = ws.rQshiftBytes(fb_Bpp);
  855. x = ws.rQshift16();
  856. y = ws.rQshift16();
  857. width = ws.rQshift16();
  858. height = ws.rQshift16();
  859. canvas.fillRect(FBU.x + x, FBU.y + y, width, height, color);
  860. FBU.subrects -= 1;
  861. }
  862. //Util.Debug(" display_rre: rects: " + FBU.rects +
  863. // ", FBU.subrects: " + FBU.subrects);
  864. if (FBU.subrects > 0) {
  865. chunk = Math.min(rre_chunk_sz, FBU.subrects);
  866. FBU.bytes = (fb_Bpp + 8) * chunk;
  867. } else {
  868. FBU.rects -= 1;
  869. FBU.bytes = 0;
  870. }
  871. //Util.Debug("<< display_rre, FBU.bytes: " + FBU.bytes);
  872. return true;
  873. };
  874. encHandlers.HEXTILE = function display_hextile() {
  875. //Util.Debug(">> display_hextile");
  876. var subencoding, subrects, tile, color, cur_tile,
  877. tile_x, x, w, tile_y, y, h, xy, s, sx, sy, wh, sw, sh,
  878. rQ = ws.get_rQ(), rQi = ws.get_rQi();
  879. if (FBU.tiles === 0) {
  880. FBU.tiles_x = Math.ceil(FBU.width/16);
  881. FBU.tiles_y = Math.ceil(FBU.height/16);
  882. FBU.total_tiles = FBU.tiles_x * FBU.tiles_y;
  883. FBU.tiles = FBU.total_tiles;
  884. }
  885. /* FBU.bytes comes in as 1, ws.rQlen() at least 1 */
  886. while (FBU.tiles > 0) {
  887. FBU.bytes = 1;
  888. if (ws.rQwait("HEXTILE subencoding", FBU.bytes)) { return false; }
  889. subencoding = rQ[rQi]; // Peek
  890. if (subencoding > 30) { // Raw
  891. fail("Disconnected: illegal hextile subencoding " + subencoding);
  892. //Util.Debug("ws.rQslice(0,30):" + ws.rQslice(0,30));
  893. return false;
  894. }
  895. subrects = 0;
  896. cur_tile = FBU.total_tiles - FBU.tiles;
  897. tile_x = cur_tile % FBU.tiles_x;
  898. tile_y = Math.floor(cur_tile / FBU.tiles_x);
  899. x = FBU.x + tile_x * 16;
  900. y = FBU.y + tile_y * 16;
  901. w = Math.min(16, (FBU.x + FBU.width) - x);
  902. h = Math.min(16, (FBU.y + FBU.height) - y);
  903. /* Figure out how much we are expecting */
  904. if (subencoding & 0x01) { // Raw
  905. //Util.Debug(" Raw subencoding");
  906. FBU.bytes += w * h * fb_Bpp;
  907. } else {
  908. if (subencoding & 0x02) { // Background
  909. FBU.bytes += fb_Bpp;
  910. }
  911. if (subencoding & 0x04) { // Foreground
  912. FBU.bytes += fb_Bpp;
  913. }
  914. if (subencoding & 0x08) { // AnySubrects
  915. FBU.bytes += 1; // Since we aren't shifting it off
  916. if (ws.rQwait("hextile subrects header", FBU.bytes)) { return false; }
  917. subrects = rQ[rQi + FBU.bytes-1]; // Peek
  918. if (subencoding & 0x10) { // SubrectsColoured
  919. FBU.bytes += subrects * (fb_Bpp + 2);
  920. } else {
  921. FBU.bytes += subrects * 2;
  922. }
  923. }
  924. }
  925. /*
  926. Util.Debug(" tile:" + cur_tile + "/" + (FBU.total_tiles - 1) +
  927. " (" + tile_x + "," + tile_y + ")" +
  928. " [" + x + "," + y + "]@" + w + "x" + h +
  929. ", subenc:" + subencoding +
  930. "(last: " + FBU.lastsubencoding + "), subrects:" +
  931. subrects +
  932. ", ws.rQlen():" + ws.rQlen() + ", FBU.bytes:" + FBU.bytes +
  933. " last:" + ws.rQslice(FBU.bytes-10, FBU.bytes) +
  934. " next:" + ws.rQslice(FBU.bytes-1, FBU.bytes+10));
  935. */
  936. if (ws.rQwait("hextile", FBU.bytes)) { return false; }
  937. /* We know the encoding and have a whole tile */
  938. FBU.subencoding = rQ[rQi];
  939. rQi += 1;
  940. if (FBU.subencoding === 0) {
  941. if (FBU.lastsubencoding & 0x01) {
  942. /* Weird: ignore blanks after RAW */
  943. Util.Debug(" Ignoring blank after RAW");
  944. } else {
  945. canvas.fillRect(x, y, w, h, FBU.background);
  946. }
  947. } else if (FBU.subencoding & 0x01) { // Raw
  948. canvas.blitImage(x, y, w, h, rQ, rQi);
  949. rQi += FBU.bytes - 1;
  950. } else {
  951. if (FBU.subencoding & 0x02) { // Background
  952. FBU.background = rQ.slice(rQi, rQi + fb_Bpp);
  953. rQi += fb_Bpp;
  954. }
  955. if (FBU.subencoding & 0x04) { // Foreground
  956. FBU.foreground = rQ.slice(rQi, rQi + fb_Bpp);
  957. rQi += fb_Bpp;
  958. }
  959. tile = canvas.getTile(x, y, w, h, FBU.background);
  960. if (FBU.subencoding & 0x08) { // AnySubrects
  961. subrects = rQ[rQi];
  962. rQi += 1;
  963. for (s = 0; s < subrects; s += 1) {
  964. if (FBU.subencoding & 0x10) { // SubrectsColoured
  965. color = rQ.slice(rQi, rQi + fb_Bpp);
  966. rQi += fb_Bpp;
  967. } else {
  968. color = FBU.foreground;
  969. }
  970. xy = rQ[rQi];
  971. rQi += 1;
  972. sx = (xy >> 4);
  973. sy = (xy & 0x0f);
  974. wh = rQ[rQi];
  975. rQi += 1;
  976. sw = (wh >> 4) + 1;
  977. sh = (wh & 0x0f) + 1;
  978. canvas.setSubTile(tile, sx, sy, sw, sh, color);
  979. }
  980. }
  981. canvas.putTile(tile);
  982. }
  983. ws.set_rQi(rQi);
  984. FBU.lastsubencoding = FBU.subencoding;
  985. FBU.bytes = 0;
  986. FBU.tiles -= 1;
  987. }
  988. if (FBU.tiles === 0) {
  989. FBU.rects -= 1;
  990. }
  991. //Util.Debug("<< display_hextile");
  992. return true;
  993. };
  994. encHandlers.TIGHT_PNG = function display_tight_png() {
  995. //Util.Debug(">> display_tight_png");
  996. var ctl, cmode, clength, getCLength, color, img;
  997. //Util.Debug(" FBU.rects: " + FBU.rects);
  998. //Util.Debug(" starting ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  999. FBU.bytes = 1; // compression-control byte
  1000. if (ws.rQwait("TIGHT compression-control", FBU.bytes)) { return false; }
  1001. // Get 'compact length' header and data size
  1002. getCLength = function (arr) {
  1003. var header = 1, data = 0;
  1004. data += arr[0] & 0x7f;
  1005. if (arr[0] & 0x80) {
  1006. header += 1;
  1007. data += (arr[1] & 0x7f) << 7;
  1008. if (arr[1] & 0x80) {
  1009. header += 1;
  1010. data += arr[2] << 14;
  1011. }
  1012. }
  1013. return [header, data];
  1014. };
  1015. ctl = ws.rQpeek8();
  1016. switch (ctl >> 4) {
  1017. case 0x08: cmode = "fill"; break;
  1018. case 0x09: cmode = "jpeg"; break;
  1019. case 0x0A: cmode = "png"; break;
  1020. default: throw("Illegal basic compression received, ctl: " + ctl);
  1021. }
  1022. switch (cmode) {
  1023. // fill uses fb_depth because TPIXELs drop the padding byte
  1024. case "fill": FBU.bytes += fb_depth; break; // TPIXEL
  1025. case "jpeg": FBU.bytes += 3; break; // max clength
  1026. case "png": FBU.bytes += 3; break; // max clength
  1027. }
  1028. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1029. //Util.Debug(" ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  1030. //Util.Debug(" cmode: " + cmode);
  1031. // Determine FBU.bytes
  1032. switch (cmode) {
  1033. case "fill":
  1034. ws.rQshift8(); // shift off ctl
  1035. color = ws.rQshiftBytes(fb_depth);
  1036. canvas.fillRect(FBU.x, FBU.y, FBU.width, FBU.height, color);
  1037. break;
  1038. case "jpeg":
  1039. case "png":
  1040. clength = getCLength(ws.rQslice(1, 4));
  1041. FBU.bytes = 1 + clength[0] + clength[1]; // ctl + clength size + jpeg-data
  1042. if (ws.rQwait("TIGHT " + cmode, FBU.bytes)) { return false; }
  1043. // We have everything, render it
  1044. //Util.Debug(" png, ws.rQlen(): " + ws.rQlen() + ", clength[0]: " + clength[0] + ", clength[1]: " + clength[1]);
  1045. ws.rQshiftBytes(1 + clength[0]); // shift off ctl + compact length
  1046. img = new Image();
  1047. img.onload = scan_tight_imgQ;
  1048. FBU.imgQ.push([img, FBU.x, FBU.y]);
  1049. img.src = "data:image/" + cmode +
  1050. extract_data_uri(ws.rQshiftBytes(clength[1]));
  1051. img = null;
  1052. break;
  1053. }
  1054. FBU.bytes = 0;
  1055. FBU.rects -= 1;
  1056. //Util.Debug(" ending ws.rQslice(0,20): " + ws.rQslice(0,20) + " (" + ws.rQlen() + ")");
  1057. //Util.Debug("<< display_tight_png");
  1058. return true;
  1059. };
  1060. extract_data_uri = function(arr) {
  1061. //var i, stra = [];
  1062. //for (i=0; i< arr.length; i += 1) {
  1063. // stra.push(String.fromCharCode(arr[i]));
  1064. //}
  1065. //return "," + escape(stra.join(''));
  1066. return ";base64," + Base64.encode(arr);
  1067. };
  1068. scan_tight_imgQ = function() {
  1069. var img, imgQ, ctx;
  1070. ctx = canvas.getContext();
  1071. if (rfb_state === 'normal') {
  1072. imgQ = FBU.imgQ;
  1073. while ((imgQ.length > 0) && (imgQ[0][0].complete)) {
  1074. img = imgQ.shift();
  1075. ctx.drawImage(img[0], img[1], img[2]);
  1076. }
  1077. setTimeout(scan_tight_imgQ, scan_imgQ_rate);
  1078. }
  1079. };
  1080. encHandlers.DesktopSize = function set_desktopsize() {
  1081. Util.Debug(">> set_desktopsize");
  1082. fb_width = FBU.width;
  1083. fb_height = FBU.height;
  1084. canvas.clear();
  1085. canvas.resize(fb_width, fb_height);
  1086. timing.fbu_rt_start = (new Date()).getTime();
  1087. // Send a new non-incremental request
  1088. ws.send(fbUpdateRequest(0));
  1089. FBU.bytes = 0;
  1090. FBU.rects -= 1;
  1091. Util.Debug("<< set_desktopsize");
  1092. return true;
  1093. };
  1094. encHandlers.Cursor = function set_cursor() {
  1095. var x, y, w, h, pixelslength, masklength;
  1096. //Util.Debug(">> set_cursor");
  1097. x = FBU.x; // hotspot-x
  1098. y = FBU.y; // hotspot-y
  1099. w = FBU.width;
  1100. h = FBU.height;
  1101. pixelslength = w * h * fb_Bpp;
  1102. masklength = Math.floor((w + 7) / 8) * h;
  1103. FBU.bytes = pixelslength + masklength;
  1104. if (ws.rQwait("cursor encoding", FBU.bytes)) { return false; }
  1105. //Util.Debug(" set_cursor, x: " + x + ", y: " + y + ", w: " + w + ", h: " + h);
  1106. canvas.changeCursor(ws.rQshiftBytes(pixelslength),
  1107. ws.rQshiftBytes(masklength),
  1108. x, y, w, h);
  1109. FBU.bytes = 0;
  1110. FBU.rects -= 1;
  1111. //Util.Debug("<< set_cursor");
  1112. return true;
  1113. };
  1114. encHandlers.JPEG_quality_lo = function set_jpeg_quality() {
  1115. Util.Error("Server sent jpeg_quality pseudo-encoding");
  1116. };
  1117. encHandlers.compress_lo = function set_compress_level() {
  1118. Util.Error("Server sent compress level pseudo-encoding");
  1119. };
  1120. /*
  1121. * Client message routines
  1122. */
  1123. pixelFormat = function() {
  1124. //Util.Debug(">> pixelFormat");
  1125. var arr;
  1126. arr = [0]; // msg-type
  1127. arr.push8(0); // padding
  1128. arr.push8(0); // padding
  1129. arr.push8(0); // padding
  1130. arr.push8(fb_Bpp * 8); // bits-per-pixel
  1131. arr.push8(fb_depth * 8); // depth
  1132. arr.push8(0); // little-endian
  1133. arr.push8(conf.true_color ? 1 : 0); // true-color
  1134. arr.push16(255); // red-max
  1135. arr.push16(255); // green-max
  1136. arr.push16(255); // blue-max
  1137. arr.push8(0); // red-shift
  1138. arr.push8(8); // green-shift
  1139. arr.push8(16); // blue-shift
  1140. arr.push8(0); // padding
  1141. arr.push8(0); // padding
  1142. arr.push8(0); // padding
  1143. //Util.Debug("<< pixelFormat");
  1144. return arr;
  1145. };
  1146. clientEncodings = function() {
  1147. //Util.Debug(">> clientEncodings");
  1148. var arr, i, encList = [];
  1149. for (i=0; i<encodings.length; i += 1) {
  1150. if ((encodings[i][0] === "Cursor") &&
  1151. (! conf.local_cursor)) {
  1152. Util.Debug("Skipping Cursor pseudo-encoding");
  1153. } else {
  1154. //Util.Debug("Adding encoding: " + encodings[i][0]);
  1155. encList.push(encodings[i][1]);
  1156. }
  1157. }
  1158. arr = [2]; // msg-type
  1159. arr.push8(0); // padding
  1160. arr.push16(encList.length); // encoding count
  1161. for (i=0; i < encList.length; i += 1) {
  1162. arr.push32(encList[i]);
  1163. }
  1164. //Util.Debug("<< clientEncodings: " + arr);
  1165. return arr;
  1166. };
  1167. fbUpdateRequest = function(incremental, x, y, xw, yw) {
  1168. //Util.Debug(">> fbUpdateRequest");
  1169. if (!x) { x = 0; }
  1170. if (!y) { y = 0; }
  1171. if (!xw) { xw = fb_width; }
  1172. if (!yw) { yw = fb_height; }
  1173. var arr;
  1174. arr = [3]; // msg-type
  1175. arr.push8(incremental);
  1176. arr.push16(x);
  1177. arr.push16(y);
  1178. arr.push16(xw);
  1179. arr.push16(yw);
  1180. //Util.Debug("<< fbUpdateRequest");
  1181. return arr;
  1182. };
  1183. keyEvent = function(keysym, down) {
  1184. //Util.Debug(">> keyEvent, keysym: " + keysym + ", down: " + down);
  1185. var arr;
  1186. arr = [4]; // msg-type
  1187. arr.push8(down);
  1188. arr.push16(0);
  1189. arr.push32(keysym);
  1190. //Util.Debug("<< keyEvent");
  1191. return arr;
  1192. };
  1193. pointerEvent = function(x, y) {
  1194. //Util.Debug(">> pointerEvent, x,y: " + x + "," + y +
  1195. // " , mask: " + mouse_buttonMask);
  1196. var arr;
  1197. arr = [5]; // msg-type
  1198. arr.push8(mouse_buttonMask);
  1199. arr.push16(x);
  1200. arr.push16(y);
  1201. //Util.Debug("<< pointerEvent");
  1202. return arr;
  1203. };
  1204. clientCutText = function(text) {
  1205. //Util.Debug(">> clientCutText");
  1206. var arr, i, n;
  1207. arr = [6]; // msg-type
  1208. arr.push8(0); // padding
  1209. arr.push8(0); // padding
  1210. arr.push8(0); // padding
  1211. arr.push32(text.length);
  1212. n = text.length;
  1213. for (i=0; i < n; i+=1) {
  1214. arr.push(text.charCodeAt(i));
  1215. }
  1216. //Util.Debug("<< clientCutText:" + arr);
  1217. return arr;
  1218. };
  1219. //
  1220. // Public API interface functions
  1221. //
  1222. that.connect = function(host, port, password) {
  1223. //Util.Debug(">> connect");
  1224. rfb_host = host;
  1225. rfb_port = port;
  1226. rfb_password = (password !== undefined) ? password : "";
  1227. if ((!rfb_host) || (!rfb_port)) {
  1228. return fail("Must set host and port");
  1229. }
  1230. updateState('connect');
  1231. //Util.Debug("<< connect");
  1232. };
  1233. that.disconnect = function() {
  1234. //Util.Debug(">> disconnect");
  1235. updateState('disconnect', 'Disconnecting');
  1236. //Util.Debug("<< disconnect");
  1237. };
  1238. that.sendPassword = function(passwd) {
  1239. rfb_password = passwd;
  1240. rfb_state = "Authentication";
  1241. setTimeout(init_msg, 1);
  1242. };
  1243. that.sendCtrlAltDel = function() {
  1244. if (rfb_state !== "normal") { return false; }
  1245. Util.Info("Sending Ctrl-Alt-Del");
  1246. var arr = [];
  1247. arr = arr.concat(keyEvent(0xFFE3, 1)); // Control
  1248. arr = arr.concat(keyEvent(0xFFE9, 1)); // Alt
  1249. arr = arr.concat(keyEvent(0xFFFF, 1)); // Delete
  1250. arr = arr.concat(keyEvent(0xFFFF, 0)); // Delete
  1251. arr = arr.concat(keyEvent(0xFFE9, 0)); // Alt
  1252. arr = arr.concat(keyEvent(0xFFE3, 0)); // Control
  1253. arr = arr.concat(fbUpdateRequest(1));
  1254. ws.send(arr);
  1255. };
  1256. // Send a key press. If 'down' is not specified then send a down key
  1257. // followed by an up key.
  1258. that.sendKey = function(code, down) {
  1259. if (rfb_state !== "normal") { return false; }
  1260. var arr = [];
  1261. if (typeof down !== 'undefined') {
  1262. Util.Info("Sending key code (" + (down ? "down" : "up") + "): " + code);
  1263. arr = arr.concat(keyEvent(code, down ? 1 : 0));
  1264. } else {
  1265. Util.Info("Sending key code (down + up): " + code);
  1266. arr = arr.concat(keyEvent(code, 1));
  1267. arr = arr.concat(keyEvent(code, 0));
  1268. }
  1269. arr = arr.concat(fbUpdateRequest(1));
  1270. ws.send(arr);
  1271. };
  1272. that.clipboardPasteFrom = function(text) {
  1273. if (rfb_state !== "normal") { return; }
  1274. //Util.Debug(">> clipboardPasteFrom: " + text.substr(0,40) + "...");
  1275. ws.send(clientCutText(text));
  1276. //Util.Debug("<< clipboardPasteFrom");
  1277. };
  1278. that.testMode = function(override_send) {
  1279. // Overridable internal functions for testing
  1280. test_mode = true;
  1281. // TODO figure out what to do here
  1282. ws.send = override_send;
  1283. that.recv_message = ws.recv_message; // Expose it
  1284. checkEvents = function () { /* Stub Out */ };
  1285. that.connect = function(host, port, password) {
  1286. rfb_host = host;
  1287. rfb_port = port;
  1288. rfb_password = password;
  1289. updateState('ProtocolVersion', "Starting VNC handshake");
  1290. };
  1291. };
  1292. return constructor(); // Return the public API interface
  1293. } // End of RFB()