90export
default class RFB extends EventTargetMixin {
91 constructor(target, urlOrChannel, options) {
93 throw new Error(
"Must specify target");
96 throw new Error(
"Must specify URL, WebSocket or RTCDataChannel");
101 if (!window.isSecureContext) {
102 Log.Error(
"noVNC requires a secure context (TLS). Expect crashes!");
107 this._target = target;
109 if (typeof urlOrChannel ===
"string") {
110 this._url = urlOrChannel;
113 this._rawChannel = urlOrChannel;
117 options = options || {};
118 this._rfbCredentials = options.credentials || {};
119 this._shared =
'shared' in options ? !!options.shared :
true;
120 this._repeaterID = options.repeaterID ||
'';
121 this._wsProtocols = options.wsProtocols || [];
123 this._overlap = options.overlap ||
false;
126 this._rfbConnectionState =
'';
127 this._rfbInitState =
'';
128 this._rfbAuthScheme = -1;
129 this._rfbCleanDisconnect =
true;
130 this._rfbRSAAESAuthenticationState =
null;
133 this._rfbVersion = 0;
134 this._rfbMaxVersion = 3.8;
135 this._rfbTightVNC =
false;
136 this._rfbVeNCryptState = 0;
144 this._capabilities = { power:
false };
146 this._supportsFence =
false;
148 this._supportsContinuousUpdates =
false;
149 this._enabledContinuousUpdates =
false;
151 this._supportsSetDesktopSize =
false;
153 this._screenFlags = 0;
154 this._pendingRemoteResize =
false;
155 this._lastResize = 0;
157 this._qemuExtKeyEventSupported =
false;
159 this._extendedPointerEventSupported =
false;
161 this._clipboardText =
null;
162 this._clipboardServerCapabilitiesActions = {};
163 this._clipboardServerCapabilitiesFormats = {};
167 this._display =
null;
168 this._flushing =
false;
169 this._keyboard =
null;
170 this._gestures =
null;
171 this._resizeObserver =
null;
174 this._disconnTimer =
null;
175 this._resizeTimeout =
null;
176 this._mouseMoveTimer =
null;
192 this._mouseButtonMask = 0;
193 this._mouseLastMoveTime = 0;
194 this._viewportDragging =
false;
195 this._viewportDragPos = {};
196 this._viewportHasMoved =
false;
197 this._accumulatedWheelDeltaX = 0;
198 this._accumulatedWheelDeltaY = 0;
201 this._gestureLastTapTime =
null;
202 this._gestureFirstDoubleTapEv =
null;
203 this._gestureLastMagnitudeX = 0;
204 this._gestureLastMagnitudeY = 0;
207 this._eventHandlers = {
208 focusCanvas: this._focusCanvas.bind(
this),
209 handleResize: this._handleResize.bind(
this),
210 handleMouse: this._handleMouse.bind(
this),
211 handleWheel: this._handleWheel.bind(
this),
212 handleGesture: this._handleGesture.bind(
this),
213 handleRSAAESCredentialsRequired: this._handleRSAAESCredentialsRequired.bind(
this),
214 handleRSAAESServerVerification: this._handleRSAAESServerVerification.bind(
this),
218 Log.Debug(
">> RFB.constructor");
221 if (!this._overlap) {
222 this._screen = document.createElement(
'div');
224 this._screen = this._target.childNodes[5];
227 this._screen.style.display =
'flex';
228 this._screen.style.width =
'100%';
229 this._screen.style.height =
'100%';
230 this._screen.style.overflow =
'auto';
231 this._screen.style.background = DEFAULT_BACKGROUND;
233 if (!this._overlap) {
234 this._canvas = document.createElement(
'canvas');
236 this._canvas = this._screen.childNodes[0];
239 this._canvas.style.margin =
'auto';
241 this._canvas.style.outline =
'none';
242 this._canvas.width = 0;
243 this._canvas.height = 0;
244 this._canvas.tabIndex = -1;
246 if (!this._overlap) {
247 this._screen.appendChild(this._canvas);
251 this._cursor =
new Cursor();
262 this._cursorImage =
RFB.cursors.none;
265 this._decoders[encodings.encodingRaw] =
new RawDecoder();
266 this._decoders[encodings.encodingCopyRect] =
new CopyRectDecoder();
267 this._decoders[encodings.encodingRRE] =
new RREDecoder();
268 this._decoders[encodings.encodingHextile] =
new HextileDecoder();
269 this._decoders[encodings.encodingZlib] =
new ZlibDecoder();
270 this._decoders[encodings.encodingTight] =
new TightDecoder();
271 this._decoders[encodings.encodingTightPNG] =
new TightPNGDecoder();
272 this._decoders[encodings.encodingZRLE] =
new ZRLEDecoder();
273 this._decoders[encodings.encodingJPEG] =
new JPEGDecoder();
274 this._decoders[encodings.encodingH264] =
new H264Decoder();
279 this._display =
new Display(this._canvas);
281 Log.Error(
"Display exception: " + exc);
285 this._keyboard =
new Keyboard(this._canvas);
286 this._keyboard.onkeyevent = this._handleKeyEvent.bind(
this);
287 this._remoteCapsLock =
null;
288 this._remoteNumLock =
null;
290 this._gestures =
new GestureHandler();
292 this._sock =
new Websock();
293 this._sock.on(
'open', this._socketOpen.bind(
this));
294 this._sock.on(
'close', this._socketClose.bind(
this));
295 this._sock.on(
'message', this._handleMessage.bind(
this));
296 this._sock.on(
'error', this._socketError.bind(
this));
298 this._expectedClientWidth =
null;
299 this._expectedClientHeight =
null;
300 this._resizeObserver =
new ResizeObserver(this._eventHandlers.handleResize);
303 this._updateConnectionState(
'connecting');
305 Log.Debug(
"<< RFB.constructor");
309 this.dragViewport =
false;
310 this.focusOnClick =
true;
312 this._viewOnly =
false;
313 this._clipViewport =
false;
314 this._clippingViewport =
false;
315 this._scaleViewport =
false;
316 this._resizeSession =
false;
318 this._showDotCursor =
false;
320 this._qualityLevel = 6;
321 this._compressionLevel = 2;
326 get viewOnly() {
return this._viewOnly; }
327 set viewOnly(viewOnly) {
328 this._viewOnly = viewOnly;
330 if (this._rfbConnectionState ===
"connecting" ||
331 this._rfbConnectionState ===
"connected") {
333 this._keyboard.ungrab();
335 this._keyboard.grab();
340 get capabilities() {
return this._capabilities; }
342 get clippingViewport() {
return this._clippingViewport; }
343 _setClippingViewport(on) {
344 if (on === this._clippingViewport) {
347 this._clippingViewport = on;
348 this.dispatchEvent(
new CustomEvent(
"clippingviewport",
349 { detail: this._clippingViewport }));
352 get touchButton() {
return 0; }
353 set touchButton(button) { Log.Warn(
"Using old API!"); }
355 get clipViewport() {
return this._clipViewport; }
356 set clipViewport(viewport) {
357 this._clipViewport = viewport;
361 get scaleViewport() {
return this._scaleViewport; }
362 set scaleViewport(scale) {
363 this._scaleViewport = scale;
366 if (scale && this._clipViewport) {
370 if (!scale && this._clipViewport) {
375 get resizeSession() {
return this._resizeSession; }
376 set resizeSession(resize) {
377 this._resizeSession = resize;
379 this._requestRemoteResize();
383 get showDotCursor() {
return this._showDotCursor; }
384 set showDotCursor(show) {
385 this._showDotCursor = show;
386 this._refreshCursor();
389 get background() {
return this._screen.style.background; }
390 set background(cssValue) { this._screen.style.background = cssValue; }
393 return this._qualityLevel;
395 set qualityLevel(qualityLevel) {
396 if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) {
397 Log.Error(
"qualityLevel must be an integer between 0 and 9");
401 if (this._qualityLevel === qualityLevel) {
405 this._qualityLevel = qualityLevel;
407 if (this._rfbConnectionState ===
'connected') {
408 this._sendEncodings();
412 get compressionLevel() {
413 return this._compressionLevel;
415 set compressionLevel(compressionLevel) {
416 if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
417 Log.Error(
"compressionLevel must be an integer between 0 and 9");
421 if (this._compressionLevel === compressionLevel) {
425 this._compressionLevel = compressionLevel;
427 if (this._rfbConnectionState ===
'connected') {
428 this._sendEncodings();
435 this._updateConnectionState(
'disconnecting');
436 this._sock.off(
'error');
437 this._sock.off(
'message');
438 this._sock.off(
'open');
439 if (this._rfbRSAAESAuthenticationState !==
null) {
440 this._rfbRSAAESAuthenticationState.disconnect();
445 if (this._rfbRSAAESAuthenticationState !==
null) {
446 this._rfbRSAAESAuthenticationState.approveServer();
450 sendCredentials(creds) {
451 this._rfbCredentials = creds;
452 this._resumeAuthentication();
456 if (this._rfbConnectionState !==
'connected' || this._viewOnly) {
return; }
457 Log.Info(
"Sending Ctrl-Alt-Del");
459 this.sendKey(KeyTable.XK_Control_L,
"ControlLeft",
true);
460 this.sendKey(KeyTable.XK_Alt_L,
"AltLeft",
true);
461 this.sendKey(KeyTable.XK_Delete,
"Delete",
true);
462 this.sendKey(KeyTable.XK_Delete,
"Delete",
false);
463 this.sendKey(KeyTable.XK_Alt_L,
"AltLeft",
false);
464 this.sendKey(KeyTable.XK_Control_L,
"ControlLeft",
false);
481 sendKey(keysym, code, down) {
482 if (this._rfbConnectionState !==
'connected' || this._viewOnly) {
return; }
484 if (down === undefined) {
485 this.sendKey(keysym, code,
true);
486 this.sendKey(keysym, code,
false);
490 const scancode = XtScancode[code];
492 if (this._qemuExtKeyEventSupported && scancode) {
494 keysym = keysym || 0;
496 Log.Info(
"Sending key (" + (down ?
"down" :
"up") +
"): keysym " + keysym +
", scancode " + scancode);
498 RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
503 Log.Info(
"Sending keysym (" + (down ?
"down" :
"up") +
"): " + keysym);
504 RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
509 this._canvas.focus(options);
516 clipboardPasteFrom(text) {
517 if (this._rfbConnectionState !==
'connected' || this._viewOnly) {
return; }
519 if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&
520 this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
522 this._clipboardText = text;
523 RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
530 for (let codePoint of text) {
534 data =
new Uint8Array(length);
537 for (let codePoint of text) {
538 let code = codePoint.codePointAt(0);
548 RFB.messages.clientCutText(this._sock, data);
553 return this._display.getImageData();
556 toDataURL(type, encoderOptions) {
557 return this._display.toDataURL(type, encoderOptions);
560 toBlob(callback, type, quality) {
561 return this._display.toBlob(callback, type, quality);
567 Log.Debug(
">> RFB.connect");
570 Log.Info(`connecting to ${this._url}`);
571 this._sock.open(this._url, this._wsProtocols);
573 Log.Info(`attaching ${this._rawChannel} to Websock`);
574 this._sock.attach(this._rawChannel);
576 if (this._sock.readyState ===
'closed') {
577 throw Error(
"Cannot use already closed WebSocket/RTCDataChannel");
580 if (this._sock.readyState ===
'open') {
589 if (!this._overlap) {
590 this._target.appendChild(this._screen);
593 this._gestures.attach(this._canvas);
595 this._cursor.attach(this._canvas);
596 this._refreshCursor();
599 this._resizeObserver.observe(this._screen);
602 this._canvas.addEventListener(
"mousedown", this._eventHandlers.focusCanvas);
603 this._canvas.addEventListener(
"touchstart", this._eventHandlers.focusCanvas);
606 this._canvas.addEventListener(
'mousedown', this._eventHandlers.handleMouse);
607 this._canvas.addEventListener(
'mouseup', this._eventHandlers.handleMouse);
608 this._canvas.addEventListener(
'mousemove', this._eventHandlers.handleMouse);
610 this._canvas.addEventListener(
'click', this._eventHandlers.handleMouse);
613 this._canvas.addEventListener(
'contextmenu', this._eventHandlers.handleMouse);
616 this._canvas.addEventListener(
"wheel", this._eventHandlers.handleWheel);
619 this._canvas.addEventListener(
"gesturestart", this._eventHandlers.handleGesture);
620 this._canvas.addEventListener(
"gesturemove", this._eventHandlers.handleGesture);
621 this._canvas.addEventListener(
"gestureend", this._eventHandlers.handleGesture);
623 Log.Debug(
"<< RFB.connect");
627 Log.Debug(
">> RFB.disconnect");
628 this._cursor.detach();
629 this._canvas.removeEventListener(
"gesturestart", this._eventHandlers.handleGesture);
630 this._canvas.removeEventListener(
"gesturemove", this._eventHandlers.handleGesture);
631 this._canvas.removeEventListener(
"gestureend", this._eventHandlers.handleGesture);
632 this._canvas.removeEventListener(
"wheel", this._eventHandlers.handleWheel);
633 this._canvas.removeEventListener(
'mousedown', this._eventHandlers.handleMouse);
634 this._canvas.removeEventListener(
'mouseup', this._eventHandlers.handleMouse);
635 this._canvas.removeEventListener(
'mousemove', this._eventHandlers.handleMouse);
636 this._canvas.removeEventListener(
'click', this._eventHandlers.handleMouse);
637 this._canvas.removeEventListener(
'contextmenu', this._eventHandlers.handleMouse);
638 this._canvas.removeEventListener(
"mousedown", this._eventHandlers.focusCanvas);
639 this._canvas.removeEventListener(
"touchstart", this._eventHandlers.focusCanvas);
640 this._resizeObserver.disconnect();
641 this._keyboard.ungrab();
642 this._gestures.detach();
645 if (!this._overlap) {
646 this._target.removeChild(this._screen);
649 if (e.name ===
'NotFoundError') {
656 clearTimeout(this._resizeTimeout);
657 clearTimeout(this._mouseMoveTimer);
658 Log.Debug(
"<< RFB.disconnect");
662 if ((this._rfbConnectionState ===
'connecting') &&
663 (this._rfbInitState ===
'')) {
664 this._rfbInitState =
'ProtocolVersion';
665 Log.Debug(
"Starting VNC handshake");
667 this._fail(
"Unexpected server connection while " +
668 this._rfbConnectionState);
673 Log.Debug(
"WebSocket on-close event");
676 msg =
"(code: " + e.code;
678 msg +=
", reason: " + e.reason;
682 switch (this._rfbConnectionState) {
684 this._fail(
"Connection closed " + msg);
688 this._updateConnectionState(
'disconnecting');
689 this._updateConnectionState(
'disconnected');
691 case 'disconnecting':
693 this._updateConnectionState(
'disconnected');
696 this._fail(
"Unexpected server disconnect " +
697 "when already disconnected " + msg);
700 this._fail(
"Unexpected server disconnect before connecting " +
704 this._sock.off(
'close');
706 this._rawChannel =
null;
710 Log.Warn(
"WebSocket on-error event");
713 _focusCanvas(event) {
714 if (!this.focusOnClick) {
718 this.focus({ preventScroll:
true });
721 _setDesktopName(name) {
723 this.dispatchEvent(
new CustomEvent(
725 { detail: { name: this._fbName } }));
728 _saveExpectedClientSize() {
729 this._expectedClientWidth = this._screen.clientWidth;
730 this._expectedClientHeight = this._screen.clientHeight;
733 _currentClientSize() {
734 return [this._screen.clientWidth, this._screen.clientHeight];
737 _clientHasExpectedSize() {
738 const [currentWidth, currentHeight] = this._currentClientSize();
739 return currentWidth == this._expectedClientWidth &&
740 currentHeight == this._expectedClientHeight;
746 if (this._clientHasExpectedSize()) {
751 window.requestAnimationFrame(() => {
754 this._saveExpectedClientSize();
759 this._requestRemoteResize();
765 const curClip = this._display.clipViewport;
766 let newClip = this._clipViewport;
768 if (this._scaleViewport) {
773 if (curClip !== newClip) {
774 this._display.clipViewport = newClip;
780 const size = this._screenSize();
781 this._display.viewportChangeSize(size.w, size.h);
782 this._fixScrollbars();
783 this._setClippingViewport(size.w <
this._display.width ||
784 size.h <
this._display.height);
786 this._setClippingViewport(
false);
791 if (curClip !== newClip) {
792 this._saveExpectedClientSize();
797 if (!this._scaleViewport) {
798 this._display.scale = 1.0;
800 const size = this._screenSize();
801 this._display.autoscale(size.w, size.h);
803 this._fixScrollbars();
808 _requestRemoteResize() {
809 if (!this._resizeSession) {
812 if (this._viewOnly) {
815 if (!this._supportsSetDesktopSize) {
820 if (this._pendingRemoteResize) {
825 if ((Date.now() -
this._lastResize) < 100) {
826 clearTimeout(this._resizeTimeout);
827 this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(
this),
828 100 - (Date.now() -
this._lastResize));
831 this._resizeTimeout =
null;
833 const size = this._screenSize();
836 if (size.w ===
this._fbWidth && size.h ===
this._fbHeight) {
840 this._pendingRemoteResize =
true;
841 this._lastResize = Date.now();
842 RFB.messages.setDesktopSize(this._sock,
843 Math.floor(size.w), Math.floor(size.h),
844 this._screenID,
this._screenFlags);
846 Log.Debug(
'Requested new desktop size: ' +
847 size.w +
'x' + size.h);
852 let r = this._screen.getBoundingClientRect();
853 return { w: r.width, h: r.height };
861 const orig = this._screen.style.overflow;
862 this._screen.style.overflow =
'hidden';
865 this._screen.getBoundingClientRect();
866 this._screen.style.overflow = orig;
876 _updateConnectionState(state) {
877 const oldstate = this._rfbConnectionState;
879 if (state === oldstate) {
880 Log.Debug(
"Already in state '" + state +
"', ignoring");
885 if (oldstate ===
'disconnected') {
886 Log.Error(
"Tried changing state of a disconnected RFB object");
893 if (oldstate !==
'connecting') {
894 Log.Error(
"Bad transition to connected state, " +
895 "previous connection state: " + oldstate);
901 if (oldstate !==
'disconnecting') {
902 Log.Error(
"Bad transition to disconnected state, " +
903 "previous connection state: " + oldstate);
909 if (oldstate !==
'') {
910 Log.Error(
"Bad transition to connecting state, " +
911 "previous connection state: " + oldstate);
916 case 'disconnecting':
917 if (oldstate !==
'connected' && oldstate !==
'connecting') {
918 Log.Error(
"Bad transition to disconnecting state, " +
919 "previous connection state: " + oldstate);
925 Log.Error(
"Unknown connection state: " + state);
931 this._rfbConnectionState = state;
933 Log.Debug(
"New state '" + state +
"', was '" + oldstate +
"'.");
935 if (this._disconnTimer && state !==
'disconnecting') {
936 Log.Debug(
"Clearing disconnect timer");
937 clearTimeout(this._disconnTimer);
938 this._disconnTimer =
null;
941 this._sock.off(
'close');
950 this.dispatchEvent(
new CustomEvent(
"connect", { detail: {} }));
953 case 'disconnecting':
956 this._disconnTimer = setTimeout(() => {
957 Log.Error(
"Disconnection timed out.");
958 this._updateConnectionState(
'disconnected');
959 }, DISCONNECT_TIMEOUT * 1000);
963 this.dispatchEvent(
new CustomEvent(
964 "disconnect", { detail:
965 { clean: this._rfbCleanDisconnect } }));
976 switch (this._rfbConnectionState) {
977 case 'disconnecting':
978 Log.Error(
"Failed when disconnecting: " + details);
981 Log.Error(
"Failed while connected: " + details);
984 Log.Error(
"Failed when connecting: " + details);
987 Log.Error(
"RFB failure: " + details);
990 this._rfbCleanDisconnect =
false;
993 this._updateConnectionState(
'disconnecting');
994 this._updateConnectionState(
'disconnected');
999 _setCapability(cap, val) {
1000 this._capabilities[cap] = val;
1001 this.dispatchEvent(
new CustomEvent(
"capabilities",
1002 { detail: { capabilities: this._capabilities } }));
1006 if (this._sock.rQwait(
"message", 1)) {
1007 Log.Warn(
"handleMessage called on an empty receive queue");
1011 switch (this._rfbConnectionState) {
1012 case 'disconnected':
1013 Log.Error(
"Got data while disconnected");
1017 if (this._flushing) {
1020 if (!this._normalMsg()) {
1023 if (this._sock.rQwait(
"message", 1)) {
1029 while (this._rfbConnectionState ===
'connecting') {
1030 if (!this._initMsg()) {
1036 Log.Error(
"Got data while in an invalid state");
1041 _handleKeyEvent(keysym, code, down, numlock, capslock) {
1047 if (code ==
'CapsLock' && down) {
1048 this._remoteCapsLock =
null;
1050 if (this._remoteCapsLock !==
null && capslock !==
null && this._remoteCapsLock !== capslock && down) {
1051 Log.Debug(
"Fixing remote caps lock");
1053 this.sendKey(KeyTable.XK_Caps_Lock,
'CapsLock',
true);
1054 this.sendKey(KeyTable.XK_Caps_Lock,
'CapsLock',
false);
1057 this._remoteCapsLock =
null;
1061 if (code ==
'NumLock' && down) {
1062 this._remoteNumLock =
null;
1064 if (this._remoteNumLock !==
null && numlock !==
null && this._remoteNumLock !== numlock && down) {
1065 Log.Debug(
"Fixing remote num lock");
1066 this.sendKey(KeyTable.XK_Num_Lock,
'NumLock',
true);
1067 this.sendKey(KeyTable.XK_Num_Lock,
'NumLock',
false);
1068 this._remoteNumLock =
null;
1070 this.sendKey(keysym, code, down);
1073 static _convertButtonMask(buttons) {
1086 const buttonMaskMap = {
1095 for (let i = 0; i < 5; i++) {
1096 if (buttons & (1 << i)) {
1097 bmask |= buttonMaskMap[i];
1109 if (ev.type ===
'click') {
1115 if (ev.target !==
this._canvas) {
1122 ev.stopPropagation();
1123 ev.preventDefault();
1125 if ((ev.type ===
'click') || (ev.type ===
'contextmenu')) {
1129 let pos = clientToElement(ev.clientX, ev.clientY,
1132 let bmask =
RFB._convertButtonMask(ev.buttons);
1134 let down = ev.type ==
'mousedown';
1138 if (this.dragViewport) {
1139 if (down && !this._viewportDragging) {
1140 this._viewportDragging =
true;
1141 this._viewportDragPos = {
'x': pos.x,
'y': pos.y};
1142 this._viewportHasMoved =
false;
1144 this._flushMouseMoveTimer(pos.x, pos.y);
1148 this._mouseButtonMask = bmask;
1151 this._viewportDragging =
false;
1155 if (this._viewportHasMoved) {
1156 this._mouseButtonMask = bmask;
1163 this._sendMouse(pos.x, pos.y,
this._mouseButtonMask);
1167 setCapture(this._canvas);
1169 this._handleMouseButton(pos.x, pos.y, bmask);
1172 if (this._viewportDragging) {
1173 const deltaX = this._viewportDragPos.x - pos.x;
1174 const deltaY = this._viewportDragPos.y - pos.y;
1176 if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
1177 Math.abs(deltaY) > dragThreshold)) {
1178 this._viewportHasMoved =
true;
1180 this._viewportDragPos = {
'x': pos.x,
'y': pos.y};
1181 this._display.viewportChangePos(deltaX, deltaY);
1187 this._handleMouseMove(pos.x, pos.y);
1192 _handleMouseButton(x, y, bmask) {
1194 this._flushMouseMoveTimer(x, y);
1196 this._mouseButtonMask = bmask;
1197 this._sendMouse(x, y, this._mouseButtonMask);
1200 _handleMouseMove(x, y) {
1201 this._mousePos = {
'x': x,
'y': y };
1204 if (this._mouseMoveTimer ==
null) {
1206 const timeSinceLastMove = Date.now() - this._mouseLastMoveTime;
1207 if (timeSinceLastMove > MOUSE_MOVE_DELAY) {
1208 this._sendMouse(x, y, this._mouseButtonMask);
1209 this._mouseLastMoveTime = Date.now();
1212 this._mouseMoveTimer = setTimeout(() => {
1213 this._handleDelayedMouseMove();
1214 }, MOUSE_MOVE_DELAY - timeSinceLastMove);
1219 _handleDelayedMouseMove() {
1220 this._mouseMoveTimer =
null;
1221 this._sendMouse(this._mousePos.x,
this._mousePos.y,
1222 this._mouseButtonMask);
1223 this._mouseLastMoveTime = Date.now();
1226 _sendMouse(x, y, mask) {
1227 if (this._rfbConnectionState !==
'connected') {
return; }
1228 if (this._viewOnly) {
return; }
1231 if (mask & 0x8000) {
1232 throw new Error(
"Illegal mouse button mask (mask: " + mask +
")");
1235 let extendedMouseButtons = mask & 0x7f80;
1237 if (this._extendedPointerEventSupported && extendedMouseButtons) {
1238 RFB.messages.extendedPointerEvent(this._sock, this._display.absX(x),
1239 this._display.absY(y), mask);
1241 RFB.messages.pointerEvent(this._sock, this._display.absX(x),
1242 this._display.absY(y), mask);
1247 if (this._rfbConnectionState !==
'connected') {
return; }
1248 if (this._viewOnly) {
return; }
1250 ev.stopPropagation();
1251 ev.preventDefault();
1253 let pos = clientToElement(ev.clientX, ev.clientY,
1256 let bmask =
RFB._convertButtonMask(ev.buttons);
1265 if (ev.deltaMode !== 0) {
1266 dX *= WHEEL_LINE_HEIGHT;
1267 dY *= WHEEL_LINE_HEIGHT;
1273 this._accumulatedWheelDeltaX += dX;
1274 this._accumulatedWheelDeltaY += dY;
1279 if (Math.abs(
this._accumulatedWheelDeltaX) >= WHEEL_STEP) {
1280 if (this._accumulatedWheelDeltaX < 0) {
1281 this._handleMouseButton(pos.x, pos.y, bmask | 1 << 5);
1282 this._handleMouseButton(pos.x, pos.y, bmask);
1283 }
else if (this._accumulatedWheelDeltaX > 0) {
1284 this._handleMouseButton(pos.x, pos.y, bmask | 1 << 6);
1285 this._handleMouseButton(pos.x, pos.y, bmask);
1288 this._accumulatedWheelDeltaX = 0;
1290 if (Math.abs(
this._accumulatedWheelDeltaY) >= WHEEL_STEP) {
1291 if (this._accumulatedWheelDeltaY < 0) {
1292 this._handleMouseButton(pos.x, pos.y, bmask | 1 << 3);
1293 this._handleMouseButton(pos.x, pos.y, bmask);
1294 }
else if (this._accumulatedWheelDeltaY > 0) {
1295 this._handleMouseButton(pos.x, pos.y, bmask | 1 << 4);
1296 this._handleMouseButton(pos.x, pos.y, bmask);
1299 this._accumulatedWheelDeltaY = 0;
1303 _fakeMouseMove(ev, elementX, elementY) {
1304 this._handleMouseMove(elementX, elementY);
1305 this._cursor.move(ev.detail.clientX, ev.detail.clientY);
1308 _handleTapEvent(ev, bmask) {
1309 let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
1315 if ((this._gestureLastTapTime !==
null) &&
1316 ((Date.now() -
this._gestureLastTapTime) < DOUBLE_TAP_TIMEOUT) &&
1317 (
this._gestureFirstDoubleTapEv.detail.type === ev.detail.type)) {
1318 let dx = this._gestureFirstDoubleTapEv.detail.clientX - ev.detail.clientX;
1319 let dy = this._gestureFirstDoubleTapEv.detail.clientY - ev.detail.clientY;
1320 let distance = Math.hypot(dx, dy);
1322 if (distance < DOUBLE_TAP_THRESHOLD) {
1323 pos = clientToElement(this._gestureFirstDoubleTapEv.detail.clientX,
1324 this._gestureFirstDoubleTapEv.detail.clientY,
1327 this._gestureFirstDoubleTapEv = ev;
1330 this._gestureFirstDoubleTapEv = ev;
1332 this._gestureLastTapTime = Date.now();
1334 this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
1335 this._handleMouseButton(pos.x, pos.y, bmask);
1336 this._handleMouseButton(pos.x, pos.y, 0x0);
1339 _handleGesture(ev) {
1342 let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
1345 case 'gesturestart':
1346 switch (ev.detail.type) {
1348 this._handleTapEvent(ev, 0x1);
1351 this._handleTapEvent(ev, 0x4);
1354 this._handleTapEvent(ev, 0x2);
1357 if (this.dragViewport) {
1358 this._viewportHasMoved =
false;
1359 this._viewportDragging =
true;
1360 this._viewportDragPos = {
'x': pos.x,
'y': pos.y};
1362 this._fakeMouseMove(ev, pos.x, pos.y);
1363 this._handleMouseButton(pos.x, pos.y, 0x1);
1367 if (this.dragViewport) {
1371 this._viewportHasMoved =
false;
1372 this._viewportDragPos = {
'x': pos.x,
'y': pos.y};
1374 this._fakeMouseMove(ev, pos.x, pos.y);
1375 this._handleMouseButton(pos.x, pos.y, 0x4);
1379 this._gestureLastMagnitudeX = ev.detail.magnitudeX;
1380 this._gestureLastMagnitudeY = ev.detail.magnitudeY;
1381 this._fakeMouseMove(ev, pos.x, pos.y);
1384 this._gestureLastMagnitudeX = Math.hypot(ev.detail.magnitudeX,
1385 ev.detail.magnitudeY);
1386 this._fakeMouseMove(ev, pos.x, pos.y);
1392 switch (ev.detail.type) {
1399 if (this.dragViewport) {
1400 this._viewportDragging =
true;
1401 const deltaX = this._viewportDragPos.x - pos.x;
1402 const deltaY = this._viewportDragPos.y - pos.y;
1404 if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
1405 Math.abs(deltaY) > dragThreshold)) {
1406 this._viewportHasMoved =
true;
1408 this._viewportDragPos = {
'x': pos.x,
'y': pos.y};
1409 this._display.viewportChangePos(deltaX, deltaY);
1412 this._fakeMouseMove(ev, pos.x, pos.y);
1419 this._fakeMouseMove(ev, pos.x, pos.y);
1420 while ((ev.detail.magnitudeY -
this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
1421 this._handleMouseButton(pos.x, pos.y, 0x8);
1422 this._handleMouseButton(pos.x, pos.y, 0x0);
1423 this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
1425 while ((ev.detail.magnitudeY -
this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
1426 this._handleMouseButton(pos.x, pos.y, 0x10);
1427 this._handleMouseButton(pos.x, pos.y, 0x0);
1428 this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
1430 while ((ev.detail.magnitudeX -
this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
1431 this._handleMouseButton(pos.x, pos.y, 0x20);
1432 this._handleMouseButton(pos.x, pos.y, 0x0);
1433 this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
1435 while ((ev.detail.magnitudeX -
this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
1436 this._handleMouseButton(pos.x, pos.y, 0x40);
1437 this._handleMouseButton(pos.x, pos.y, 0x0);
1438 this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
1445 this._fakeMouseMove(ev, pos.x, pos.y);
1446 magnitude = Math.hypot(ev.detail.magnitudeX, ev.detail.magnitudeY);
1447 if (Math.abs(magnitude -
this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
1448 this._handleKeyEvent(KeyTable.XK_Control_L,
"ControlLeft",
true);
1449 while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
1450 this._handleMouseButton(pos.x, pos.y, 0x8);
1451 this._handleMouseButton(pos.x, pos.y, 0x0);
1452 this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
1454 while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
1455 this._handleMouseButton(pos.x, pos.y, 0x10);
1456 this._handleMouseButton(pos.x, pos.y, 0x0);
1457 this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
1460 this._handleKeyEvent(KeyTable.XK_Control_L,
"ControlLeft",
false);
1466 switch (ev.detail.type) {
1474 if (this.dragViewport) {
1475 this._viewportDragging =
false;
1477 this._fakeMouseMove(ev, pos.x, pos.y);
1478 this._handleMouseButton(pos.x, pos.y, 0x0);
1482 if (this._viewportHasMoved) {
1488 if (this.dragViewport && !this._viewportHasMoved) {
1489 this._fakeMouseMove(ev, pos.x, pos.y);
1493 this._handleMouseButton(pos.x, pos.y, 0x4);
1494 this._handleMouseButton(pos.x, pos.y, 0x0);
1495 this._viewportDragging =
false;
1497 this._fakeMouseMove(ev, pos.x, pos.y);
1498 this._handleMouseButton(pos.x, pos.y, 0x0);
1506 _flushMouseMoveTimer(x, y) {
1507 if (this._mouseMoveTimer !==
null) {
1508 clearTimeout(this._mouseMoveTimer);
1509 this._mouseMoveTimer =
null;
1510 this._sendMouse(x, y, this._mouseButtonMask);
1516 _negotiateProtocolVersion() {
1517 if (this._sock.rQwait(
"version", 12)) {
1521 const sversion = this._sock.rQshiftStr(12).substr(4, 7);
1522 Log.Info(
"Server ProtocolVersion: " + sversion);
1530 this._rfbVersion = 3.3;
1533 this._rfbVersion = 3.7;
1540 this._rfbVersion = 3.8;
1543 return this._fail(
"Invalid server version " + sversion);
1547 let repeaterID =
"ID:" + this._repeaterID;
1548 while (repeaterID.length < 250) {
1551 this._sock.sQpushString(repeaterID);
1556 if (this._rfbVersion > this._rfbMaxVersion) {
1557 this._rfbVersion = this._rfbMaxVersion;
1560 const cversion =
"00" + parseInt(this._rfbVersion, 10) +
1561 ".00" + ((this._rfbVersion * 10) % 10);
1562 this._sock.sQpushString(
"RFB " + cversion +
"\n");
1564 Log.Debug(
'Sent ProtocolVersion: ' + cversion);
1566 this._rfbInitState =
'Security';
1569 _isSupportedSecurityType(type) {
1570 const clientTypes = [
1572 securityTypeVNCAuth,
1575 securityTypeVeNCrypt,
1578 securityTypeMSLogonII,
1582 return clientTypes.includes(type);
1585 _negotiateSecurity() {
1586 if (this._rfbVersion >= 3.7) {
1588 const numTypes = this._sock.rQshift8();
1589 if (this._sock.rQwait(
"security type", numTypes, 1)) {
return false; }
1591 if (numTypes === 0) {
1592 this._rfbInitState =
"SecurityReason";
1593 this._securityContext =
"no security types";
1594 this._securityStatus = 1;
1598 const types = this._sock.rQshiftBytes(numTypes);
1599 Log.Debug(
"Server security types: " + types);
1603 this._rfbAuthScheme = -1;
1604 for (let type of types) {
1605 if (this._isSupportedSecurityType(type)) {
1606 this._rfbAuthScheme = type;
1611 if (this._rfbAuthScheme === -1) {
1612 return this._fail(
"Unsupported security types (types: " + types +
")");
1615 this._sock.sQpush8(this._rfbAuthScheme);
1619 if (this._sock.rQwait(
"security scheme", 4)) {
return false; }
1620 this._rfbAuthScheme = this._sock.rQshift32();
1622 if (this._rfbAuthScheme == 0) {
1623 this._rfbInitState =
"SecurityReason";
1624 this._securityContext =
"authentication scheme";
1625 this._securityStatus = 1;
1630 this._rfbInitState =
'Authentication';
1631 Log.Debug(
'Authenticating using scheme: ' + this._rfbAuthScheme);
1636 _handleSecurityReason() {
1637 if (this._sock.rQwait(
"reason length", 4)) {
1640 const strlen = this._sock.rQshift32();
1644 if (this._sock.rQwait(
"reason", strlen, 4)) {
return false; }
1645 reason = this._sock.rQshiftStr(strlen);
1648 if (reason !==
"") {
1649 this.dispatchEvent(
new CustomEvent(
1651 { detail: { status: this._securityStatus,
1652 reason: reason } }));
1654 return this._fail(
"Security negotiation failed on " +
1655 this._securityContext +
1656 " (reason: " + reason +
")");
1658 this.dispatchEvent(
new CustomEvent(
1660 { detail: { status: this._securityStatus } }));
1662 return this._fail(
"Security negotiation failed on " +
1663 this._securityContext);
1668 _negotiateXvpAuth() {
1669 if (this._rfbCredentials.username === undefined ||
1670 this._rfbCredentials.password === undefined ||
1671 this._rfbCredentials.target === undefined) {
1672 this.dispatchEvent(
new CustomEvent(
1673 "credentialsrequired",
1674 { detail: { types: [
"username",
"password",
"target"] } }));
1678 this._sock.sQpush8(this._rfbCredentials.username.length);
1679 this._sock.sQpush8(this._rfbCredentials.target.length);
1680 this._sock.sQpushString(this._rfbCredentials.username);
1681 this._sock.sQpushString(this._rfbCredentials.target);
1685 this._rfbAuthScheme = securityTypeVNCAuth;
1687 return this._negotiateAuthentication();
1691 _negotiateVeNCryptAuth() {
1694 if (this._rfbVeNCryptState == 0) {
1695 if (this._sock.rQwait(
"vencrypt version", 2)) {
return false; }
1697 const major = this._sock.rQshift8();
1698 const minor = this._sock.rQshift8();
1700 if (!(major == 0 && minor == 2)) {
1701 return this._fail(
"Unsupported VeNCrypt version " + major +
"." + minor);
1704 this._sock.sQpush8(0);
1705 this._sock.sQpush8(2);
1707 this._rfbVeNCryptState = 1;
1711 if (this._rfbVeNCryptState == 1) {
1712 if (this._sock.rQwait(
"vencrypt ack", 1)) {
return false; }
1714 const res = this._sock.rQshift8();
1717 return this._fail(
"VeNCrypt failure " + res);
1720 this._rfbVeNCryptState = 2;
1725 if (this._rfbVeNCryptState == 2) {
1726 if (this._sock.rQwait(
"vencrypt subtypes length", 1)) {
return false; }
1728 const subtypesLength = this._sock.rQshift8();
1729 if (subtypesLength < 1) {
1730 return this._fail(
"VeNCrypt subtypes empty");
1733 this._rfbVeNCryptSubtypesLength = subtypesLength;
1734 this._rfbVeNCryptState = 3;
1738 if (this._rfbVeNCryptState == 3) {
1739 if (this._sock.rQwait(
"vencrypt subtypes", 4 *
this._rfbVeNCryptSubtypesLength)) {
return false; }
1741 const subtypes = [];
1742 for (let i = 0; i < this._rfbVeNCryptSubtypesLength; i++) {
1743 subtypes.push(this._sock.rQshift32());
1748 this._rfbAuthScheme = -1;
1749 for (let type of subtypes) {
1751 if (type === securityTypeVeNCrypt) {
1755 if (this._isSupportedSecurityType(type)) {
1756 this._rfbAuthScheme = type;
1761 if (this._rfbAuthScheme === -1) {
1762 return this._fail(
"Unsupported security types (types: " + subtypes +
")");
1765 this._sock.sQpush32(this._rfbAuthScheme);
1768 this._rfbVeNCryptState = 4;
1773 _negotiatePlainAuth() {
1774 if (this._rfbCredentials.username === undefined ||
1775 this._rfbCredentials.password === undefined) {
1776 this.dispatchEvent(
new CustomEvent(
1777 "credentialsrequired",
1778 { detail: { types: [
"username",
"password"] } }));
1782 const user = encodeUTF8(this._rfbCredentials.username);
1783 const pass = encodeUTF8(this._rfbCredentials.password);
1785 this._sock.sQpush32(user.length);
1786 this._sock.sQpush32(pass.length);
1787 this._sock.sQpushString(user);
1788 this._sock.sQpushString(pass);
1791 this._rfbInitState =
"SecurityResult";
1795 _negotiateStdVNCAuth() {
1796 if (this._sock.rQwait(
"auth challenge", 16)) {
return false; }
1798 if (this._rfbCredentials.password === undefined) {
1799 this.dispatchEvent(
new CustomEvent(
1800 "credentialsrequired",
1801 { detail: { types: [
"password"] } }));
1806 const challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
1807 const response =
RFB.genDES(this._rfbCredentials.password, challenge);
1808 this._sock.sQpushBytes(response);
1810 this._rfbInitState =
"SecurityResult";
1814 _negotiateARDAuth() {
1816 if (this._rfbCredentials.username === undefined ||
1817 this._rfbCredentials.password === undefined) {
1818 this.dispatchEvent(
new CustomEvent(
1819 "credentialsrequired",
1820 { detail: { types: [
"username",
"password"] } }));
1824 if (this._rfbCredentials.ardPublicKey != undefined &&
1825 this._rfbCredentials.ardCredentials != undefined) {
1827 this._sock.sQpushBytes(this._rfbCredentials.ardCredentials);
1828 this._sock.sQpushBytes(this._rfbCredentials.ardPublicKey);
1830 this._rfbCredentials.ardCredentials =
null;
1831 this._rfbCredentials.ardPublicKey =
null;
1832 this._rfbInitState =
"SecurityResult";
1836 if (this._sock.rQwait(
"read ard", 4)) {
return false; }
1838 let generator = this._sock.rQshiftBytes(2);
1840 let keyLength = this._sock.rQshift16();
1842 if (this._sock.rQwait(
"read ard keylength", keyLength*2, 4)) {
return false; }
1845 let prime = this._sock.rQshiftBytes(keyLength);
1846 let serverPublicKey = this._sock.rQshiftBytes(keyLength);
1848 let clientKey = legacyCrypto.generateKey(
1849 { name:
"DH", g: generator, p: prime },
false, [
"deriveBits"]);
1850 this._negotiateARDAuthAsync(keyLength, serverPublicKey, clientKey);
1855 async _negotiateARDAuthAsync(keyLength, serverPublicKey, clientKey) {
1856 const clientPublicKey = legacyCrypto.exportKey(
"raw", clientKey.publicKey);
1857 const sharedKey = legacyCrypto.deriveBits(
1858 { name:
"DH",
public: serverPublicKey }, clientKey.privateKey, keyLength * 8);
1860 const username = encodeUTF8(this._rfbCredentials.username).substring(0, 63);
1861 const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);
1863 const credentials = window.crypto.getRandomValues(
new Uint8Array(128));
1864 for (let i = 0; i < username.length; i++) {
1865 credentials[i] = username.charCodeAt(i);
1867 credentials[username.length] = 0;
1868 for (let i = 0; i < password.length; i++) {
1869 credentials[64 + i] = password.charCodeAt(i);
1871 credentials[64 + password.length] = 0;
1873 const key = await legacyCrypto.digest(
"MD5", sharedKey);
1874 const cipher = await legacyCrypto.importKey(
1875 "raw", key, { name:
"AES-ECB" },
false, [
"encrypt"]);
1876 const encrypted = await legacyCrypto.encrypt({ name:
"AES-ECB" }, cipher, credentials);
1878 this._rfbCredentials.ardCredentials = encrypted;
1879 this._rfbCredentials.ardPublicKey = clientPublicKey;
1881 this._resumeAuthentication();
1884 _negotiateTightUnixAuth() {
1885 if (this._rfbCredentials.username === undefined ||
1886 this._rfbCredentials.password === undefined) {
1887 this.dispatchEvent(
new CustomEvent(
1888 "credentialsrequired",
1889 { detail: { types: [
"username",
"password"] } }));
1893 this._sock.sQpush32(this._rfbCredentials.username.length);
1894 this._sock.sQpush32(this._rfbCredentials.password.length);
1895 this._sock.sQpushString(this._rfbCredentials.username);
1896 this._sock.sQpushString(this._rfbCredentials.password);
1899 this._rfbInitState =
"SecurityResult";
1903 _negotiateTightTunnels(numTunnels) {
1904 const clientSupportedTunnelTypes = {
1905 0: { vendor:
'TGHT', signature:
'NOTUNNEL' }
1907 const serverSupportedTunnelTypes = {};
1909 for (let i = 0; i < numTunnels; i++) {
1910 const capCode = this._sock.rQshift32();
1911 const capVendor = this._sock.rQshiftStr(4);
1912 const capSignature = this._sock.rQshiftStr(8);
1913 serverSupportedTunnelTypes[capCode] = { vendor: capVendor, signature: capSignature };
1916 Log.Debug(
"Server Tight tunnel types: " + serverSupportedTunnelTypes);
1921 if (serverSupportedTunnelTypes[1] &&
1922 (serverSupportedTunnelTypes[1].vendor ===
"SICR") &&
1923 (serverSupportedTunnelTypes[1].signature ===
"SCHANNEL")) {
1924 Log.Debug(
"Detected Siemens server. Assuming NOTUNNEL support.");
1925 serverSupportedTunnelTypes[0] = { vendor:
'TGHT', signature:
'NOTUNNEL' };
1929 if (serverSupportedTunnelTypes[0]) {
1930 if (serverSupportedTunnelTypes[0].vendor != clientSupportedTunnelTypes[0].vendor ||
1931 serverSupportedTunnelTypes[0].signature != clientSupportedTunnelTypes[0].signature) {
1932 return this._fail(
"Client's tunnel type had the incorrect " +
1933 "vendor or signature");
1935 Log.Debug(
"Selected tunnel type: " + clientSupportedTunnelTypes[0]);
1936 this._sock.sQpush32(0);
1940 return this._fail(
"Server wanted tunnels, but doesn't support " +
1941 "the notunnel type");
1945 _negotiateTightAuth() {
1946 if (!this._rfbTightVNC) {
1947 if (this._sock.rQwait(
"num tunnels", 4)) {
return false; }
1948 const numTunnels = this._sock.rQshift32();
1949 if (numTunnels > 0 && this._sock.rQwait(
"tunnel capabilities", 16 * numTunnels, 4)) {
return false; }
1951 this._rfbTightVNC =
true;
1953 if (numTunnels > 0) {
1954 this._negotiateTightTunnels(numTunnels);
1960 if (this._sock.rQwait(
"sub auth count", 4)) {
return false; }
1961 const subAuthCount = this._sock.rQshift32();
1962 if (subAuthCount === 0) {
1963 this._rfbInitState =
'SecurityResult';
1967 if (this._sock.rQwait(
"sub auth capabilities", 16 * subAuthCount, 4)) {
return false; }
1969 const clientSupportedTypes = {
1975 const serverSupportedTypes = [];
1977 for (let i = 0; i < subAuthCount; i++) {
1978 this._sock.rQshift32();
1979 const capabilities = this._sock.rQshiftStr(12);
1980 serverSupportedTypes.push(capabilities);
1983 Log.Debug(
"Server Tight authentication types: " + serverSupportedTypes);
1985 for (let authType in clientSupportedTypes) {
1986 if (serverSupportedTypes.indexOf(authType) != -1) {
1987 this._sock.sQpush32(clientSupportedTypes[authType]);
1989 Log.Debug(
"Selected authentication type: " + authType);
1992 case 'STDVNOAUTH__':
1993 this._rfbInitState =
'SecurityResult';
1995 case 'STDVVNCAUTH_':
1996 this._rfbAuthScheme = securityTypeVNCAuth;
1998 case 'TGHTULGNAUTH':
1999 this._rfbAuthScheme = securityTypeUnixLogon;
2002 return this._fail(
"Unsupported tiny auth scheme " +
2003 "(scheme: " + authType +
")");
2008 return this._fail(
"No supported sub-auth types!");
2011 _handleRSAAESCredentialsRequired(event) {
2012 this.dispatchEvent(event);
2015 _handleRSAAESServerVerification(event) {
2016 this.dispatchEvent(event);
2019 _negotiateRA2neAuth() {
2020 if (this._rfbRSAAESAuthenticationState ===
null) {
2021 this._rfbRSAAESAuthenticationState =
new RSAAESAuthenticationState(this._sock, () => this._rfbCredentials);
2022 this._rfbRSAAESAuthenticationState.addEventListener(
2023 "serververification", this._eventHandlers.handleRSAAESServerVerification);
2024 this._rfbRSAAESAuthenticationState.addEventListener(
2025 "credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired);
2027 this._rfbRSAAESAuthenticationState.checkInternalEvents();
2028 if (!this._rfbRSAAESAuthenticationState.hasStarted) {
2029 this._rfbRSAAESAuthenticationState.negotiateRA2neAuthAsync()
2031 if (e.message !==
"disconnect normally") {
2032 this._fail(e.message);
2036 this._rfbInitState =
"SecurityResult";
2039 this._rfbRSAAESAuthenticationState.removeEventListener(
2040 "serververification", this._eventHandlers.handleRSAAESServerVerification);
2041 this._rfbRSAAESAuthenticationState.removeEventListener(
2042 "credentialsrequired", this._eventHandlers.handleRSAAESCredentialsRequired);
2043 this._rfbRSAAESAuthenticationState =
null;
2049 _negotiateMSLogonIIAuth() {
2050 if (this._sock.rQwait(
"mslogonii dh param", 24)) {
return false; }
2052 if (this._rfbCredentials.username === undefined ||
2053 this._rfbCredentials.password === undefined) {
2054 this.dispatchEvent(
new CustomEvent(
2055 "credentialsrequired",
2056 { detail: { types: [
"username",
"password"] } }));
2060 const g = this._sock.rQshiftBytes(8);
2061 const p = this._sock.rQshiftBytes(8);
2062 const A = this._sock.rQshiftBytes(8);
2063 const dhKey = legacyCrypto.generateKey({ name:
"DH", g: g, p: p },
true, [
"deriveBits"]);
2064 const B = legacyCrypto.exportKey(
"raw", dhKey.publicKey);
2065 const secret = legacyCrypto.deriveBits({ name:
"DH",
public: A }, dhKey.privateKey, 64);
2067 const key = legacyCrypto.importKey(
"raw", secret, { name:
"DES-CBC" },
false, [
"encrypt"]);
2068 const username = encodeUTF8(this._rfbCredentials.username).substring(0, 255);
2069 const password = encodeUTF8(this._rfbCredentials.password).substring(0, 63);
2070 let usernameBytes =
new Uint8Array(256);
2071 let passwordBytes =
new Uint8Array(64);
2072 window.crypto.getRandomValues(usernameBytes);
2073 window.crypto.getRandomValues(passwordBytes);
2074 for (let i = 0; i < username.length; i++) {
2075 usernameBytes[i] = username.charCodeAt(i);
2077 usernameBytes[username.length] = 0;
2078 for (let i = 0; i < password.length; i++) {
2079 passwordBytes[i] = password.charCodeAt(i);
2081 passwordBytes[password.length] = 0;
2082 usernameBytes = legacyCrypto.encrypt({ name:
"DES-CBC", iv: secret }, key, usernameBytes);
2083 passwordBytes = legacyCrypto.encrypt({ name:
"DES-CBC", iv: secret }, key, passwordBytes);
2084 this._sock.sQpushBytes(B);
2085 this._sock.sQpushBytes(usernameBytes);
2086 this._sock.sQpushBytes(passwordBytes);
2088 this._rfbInitState =
"SecurityResult";
2092 _negotiateAuthentication() {
2093 switch (this._rfbAuthScheme) {
2094 case securityTypeNone:
2095 if (this._rfbVersion >= 3.8) {
2096 this._rfbInitState =
'SecurityResult';
2098 this._rfbInitState =
'ClientInitialisation';
2102 case securityTypeXVP:
2103 return this._negotiateXvpAuth();
2105 case securityTypeARD:
2106 return this._negotiateARDAuth();
2108 case securityTypeVNCAuth:
2109 return this._negotiateStdVNCAuth();
2111 case securityTypeTight:
2112 return this._negotiateTightAuth();
2114 case securityTypeVeNCrypt:
2115 return this._negotiateVeNCryptAuth();
2117 case securityTypePlain:
2118 return this._negotiatePlainAuth();
2120 case securityTypeUnixLogon:
2121 return this._negotiateTightUnixAuth();
2123 case securityTypeRA2ne:
2124 return this._negotiateRA2neAuth();
2126 case securityTypeMSLogonII:
2127 return this._negotiateMSLogonIIAuth();
2130 return this._fail(
"Unsupported auth scheme (scheme: " +
2131 this._rfbAuthScheme +
")");
2135 _handleSecurityResult() {
2136 if (this._sock.rQwait(
'VNC auth response ', 4)) {
return false; }
2138 const status = this._sock.rQshift32();
2141 this._rfbInitState =
'ClientInitialisation';
2142 Log.Debug(
'Authentication OK');
2145 if (this._rfbVersion >= 3.8) {
2146 this._rfbInitState =
"SecurityReason";
2147 this._securityContext =
"security result";
2148 this._securityStatus = status;
2151 this.dispatchEvent(
new CustomEvent(
2153 { detail: { status: status } }));
2155 return this._fail(
"Security handshake failed");
2160 _negotiateServerInit() {
2161 if (this._sock.rQwait(
"server initialization", 24)) {
return false; }
2164 const width = this._sock.rQshift16();
2165 const height = this._sock.rQshift16();
2168 const bpp = this._sock.rQshift8();
2169 const depth = this._sock.rQshift8();
2170 const bigEndian = this._sock.rQshift8();
2171 const trueColor = this._sock.rQshift8();
2173 const redMax = this._sock.rQshift16();
2174 const greenMax = this._sock.rQshift16();
2175 const blueMax = this._sock.rQshift16();
2176 const redShift = this._sock.rQshift8();
2177 const greenShift = this._sock.rQshift8();
2178 const blueShift = this._sock.rQshift8();
2179 this._sock.rQskipBytes(3);
2185 const nameLength = this._sock.rQshift32();
2186 if (this._sock.rQwait(
'server init name', nameLength, 24)) {
return false; }
2187 let name = this._sock.rQshiftStr(nameLength);
2188 name = decodeUTF8(name,
true);
2190 if (this._rfbTightVNC) {
2191 if (this._sock.rQwait(
'TightVNC extended server init header', 8, 24 + nameLength)) {
return false; }
2193 const numServerMessages = this._sock.rQshift16();
2194 const numClientMessages = this._sock.rQshift16();
2195 const numEncodings = this._sock.rQshift16();
2196 this._sock.rQskipBytes(2);
2198 const totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;
2199 if (this._sock.rQwait(
'TightVNC extended server init header', totalMessagesLength, 32 + nameLength)) {
return false; }
2205 this._sock.rQskipBytes(16 * numServerMessages);
2208 this._sock.rQskipBytes(16 * numClientMessages);
2211 this._sock.rQskipBytes(16 * numEncodings);
2216 Log.Info(
"Screen: " + width +
"x" + height +
2217 ", bpp: " + bpp +
", depth: " + depth +
2218 ", bigEndian: " + bigEndian +
2219 ", trueColor: " + trueColor +
2220 ", redMax: " + redMax +
2221 ", greenMax: " + greenMax +
2222 ", blueMax: " + blueMax +
2223 ", redShift: " + redShift +
2224 ", greenShift: " + greenShift +
2225 ", blueShift: " + blueShift);
2228 this._setDesktopName(name);
2229 this._resize(width, height);
2231 if (!this._viewOnly) { this._keyboard.grab(); }
2235 if (this._fbName ===
"Intel(r) AMT KVM") {
2236 Log.Warn(
"Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
2240 RFB.messages.pixelFormat(this._sock, this._fbDepth,
true);
2241 this._sendEncodings();
2242 RFB.messages.fbUpdateRequest(this._sock,
false, 0, 0, this._fbWidth, this._fbHeight);
2244 this._updateConnectionState(
'connected');
2252 encs.push(encodings.encodingCopyRect);
2254 if (this._fbDepth == 24) {
2255 if (supportsWebCodecsH264Decode) {
2256 encs.push(encodings.encodingH264);
2258 encs.push(encodings.encodingTight);
2259 encs.push(encodings.encodingTightPNG);
2260 encs.push(encodings.encodingZRLE);
2261 encs.push(encodings.encodingJPEG);
2262 encs.push(encodings.encodingHextile);
2263 encs.push(encodings.encodingRRE);
2264 encs.push(encodings.encodingZlib);
2266 encs.push(encodings.encodingRaw);
2269 encs.push(encodings.pseudoEncodingQualityLevel0 +
this._qualityLevel);
2270 encs.push(encodings.pseudoEncodingCompressLevel0 +
this._compressionLevel);
2272 encs.push(encodings.pseudoEncodingDesktopSize);
2273 encs.push(encodings.pseudoEncodingLastRect);
2274 encs.push(encodings.pseudoEncodingQEMUExtendedKeyEvent);
2275 encs.push(encodings.pseudoEncodingQEMULedEvent);
2276 encs.push(encodings.pseudoEncodingExtendedDesktopSize);
2277 encs.push(encodings.pseudoEncodingXvp);
2278 encs.push(encodings.pseudoEncodingFence);
2279 encs.push(encodings.pseudoEncodingContinuousUpdates);
2280 encs.push(encodings.pseudoEncodingDesktopName);
2281 encs.push(encodings.pseudoEncodingExtendedClipboard);
2282 encs.push(encodings.pseudoEncodingExtendedMouseButtons);
2284 if (this._fbDepth == 24) {
2285 encs.push(encodings.pseudoEncodingVMwareCursor);
2286 encs.push(encodings.pseudoEncodingCursor);
2289 RFB.messages.clientEncodings(this._sock, encs);
2301 switch (this._rfbInitState) {
2302 case 'ProtocolVersion':
2303 return this._negotiateProtocolVersion();
2306 return this._negotiateSecurity();
2308 case 'Authentication':
2309 return this._negotiateAuthentication();
2311 case 'SecurityResult':
2312 return this._handleSecurityResult();
2314 case 'SecurityReason':
2315 return this._handleSecurityReason();
2317 case 'ClientInitialisation':
2318 this._sock.sQpush8(this._shared ? 1 : 0);
2320 this._rfbInitState =
'ServerInitialisation';
2323 case 'ServerInitialisation':
2324 return this._negotiateServerInit();
2327 return this._fail(
"Unknown init state (state: " +
2328 this._rfbInitState +
")");
2334 _resumeAuthentication() {
2337 setTimeout(this._initMsg.bind(
this), 0);
2340 _handleSetColourMapMsg() {
2341 Log.Debug(
"SetColorMapEntries");
2343 return this._fail(
"Unexpected SetColorMapEntries message");
2346 _handleServerCutText() {
2347 Log.Debug(
"ServerCutText");
2349 if (this._sock.rQwait(
"ServerCutText header", 7, 1)) {
return false; }
2351 this._sock.rQskipBytes(3);
2353 let length = this._sock.rQshift32();
2354 length = toSigned32bit(length);
2356 if (this._sock.rQwait(
"ServerCutText content", Math.abs(length), 8)) {
return false; }
2360 const text = this._sock.rQshiftStr(length);
2361 if (this._viewOnly) {
2365 this.dispatchEvent(
new CustomEvent(
2367 { detail: { text: text } }));
2371 length = Math.abs(length);
2372 const flags = this._sock.rQshift32();
2373 let formats = flags & 0x0000FFFF;
2374 let actions = flags & 0xFF000000;
2376 let isCaps = (!!(actions & extendedClipboardActionCaps));
2378 this._clipboardServerCapabilitiesFormats = {};
2379 this._clipboardServerCapabilitiesActions = {};
2382 for (let i = 0; i <= 15; i++) {
2386 if ((formats & index)) {
2387 this._clipboardServerCapabilitiesFormats[index] =
true;
2390 this._sock.rQshift32();
2395 for (let i = 24; i <= 31; i++) {
2397 this._clipboardServerCapabilitiesActions[index] = !!(actions & index);
2402 let clientActions = [
2403 extendedClipboardActionCaps,
2404 extendedClipboardActionRequest,
2405 extendedClipboardActionPeek,
2406 extendedClipboardActionNotify,
2407 extendedClipboardActionProvide
2409 RFB.messages.extendedClipboardCaps(this._sock, clientActions, {extendedClipboardFormatText: 0});
2411 }
else if (actions === extendedClipboardActionRequest) {
2412 if (this._viewOnly) {
2417 if (this._clipboardText !=
null &&
2418 this._clipboardServerCapabilitiesActions[extendedClipboardActionProvide]) {
2420 if (formats & extendedClipboardFormatText) {
2421 RFB.messages.extendedClipboardProvide(this._sock, [extendedClipboardFormatText], [this._clipboardText]);
2425 }
else if (actions === extendedClipboardActionPeek) {
2426 if (this._viewOnly) {
2430 if (this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
2432 if (this._clipboardText !=
null) {
2433 RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
2435 RFB.messages.extendedClipboardNotify(this._sock, []);
2439 }
else if (actions === extendedClipboardActionNotify) {
2440 if (this._viewOnly) {
2444 if (this._clipboardServerCapabilitiesActions[extendedClipboardActionRequest]) {
2446 if (formats & extendedClipboardFormatText) {
2447 RFB.messages.extendedClipboardRequest(this._sock, [extendedClipboardFormatText]);
2451 }
else if (actions === extendedClipboardActionProvide) {
2452 if (this._viewOnly) {
2456 if (!(formats & extendedClipboardFormatText)) {
2460 this._clipboardText =
null;
2463 let zlibStream = this._sock.rQshiftBytes(length - 4);
2464 let streamInflator =
new Inflator();
2465 let textData =
null;
2467 streamInflator.setInput(zlibStream);
2468 for (let i = 0; i <= 15; i++) {
2469 let format = 1 << i;
2471 if (formats & format) {
2474 let sizeArray = streamInflator.inflate(4);
2476 size |= (sizeArray[0] << 24);
2477 size |= (sizeArray[1] << 16);
2478 size |= (sizeArray[2] << 8);
2479 size |= (sizeArray[3]);
2480 let chunk = streamInflator.inflate(size);
2482 if (format === extendedClipboardFormatText) {
2487 streamInflator.setInput(
null);
2489 if (textData !==
null) {
2491 for (let i = 0; i < textData.length; i++) {
2492 tmpText += String.fromCharCode(textData[i]);
2496 textData = decodeUTF8(textData);
2497 if ((textData.length > 0) &&
"\0" === textData.charAt(textData.length - 1)) {
2498 textData = textData.slice(0, -1);
2501 textData = textData.replaceAll(
"\r\n",
"\n");
2503 this.dispatchEvent(
new CustomEvent(
2505 { detail: { text: textData } }));
2508 return this._fail(
"Unexpected action in extended clipboard message: " + actions);
2514 _handleServerFenceMsg() {
2515 if (this._sock.rQwait(
"ServerFence header", 8, 1)) {
return false; }
2516 this._sock.rQskipBytes(3);
2517 let flags = this._sock.rQshift32();
2518 let length = this._sock.rQshift8();
2520 if (this._sock.rQwait(
"ServerFence payload", length, 9)) {
return false; }
2523 Log.Warn(
"Bad payload length (" + length +
") in fence response");
2527 const payload = this._sock.rQshiftStr(length);
2529 this._supportsFence =
true;
2540 if (!(flags & (1<<31))) {
2541 return this._fail(
"Unexpected fence response");
2546 flags &= (1<<0) | (1<<1);
2551 RFB.messages.clientFence(this._sock, flags, payload);
2557 if (this._sock.rQwait(
"XVP version and message", 3, 1)) {
return false; }
2558 this._sock.rQskipBytes(1);
2559 const xvpVer = this._sock.rQshift8();
2560 const xvpMsg = this._sock.rQshift8();
2564 Log.Error(
"XVP operation failed");
2567 this._rfbXvpVer = xvpVer;
2568 Log.Info(
"XVP extensions enabled (version " + this._rfbXvpVer +
")");
2569 this._setCapability(
"power",
true);
2572 this._fail(
"Illegal server XVP message (msg: " + xvpMsg +
")");
2581 if (this._FBU.rects > 0) {
2584 msgType = this._sock.rQshift8();
2590 ret = this._framebufferUpdate();
2591 if (ret && !this._enabledContinuousUpdates) {
2592 RFB.messages.fbUpdateRequest(this._sock,
true, 0, 0,
2593 this._fbWidth, this._fbHeight);
2598 return this._handleSetColourMapMsg();
2602 this.dispatchEvent(
new CustomEvent(
2608 return this._handleServerCutText();
2611 first = !this._supportsContinuousUpdates;
2612 this._supportsContinuousUpdates =
true;
2613 this._enabledContinuousUpdates =
false;
2615 this._enabledContinuousUpdates =
true;
2616 this._updateContinuousUpdates();
2617 Log.Info(
"Enabling continuous updates.");
2625 return this._handleServerFenceMsg();
2628 return this._handleXvpMsg();
2631 this._fail(
"Unexpected server message (type " + msgType +
")");
2632 Log.Debug(
"sock.rQpeekBytes(30): " + this._sock.rQpeekBytes(30));
2637 _framebufferUpdate() {
2638 if (this._FBU.rects === 0) {
2639 if (this._sock.rQwait(
"FBU header", 3, 1)) {
return false; }
2640 this._sock.rQskipBytes(1);
2641 this._FBU.rects = this._sock.rQshift16();
2645 if (this._display.pending()) {
2646 this._flushing =
true;
2647 this._display.flush()
2649 this._flushing =
false;
2651 if (!this._sock.rQwait(
"message", 1)) {
2652 this._handleMessage();
2659 while (this._FBU.rects > 0) {
2660 if (this._FBU.encoding ===
null) {
2661 if (this._sock.rQwait(
"rect header", 12)) {
return false; }
2664 this._FBU.x = this._sock.rQshift16();
2665 this._FBU.y = this._sock.rQshift16();
2666 this._FBU.width = this._sock.rQshift16();
2667 this._FBU.height = this._sock.rQshift16();
2668 this._FBU.encoding = this._sock.rQshift32();
2670 this._FBU.encoding >>= 0;
2673 if (!this._handleRect()) {
2678 this._FBU.encoding =
null;
2681 this._display.flip();
2687 switch (this._FBU.encoding) {
2688 case encodings.pseudoEncodingLastRect:
2689 this._FBU.rects = 1;
2692 case encodings.pseudoEncodingVMwareCursor:
2693 return this._handleVMwareCursor();
2695 case encodings.pseudoEncodingCursor:
2696 return this._handleCursor();
2698 case encodings.pseudoEncodingQEMUExtendedKeyEvent:
2699 this._qemuExtKeyEventSupported =
true;
2702 case encodings.pseudoEncodingDesktopName:
2703 return this._handleDesktopName();
2705 case encodings.pseudoEncodingDesktopSize:
2706 this._resize(this._FBU.width,
this._FBU.height);
2709 case encodings.pseudoEncodingExtendedDesktopSize:
2710 return this._handleExtendedDesktopSize();
2712 case encodings.pseudoEncodingExtendedMouseButtons:
2713 this._extendedPointerEventSupported =
true;
2716 case encodings.pseudoEncodingQEMULedEvent:
2717 return this._handleLedEvent();
2720 return this._handleDataRect();
2724 _handleVMwareCursor() {
2725 const hotx = this._FBU.x;
2726 const hoty = this._FBU.y;
2727 const w = this._FBU.width;
2728 const h = this._FBU.height;
2729 if (this._sock.rQwait(
"VMware cursor encoding", 1)) {
2733 const cursorType = this._sock.rQshift8();
2735 this._sock.rQshift8();
2738 const bytesPerPixel = 4;
2741 if (cursorType == 0) {
2744 const PIXEL_MASK = 0xffffff00 | 0;
2745 rgba =
new Array(w * h * bytesPerPixel);
2747 if (this._sock.rQwait(
"VMware cursor classic encoding",
2748 (w * h * bytesPerPixel) * 2, 2)) {
2752 let andMask =
new Array(w * h);
2753 for (let pixel = 0; pixel < (w * h); pixel++) {
2754 andMask[pixel] = this._sock.rQshift32();
2757 let xorMask =
new Array(w * h);
2758 for (let pixel = 0; pixel < (w * h); pixel++) {
2759 xorMask[pixel] = this._sock.rQshift32();
2762 for (let pixel = 0; pixel < (w * h); pixel++) {
2763 if (andMask[pixel] == 0) {
2765 let bgr = xorMask[pixel];
2766 let r = bgr >> 8 & 0xff;
2767 let g = bgr >> 16 & 0xff;
2768 let b = bgr >> 24 & 0xff;
2770 rgba[(pixel * bytesPerPixel) ] = r;
2771 rgba[(pixel * bytesPerPixel) + 1 ] = g;
2772 rgba[(pixel * bytesPerPixel) + 2 ] = b;
2773 rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
2775 }
else if ((andMask[pixel] & PIXEL_MASK) ==
2778 if (xorMask[pixel] == 0) {
2780 rgba[(pixel * bytesPerPixel) ] = 0x00;
2781 rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
2782 rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
2783 rgba[(pixel * bytesPerPixel) + 3 ] = 0x00;
2785 }
else if ((xorMask[pixel] & PIXEL_MASK) ==
2789 rgba[(pixel * bytesPerPixel) ] = 0x00;
2790 rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
2791 rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
2792 rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
2796 rgba[(pixel * bytesPerPixel) ] = 0x00;
2797 rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
2798 rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
2799 rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
2804 rgba[(pixel * bytesPerPixel) ] = 0x00;
2805 rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
2806 rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
2807 rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
2812 }
else if (cursorType == 1) {
2813 if (this._sock.rQwait(
"VMware cursor alpha encoding",
2818 rgba =
new Array(w * h * bytesPerPixel);
2820 for (let pixel = 0; pixel < (w * h); pixel++) {
2821 let data = this._sock.rQshift32();
2823 rgba[(pixel * 4) ] = data >> 24 & 0xff;
2824 rgba[(pixel * 4) + 1 ] = data >> 16 & 0xff;
2825 rgba[(pixel * 4) + 2 ] = data >> 8 & 0xff;
2826 rgba[(pixel * 4) + 3 ] = data & 0xff;
2830 Log.Warn(
"The given cursor type is not supported: "
2831 + cursorType +
" given.");
2835 this._updateCursor(rgba, hotx, hoty, w, h);
2841 const hotx = this._FBU.x;
2842 const hoty = this._FBU.y;
2843 const w = this._FBU.width;
2844 const h = this._FBU.height;
2846 const pixelslength = w * h * 4;
2847 const masklength = Math.ceil(w / 8) * h;
2849 let bytes = pixelslength + masklength;
2850 if (this._sock.rQwait(
"cursor encoding", bytes)) {
2855 const pixels = this._sock.rQshiftBytes(pixelslength);
2856 const mask = this._sock.rQshiftBytes(masklength);
2857 let rgba =
new Uint8Array(w * h * 4);
2860 for (let y = 0; y < h; y++) {
2861 for (let x = 0; x < w; x++) {
2862 let maskIdx = y * Math.ceil(w / 8) + Math.floor(x / 8);
2863 let alpha = (mask[maskIdx] << (x % 8)) & 0x80 ? 255 : 0;
2864 rgba[pixIdx ] = pixels[pixIdx + 2];
2865 rgba[pixIdx + 1] = pixels[pixIdx + 1];
2866 rgba[pixIdx + 2] = pixels[pixIdx];
2867 rgba[pixIdx + 3] = alpha;
2872 this._updateCursor(rgba, hotx, hoty, w, h);
2877 _handleDesktopName() {
2878 if (this._sock.rQwait(
"DesktopName", 4)) {
2882 let length = this._sock.rQshift32();
2884 if (this._sock.rQwait(
"DesktopName", length, 4)) {
2888 let name = this._sock.rQshiftStr(length);
2889 name = decodeUTF8(name,
true);
2891 this._setDesktopName(name);
2897 if (this._sock.rQwait(
"LED status", 1)) {
2901 let data = this._sock.rQshift8();
2903 let numLock = data & 2 ? true :
false;
2904 let capsLock = data & 4 ? true :
false;
2905 this._remoteCapsLock = capsLock;
2906 this._remoteNumLock = numLock;
2911 _handleExtendedDesktopSize() {
2912 if (this._sock.rQwait(
"ExtendedDesktopSize", 4)) {
2916 const numberOfScreens = this._sock.rQpeek8();
2918 let bytes = 4 + (numberOfScreens * 16);
2919 if (this._sock.rQwait(
"ExtendedDesktopSize", bytes)) {
2923 const firstUpdate = !this._supportsSetDesktopSize;
2924 this._supportsSetDesktopSize =
true;
2926 this._sock.rQskipBytes(1);
2927 this._sock.rQskipBytes(3);
2929 for (let i = 0; i < numberOfScreens; i += 1) {
2932 this._screenID = this._sock.rQshift32();
2933 this._sock.rQskipBytes(2);
2934 this._sock.rQskipBytes(2);
2935 this._sock.rQskipBytes(2);
2936 this._sock.rQskipBytes(2);
2937 this._screenFlags = this._sock.rQshift32();
2939 this._sock.rQskipBytes(16);
2951 if (this._FBU.x === 1) {
2952 this._pendingRemoteResize =
false;
2956 if (this._FBU.x === 1 &&
this._FBU.y !== 0) {
2959 switch (this._FBU.y) {
2961 msg =
"Resize is administratively prohibited";
2964 msg =
"Out of resources";
2967 msg =
"Invalid screen layout";
2970 msg =
"Unknown reason";
2973 Log.Warn(
"Server did not accept the resize request: "
2976 this._resize(this._FBU.width,
this._FBU.height);
2984 this._requestRemoteResize();
2987 if (this._FBU.x === 1 &&
this._FBU.y === 0) {
2990 this._requestRemoteResize();
2997 let decoder = this._decoders[this._FBU.encoding];
2999 this._fail(
"Unsupported encoding (encoding: " +
3000 this._FBU.encoding +
")");
3005 return decoder.decodeRect(this._FBU.x,
this._FBU.y,
3006 this._FBU.width,
this._FBU.height,
3007 this._sock,
this._display,
3010 this._fail(
"Error decoding rect: " + err);
3015 _updateContinuousUpdates() {
3016 if (!this._enabledContinuousUpdates) {
return; }
3018 RFB.messages.enableContinuousUpdates(this._sock,
true, 0, 0,
3019 this._fbWidth, this._fbHeight);
3023 _resize(width, height) {
3024 this._fbWidth = width;
3025 this._fbHeight = height;
3027 this._display.resize(this._fbWidth, this._fbHeight);
3031 this._updateScale();
3033 this._updateContinuousUpdates();
3036 this._saveExpectedClientSize();
3040 if (this._rfbXvpVer < ver) {
return; }
3041 Log.Info(
"Sending XVP operation " + op +
" (version " + ver +
")");
3042 RFB.messages.xvpOp(this._sock, ver, op);
3045 _updateCursor(rgba, hotx, hoty, w, h) {
3046 this._cursorImage = {
3048 hotx: hotx, hoty: hoty, w: w, h: h,
3050 this._refreshCursor();
3053 _shouldShowDotCursor() {
3055 if (!this._showDotCursor) {
3064 for (let i = 3; i < this._cursorImage.rgbaPixels.length; i += 4) {
3065 if (this._cursorImage.rgbaPixels[i]) {
3076 if (this._rfbConnectionState !==
"connecting" &&
3077 this._rfbConnectionState !==
"connected") {
3080 const image = this._shouldShowDotCursor() ?
RFB.cursors.dot : this._cursorImage;
3081 this._cursor.change(image.rgbaPixels,
3082 image.hotx, image.hoty,
3087 static genDES(password, challenge) {
3088 const passwordChars = password.split(
'').map(c => c.charCodeAt(0));
3089 const key = legacyCrypto.importKey(
3090 "raw", passwordChars, { name:
"DES-ECB" },
false, [
"encrypt"]);
3091 return legacyCrypto.encrypt({ name:
"DES-ECB" }, key, challenge);