rfb.js 103 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988
  1. /*
  2. * noVNC: HTML5 VNC client
  3. * Copyright (C) 2020 The noVNC Authors
  4. * Licensed under MPL 2.0 (see LICENSE.txt)
  5. *
  6. * See README.md for usage and integration instructions.
  7. *
  8. */
  9. import { toUnsigned32bit, toSigned32bit } from './util/int.js';
  10. import * as Log from './util/logging.js';
  11. import { encodeUTF8, decodeUTF8 } from './util/strings.js';
  12. import { dragThreshold } from './util/browser.js';
  13. import { clientToElement } from './util/element.js';
  14. import { setCapture } from './util/events.js';
  15. import EventTargetMixin from './util/eventtarget.js';
  16. import Display from "./display.js";
  17. import Inflator from "./inflator.js";
  18. import Deflator from "./deflator.js";
  19. import Keyboard from "./input/keyboard.js";
  20. import GestureHandler from "./input/gesturehandler.js";
  21. import Cursor from "./util/cursor.js";
  22. import Websock from "./websock.js";
  23. import DES from "./des.js";
  24. import KeyTable from "./input/keysym.js";
  25. import XtScancode from "./input/xtscancodes.js";
  26. import { encodings } from "./encodings.js";
  27. import RawDecoder from "./decoders/raw.js";
  28. import CopyRectDecoder from "./decoders/copyrect.js";
  29. import RREDecoder from "./decoders/rre.js";
  30. import HextileDecoder from "./decoders/hextile.js";
  31. import TightDecoder from "./decoders/tight.js";
  32. import TightPNGDecoder from "./decoders/tightpng.js";
  33. // How many seconds to wait for a disconnect to finish
  34. const DISCONNECT_TIMEOUT = 3;
  35. const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)';
  36. // Minimum wait (ms) between two mouse moves
  37. const MOUSE_MOVE_DELAY = 17;
  38. // Wheel thresholds
  39. const WHEEL_STEP = 50; // Pixels needed for one step
  40. const WHEEL_LINE_HEIGHT = 19; // Assumed pixels for one line step
  41. // Gesture thresholds
  42. const GESTURE_ZOOMSENS = 75;
  43. const GESTURE_SCRLSENS = 50;
  44. const DOUBLE_TAP_TIMEOUT = 1000;
  45. const DOUBLE_TAP_THRESHOLD = 50;
  46. // Extended clipboard pseudo-encoding formats
  47. const extendedClipboardFormatText = 1;
  48. /*eslint-disable no-unused-vars */
  49. const extendedClipboardFormatRtf = 1 << 1;
  50. const extendedClipboardFormatHtml = 1 << 2;
  51. const extendedClipboardFormatDib = 1 << 3;
  52. const extendedClipboardFormatFiles = 1 << 4;
  53. /*eslint-enable */
  54. // Extended clipboard pseudo-encoding actions
  55. const extendedClipboardActionCaps = 1 << 24;
  56. const extendedClipboardActionRequest = 1 << 25;
  57. const extendedClipboardActionPeek = 1 << 26;
  58. const extendedClipboardActionNotify = 1 << 27;
  59. const extendedClipboardActionProvide = 1 << 28;
  60. export default class RFB extends EventTargetMixin {
  61. constructor(target, urlOrChannel, options) {
  62. if (!target) {
  63. throw new Error("Must specify target");
  64. }
  65. if (!urlOrChannel) {
  66. throw new Error("Must specify URL, WebSocket or RTCDataChannel");
  67. }
  68. super();
  69. this._target = target;
  70. if (typeof urlOrChannel === "string") {
  71. this._url = urlOrChannel;
  72. } else {
  73. this._url = null;
  74. this._rawChannel = urlOrChannel;
  75. }
  76. // Connection details
  77. options = options || {};
  78. this._rfbCredentials = options.credentials || {};
  79. this._shared = 'shared' in options ? !!options.shared : true;
  80. this._repeaterID = options.repeaterID || '';
  81. this._wsProtocols = options.wsProtocols || [];
  82. // Internal state
  83. this._rfbConnectionState = '';
  84. this._rfbInitState = '';
  85. this._rfbAuthScheme = -1;
  86. this._rfbCleanDisconnect = true;
  87. // Server capabilities
  88. this._rfbVersion = 0;
  89. this._rfbMaxVersion = 3.8;
  90. this._rfbTightVNC = false;
  91. this._rfbVeNCryptState = 0;
  92. this._rfbXvpVer = 0;
  93. this._fbWidth = 0;
  94. this._fbHeight = 0;
  95. this._fbName = "";
  96. this._capabilities = { power: false };
  97. this._supportsFence = false;
  98. this._supportsContinuousUpdates = false;
  99. this._enabledContinuousUpdates = false;
  100. this._supportsSetDesktopSize = false;
  101. this._screenID = 0;
  102. this._screenFlags = 0;
  103. this._qemuExtKeyEventSupported = false;
  104. this._clipboardText = null;
  105. this._clipboardServerCapabilitiesActions = {};
  106. this._clipboardServerCapabilitiesFormats = {};
  107. // Internal objects
  108. this._sock = null; // Websock object
  109. this._display = null; // Display object
  110. this._flushing = false; // Display flushing state
  111. this._keyboard = null; // Keyboard input handler object
  112. this._gestures = null; // Gesture input handler object
  113. this._resizeObserver = null; // Resize observer object
  114. // Timers
  115. this._disconnTimer = null; // disconnection timer
  116. this._resizeTimeout = null; // resize rate limiting
  117. this._mouseMoveTimer = null;
  118. // Decoder states
  119. this._decoders = {};
  120. this._FBU = {
  121. rects: 0,
  122. x: 0,
  123. y: 0,
  124. width: 0,
  125. height: 0,
  126. encoding: null,
  127. };
  128. // Mouse state
  129. this._mousePos = {};
  130. this._mouseButtonMask = 0;
  131. this._mouseLastMoveTime = 0;
  132. this._viewportDragging = false;
  133. this._viewportDragPos = {};
  134. this._viewportHasMoved = false;
  135. this._accumulatedWheelDeltaX = 0;
  136. this._accumulatedWheelDeltaY = 0;
  137. // Gesture state
  138. this._gestureLastTapTime = null;
  139. this._gestureFirstDoubleTapEv = null;
  140. this._gestureLastMagnitudeX = 0;
  141. this._gestureLastMagnitudeY = 0;
  142. // Bound event handlers
  143. this._eventHandlers = {
  144. focusCanvas: this._focusCanvas.bind(this),
  145. handleResize: this._handleResize.bind(this),
  146. handleMouse: this._handleMouse.bind(this),
  147. handleWheel: this._handleWheel.bind(this),
  148. handleGesture: this._handleGesture.bind(this),
  149. };
  150. // main setup
  151. Log.Debug(">> RFB.constructor");
  152. // Create DOM elements
  153. this._screen = document.createElement('div');
  154. this._screen.style.display = 'flex';
  155. this._screen.style.width = '100%';
  156. this._screen.style.height = '100%';
  157. this._screen.style.overflow = 'auto';
  158. this._screen.style.background = DEFAULT_BACKGROUND;
  159. this._canvas = document.createElement('canvas');
  160. this._canvas.style.margin = 'auto';
  161. // Some browsers add an outline on focus
  162. this._canvas.style.outline = 'none';
  163. this._canvas.width = 0;
  164. this._canvas.height = 0;
  165. this._canvas.tabIndex = -1;
  166. this._screen.appendChild(this._canvas);
  167. // Cursor
  168. this._cursor = new Cursor();
  169. // XXX: TightVNC 2.8.11 sends no cursor at all until Windows changes
  170. // it. Result: no cursor at all until a window border or an edit field
  171. // is hit blindly. But there are also VNC servers that draw the cursor
  172. // in the framebuffer and don't send the empty local cursor. There is
  173. // no way to satisfy both sides.
  174. //
  175. // The spec is unclear on this "initial cursor" issue. Many other
  176. // viewers (TigerVNC, RealVNC, Remmina) display an arrow as the
  177. // initial cursor instead.
  178. this._cursorImage = RFB.cursors.none;
  179. // populate decoder array with objects
  180. this._decoders[encodings.encodingRaw] = new RawDecoder();
  181. this._decoders[encodings.encodingCopyRect] = new CopyRectDecoder();
  182. this._decoders[encodings.encodingRRE] = new RREDecoder();
  183. this._decoders[encodings.encodingHextile] = new HextileDecoder();
  184. this._decoders[encodings.encodingTight] = new TightDecoder();
  185. this._decoders[encodings.encodingTightPNG] = new TightPNGDecoder();
  186. // NB: nothing that needs explicit teardown should be done
  187. // before this point, since this can throw an exception
  188. try {
  189. this._display = new Display(this._canvas);
  190. } catch (exc) {
  191. Log.Error("Display exception: " + exc);
  192. throw exc;
  193. }
  194. this._display.onflush = this._onFlush.bind(this);
  195. this._keyboard = new Keyboard(this._canvas);
  196. this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
  197. this._gestures = new GestureHandler();
  198. this._sock = new Websock();
  199. this._sock.on('open', this._socketOpen.bind(this));
  200. this._sock.on('close', this._socketClose.bind(this));
  201. this._sock.on('message', this._handleMessage.bind(this));
  202. this._sock.on('error', this._socketError.bind(this));
  203. this._resizeObserver = new ResizeObserver(this._eventHandlers.handleResize);
  204. // All prepared, kick off the connection
  205. this._updateConnectionState('connecting');
  206. Log.Debug("<< RFB.constructor");
  207. // ===== PROPERTIES =====
  208. this.dragViewport = false;
  209. this.focusOnClick = true;
  210. this._viewOnly = false;
  211. this._clipViewport = false;
  212. this._scaleViewport = false;
  213. this._resizeSession = false;
  214. this._showDotCursor = false;
  215. if (options.showDotCursor !== undefined) {
  216. Log.Warn("Specifying showDotCursor as a RFB constructor argument is deprecated");
  217. this._showDotCursor = options.showDotCursor;
  218. }
  219. this._qualityLevel = 6;
  220. this._compressionLevel = 2;
  221. }
  222. // ===== PROPERTIES =====
  223. get viewOnly() { return this._viewOnly; }
  224. set viewOnly(viewOnly) {
  225. this._viewOnly = viewOnly;
  226. if (this._rfbConnectionState === "connecting" ||
  227. this._rfbConnectionState === "connected") {
  228. if (viewOnly) {
  229. this._keyboard.ungrab();
  230. } else {
  231. this._keyboard.grab();
  232. }
  233. }
  234. }
  235. get capabilities() { return this._capabilities; }
  236. get touchButton() { return 0; }
  237. set touchButton(button) { Log.Warn("Using old API!"); }
  238. get clipViewport() { return this._clipViewport; }
  239. set clipViewport(viewport) {
  240. this._clipViewport = viewport;
  241. this._updateClip();
  242. }
  243. get scaleViewport() { return this._scaleViewport; }
  244. set scaleViewport(scale) {
  245. this._scaleViewport = scale;
  246. // Scaling trumps clipping, so we may need to adjust
  247. // clipping when enabling or disabling scaling
  248. if (scale && this._clipViewport) {
  249. this._updateClip();
  250. }
  251. this._updateScale();
  252. if (!scale && this._clipViewport) {
  253. this._updateClip();
  254. }
  255. }
  256. get resizeSession() { return this._resizeSession; }
  257. set resizeSession(resize) {
  258. this._resizeSession = resize;
  259. if (resize) {
  260. this._requestRemoteResize();
  261. }
  262. }
  263. get showDotCursor() { return this._showDotCursor; }
  264. set showDotCursor(show) {
  265. this._showDotCursor = show;
  266. this._refreshCursor();
  267. }
  268. get background() { return this._screen.style.background; }
  269. set background(cssValue) { this._screen.style.background = cssValue; }
  270. get qualityLevel() {
  271. return this._qualityLevel;
  272. }
  273. set qualityLevel(qualityLevel) {
  274. if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) {
  275. Log.Error("qualityLevel must be an integer between 0 and 9");
  276. return;
  277. }
  278. if (this._qualityLevel === qualityLevel) {
  279. return;
  280. }
  281. this._qualityLevel = qualityLevel;
  282. if (this._rfbConnectionState === 'connected') {
  283. this._sendEncodings();
  284. }
  285. }
  286. get compressionLevel() {
  287. return this._compressionLevel;
  288. }
  289. set compressionLevel(compressionLevel) {
  290. if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
  291. Log.Error("compressionLevel must be an integer between 0 and 9");
  292. return;
  293. }
  294. if (this._compressionLevel === compressionLevel) {
  295. return;
  296. }
  297. this._compressionLevel = compressionLevel;
  298. if (this._rfbConnectionState === 'connected') {
  299. this._sendEncodings();
  300. }
  301. }
  302. // ===== PUBLIC METHODS =====
  303. disconnect() {
  304. this._updateConnectionState('disconnecting');
  305. this._sock.off('error');
  306. this._sock.off('message');
  307. this._sock.off('open');
  308. }
  309. sendCredentials(creds) {
  310. this._rfbCredentials = creds;
  311. setTimeout(this._initMsg.bind(this), 0);
  312. }
  313. sendCtrlAltDel() {
  314. if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
  315. Log.Info("Sending Ctrl-Alt-Del");
  316. this.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
  317. this.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
  318. this.sendKey(KeyTable.XK_Delete, "Delete", true);
  319. this.sendKey(KeyTable.XK_Delete, "Delete", false);
  320. this.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
  321. this.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
  322. }
  323. machineShutdown() {
  324. this._xvpOp(1, 2);
  325. }
  326. machineReboot() {
  327. this._xvpOp(1, 3);
  328. }
  329. machineReset() {
  330. this._xvpOp(1, 4);
  331. }
  332. // Send a key press. If 'down' is not specified then send a down key
  333. // followed by an up key.
  334. sendKey(keysym, code, down) {
  335. if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
  336. if (down === undefined) {
  337. this.sendKey(keysym, code, true);
  338. this.sendKey(keysym, code, false);
  339. return;
  340. }
  341. const scancode = XtScancode[code];
  342. if (this._qemuExtKeyEventSupported && scancode) {
  343. // 0 is NoSymbol
  344. keysym = keysym || 0;
  345. Log.Info("Sending key (" + (down ? "down" : "up") + "): keysym " + keysym + ", scancode " + scancode);
  346. RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
  347. } else {
  348. if (!keysym) {
  349. return;
  350. }
  351. Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
  352. RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
  353. }
  354. }
  355. focus() {
  356. this._canvas.focus();
  357. }
  358. blur() {
  359. this._canvas.blur();
  360. }
  361. clipboardPasteFrom(text) {
  362. if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
  363. if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&
  364. this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
  365. this._clipboardText = text;
  366. RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
  367. } else {
  368. let data = new Uint8Array(text.length);
  369. for (let i = 0; i < text.length; i++) {
  370. // FIXME: text can have values outside of Latin1/Uint8
  371. data[i] = text.charCodeAt(i);
  372. }
  373. RFB.messages.clientCutText(this._sock, data);
  374. }
  375. }
  376. // ===== PRIVATE METHODS =====
  377. _connect() {
  378. Log.Debug(">> RFB.connect");
  379. if (this._url) {
  380. Log.Info(`connecting to ${this._url}`);
  381. this._sock.open(this._url, this._wsProtocols);
  382. } else {
  383. Log.Info(`attaching ${this._rawChannel} to Websock`);
  384. this._sock.attach(this._rawChannel);
  385. if (this._sock.readyState === 'closed') {
  386. throw Error("Cannot use already closed WebSocket/RTCDataChannel");
  387. }
  388. if (this._sock.readyState === 'open') {
  389. // FIXME: _socketOpen() can in theory call _fail(), which
  390. // isn't allowed this early, but I'm not sure that can
  391. // happen without a bug messing up our state variables
  392. this._socketOpen();
  393. }
  394. }
  395. // Make our elements part of the page
  396. this._target.appendChild(this._screen);
  397. this._gestures.attach(this._canvas);
  398. this._cursor.attach(this._canvas);
  399. this._refreshCursor();
  400. // Monitor size changes of the screen element
  401. this._resizeObserver.observe(this._screen);
  402. // Always grab focus on some kind of click event
  403. this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
  404. this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
  405. // Mouse events
  406. this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);
  407. this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);
  408. this._canvas.addEventListener('mousemove', this._eventHandlers.handleMouse);
  409. // Prevent middle-click pasting (see handler for why we bind to document)
  410. this._canvas.addEventListener('click', this._eventHandlers.handleMouse);
  411. // preventDefault() on mousedown doesn't stop this event for some
  412. // reason so we have to explicitly block it
  413. this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);
  414. // Wheel events
  415. this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel);
  416. // Gesture events
  417. this._canvas.addEventListener("gesturestart", this._eventHandlers.handleGesture);
  418. this._canvas.addEventListener("gesturemove", this._eventHandlers.handleGesture);
  419. this._canvas.addEventListener("gestureend", this._eventHandlers.handleGesture);
  420. Log.Debug("<< RFB.connect");
  421. }
  422. _disconnect() {
  423. Log.Debug(">> RFB.disconnect");
  424. this._cursor.detach();
  425. this._canvas.removeEventListener("gesturestart", this._eventHandlers.handleGesture);
  426. this._canvas.removeEventListener("gesturemove", this._eventHandlers.handleGesture);
  427. this._canvas.removeEventListener("gestureend", this._eventHandlers.handleGesture);
  428. this._canvas.removeEventListener("wheel", this._eventHandlers.handleWheel);
  429. this._canvas.removeEventListener('mousedown', this._eventHandlers.handleMouse);
  430. this._canvas.removeEventListener('mouseup', this._eventHandlers.handleMouse);
  431. this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse);
  432. this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);
  433. this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);
  434. this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
  435. this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
  436. this._resizeObserver.disconnect();
  437. this._keyboard.ungrab();
  438. this._gestures.detach();
  439. this._sock.close();
  440. try {
  441. this._target.removeChild(this._screen);
  442. } catch (e) {
  443. if (e.name === 'NotFoundError') {
  444. // Some cases where the initial connection fails
  445. // can disconnect before the _screen is created
  446. } else {
  447. throw e;
  448. }
  449. }
  450. clearTimeout(this._resizeTimeout);
  451. clearTimeout(this._mouseMoveTimer);
  452. Log.Debug("<< RFB.disconnect");
  453. }
  454. _socketOpen() {
  455. if ((this._rfbConnectionState === 'connecting') &&
  456. (this._rfbInitState === '')) {
  457. this._rfbInitState = 'ProtocolVersion';
  458. Log.Debug("Starting VNC handshake");
  459. } else {
  460. this._fail("Unexpected server connection while " +
  461. this._rfbConnectionState);
  462. }
  463. }
  464. _socketClose(e) {
  465. Log.Debug("WebSocket on-close event");
  466. let msg = "";
  467. if (e.code) {
  468. msg = "(code: " + e.code;
  469. if (e.reason) {
  470. msg += ", reason: " + e.reason;
  471. }
  472. msg += ")";
  473. }
  474. switch (this._rfbConnectionState) {
  475. case 'connecting':
  476. this._fail("Connection closed " + msg);
  477. break;
  478. case 'connected':
  479. // Handle disconnects that were initiated server-side
  480. this._updateConnectionState('disconnecting');
  481. this._updateConnectionState('disconnected');
  482. break;
  483. case 'disconnecting':
  484. // Normal disconnection path
  485. this._updateConnectionState('disconnected');
  486. break;
  487. case 'disconnected':
  488. this._fail("Unexpected server disconnect " +
  489. "when already disconnected " + msg);
  490. break;
  491. default:
  492. this._fail("Unexpected server disconnect before connecting " +
  493. msg);
  494. break;
  495. }
  496. this._sock.off('close');
  497. // Delete reference to raw channel to allow cleanup.
  498. this._rawChannel = null;
  499. }
  500. _socketError(e) {
  501. Log.Warn("WebSocket on-error event");
  502. }
  503. _focusCanvas(event) {
  504. if (!this.focusOnClick) {
  505. return;
  506. }
  507. this.focus();
  508. }
  509. _setDesktopName(name) {
  510. this._fbName = name;
  511. this.dispatchEvent(new CustomEvent(
  512. "desktopname",
  513. { detail: { name: this._fbName } }));
  514. }
  515. _handleResize() {
  516. // If the window resized then our screen element might have
  517. // as well. Update the viewport dimensions.
  518. window.requestAnimationFrame(() => {
  519. this._updateClip();
  520. this._updateScale();
  521. });
  522. if (this._resizeSession) {
  523. // Request changing the resolution of the remote display to
  524. // the size of the local browser viewport.
  525. // In order to not send multiple requests before the browser-resize
  526. // is finished we wait 0.5 seconds before sending the request.
  527. clearTimeout(this._resizeTimeout);
  528. this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
  529. }
  530. }
  531. // Update state of clipping in Display object, and make sure the
  532. // configured viewport matches the current screen size
  533. _updateClip() {
  534. const curClip = this._display.clipViewport;
  535. let newClip = this._clipViewport;
  536. if (this._scaleViewport) {
  537. // Disable viewport clipping if we are scaling
  538. newClip = false;
  539. }
  540. if (curClip !== newClip) {
  541. this._display.clipViewport = newClip;
  542. }
  543. if (newClip) {
  544. // When clipping is enabled, the screen is limited to
  545. // the size of the container.
  546. const size = this._screenSize();
  547. this._display.viewportChangeSize(size.w, size.h);
  548. this._fixScrollbars();
  549. }
  550. }
  551. _updateScale() {
  552. if (!this._scaleViewport) {
  553. this._display.scale = 1.0;
  554. } else {
  555. const size = this._screenSize();
  556. this._display.autoscale(size.w, size.h);
  557. }
  558. this._fixScrollbars();
  559. }
  560. // Requests a change of remote desktop size. This message is an extension
  561. // and may only be sent if we have received an ExtendedDesktopSize message
  562. _requestRemoteResize() {
  563. clearTimeout(this._resizeTimeout);
  564. this._resizeTimeout = null;
  565. if (!this._resizeSession || this._viewOnly ||
  566. !this._supportsSetDesktopSize) {
  567. return;
  568. }
  569. const size = this._screenSize();
  570. RFB.messages.setDesktopSize(this._sock,
  571. Math.floor(size.w), Math.floor(size.h),
  572. this._screenID, this._screenFlags);
  573. Log.Debug('Requested new desktop size: ' +
  574. size.w + 'x' + size.h);
  575. }
  576. // Gets the the size of the available screen
  577. _screenSize() {
  578. let r = this._screen.getBoundingClientRect();
  579. return { w: r.width, h: r.height };
  580. }
  581. _fixScrollbars() {
  582. // This is a hack because Chrome screws up the calculation
  583. // for when scrollbars are needed. So to fix it we temporarily
  584. // toggle them off and on.
  585. const orig = this._screen.style.overflow;
  586. this._screen.style.overflow = 'hidden';
  587. // Force Chrome to recalculate the layout by asking for
  588. // an element's dimensions
  589. this._screen.getBoundingClientRect();
  590. this._screen.style.overflow = orig;
  591. }
  592. /*
  593. * Connection states:
  594. * connecting
  595. * connected
  596. * disconnecting
  597. * disconnected - permanent state
  598. */
  599. _updateConnectionState(state) {
  600. const oldstate = this._rfbConnectionState;
  601. if (state === oldstate) {
  602. Log.Debug("Already in state '" + state + "', ignoring");
  603. return;
  604. }
  605. // The 'disconnected' state is permanent for each RFB object
  606. if (oldstate === 'disconnected') {
  607. Log.Error("Tried changing state of a disconnected RFB object");
  608. return;
  609. }
  610. // Ensure proper transitions before doing anything
  611. switch (state) {
  612. case 'connected':
  613. if (oldstate !== 'connecting') {
  614. Log.Error("Bad transition to connected state, " +
  615. "previous connection state: " + oldstate);
  616. return;
  617. }
  618. break;
  619. case 'disconnected':
  620. if (oldstate !== 'disconnecting') {
  621. Log.Error("Bad transition to disconnected state, " +
  622. "previous connection state: " + oldstate);
  623. return;
  624. }
  625. break;
  626. case 'connecting':
  627. if (oldstate !== '') {
  628. Log.Error("Bad transition to connecting state, " +
  629. "previous connection state: " + oldstate);
  630. return;
  631. }
  632. break;
  633. case 'disconnecting':
  634. if (oldstate !== 'connected' && oldstate !== 'connecting') {
  635. Log.Error("Bad transition to disconnecting state, " +
  636. "previous connection state: " + oldstate);
  637. return;
  638. }
  639. break;
  640. default:
  641. Log.Error("Unknown connection state: " + state);
  642. return;
  643. }
  644. // State change actions
  645. this._rfbConnectionState = state;
  646. Log.Debug("New state '" + state + "', was '" + oldstate + "'.");
  647. if (this._disconnTimer && state !== 'disconnecting') {
  648. Log.Debug("Clearing disconnect timer");
  649. clearTimeout(this._disconnTimer);
  650. this._disconnTimer = null;
  651. // make sure we don't get a double event
  652. this._sock.off('close');
  653. }
  654. switch (state) {
  655. case 'connecting':
  656. this._connect();
  657. break;
  658. case 'connected':
  659. this.dispatchEvent(new CustomEvent("connect", { detail: {} }));
  660. break;
  661. case 'disconnecting':
  662. this._disconnect();
  663. this._disconnTimer = setTimeout(() => {
  664. Log.Error("Disconnection timed out.");
  665. this._updateConnectionState('disconnected');
  666. }, DISCONNECT_TIMEOUT * 1000);
  667. break;
  668. case 'disconnected':
  669. this.dispatchEvent(new CustomEvent(
  670. "disconnect", { detail:
  671. { clean: this._rfbCleanDisconnect } }));
  672. break;
  673. }
  674. }
  675. /* Print errors and disconnect
  676. *
  677. * The parameter 'details' is used for information that
  678. * should be logged but not sent to the user interface.
  679. */
  680. _fail(details) {
  681. switch (this._rfbConnectionState) {
  682. case 'disconnecting':
  683. Log.Error("Failed when disconnecting: " + details);
  684. break;
  685. case 'connected':
  686. Log.Error("Failed while connected: " + details);
  687. break;
  688. case 'connecting':
  689. Log.Error("Failed when connecting: " + details);
  690. break;
  691. default:
  692. Log.Error("RFB failure: " + details);
  693. break;
  694. }
  695. this._rfbCleanDisconnect = false; //This is sent to the UI
  696. // Transition to disconnected without waiting for socket to close
  697. this._updateConnectionState('disconnecting');
  698. this._updateConnectionState('disconnected');
  699. return false;
  700. }
  701. _setCapability(cap, val) {
  702. this._capabilities[cap] = val;
  703. this.dispatchEvent(new CustomEvent("capabilities",
  704. { detail: { capabilities: this._capabilities } }));
  705. }
  706. _handleMessage() {
  707. if (this._sock.rQlen === 0) {
  708. Log.Warn("handleMessage called on an empty receive queue");
  709. return;
  710. }
  711. switch (this._rfbConnectionState) {
  712. case 'disconnected':
  713. Log.Error("Got data while disconnected");
  714. break;
  715. case 'connected':
  716. while (true) {
  717. if (this._flushing) {
  718. break;
  719. }
  720. if (!this._normalMsg()) {
  721. break;
  722. }
  723. if (this._sock.rQlen === 0) {
  724. break;
  725. }
  726. }
  727. break;
  728. default:
  729. this._initMsg();
  730. break;
  731. }
  732. }
  733. _handleKeyEvent(keysym, code, down) {
  734. this.sendKey(keysym, code, down);
  735. }
  736. _handleMouse(ev) {
  737. /*
  738. * We don't check connection status or viewOnly here as the
  739. * mouse events might be used to control the viewport
  740. */
  741. if (ev.type === 'click') {
  742. /*
  743. * Note: This is only needed for the 'click' event as it fails
  744. * to fire properly for the target element so we have
  745. * to listen on the document element instead.
  746. */
  747. if (ev.target !== this._canvas) {
  748. return;
  749. }
  750. }
  751. // FIXME: if we're in view-only and not dragging,
  752. // should we stop events?
  753. ev.stopPropagation();
  754. ev.preventDefault();
  755. if ((ev.type === 'click') || (ev.type === 'contextmenu')) {
  756. return;
  757. }
  758. let pos = clientToElement(ev.clientX, ev.clientY,
  759. this._canvas);
  760. switch (ev.type) {
  761. case 'mousedown':
  762. setCapture(this._canvas);
  763. this._handleMouseButton(pos.x, pos.y,
  764. true, 1 << ev.button);
  765. break;
  766. case 'mouseup':
  767. this._handleMouseButton(pos.x, pos.y,
  768. false, 1 << ev.button);
  769. break;
  770. case 'mousemove':
  771. this._handleMouseMove(pos.x, pos.y);
  772. break;
  773. }
  774. }
  775. _handleMouseButton(x, y, down, bmask) {
  776. if (this.dragViewport) {
  777. if (down && !this._viewportDragging) {
  778. this._viewportDragging = true;
  779. this._viewportDragPos = {'x': x, 'y': y};
  780. this._viewportHasMoved = false;
  781. // Skip sending mouse events
  782. return;
  783. } else {
  784. this._viewportDragging = false;
  785. // If we actually performed a drag then we are done
  786. // here and should not send any mouse events
  787. if (this._viewportHasMoved) {
  788. return;
  789. }
  790. // Otherwise we treat this as a mouse click event.
  791. // Send the button down event here, as the button up
  792. // event is sent at the end of this function.
  793. this._sendMouse(x, y, bmask);
  794. }
  795. }
  796. // Flush waiting move event first
  797. if (this._mouseMoveTimer !== null) {
  798. clearTimeout(this._mouseMoveTimer);
  799. this._mouseMoveTimer = null;
  800. this._sendMouse(x, y, this._mouseButtonMask);
  801. }
  802. if (down) {
  803. this._mouseButtonMask |= bmask;
  804. } else {
  805. this._mouseButtonMask &= ~bmask;
  806. }
  807. this._sendMouse(x, y, this._mouseButtonMask);
  808. }
  809. _handleMouseMove(x, y) {
  810. if (this._viewportDragging) {
  811. const deltaX = this._viewportDragPos.x - x;
  812. const deltaY = this._viewportDragPos.y - y;
  813. if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
  814. Math.abs(deltaY) > dragThreshold)) {
  815. this._viewportHasMoved = true;
  816. this._viewportDragPos = {'x': x, 'y': y};
  817. this._display.viewportChangePos(deltaX, deltaY);
  818. }
  819. // Skip sending mouse events
  820. return;
  821. }
  822. this._mousePos = { 'x': x, 'y': y };
  823. // Limit many mouse move events to one every MOUSE_MOVE_DELAY ms
  824. if (this._mouseMoveTimer == null) {
  825. const timeSinceLastMove = Date.now() - this._mouseLastMoveTime;
  826. if (timeSinceLastMove > MOUSE_MOVE_DELAY) {
  827. this._sendMouse(x, y, this._mouseButtonMask);
  828. this._mouseLastMoveTime = Date.now();
  829. } else {
  830. // Too soon since the latest move, wait the remaining time
  831. this._mouseMoveTimer = setTimeout(() => {
  832. this._handleDelayedMouseMove();
  833. }, MOUSE_MOVE_DELAY - timeSinceLastMove);
  834. }
  835. }
  836. }
  837. _handleDelayedMouseMove() {
  838. this._mouseMoveTimer = null;
  839. this._sendMouse(this._mousePos.x, this._mousePos.y,
  840. this._mouseButtonMask);
  841. this._mouseLastMoveTime = Date.now();
  842. }
  843. _sendMouse(x, y, mask) {
  844. if (this._rfbConnectionState !== 'connected') { return; }
  845. if (this._viewOnly) { return; } // View only, skip mouse events
  846. RFB.messages.pointerEvent(this._sock, this._display.absX(x),
  847. this._display.absY(y), mask);
  848. }
  849. _handleWheel(ev) {
  850. if (this._rfbConnectionState !== 'connected') { return; }
  851. if (this._viewOnly) { return; } // View only, skip mouse events
  852. ev.stopPropagation();
  853. ev.preventDefault();
  854. let pos = clientToElement(ev.clientX, ev.clientY,
  855. this._canvas);
  856. let dX = ev.deltaX;
  857. let dY = ev.deltaY;
  858. // Pixel units unless it's non-zero.
  859. // Note that if deltamode is line or page won't matter since we aren't
  860. // sending the mouse wheel delta to the server anyway.
  861. // The difference between pixel and line can be important however since
  862. // we have a threshold that can be smaller than the line height.
  863. if (ev.deltaMode !== 0) {
  864. dX *= WHEEL_LINE_HEIGHT;
  865. dY *= WHEEL_LINE_HEIGHT;
  866. }
  867. // Mouse wheel events are sent in steps over VNC. This means that the VNC
  868. // protocol can't handle a wheel event with specific distance or speed.
  869. // Therefor, if we get a lot of small mouse wheel events we combine them.
  870. this._accumulatedWheelDeltaX += dX;
  871. this._accumulatedWheelDeltaY += dY;
  872. // Generate a mouse wheel step event when the accumulated delta
  873. // for one of the axes is large enough.
  874. if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) {
  875. if (this._accumulatedWheelDeltaX < 0) {
  876. this._handleMouseButton(pos.x, pos.y, true, 1 << 5);
  877. this._handleMouseButton(pos.x, pos.y, false, 1 << 5);
  878. } else if (this._accumulatedWheelDeltaX > 0) {
  879. this._handleMouseButton(pos.x, pos.y, true, 1 << 6);
  880. this._handleMouseButton(pos.x, pos.y, false, 1 << 6);
  881. }
  882. this._accumulatedWheelDeltaX = 0;
  883. }
  884. if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) {
  885. if (this._accumulatedWheelDeltaY < 0) {
  886. this._handleMouseButton(pos.x, pos.y, true, 1 << 3);
  887. this._handleMouseButton(pos.x, pos.y, false, 1 << 3);
  888. } else if (this._accumulatedWheelDeltaY > 0) {
  889. this._handleMouseButton(pos.x, pos.y, true, 1 << 4);
  890. this._handleMouseButton(pos.x, pos.y, false, 1 << 4);
  891. }
  892. this._accumulatedWheelDeltaY = 0;
  893. }
  894. }
  895. _fakeMouseMove(ev, elementX, elementY) {
  896. this._handleMouseMove(elementX, elementY);
  897. this._cursor.move(ev.detail.clientX, ev.detail.clientY);
  898. }
  899. _handleTapEvent(ev, bmask) {
  900. let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
  901. this._canvas);
  902. // If the user quickly taps multiple times we assume they meant to
  903. // hit the same spot, so slightly adjust coordinates
  904. if ((this._gestureLastTapTime !== null) &&
  905. ((Date.now() - this._gestureLastTapTime) < DOUBLE_TAP_TIMEOUT) &&
  906. (this._gestureFirstDoubleTapEv.detail.type === ev.detail.type)) {
  907. let dx = this._gestureFirstDoubleTapEv.detail.clientX - ev.detail.clientX;
  908. let dy = this._gestureFirstDoubleTapEv.detail.clientY - ev.detail.clientY;
  909. let distance = Math.hypot(dx, dy);
  910. if (distance < DOUBLE_TAP_THRESHOLD) {
  911. pos = clientToElement(this._gestureFirstDoubleTapEv.detail.clientX,
  912. this._gestureFirstDoubleTapEv.detail.clientY,
  913. this._canvas);
  914. } else {
  915. this._gestureFirstDoubleTapEv = ev;
  916. }
  917. } else {
  918. this._gestureFirstDoubleTapEv = ev;
  919. }
  920. this._gestureLastTapTime = Date.now();
  921. this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
  922. this._handleMouseButton(pos.x, pos.y, true, bmask);
  923. this._handleMouseButton(pos.x, pos.y, false, bmask);
  924. }
  925. _handleGesture(ev) {
  926. let magnitude;
  927. let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
  928. this._canvas);
  929. switch (ev.type) {
  930. case 'gesturestart':
  931. switch (ev.detail.type) {
  932. case 'onetap':
  933. this._handleTapEvent(ev, 0x1);
  934. break;
  935. case 'twotap':
  936. this._handleTapEvent(ev, 0x4);
  937. break;
  938. case 'threetap':
  939. this._handleTapEvent(ev, 0x2);
  940. break;
  941. case 'drag':
  942. this._fakeMouseMove(ev, pos.x, pos.y);
  943. this._handleMouseButton(pos.x, pos.y, true, 0x1);
  944. break;
  945. case 'longpress':
  946. this._fakeMouseMove(ev, pos.x, pos.y);
  947. this._handleMouseButton(pos.x, pos.y, true, 0x4);
  948. break;
  949. case 'twodrag':
  950. this._gestureLastMagnitudeX = ev.detail.magnitudeX;
  951. this._gestureLastMagnitudeY = ev.detail.magnitudeY;
  952. this._fakeMouseMove(ev, pos.x, pos.y);
  953. break;
  954. case 'pinch':
  955. this._gestureLastMagnitudeX = Math.hypot(ev.detail.magnitudeX,
  956. ev.detail.magnitudeY);
  957. this._fakeMouseMove(ev, pos.x, pos.y);
  958. break;
  959. }
  960. break;
  961. case 'gesturemove':
  962. switch (ev.detail.type) {
  963. case 'onetap':
  964. case 'twotap':
  965. case 'threetap':
  966. break;
  967. case 'drag':
  968. case 'longpress':
  969. this._fakeMouseMove(ev, pos.x, pos.y);
  970. break;
  971. case 'twodrag':
  972. // Always scroll in the same position.
  973. // We don't know if the mouse was moved so we need to move it
  974. // every update.
  975. this._fakeMouseMove(ev, pos.x, pos.y);
  976. while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
  977. this._handleMouseButton(pos.x, pos.y, true, 0x8);
  978. this._handleMouseButton(pos.x, pos.y, false, 0x8);
  979. this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
  980. }
  981. while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
  982. this._handleMouseButton(pos.x, pos.y, true, 0x10);
  983. this._handleMouseButton(pos.x, pos.y, false, 0x10);
  984. this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
  985. }
  986. while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
  987. this._handleMouseButton(pos.x, pos.y, true, 0x20);
  988. this._handleMouseButton(pos.x, pos.y, false, 0x20);
  989. this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
  990. }
  991. while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
  992. this._handleMouseButton(pos.x, pos.y, true, 0x40);
  993. this._handleMouseButton(pos.x, pos.y, false, 0x40);
  994. this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
  995. }
  996. break;
  997. case 'pinch':
  998. // Always scroll in the same position.
  999. // We don't know if the mouse was moved so we need to move it
  1000. // every update.
  1001. this._fakeMouseMove(ev, pos.x, pos.y);
  1002. magnitude = Math.hypot(ev.detail.magnitudeX, ev.detail.magnitudeY);
  1003. if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
  1004. this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
  1005. while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
  1006. this._handleMouseButton(pos.x, pos.y, true, 0x8);
  1007. this._handleMouseButton(pos.x, pos.y, false, 0x8);
  1008. this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
  1009. }
  1010. while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
  1011. this._handleMouseButton(pos.x, pos.y, true, 0x10);
  1012. this._handleMouseButton(pos.x, pos.y, false, 0x10);
  1013. this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
  1014. }
  1015. }
  1016. this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", false);
  1017. break;
  1018. }
  1019. break;
  1020. case 'gestureend':
  1021. switch (ev.detail.type) {
  1022. case 'onetap':
  1023. case 'twotap':
  1024. case 'threetap':
  1025. case 'pinch':
  1026. case 'twodrag':
  1027. break;
  1028. case 'drag':
  1029. this._fakeMouseMove(ev, pos.x, pos.y);
  1030. this._handleMouseButton(pos.x, pos.y, false, 0x1);
  1031. break;
  1032. case 'longpress':
  1033. this._fakeMouseMove(ev, pos.x, pos.y);
  1034. this._handleMouseButton(pos.x, pos.y, false, 0x4);
  1035. break;
  1036. }
  1037. break;
  1038. }
  1039. }
  1040. // Message Handlers
  1041. _negotiateProtocolVersion() {
  1042. if (this._sock.rQwait("version", 12)) {
  1043. return false;
  1044. }
  1045. const sversion = this._sock.rQshiftStr(12).substr(4, 7);
  1046. Log.Info("Server ProtocolVersion: " + sversion);
  1047. let isRepeater = 0;
  1048. switch (sversion) {
  1049. case "000.000": // UltraVNC repeater
  1050. isRepeater = 1;
  1051. break;
  1052. case "003.003":
  1053. case "003.006": // UltraVNC
  1054. case "003.889": // Apple Remote Desktop
  1055. this._rfbVersion = 3.3;
  1056. break;
  1057. case "003.007":
  1058. this._rfbVersion = 3.7;
  1059. break;
  1060. case "003.008":
  1061. case "004.000": // Intel AMT KVM
  1062. case "004.001": // RealVNC 4.6
  1063. case "005.000": // RealVNC 5.3
  1064. this._rfbVersion = 3.8;
  1065. break;
  1066. default:
  1067. return this._fail("Invalid server version " + sversion);
  1068. }
  1069. if (isRepeater) {
  1070. let repeaterID = "ID:" + this._repeaterID;
  1071. while (repeaterID.length < 250) {
  1072. repeaterID += "\0";
  1073. }
  1074. this._sock.sendString(repeaterID);
  1075. return true;
  1076. }
  1077. if (this._rfbVersion > this._rfbMaxVersion) {
  1078. this._rfbVersion = this._rfbMaxVersion;
  1079. }
  1080. const cversion = "00" + parseInt(this._rfbVersion, 10) +
  1081. ".00" + ((this._rfbVersion * 10) % 10);
  1082. this._sock.sendString("RFB " + cversion + "\n");
  1083. Log.Debug('Sent ProtocolVersion: ' + cversion);
  1084. this._rfbInitState = 'Security';
  1085. }
  1086. _negotiateSecurity() {
  1087. if (this._rfbVersion >= 3.7) {
  1088. // Server sends supported list, client decides
  1089. const numTypes = this._sock.rQshift8();
  1090. if (this._sock.rQwait("security type", numTypes, 1)) { return false; }
  1091. if (numTypes === 0) {
  1092. this._rfbInitState = "SecurityReason";
  1093. this._securityContext = "no security types";
  1094. this._securityStatus = 1;
  1095. return this._initMsg();
  1096. }
  1097. const types = this._sock.rQshiftBytes(numTypes);
  1098. Log.Debug("Server security types: " + types);
  1099. // Look for each auth in preferred order
  1100. if (types.includes(1)) {
  1101. this._rfbAuthScheme = 1; // None
  1102. } else if (types.includes(22)) {
  1103. this._rfbAuthScheme = 22; // XVP
  1104. } else if (types.includes(16)) {
  1105. this._rfbAuthScheme = 16; // Tight
  1106. } else if (types.includes(2)) {
  1107. this._rfbAuthScheme = 2; // VNC Auth
  1108. } else if (types.includes(19)) {
  1109. this._rfbAuthScheme = 19; // VeNCrypt Auth
  1110. } else {
  1111. return this._fail("Unsupported security types (types: " + types + ")");
  1112. }
  1113. this._sock.send([this._rfbAuthScheme]);
  1114. } else {
  1115. // Server decides
  1116. if (this._sock.rQwait("security scheme", 4)) { return false; }
  1117. this._rfbAuthScheme = this._sock.rQshift32();
  1118. if (this._rfbAuthScheme == 0) {
  1119. this._rfbInitState = "SecurityReason";
  1120. this._securityContext = "authentication scheme";
  1121. this._securityStatus = 1;
  1122. return this._initMsg();
  1123. }
  1124. }
  1125. this._rfbInitState = 'Authentication';
  1126. Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
  1127. return this._initMsg(); // jump to authentication
  1128. }
  1129. _handleSecurityReason() {
  1130. if (this._sock.rQwait("reason length", 4)) {
  1131. return false;
  1132. }
  1133. const strlen = this._sock.rQshift32();
  1134. let reason = "";
  1135. if (strlen > 0) {
  1136. if (this._sock.rQwait("reason", strlen, 4)) { return false; }
  1137. reason = this._sock.rQshiftStr(strlen);
  1138. }
  1139. if (reason !== "") {
  1140. this.dispatchEvent(new CustomEvent(
  1141. "securityfailure",
  1142. { detail: { status: this._securityStatus,
  1143. reason: reason } }));
  1144. return this._fail("Security negotiation failed on " +
  1145. this._securityContext +
  1146. " (reason: " + reason + ")");
  1147. } else {
  1148. this.dispatchEvent(new CustomEvent(
  1149. "securityfailure",
  1150. { detail: { status: this._securityStatus } }));
  1151. return this._fail("Security negotiation failed on " +
  1152. this._securityContext);
  1153. }
  1154. }
  1155. // authentication
  1156. _negotiateXvpAuth() {
  1157. if (this._rfbCredentials.username === undefined ||
  1158. this._rfbCredentials.password === undefined ||
  1159. this._rfbCredentials.target === undefined) {
  1160. this.dispatchEvent(new CustomEvent(
  1161. "credentialsrequired",
  1162. { detail: { types: ["username", "password", "target"] } }));
  1163. return false;
  1164. }
  1165. const xvpAuthStr = String.fromCharCode(this._rfbCredentials.username.length) +
  1166. String.fromCharCode(this._rfbCredentials.target.length) +
  1167. this._rfbCredentials.username +
  1168. this._rfbCredentials.target;
  1169. this._sock.sendString(xvpAuthStr);
  1170. this._rfbAuthScheme = 2;
  1171. return this._negotiateAuthentication();
  1172. }
  1173. // VeNCrypt authentication, currently only supports version 0.2 and only Plain subtype
  1174. _negotiateVeNCryptAuth() {
  1175. // waiting for VeNCrypt version
  1176. if (this._rfbVeNCryptState == 0) {
  1177. if (this._sock.rQwait("vencrypt version", 2)) { return false; }
  1178. const major = this._sock.rQshift8();
  1179. const minor = this._sock.rQshift8();
  1180. if (!(major == 0 && minor == 2)) {
  1181. return this._fail("Unsupported VeNCrypt version " + major + "." + minor);
  1182. }
  1183. this._sock.send([0, 2]);
  1184. this._rfbVeNCryptState = 1;
  1185. }
  1186. // waiting for ACK
  1187. if (this._rfbVeNCryptState == 1) {
  1188. if (this._sock.rQwait("vencrypt ack", 1)) { return false; }
  1189. const res = this._sock.rQshift8();
  1190. if (res != 0) {
  1191. return this._fail("VeNCrypt failure " + res);
  1192. }
  1193. this._rfbVeNCryptState = 2;
  1194. }
  1195. // must fall through here (i.e. no "else if"), beacause we may have already received
  1196. // the subtypes length and won't be called again
  1197. if (this._rfbVeNCryptState == 2) { // waiting for subtypes length
  1198. if (this._sock.rQwait("vencrypt subtypes length", 1)) { return false; }
  1199. const subtypesLength = this._sock.rQshift8();
  1200. if (subtypesLength < 1) {
  1201. return this._fail("VeNCrypt subtypes empty");
  1202. }
  1203. this._rfbVeNCryptSubtypesLength = subtypesLength;
  1204. this._rfbVeNCryptState = 3;
  1205. }
  1206. // waiting for subtypes list
  1207. if (this._rfbVeNCryptState == 3) {
  1208. if (this._sock.rQwait("vencrypt subtypes", 4 * this._rfbVeNCryptSubtypesLength)) { return false; }
  1209. const subtypes = [];
  1210. for (let i = 0; i < this._rfbVeNCryptSubtypesLength; i++) {
  1211. subtypes.push(this._sock.rQshift32());
  1212. }
  1213. // 256 = Plain subtype
  1214. if (subtypes.indexOf(256) != -1) {
  1215. // 0x100 = 256
  1216. this._sock.send([0, 0, 1, 0]);
  1217. this._rfbVeNCryptState = 4;
  1218. } else {
  1219. return this._fail("VeNCrypt Plain subtype not offered by server");
  1220. }
  1221. }
  1222. // negotiated Plain subtype, server waits for password
  1223. if (this._rfbVeNCryptState == 4) {
  1224. if (this._rfbCredentials.username === undefined ||
  1225. this._rfbCredentials.password === undefined) {
  1226. this.dispatchEvent(new CustomEvent(
  1227. "credentialsrequired",
  1228. { detail: { types: ["username", "password"] } }));
  1229. return false;
  1230. }
  1231. const user = encodeUTF8(this._rfbCredentials.username);
  1232. const pass = encodeUTF8(this._rfbCredentials.password);
  1233. this._sock.send([
  1234. (user.length >> 24) & 0xFF,
  1235. (user.length >> 16) & 0xFF,
  1236. (user.length >> 8) & 0xFF,
  1237. user.length & 0xFF
  1238. ]);
  1239. this._sock.send([
  1240. (pass.length >> 24) & 0xFF,
  1241. (pass.length >> 16) & 0xFF,
  1242. (pass.length >> 8) & 0xFF,
  1243. pass.length & 0xFF
  1244. ]);
  1245. this._sock.sendString(user);
  1246. this._sock.sendString(pass);
  1247. this._rfbInitState = "SecurityResult";
  1248. return true;
  1249. }
  1250. }
  1251. _negotiateStdVNCAuth() {
  1252. if (this._sock.rQwait("auth challenge", 16)) { return false; }
  1253. if (this._rfbCredentials.password === undefined) {
  1254. this.dispatchEvent(new CustomEvent(
  1255. "credentialsrequired",
  1256. { detail: { types: ["password"] } }));
  1257. return false;
  1258. }
  1259. // TODO(directxman12): make genDES not require an Array
  1260. const challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
  1261. const response = RFB.genDES(this._rfbCredentials.password, challenge);
  1262. this._sock.send(response);
  1263. this._rfbInitState = "SecurityResult";
  1264. return true;
  1265. }
  1266. _negotiateTightUnixAuth() {
  1267. if (this._rfbCredentials.username === undefined ||
  1268. this._rfbCredentials.password === undefined) {
  1269. this.dispatchEvent(new CustomEvent(
  1270. "credentialsrequired",
  1271. { detail: { types: ["username", "password"] } }));
  1272. return false;
  1273. }
  1274. this._sock.send([0, 0, 0, this._rfbCredentials.username.length]);
  1275. this._sock.send([0, 0, 0, this._rfbCredentials.password.length]);
  1276. this._sock.sendString(this._rfbCredentials.username);
  1277. this._sock.sendString(this._rfbCredentials.password);
  1278. this._rfbInitState = "SecurityResult";
  1279. return true;
  1280. }
  1281. _negotiateTightTunnels(numTunnels) {
  1282. const clientSupportedTunnelTypes = {
  1283. 0: { vendor: 'TGHT', signature: 'NOTUNNEL' }
  1284. };
  1285. const serverSupportedTunnelTypes = {};
  1286. // receive tunnel capabilities
  1287. for (let i = 0; i < numTunnels; i++) {
  1288. const capCode = this._sock.rQshift32();
  1289. const capVendor = this._sock.rQshiftStr(4);
  1290. const capSignature = this._sock.rQshiftStr(8);
  1291. serverSupportedTunnelTypes[capCode] = { vendor: capVendor, signature: capSignature };
  1292. }
  1293. Log.Debug("Server Tight tunnel types: " + serverSupportedTunnelTypes);
  1294. // Siemens touch panels have a VNC server that supports NOTUNNEL,
  1295. // but forgets to advertise it. Try to detect such servers by
  1296. // looking for their custom tunnel type.
  1297. if (serverSupportedTunnelTypes[1] &&
  1298. (serverSupportedTunnelTypes[1].vendor === "SICR") &&
  1299. (serverSupportedTunnelTypes[1].signature === "SCHANNEL")) {
  1300. Log.Debug("Detected Siemens server. Assuming NOTUNNEL support.");
  1301. serverSupportedTunnelTypes[0] = { vendor: 'TGHT', signature: 'NOTUNNEL' };
  1302. }
  1303. // choose the notunnel type
  1304. if (serverSupportedTunnelTypes[0]) {
  1305. if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor ||
  1306. serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) {
  1307. return this._fail("Client's tunnel type had the incorrect " +
  1308. "vendor or signature");
  1309. }
  1310. Log.Debug("Selected tunnel type: " + clientSupportedTunnelTypes[0]);
  1311. this._sock.send([0, 0, 0, 0]); // use NOTUNNEL
  1312. return false; // wait until we receive the sub auth count to continue
  1313. } else {
  1314. return this._fail("Server wanted tunnels, but doesn't support " +
  1315. "the notunnel type");
  1316. }
  1317. }
  1318. _negotiateTightAuth() {
  1319. if (!this._rfbTightVNC) { // first pass, do the tunnel negotiation
  1320. if (this._sock.rQwait("num tunnels", 4)) { return false; }
  1321. const numTunnels = this._sock.rQshift32();
  1322. if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; }
  1323. this._rfbTightVNC = true;
  1324. if (numTunnels > 0) {
  1325. this._negotiateTightTunnels(numTunnels);
  1326. return false; // wait until we receive the sub auth to continue
  1327. }
  1328. }
  1329. // second pass, do the sub-auth negotiation
  1330. if (this._sock.rQwait("sub auth count", 4)) { return false; }
  1331. const subAuthCount = this._sock.rQshift32();
  1332. if (subAuthCount === 0) { // empty sub-auth list received means 'no auth' subtype selected
  1333. this._rfbInitState = 'SecurityResult';
  1334. return true;
  1335. }
  1336. if (this._sock.rQwait("sub auth capabilities", 16 * subAuthCount, 4)) { return false; }
  1337. const clientSupportedTypes = {
  1338. 'STDVNOAUTH__': 1,
  1339. 'STDVVNCAUTH_': 2,
  1340. 'TGHTULGNAUTH': 129
  1341. };
  1342. const serverSupportedTypes = [];
  1343. for (let i = 0; i < subAuthCount; i++) {
  1344. this._sock.rQshift32(); // capNum
  1345. const capabilities = this._sock.rQshiftStr(12);
  1346. serverSupportedTypes.push(capabilities);
  1347. }
  1348. Log.Debug("Server Tight authentication types: " + serverSupportedTypes);
  1349. for (let authType in clientSupportedTypes) {
  1350. if (serverSupportedTypes.indexOf(authType) != -1) {
  1351. this._sock.send([0, 0, 0, clientSupportedTypes[authType]]);
  1352. Log.Debug("Selected authentication type: " + authType);
  1353. switch (authType) {
  1354. case 'STDVNOAUTH__': // no auth
  1355. this._rfbInitState = 'SecurityResult';
  1356. return true;
  1357. case 'STDVVNCAUTH_': // VNC auth
  1358. this._rfbAuthScheme = 2;
  1359. return this._initMsg();
  1360. case 'TGHTULGNAUTH': // UNIX auth
  1361. this._rfbAuthScheme = 129;
  1362. return this._initMsg();
  1363. default:
  1364. return this._fail("Unsupported tiny auth scheme " +
  1365. "(scheme: " + authType + ")");
  1366. }
  1367. }
  1368. }
  1369. return this._fail("No supported sub-auth types!");
  1370. }
  1371. _negotiateAuthentication() {
  1372. switch (this._rfbAuthScheme) {
  1373. case 1: // no auth
  1374. if (this._rfbVersion >= 3.8) {
  1375. this._rfbInitState = 'SecurityResult';
  1376. return true;
  1377. }
  1378. this._rfbInitState = 'ClientInitialisation';
  1379. return this._initMsg();
  1380. case 22: // XVP auth
  1381. return this._negotiateXvpAuth();
  1382. case 2: // VNC authentication
  1383. return this._negotiateStdVNCAuth();
  1384. case 16: // TightVNC Security Type
  1385. return this._negotiateTightAuth();
  1386. case 19: // VeNCrypt Security Type
  1387. return this._negotiateVeNCryptAuth();
  1388. case 129: // TightVNC UNIX Security Type
  1389. return this._negotiateTightUnixAuth();
  1390. default:
  1391. return this._fail("Unsupported auth scheme (scheme: " +
  1392. this._rfbAuthScheme + ")");
  1393. }
  1394. }
  1395. _handleSecurityResult() {
  1396. if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
  1397. const status = this._sock.rQshift32();
  1398. if (status === 0) { // OK
  1399. this._rfbInitState = 'ClientInitialisation';
  1400. Log.Debug('Authentication OK');
  1401. return this._initMsg();
  1402. } else {
  1403. if (this._rfbVersion >= 3.8) {
  1404. this._rfbInitState = "SecurityReason";
  1405. this._securityContext = "security result";
  1406. this._securityStatus = status;
  1407. return this._initMsg();
  1408. } else {
  1409. this.dispatchEvent(new CustomEvent(
  1410. "securityfailure",
  1411. { detail: { status: status } }));
  1412. return this._fail("Security handshake failed");
  1413. }
  1414. }
  1415. }
  1416. _negotiateServerInit() {
  1417. if (this._sock.rQwait("server initialization", 24)) { return false; }
  1418. /* Screen size */
  1419. const width = this._sock.rQshift16();
  1420. const height = this._sock.rQshift16();
  1421. /* PIXEL_FORMAT */
  1422. const bpp = this._sock.rQshift8();
  1423. const depth = this._sock.rQshift8();
  1424. const bigEndian = this._sock.rQshift8();
  1425. const trueColor = this._sock.rQshift8();
  1426. const redMax = this._sock.rQshift16();
  1427. const greenMax = this._sock.rQshift16();
  1428. const blueMax = this._sock.rQshift16();
  1429. const redShift = this._sock.rQshift8();
  1430. const greenShift = this._sock.rQshift8();
  1431. const blueShift = this._sock.rQshift8();
  1432. this._sock.rQskipBytes(3); // padding
  1433. // NB(directxman12): we don't want to call any callbacks or print messages until
  1434. // *after* we're past the point where we could backtrack
  1435. /* Connection name/title */
  1436. const nameLength = this._sock.rQshift32();
  1437. if (this._sock.rQwait('server init name', nameLength, 24)) { return false; }
  1438. let name = this._sock.rQshiftStr(nameLength);
  1439. name = decodeUTF8(name, true);
  1440. if (this._rfbTightVNC) {
  1441. if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + nameLength)) { return false; }
  1442. // In TightVNC mode, ServerInit message is extended
  1443. const numServerMessages = this._sock.rQshift16();
  1444. const numClientMessages = this._sock.rQshift16();
  1445. const numEncodings = this._sock.rQshift16();
  1446. this._sock.rQskipBytes(2); // padding
  1447. const totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;
  1448. if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + nameLength)) { return false; }
  1449. // we don't actually do anything with the capability information that TIGHT sends,
  1450. // so we just skip the all of this.
  1451. // TIGHT server message capabilities
  1452. this._sock.rQskipBytes(16 * numServerMessages);
  1453. // TIGHT client message capabilities
  1454. this._sock.rQskipBytes(16 * numClientMessages);
  1455. // TIGHT encoding capabilities
  1456. this._sock.rQskipBytes(16 * numEncodings);
  1457. }
  1458. // NB(directxman12): these are down here so that we don't run them multiple times
  1459. // if we backtrack
  1460. Log.Info("Screen: " + width + "x" + height +
  1461. ", bpp: " + bpp + ", depth: " + depth +
  1462. ", bigEndian: " + bigEndian +
  1463. ", trueColor: " + trueColor +
  1464. ", redMax: " + redMax +
  1465. ", greenMax: " + greenMax +
  1466. ", blueMax: " + blueMax +
  1467. ", redShift: " + redShift +
  1468. ", greenShift: " + greenShift +
  1469. ", blueShift: " + blueShift);
  1470. // we're past the point where we could backtrack, so it's safe to call this
  1471. this._setDesktopName(name);
  1472. this._resize(width, height);
  1473. if (!this._viewOnly) { this._keyboard.grab(); }
  1474. this._fbDepth = 24;
  1475. if (this._fbName === "Intel(r) AMT KVM") {
  1476. Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
  1477. this._fbDepth = 8;
  1478. }
  1479. RFB.messages.pixelFormat(this._sock, this._fbDepth, true);
  1480. this._sendEncodings();
  1481. RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight);
  1482. this._updateConnectionState('connected');
  1483. return true;
  1484. }
  1485. _sendEncodings() {
  1486. const encs = [];
  1487. // In preference order
  1488. encs.push(encodings.encodingCopyRect);
  1489. // Only supported with full depth support
  1490. if (this._fbDepth == 24) {
  1491. encs.push(encodings.encodingTight);
  1492. encs.push(encodings.encodingTightPNG);
  1493. encs.push(encodings.encodingHextile);
  1494. encs.push(encodings.encodingRRE);
  1495. }
  1496. encs.push(encodings.encodingRaw);
  1497. // Psuedo-encoding settings
  1498. encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel);
  1499. encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel);
  1500. encs.push(encodings.pseudoEncodingDesktopSize);
  1501. encs.push(encodings.pseudoEncodingLastRect);
  1502. encs.push(encodings.pseudoEncodingQEMUExtendedKeyEvent);
  1503. encs.push(encodings.pseudoEncodingExtendedDesktopSize);
  1504. encs.push(encodings.pseudoEncodingXvp);
  1505. encs.push(encodings.pseudoEncodingFence);
  1506. encs.push(encodings.pseudoEncodingContinuousUpdates);
  1507. encs.push(encodings.pseudoEncodingDesktopName);
  1508. encs.push(encodings.pseudoEncodingExtendedClipboard);
  1509. if (this._fbDepth == 24) {
  1510. encs.push(encodings.pseudoEncodingVMwareCursor);
  1511. encs.push(encodings.pseudoEncodingCursor);
  1512. }
  1513. RFB.messages.clientEncodings(this._sock, encs);
  1514. }
  1515. /* RFB protocol initialization states:
  1516. * ProtocolVersion
  1517. * Security
  1518. * Authentication
  1519. * SecurityResult
  1520. * ClientInitialization - not triggered by server message
  1521. * ServerInitialization
  1522. */
  1523. _initMsg() {
  1524. switch (this._rfbInitState) {
  1525. case 'ProtocolVersion':
  1526. return this._negotiateProtocolVersion();
  1527. case 'Security':
  1528. return this._negotiateSecurity();
  1529. case 'Authentication':
  1530. return this._negotiateAuthentication();
  1531. case 'SecurityResult':
  1532. return this._handleSecurityResult();
  1533. case 'SecurityReason':
  1534. return this._handleSecurityReason();
  1535. case 'ClientInitialisation':
  1536. this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation
  1537. this._rfbInitState = 'ServerInitialisation';
  1538. return true;
  1539. case 'ServerInitialisation':
  1540. return this._negotiateServerInit();
  1541. default:
  1542. return this._fail("Unknown init state (state: " +
  1543. this._rfbInitState + ")");
  1544. }
  1545. }
  1546. _handleSetColourMapMsg() {
  1547. Log.Debug("SetColorMapEntries");
  1548. return this._fail("Unexpected SetColorMapEntries message");
  1549. }
  1550. _handleServerCutText() {
  1551. Log.Debug("ServerCutText");
  1552. if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
  1553. this._sock.rQskipBytes(3); // Padding
  1554. let length = this._sock.rQshift32();
  1555. length = toSigned32bit(length);
  1556. if (this._sock.rQwait("ServerCutText content", Math.abs(length), 8)) { return false; }
  1557. if (length >= 0) {
  1558. //Standard msg
  1559. const text = this._sock.rQshiftStr(length);
  1560. if (this._viewOnly) {
  1561. return true;
  1562. }
  1563. this.dispatchEvent(new CustomEvent(
  1564. "clipboard",
  1565. { detail: { text: text } }));
  1566. } else {
  1567. //Extended msg.
  1568. length = Math.abs(length);
  1569. const flags = this._sock.rQshift32();
  1570. let formats = flags & 0x0000FFFF;
  1571. let actions = flags & 0xFF000000;
  1572. let isCaps = (!!(actions & extendedClipboardActionCaps));
  1573. if (isCaps) {
  1574. this._clipboardServerCapabilitiesFormats = {};
  1575. this._clipboardServerCapabilitiesActions = {};
  1576. // Update our server capabilities for Formats
  1577. for (let i = 0; i <= 15; i++) {
  1578. let index = 1 << i;
  1579. // Check if format flag is set.
  1580. if ((formats & index)) {
  1581. this._clipboardServerCapabilitiesFormats[index] = true;
  1582. // We don't send unsolicited clipboard, so we
  1583. // ignore the size
  1584. this._sock.rQshift32();
  1585. }
  1586. }
  1587. // Update our server capabilities for Actions
  1588. for (let i = 24; i <= 31; i++) {
  1589. let index = 1 << i;
  1590. this._clipboardServerCapabilitiesActions[index] = !!(actions & index);
  1591. }
  1592. /* Caps handling done, send caps with the clients
  1593. capabilities set as a response */
  1594. let clientActions = [
  1595. extendedClipboardActionCaps,
  1596. extendedClipboardActionRequest,
  1597. extendedClipboardActionPeek,
  1598. extendedClipboardActionNotify,
  1599. extendedClipboardActionProvide
  1600. ];
  1601. RFB.messages.extendedClipboardCaps(this._sock, clientActions, {extendedClipboardFormatText: 0});
  1602. } else if (actions === extendedClipboardActionRequest) {
  1603. if (this._viewOnly) {
  1604. return true;
  1605. }
  1606. // Check if server has told us it can handle Provide and there is clipboard data to send.
  1607. if (this._clipboardText != null &&
  1608. this._clipboardServerCapabilitiesActions[extendedClipboardActionProvide]) {
  1609. if (formats & extendedClipboardFormatText) {
  1610. RFB.messages.extendedClipboardProvide(this._sock, [extendedClipboardFormatText], [this._clipboardText]);
  1611. }
  1612. }
  1613. } else if (actions === extendedClipboardActionPeek) {
  1614. if (this._viewOnly) {
  1615. return true;
  1616. }
  1617. if (this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
  1618. if (this._clipboardText != null) {
  1619. RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
  1620. } else {
  1621. RFB.messages.extendedClipboardNotify(this._sock, []);
  1622. }
  1623. }
  1624. } else if (actions === extendedClipboardActionNotify) {
  1625. if (this._viewOnly) {
  1626. return true;
  1627. }
  1628. if (this._clipboardServerCapabilitiesActions[extendedClipboardActionRequest]) {
  1629. if (formats & extendedClipboardFormatText) {
  1630. RFB.messages.extendedClipboardRequest(this._sock, [extendedClipboardFormatText]);
  1631. }
  1632. }
  1633. } else if (actions === extendedClipboardActionProvide) {
  1634. if (this._viewOnly) {
  1635. return true;
  1636. }
  1637. if (!(formats & extendedClipboardFormatText)) {
  1638. return true;
  1639. }
  1640. // Ignore what we had in our clipboard client side.
  1641. this._clipboardText = null;
  1642. // FIXME: Should probably verify that this data was actually requested
  1643. let zlibStream = this._sock.rQshiftBytes(length - 4);
  1644. let streamInflator = new Inflator();
  1645. let textData = null;
  1646. streamInflator.setInput(zlibStream);
  1647. for (let i = 0; i <= 15; i++) {
  1648. let format = 1 << i;
  1649. if (formats & format) {
  1650. let size = 0x00;
  1651. let sizeArray = streamInflator.inflate(4);
  1652. size |= (sizeArray[0] << 24);
  1653. size |= (sizeArray[1] << 16);
  1654. size |= (sizeArray[2] << 8);
  1655. size |= (sizeArray[3]);
  1656. let chunk = streamInflator.inflate(size);
  1657. if (format === extendedClipboardFormatText) {
  1658. textData = chunk;
  1659. }
  1660. }
  1661. }
  1662. streamInflator.setInput(null);
  1663. if (textData !== null) {
  1664. let tmpText = "";
  1665. for (let i = 0; i < textData.length; i++) {
  1666. tmpText += String.fromCharCode(textData[i]);
  1667. }
  1668. textData = tmpText;
  1669. textData = decodeUTF8(textData);
  1670. if ((textData.length > 0) && "\0" === textData.charAt(textData.length - 1)) {
  1671. textData = textData.slice(0, -1);
  1672. }
  1673. textData = textData.replace("\r\n", "\n");
  1674. this.dispatchEvent(new CustomEvent(
  1675. "clipboard",
  1676. { detail: { text: textData } }));
  1677. }
  1678. } else {
  1679. return this._fail("Unexpected action in extended clipboard message: " + actions);
  1680. }
  1681. }
  1682. return true;
  1683. }
  1684. _handleServerFenceMsg() {
  1685. if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; }
  1686. this._sock.rQskipBytes(3); // Padding
  1687. let flags = this._sock.rQshift32();
  1688. let length = this._sock.rQshift8();
  1689. if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
  1690. if (length > 64) {
  1691. Log.Warn("Bad payload length (" + length + ") in fence response");
  1692. length = 64;
  1693. }
  1694. const payload = this._sock.rQshiftStr(length);
  1695. this._supportsFence = true;
  1696. /*
  1697. * Fence flags
  1698. *
  1699. * (1<<0) - BlockBefore
  1700. * (1<<1) - BlockAfter
  1701. * (1<<2) - SyncNext
  1702. * (1<<31) - Request
  1703. */
  1704. if (!(flags & (1<<31))) {
  1705. return this._fail("Unexpected fence response");
  1706. }
  1707. // Filter out unsupported flags
  1708. // FIXME: support syncNext
  1709. flags &= (1<<0) | (1<<1);
  1710. // BlockBefore and BlockAfter are automatically handled by
  1711. // the fact that we process each incoming message
  1712. // synchronuosly.
  1713. RFB.messages.clientFence(this._sock, flags, payload);
  1714. return true;
  1715. }
  1716. _handleXvpMsg() {
  1717. if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; }
  1718. this._sock.rQskipBytes(1); // Padding
  1719. const xvpVer = this._sock.rQshift8();
  1720. const xvpMsg = this._sock.rQshift8();
  1721. switch (xvpMsg) {
  1722. case 0: // XVP_FAIL
  1723. Log.Error("XVP Operation Failed");
  1724. break;
  1725. case 1: // XVP_INIT
  1726. this._rfbXvpVer = xvpVer;
  1727. Log.Info("XVP extensions enabled (version " + this._rfbXvpVer + ")");
  1728. this._setCapability("power", true);
  1729. break;
  1730. default:
  1731. this._fail("Illegal server XVP message (msg: " + xvpMsg + ")");
  1732. break;
  1733. }
  1734. return true;
  1735. }
  1736. _normalMsg() {
  1737. let msgType;
  1738. if (this._FBU.rects > 0) {
  1739. msgType = 0;
  1740. } else {
  1741. msgType = this._sock.rQshift8();
  1742. }
  1743. let first, ret;
  1744. switch (msgType) {
  1745. case 0: // FramebufferUpdate
  1746. ret = this._framebufferUpdate();
  1747. if (ret && !this._enabledContinuousUpdates) {
  1748. RFB.messages.fbUpdateRequest(this._sock, true, 0, 0,
  1749. this._fbWidth, this._fbHeight);
  1750. }
  1751. return ret;
  1752. case 1: // SetColorMapEntries
  1753. return this._handleSetColourMapMsg();
  1754. case 2: // Bell
  1755. Log.Debug("Bell");
  1756. this.dispatchEvent(new CustomEvent(
  1757. "bell",
  1758. { detail: {} }));
  1759. return true;
  1760. case 3: // ServerCutText
  1761. return this._handleServerCutText();
  1762. case 150: // EndOfContinuousUpdates
  1763. first = !this._supportsContinuousUpdates;
  1764. this._supportsContinuousUpdates = true;
  1765. this._enabledContinuousUpdates = false;
  1766. if (first) {
  1767. this._enabledContinuousUpdates = true;
  1768. this._updateContinuousUpdates();
  1769. Log.Info("Enabling continuous updates.");
  1770. } else {
  1771. // FIXME: We need to send a framebufferupdaterequest here
  1772. // if we add support for turning off continuous updates
  1773. }
  1774. return true;
  1775. case 248: // ServerFence
  1776. return this._handleServerFenceMsg();
  1777. case 250: // XVP
  1778. return this._handleXvpMsg();
  1779. default:
  1780. this._fail("Unexpected server message (type " + msgType + ")");
  1781. Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
  1782. return true;
  1783. }
  1784. }
  1785. _onFlush() {
  1786. this._flushing = false;
  1787. // Resume processing
  1788. if (this._sock.rQlen > 0) {
  1789. this._handleMessage();
  1790. }
  1791. }
  1792. _framebufferUpdate() {
  1793. if (this._FBU.rects === 0) {
  1794. if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
  1795. this._sock.rQskipBytes(1); // Padding
  1796. this._FBU.rects = this._sock.rQshift16();
  1797. // Make sure the previous frame is fully rendered first
  1798. // to avoid building up an excessive queue
  1799. if (this._display.pending()) {
  1800. this._flushing = true;
  1801. this._display.flush();
  1802. return false;
  1803. }
  1804. }
  1805. while (this._FBU.rects > 0) {
  1806. if (this._FBU.encoding === null) {
  1807. if (this._sock.rQwait("rect header", 12)) { return false; }
  1808. /* New FramebufferUpdate */
  1809. const hdr = this._sock.rQshiftBytes(12);
  1810. this._FBU.x = (hdr[0] << 8) + hdr[1];
  1811. this._FBU.y = (hdr[2] << 8) + hdr[3];
  1812. this._FBU.width = (hdr[4] << 8) + hdr[5];
  1813. this._FBU.height = (hdr[6] << 8) + hdr[7];
  1814. this._FBU.encoding = parseInt((hdr[8] << 24) + (hdr[9] << 16) +
  1815. (hdr[10] << 8) + hdr[11], 10);
  1816. }
  1817. if (!this._handleRect()) {
  1818. return false;
  1819. }
  1820. this._FBU.rects--;
  1821. this._FBU.encoding = null;
  1822. }
  1823. this._display.flip();
  1824. return true; // We finished this FBU
  1825. }
  1826. _handleRect() {
  1827. switch (this._FBU.encoding) {
  1828. case encodings.pseudoEncodingLastRect:
  1829. this._FBU.rects = 1; // Will be decreased when we return
  1830. return true;
  1831. case encodings.pseudoEncodingVMwareCursor:
  1832. return this._handleVMwareCursor();
  1833. case encodings.pseudoEncodingCursor:
  1834. return this._handleCursor();
  1835. case encodings.pseudoEncodingQEMUExtendedKeyEvent:
  1836. this._qemuExtKeyEventSupported = true;
  1837. return true;
  1838. case encodings.pseudoEncodingDesktopName:
  1839. return this._handleDesktopName();
  1840. case encodings.pseudoEncodingDesktopSize:
  1841. this._resize(this._FBU.width, this._FBU.height);
  1842. return true;
  1843. case encodings.pseudoEncodingExtendedDesktopSize:
  1844. return this._handleExtendedDesktopSize();
  1845. default:
  1846. return this._handleDataRect();
  1847. }
  1848. }
  1849. _handleVMwareCursor() {
  1850. const hotx = this._FBU.x; // hotspot-x
  1851. const hoty = this._FBU.y; // hotspot-y
  1852. const w = this._FBU.width;
  1853. const h = this._FBU.height;
  1854. if (this._sock.rQwait("VMware cursor encoding", 1)) {
  1855. return false;
  1856. }
  1857. const cursorType = this._sock.rQshift8();
  1858. this._sock.rQshift8(); //Padding
  1859. let rgba;
  1860. const bytesPerPixel = 4;
  1861. //Classic cursor
  1862. if (cursorType == 0) {
  1863. //Used to filter away unimportant bits.
  1864. //OR is used for correct conversion in js.
  1865. const PIXEL_MASK = 0xffffff00 | 0;
  1866. rgba = new Array(w * h * bytesPerPixel);
  1867. if (this._sock.rQwait("VMware cursor classic encoding",
  1868. (w * h * bytesPerPixel) * 2, 2)) {
  1869. return false;
  1870. }
  1871. let andMask = new Array(w * h);
  1872. for (let pixel = 0; pixel < (w * h); pixel++) {
  1873. andMask[pixel] = this._sock.rQshift32();
  1874. }
  1875. let xorMask = new Array(w * h);
  1876. for (let pixel = 0; pixel < (w * h); pixel++) {
  1877. xorMask[pixel] = this._sock.rQshift32();
  1878. }
  1879. for (let pixel = 0; pixel < (w * h); pixel++) {
  1880. if (andMask[pixel] == 0) {
  1881. //Fully opaque pixel
  1882. let bgr = xorMask[pixel];
  1883. let r = bgr >> 8 & 0xff;
  1884. let g = bgr >> 16 & 0xff;
  1885. let b = bgr >> 24 & 0xff;
  1886. rgba[(pixel * bytesPerPixel) ] = r; //r
  1887. rgba[(pixel * bytesPerPixel) + 1 ] = g; //g
  1888. rgba[(pixel * bytesPerPixel) + 2 ] = b; //b
  1889. rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; //a
  1890. } else if ((andMask[pixel] & PIXEL_MASK) ==
  1891. PIXEL_MASK) {
  1892. //Only screen value matters, no mouse colouring
  1893. if (xorMask[pixel] == 0) {
  1894. //Transparent pixel
  1895. rgba[(pixel * bytesPerPixel) ] = 0x00;
  1896. rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
  1897. rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
  1898. rgba[(pixel * bytesPerPixel) + 3 ] = 0x00;
  1899. } else if ((xorMask[pixel] & PIXEL_MASK) ==
  1900. PIXEL_MASK) {
  1901. //Inverted pixel, not supported in browsers.
  1902. //Fully opaque instead.
  1903. rgba[(pixel * bytesPerPixel) ] = 0x00;
  1904. rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
  1905. rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
  1906. rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
  1907. } else {
  1908. //Unhandled xorMask
  1909. rgba[(pixel * bytesPerPixel) ] = 0x00;
  1910. rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
  1911. rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
  1912. rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
  1913. }
  1914. } else {
  1915. //Unhandled andMask
  1916. rgba[(pixel * bytesPerPixel) ] = 0x00;
  1917. rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
  1918. rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
  1919. rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
  1920. }
  1921. }
  1922. //Alpha cursor.
  1923. } else if (cursorType == 1) {
  1924. if (this._sock.rQwait("VMware cursor alpha encoding",
  1925. (w * h * 4), 2)) {
  1926. return false;
  1927. }
  1928. rgba = new Array(w * h * bytesPerPixel);
  1929. for (let pixel = 0; pixel < (w * h); pixel++) {
  1930. let data = this._sock.rQshift32();
  1931. rgba[(pixel * 4) ] = data >> 24 & 0xff; //r
  1932. rgba[(pixel * 4) + 1 ] = data >> 16 & 0xff; //g
  1933. rgba[(pixel * 4) + 2 ] = data >> 8 & 0xff; //b
  1934. rgba[(pixel * 4) + 3 ] = data & 0xff; //a
  1935. }
  1936. } else {
  1937. Log.Warn("The given cursor type is not supported: "
  1938. + cursorType + " given.");
  1939. return false;
  1940. }
  1941. this._updateCursor(rgba, hotx, hoty, w, h);
  1942. return true;
  1943. }
  1944. _handleCursor() {
  1945. const hotx = this._FBU.x; // hotspot-x
  1946. const hoty = this._FBU.y; // hotspot-y
  1947. const w = this._FBU.width;
  1948. const h = this._FBU.height;
  1949. const pixelslength = w * h * 4;
  1950. const masklength = Math.ceil(w / 8) * h;
  1951. let bytes = pixelslength + masklength;
  1952. if (this._sock.rQwait("cursor encoding", bytes)) {
  1953. return false;
  1954. }
  1955. // Decode from BGRX pixels + bit mask to RGBA
  1956. const pixels = this._sock.rQshiftBytes(pixelslength);
  1957. const mask = this._sock.rQshiftBytes(masklength);
  1958. let rgba = new Uint8Array(w * h * 4);
  1959. let pixIdx = 0;
  1960. for (let y = 0; y < h; y++) {
  1961. for (let x = 0; x < w; x++) {
  1962. let maskIdx = y * Math.ceil(w / 8) + Math.floor(x / 8);
  1963. let alpha = (mask[maskIdx] << (x % 8)) & 0x80 ? 255 : 0;
  1964. rgba[pixIdx ] = pixels[pixIdx + 2];
  1965. rgba[pixIdx + 1] = pixels[pixIdx + 1];
  1966. rgba[pixIdx + 2] = pixels[pixIdx];
  1967. rgba[pixIdx + 3] = alpha;
  1968. pixIdx += 4;
  1969. }
  1970. }
  1971. this._updateCursor(rgba, hotx, hoty, w, h);
  1972. return true;
  1973. }
  1974. _handleDesktopName() {
  1975. if (this._sock.rQwait("DesktopName", 4)) {
  1976. return false;
  1977. }
  1978. let length = this._sock.rQshift32();
  1979. if (this._sock.rQwait("DesktopName", length, 4)) {
  1980. return false;
  1981. }
  1982. let name = this._sock.rQshiftStr(length);
  1983. name = decodeUTF8(name, true);
  1984. this._setDesktopName(name);
  1985. return true;
  1986. }
  1987. _handleExtendedDesktopSize() {
  1988. if (this._sock.rQwait("ExtendedDesktopSize", 4)) {
  1989. return false;
  1990. }
  1991. const numberOfScreens = this._sock.rQpeek8();
  1992. let bytes = 4 + (numberOfScreens * 16);
  1993. if (this._sock.rQwait("ExtendedDesktopSize", bytes)) {
  1994. return false;
  1995. }
  1996. const firstUpdate = !this._supportsSetDesktopSize;
  1997. this._supportsSetDesktopSize = true;
  1998. // Normally we only apply the current resize mode after a
  1999. // window resize event. However there is no such trigger on the
  2000. // initial connect. And we don't know if the server supports
  2001. // resizing until we've gotten here.
  2002. if (firstUpdate) {
  2003. this._requestRemoteResize();
  2004. }
  2005. this._sock.rQskipBytes(1); // number-of-screens
  2006. this._sock.rQskipBytes(3); // padding
  2007. for (let i = 0; i < numberOfScreens; i += 1) {
  2008. // Save the id and flags of the first screen
  2009. if (i === 0) {
  2010. this._screenID = this._sock.rQshiftBytes(4); // id
  2011. this._sock.rQskipBytes(2); // x-position
  2012. this._sock.rQskipBytes(2); // y-position
  2013. this._sock.rQskipBytes(2); // width
  2014. this._sock.rQskipBytes(2); // height
  2015. this._screenFlags = this._sock.rQshiftBytes(4); // flags
  2016. } else {
  2017. this._sock.rQskipBytes(16);
  2018. }
  2019. }
  2020. /*
  2021. * The x-position indicates the reason for the change:
  2022. *
  2023. * 0 - server resized on its own
  2024. * 1 - this client requested the resize
  2025. * 2 - another client requested the resize
  2026. */
  2027. // We need to handle errors when we requested the resize.
  2028. if (this._FBU.x === 1 && this._FBU.y !== 0) {
  2029. let msg = "";
  2030. // The y-position indicates the status code from the server
  2031. switch (this._FBU.y) {
  2032. case 1:
  2033. msg = "Resize is administratively prohibited";
  2034. break;
  2035. case 2:
  2036. msg = "Out of resources";
  2037. break;
  2038. case 3:
  2039. msg = "Invalid screen layout";
  2040. break;
  2041. default:
  2042. msg = "Unknown reason";
  2043. break;
  2044. }
  2045. Log.Warn("Server did not accept the resize request: "
  2046. + msg);
  2047. } else {
  2048. this._resize(this._FBU.width, this._FBU.height);
  2049. }
  2050. return true;
  2051. }
  2052. _handleDataRect() {
  2053. let decoder = this._decoders[this._FBU.encoding];
  2054. if (!decoder) {
  2055. this._fail("Unsupported encoding (encoding: " +
  2056. this._FBU.encoding + ")");
  2057. return false;
  2058. }
  2059. try {
  2060. return decoder.decodeRect(this._FBU.x, this._FBU.y,
  2061. this._FBU.width, this._FBU.height,
  2062. this._sock, this._display,
  2063. this._fbDepth);
  2064. } catch (err) {
  2065. this._fail("Error decoding rect: " + err);
  2066. return false;
  2067. }
  2068. }
  2069. _updateContinuousUpdates() {
  2070. if (!this._enabledContinuousUpdates) { return; }
  2071. RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
  2072. this._fbWidth, this._fbHeight);
  2073. }
  2074. _resize(width, height) {
  2075. this._fbWidth = width;
  2076. this._fbHeight = height;
  2077. this._display.resize(this._fbWidth, this._fbHeight);
  2078. // Adjust the visible viewport based on the new dimensions
  2079. this._updateClip();
  2080. this._updateScale();
  2081. this._updateContinuousUpdates();
  2082. }
  2083. _xvpOp(ver, op) {
  2084. if (this._rfbXvpVer < ver) { return; }
  2085. Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
  2086. RFB.messages.xvpOp(this._sock, ver, op);
  2087. }
  2088. _updateCursor(rgba, hotx, hoty, w, h) {
  2089. this._cursorImage = {
  2090. rgbaPixels: rgba,
  2091. hotx: hotx, hoty: hoty, w: w, h: h,
  2092. };
  2093. this._refreshCursor();
  2094. }
  2095. _shouldShowDotCursor() {
  2096. // Called when this._cursorImage is updated
  2097. if (!this._showDotCursor) {
  2098. // User does not want to see the dot, so...
  2099. return false;
  2100. }
  2101. // The dot should not be shown if the cursor is already visible,
  2102. // i.e. contains at least one not-fully-transparent pixel.
  2103. // So iterate through all alpha bytes in rgba and stop at the
  2104. // first non-zero.
  2105. for (let i = 3; i < this._cursorImage.rgbaPixels.length; i += 4) {
  2106. if (this._cursorImage.rgbaPixels[i]) {
  2107. return false;
  2108. }
  2109. }
  2110. // At this point, we know that the cursor is fully transparent, and
  2111. // the user wants to see the dot instead of this.
  2112. return true;
  2113. }
  2114. _refreshCursor() {
  2115. if (this._rfbConnectionState !== "connecting" &&
  2116. this._rfbConnectionState !== "connected") {
  2117. return;
  2118. }
  2119. const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage;
  2120. this._cursor.change(image.rgbaPixels,
  2121. image.hotx, image.hoty,
  2122. image.w, image.h
  2123. );
  2124. }
  2125. static genDES(password, challenge) {
  2126. const passwordChars = password.split('').map(c => c.charCodeAt(0));
  2127. return (new DES(passwordChars)).encrypt(challenge);
  2128. }
  2129. }
  2130. // Class Methods
  2131. RFB.messages = {
  2132. keyEvent(sock, keysym, down) {
  2133. const buff = sock._sQ;
  2134. const offset = sock._sQlen;
  2135. buff[offset] = 4; // msg-type
  2136. buff[offset + 1] = down;
  2137. buff[offset + 2] = 0;
  2138. buff[offset + 3] = 0;
  2139. buff[offset + 4] = (keysym >> 24);
  2140. buff[offset + 5] = (keysym >> 16);
  2141. buff[offset + 6] = (keysym >> 8);
  2142. buff[offset + 7] = keysym;
  2143. sock._sQlen += 8;
  2144. sock.flush();
  2145. },
  2146. QEMUExtendedKeyEvent(sock, keysym, down, keycode) {
  2147. function getRFBkeycode(xtScanCode) {
  2148. const upperByte = (keycode >> 8);
  2149. const lowerByte = (keycode & 0x00ff);
  2150. if (upperByte === 0xe0 && lowerByte < 0x7f) {
  2151. return lowerByte | 0x80;
  2152. }
  2153. return xtScanCode;
  2154. }
  2155. const buff = sock._sQ;
  2156. const offset = sock._sQlen;
  2157. buff[offset] = 255; // msg-type
  2158. buff[offset + 1] = 0; // sub msg-type
  2159. buff[offset + 2] = (down >> 8);
  2160. buff[offset + 3] = down;
  2161. buff[offset + 4] = (keysym >> 24);
  2162. buff[offset + 5] = (keysym >> 16);
  2163. buff[offset + 6] = (keysym >> 8);
  2164. buff[offset + 7] = keysym;
  2165. const RFBkeycode = getRFBkeycode(keycode);
  2166. buff[offset + 8] = (RFBkeycode >> 24);
  2167. buff[offset + 9] = (RFBkeycode >> 16);
  2168. buff[offset + 10] = (RFBkeycode >> 8);
  2169. buff[offset + 11] = RFBkeycode;
  2170. sock._sQlen += 12;
  2171. sock.flush();
  2172. },
  2173. pointerEvent(sock, x, y, mask) {
  2174. const buff = sock._sQ;
  2175. const offset = sock._sQlen;
  2176. buff[offset] = 5; // msg-type
  2177. buff[offset + 1] = mask;
  2178. buff[offset + 2] = x >> 8;
  2179. buff[offset + 3] = x;
  2180. buff[offset + 4] = y >> 8;
  2181. buff[offset + 5] = y;
  2182. sock._sQlen += 6;
  2183. sock.flush();
  2184. },
  2185. // Used to build Notify and Request data.
  2186. _buildExtendedClipboardFlags(actions, formats) {
  2187. let data = new Uint8Array(4);
  2188. let formatFlag = 0x00000000;
  2189. let actionFlag = 0x00000000;
  2190. for (let i = 0; i < actions.length; i++) {
  2191. actionFlag |= actions[i];
  2192. }
  2193. for (let i = 0; i < formats.length; i++) {
  2194. formatFlag |= formats[i];
  2195. }
  2196. data[0] = actionFlag >> 24; // Actions
  2197. data[1] = 0x00; // Reserved
  2198. data[2] = 0x00; // Reserved
  2199. data[3] = formatFlag; // Formats
  2200. return data;
  2201. },
  2202. extendedClipboardProvide(sock, formats, inData) {
  2203. // Deflate incomming data and their sizes
  2204. let deflator = new Deflator();
  2205. let dataToDeflate = [];
  2206. for (let i = 0; i < formats.length; i++) {
  2207. // We only support the format Text at this time
  2208. if (formats[i] != extendedClipboardFormatText) {
  2209. throw new Error("Unsupported extended clipboard format for Provide message.");
  2210. }
  2211. // Change lone \r or \n into \r\n as defined in rfbproto
  2212. inData[i] = inData[i].replace(/\r\n|\r|\n/gm, "\r\n");
  2213. // Check if it already has \0
  2214. let text = encodeUTF8(inData[i] + "\0");
  2215. dataToDeflate.push( (text.length >> 24) & 0xFF,
  2216. (text.length >> 16) & 0xFF,
  2217. (text.length >> 8) & 0xFF,
  2218. (text.length & 0xFF));
  2219. for (let j = 0; j < text.length; j++) {
  2220. dataToDeflate.push(text.charCodeAt(j));
  2221. }
  2222. }
  2223. let deflatedData = deflator.deflate(new Uint8Array(dataToDeflate));
  2224. // Build data to send
  2225. let data = new Uint8Array(4 + deflatedData.length);
  2226. data.set(RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionProvide],
  2227. formats));
  2228. data.set(deflatedData, 4);
  2229. RFB.messages.clientCutText(sock, data, true);
  2230. },
  2231. extendedClipboardNotify(sock, formats) {
  2232. let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionNotify],
  2233. formats);
  2234. RFB.messages.clientCutText(sock, flags, true);
  2235. },
  2236. extendedClipboardRequest(sock, formats) {
  2237. let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionRequest],
  2238. formats);
  2239. RFB.messages.clientCutText(sock, flags, true);
  2240. },
  2241. extendedClipboardCaps(sock, actions, formats) {
  2242. let formatKeys = Object.keys(formats);
  2243. let data = new Uint8Array(4 + (4 * formatKeys.length));
  2244. formatKeys.map(x => parseInt(x));
  2245. formatKeys.sort((a, b) => a - b);
  2246. data.set(RFB.messages._buildExtendedClipboardFlags(actions, []));
  2247. let loopOffset = 4;
  2248. for (let i = 0; i < formatKeys.length; i++) {
  2249. data[loopOffset] = formats[formatKeys[i]] >> 24;
  2250. data[loopOffset + 1] = formats[formatKeys[i]] >> 16;
  2251. data[loopOffset + 2] = formats[formatKeys[i]] >> 8;
  2252. data[loopOffset + 3] = formats[formatKeys[i]] >> 0;
  2253. loopOffset += 4;
  2254. data[3] |= (1 << formatKeys[i]); // Update our format flags
  2255. }
  2256. RFB.messages.clientCutText(sock, data, true);
  2257. },
  2258. clientCutText(sock, data, extended = false) {
  2259. const buff = sock._sQ;
  2260. const offset = sock._sQlen;
  2261. buff[offset] = 6; // msg-type
  2262. buff[offset + 1] = 0; // padding
  2263. buff[offset + 2] = 0; // padding
  2264. buff[offset + 3] = 0; // padding
  2265. let length;
  2266. if (extended) {
  2267. length = toUnsigned32bit(-data.length);
  2268. } else {
  2269. length = data.length;
  2270. }
  2271. buff[offset + 4] = length >> 24;
  2272. buff[offset + 5] = length >> 16;
  2273. buff[offset + 6] = length >> 8;
  2274. buff[offset + 7] = length;
  2275. sock._sQlen += 8;
  2276. // We have to keep track of from where in the data we begin creating the
  2277. // buffer for the flush in the next iteration.
  2278. let dataOffset = 0;
  2279. let remaining = data.length;
  2280. while (remaining > 0) {
  2281. let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
  2282. for (let i = 0; i < flushSize; i++) {
  2283. buff[sock._sQlen + i] = data[dataOffset + i];
  2284. }
  2285. sock._sQlen += flushSize;
  2286. sock.flush();
  2287. remaining -= flushSize;
  2288. dataOffset += flushSize;
  2289. }
  2290. },
  2291. setDesktopSize(sock, width, height, id, flags) {
  2292. const buff = sock._sQ;
  2293. const offset = sock._sQlen;
  2294. buff[offset] = 251; // msg-type
  2295. buff[offset + 1] = 0; // padding
  2296. buff[offset + 2] = width >> 8; // width
  2297. buff[offset + 3] = width;
  2298. buff[offset + 4] = height >> 8; // height
  2299. buff[offset + 5] = height;
  2300. buff[offset + 6] = 1; // number-of-screens
  2301. buff[offset + 7] = 0; // padding
  2302. // screen array
  2303. buff[offset + 8] = id >> 24; // id
  2304. buff[offset + 9] = id >> 16;
  2305. buff[offset + 10] = id >> 8;
  2306. buff[offset + 11] = id;
  2307. buff[offset + 12] = 0; // x-position
  2308. buff[offset + 13] = 0;
  2309. buff[offset + 14] = 0; // y-position
  2310. buff[offset + 15] = 0;
  2311. buff[offset + 16] = width >> 8; // width
  2312. buff[offset + 17] = width;
  2313. buff[offset + 18] = height >> 8; // height
  2314. buff[offset + 19] = height;
  2315. buff[offset + 20] = flags >> 24; // flags
  2316. buff[offset + 21] = flags >> 16;
  2317. buff[offset + 22] = flags >> 8;
  2318. buff[offset + 23] = flags;
  2319. sock._sQlen += 24;
  2320. sock.flush();
  2321. },
  2322. clientFence(sock, flags, payload) {
  2323. const buff = sock._sQ;
  2324. const offset = sock._sQlen;
  2325. buff[offset] = 248; // msg-type
  2326. buff[offset + 1] = 0; // padding
  2327. buff[offset + 2] = 0; // padding
  2328. buff[offset + 3] = 0; // padding
  2329. buff[offset + 4] = flags >> 24; // flags
  2330. buff[offset + 5] = flags >> 16;
  2331. buff[offset + 6] = flags >> 8;
  2332. buff[offset + 7] = flags;
  2333. const n = payload.length;
  2334. buff[offset + 8] = n; // length
  2335. for (let i = 0; i < n; i++) {
  2336. buff[offset + 9 + i] = payload.charCodeAt(i);
  2337. }
  2338. sock._sQlen += 9 + n;
  2339. sock.flush();
  2340. },
  2341. enableContinuousUpdates(sock, enable, x, y, width, height) {
  2342. const buff = sock._sQ;
  2343. const offset = sock._sQlen;
  2344. buff[offset] = 150; // msg-type
  2345. buff[offset + 1] = enable; // enable-flag
  2346. buff[offset + 2] = x >> 8; // x
  2347. buff[offset + 3] = x;
  2348. buff[offset + 4] = y >> 8; // y
  2349. buff[offset + 5] = y;
  2350. buff[offset + 6] = width >> 8; // width
  2351. buff[offset + 7] = width;
  2352. buff[offset + 8] = height >> 8; // height
  2353. buff[offset + 9] = height;
  2354. sock._sQlen += 10;
  2355. sock.flush();
  2356. },
  2357. pixelFormat(sock, depth, trueColor) {
  2358. const buff = sock._sQ;
  2359. const offset = sock._sQlen;
  2360. let bpp;
  2361. if (depth > 16) {
  2362. bpp = 32;
  2363. } else if (depth > 8) {
  2364. bpp = 16;
  2365. } else {
  2366. bpp = 8;
  2367. }
  2368. const bits = Math.floor(depth/3);
  2369. buff[offset] = 0; // msg-type
  2370. buff[offset + 1] = 0; // padding
  2371. buff[offset + 2] = 0; // padding
  2372. buff[offset + 3] = 0; // padding
  2373. buff[offset + 4] = bpp; // bits-per-pixel
  2374. buff[offset + 5] = depth; // depth
  2375. buff[offset + 6] = 0; // little-endian
  2376. buff[offset + 7] = trueColor ? 1 : 0; // true-color
  2377. buff[offset + 8] = 0; // red-max
  2378. buff[offset + 9] = (1 << bits) - 1; // red-max
  2379. buff[offset + 10] = 0; // green-max
  2380. buff[offset + 11] = (1 << bits) - 1; // green-max
  2381. buff[offset + 12] = 0; // blue-max
  2382. buff[offset + 13] = (1 << bits) - 1; // blue-max
  2383. buff[offset + 14] = bits * 0; // red-shift
  2384. buff[offset + 15] = bits * 1; // green-shift
  2385. buff[offset + 16] = bits * 2; // blue-shift
  2386. buff[offset + 17] = 0; // padding
  2387. buff[offset + 18] = 0; // padding
  2388. buff[offset + 19] = 0; // padding
  2389. sock._sQlen += 20;
  2390. sock.flush();
  2391. },
  2392. clientEncodings(sock, encodings) {
  2393. const buff = sock._sQ;
  2394. const offset = sock._sQlen;
  2395. buff[offset] = 2; // msg-type
  2396. buff[offset + 1] = 0; // padding
  2397. buff[offset + 2] = encodings.length >> 8;
  2398. buff[offset + 3] = encodings.length;
  2399. let j = offset + 4;
  2400. for (let i = 0; i < encodings.length; i++) {
  2401. const enc = encodings[i];
  2402. buff[j] = enc >> 24;
  2403. buff[j + 1] = enc >> 16;
  2404. buff[j + 2] = enc >> 8;
  2405. buff[j + 3] = enc;
  2406. j += 4;
  2407. }
  2408. sock._sQlen += j - offset;
  2409. sock.flush();
  2410. },
  2411. fbUpdateRequest(sock, incremental, x, y, w, h) {
  2412. const buff = sock._sQ;
  2413. const offset = sock._sQlen;
  2414. if (typeof(x) === "undefined") { x = 0; }
  2415. if (typeof(y) === "undefined") { y = 0; }
  2416. buff[offset] = 3; // msg-type
  2417. buff[offset + 1] = incremental ? 1 : 0;
  2418. buff[offset + 2] = (x >> 8) & 0xFF;
  2419. buff[offset + 3] = x & 0xFF;
  2420. buff[offset + 4] = (y >> 8) & 0xFF;
  2421. buff[offset + 5] = y & 0xFF;
  2422. buff[offset + 6] = (w >> 8) & 0xFF;
  2423. buff[offset + 7] = w & 0xFF;
  2424. buff[offset + 8] = (h >> 8) & 0xFF;
  2425. buff[offset + 9] = h & 0xFF;
  2426. sock._sQlen += 10;
  2427. sock.flush();
  2428. },
  2429. xvpOp(sock, ver, op) {
  2430. const buff = sock._sQ;
  2431. const offset = sock._sQlen;
  2432. buff[offset] = 250; // msg-type
  2433. buff[offset + 1] = 0; // padding
  2434. buff[offset + 2] = ver;
  2435. buff[offset + 3] = op;
  2436. sock._sQlen += 4;
  2437. sock.flush();
  2438. }
  2439. };
  2440. RFB.cursors = {
  2441. none: {
  2442. rgbaPixels: new Uint8Array(),
  2443. w: 0, h: 0,
  2444. hotx: 0, hoty: 0,
  2445. },
  2446. dot: {
  2447. /* eslint-disable indent */
  2448. rgbaPixels: new Uint8Array([
  2449. 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255,
  2450. 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255,
  2451. 255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255,
  2452. ]),
  2453. /* eslint-enable indent */
  2454. w: 3, h: 3,
  2455. hotx: 1, hoty: 1,
  2456. }
  2457. };