rfb.js 49 KB

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