@novnc/novnc 1.3.0-g1691617 → 1.3.0-g1ff2ecd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +7 -2
  2. package/core/decoders/jpeg.js +141 -0
  3. package/core/decoders/raw.js +1 -1
  4. package/core/decoders/zrle.js +185 -0
  5. package/core/des.js +1 -1
  6. package/core/display.js +12 -0
  7. package/core/encodings.js +4 -0
  8. package/core/input/keyboard.js +10 -0
  9. package/core/ra2.js +567 -0
  10. package/core/rfb.js +466 -81
  11. package/core/util/browser.js +56 -7
  12. package/core/util/cursor.js +4 -0
  13. package/core/util/md5.js +79 -0
  14. package/docs/API.md +263 -154
  15. package/lib/base64.js +20 -34
  16. package/lib/decoders/copyrect.js +5 -12
  17. package/lib/decoders/hextile.js +17 -47
  18. package/lib/decoders/jpeg.js +149 -0
  19. package/lib/decoders/raw.js +10 -23
  20. package/lib/decoders/rre.js +5 -16
  21. package/lib/decoders/tight.js +13 -79
  22. package/lib/decoders/tightpng.js +8 -28
  23. package/lib/decoders/zrle.js +188 -0
  24. package/lib/deflator.js +9 -23
  25. package/lib/des.js +24 -37
  26. package/lib/display.js +62 -108
  27. package/lib/encodings.js +7 -8
  28. package/lib/inflator.js +6 -19
  29. package/lib/input/domkeytable.js +77 -48
  30. package/lib/input/fixedkeys.js +8 -3
  31. package/lib/input/gesturehandler.js +86 -153
  32. package/lib/input/keyboard.js +62 -91
  33. package/lib/input/keysym.js +14 -270
  34. package/lib/input/keysymdef.js +5 -7
  35. package/lib/input/util.js +43 -85
  36. package/lib/input/vkeys.js +0 -3
  37. package/lib/input/xtscancodes.js +1 -168
  38. package/lib/ra2.js +1005 -0
  39. package/lib/rfb.js +790 -920
  40. package/lib/util/browser.js +66 -29
  41. package/lib/util/cursor.js +29 -66
  42. package/lib/util/element.js +3 -5
  43. package/lib/util/events.js +23 -30
  44. package/lib/util/eventtarget.js +5 -14
  45. package/lib/util/int.js +1 -2
  46. package/lib/util/logging.js +1 -19
  47. package/lib/util/md5.js +77 -0
  48. package/lib/util/strings.js +3 -5
  49. package/lib/vendor/pako/lib/utils/common.js +8 -17
  50. package/lib/vendor/pako/lib/zlib/adler32.js +3 -7
  51. package/lib/vendor/pako/lib/zlib/constants.js +2 -5
  52. package/lib/vendor/pako/lib/zlib/crc32.js +5 -12
  53. package/lib/vendor/pako/lib/zlib/deflate.js +213 -618
  54. package/lib/vendor/pako/lib/zlib/gzheader.js +1 -13
  55. package/lib/vendor/pako/lib/zlib/inffast.js +60 -176
  56. package/lib/vendor/pako/lib/zlib/inflate.js +398 -888
  57. package/lib/vendor/pako/lib/zlib/inftrees.js +63 -169
  58. package/lib/vendor/pako/lib/zlib/messages.js +1 -11
  59. package/lib/vendor/pako/lib/zlib/trees.js +246 -588
  60. package/lib/vendor/pako/lib/zlib/zstream.js +2 -18
  61. package/lib/websock.js +37 -88
  62. package/package.json +32 -35
package/README.md CHANGED
@@ -128,13 +128,17 @@ Running the command below will install the latest release of noVNC from Snap:
128
128
 
129
129
  `sudo snap install novnc`
130
130
 
131
- #### Running noVNC
131
+ #### Running noVNC from Snap Directly
132
132
 
133
133
  You can run the Snap-package installed novnc directly with, for example:
134
134
 
135
135
  `novnc --listen 6081 --vnc localhost:5901 # /snap/bin/novnc if /snap/bin is not in your PATH`
136
136
 
137
- #### Running as a Service (Daemon)
137
+ If you want to use certificate files, due to standard Snap confinement restrictions you need to have them in the /home/\<user\>/snap/novnc/current/ directory. If your username is jsmith an example command would be:
138
+
139
+ `novnc --listen 8443 --cert ~jsmith/snap/novnc/current/self.crt --key ~jsmith/snap/novnc/current/self.key --vnc ubuntu.example.com:5901`
140
+
141
+ #### Running noVNC from Snap as a Service (Daemon)
138
142
  The Snap package also has the capability to run a 'novnc' service which can be
139
143
  configured to listen on multiple ports connecting to multiple VNC servers
140
144
  (effectively a service runing multiple instances of novnc).
@@ -208,6 +212,7 @@ that list and you think you should be, feel free to send a PR to fix that.
208
212
  * UI and Icons : Pierre Ossman, Chris Gordon
209
213
  * Original Logo : Michael Sersen
210
214
  * tight encoding : Michael Tinglof (Mercuri.ca)
215
+ * RealVNC RSA AES authentication : USTC Vlab Team
211
216
 
212
217
  * Included libraries:
213
218
  * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
@@ -0,0 +1,141 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2019 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
+
10
+ export default class JPEGDecoder {
11
+ constructor() {
12
+ // RealVNC will reuse the quantization tables
13
+ // and Huffman tables, so we need to cache them.
14
+ this._quantTables = [];
15
+ this._huffmanTables = [];
16
+ this._cachedQuantTables = [];
17
+ this._cachedHuffmanTables = [];
18
+
19
+ this._jpegLength = 0;
20
+ this._segments = [];
21
+ }
22
+
23
+ decodeRect(x, y, width, height, sock, display, depth) {
24
+ // A rect of JPEG encodings is simply a JPEG file
25
+ if (!this._parseJPEG(sock.rQslice(0))) {
26
+ return false;
27
+ }
28
+ const data = sock.rQshiftBytes(this._jpegLength);
29
+ if (this._quantTables.length != 0 && this._huffmanTables.length != 0) {
30
+ // If there are quantization tables and Huffman tables in the JPEG
31
+ // image, we can directly render it.
32
+ display.imageRect(x, y, width, height, "image/jpeg", data);
33
+ return true;
34
+ } else {
35
+ // Otherwise we need to insert cached tables.
36
+ const sofIndex = this._segments.findIndex(
37
+ x => x[1] == 0xC0 || x[1] == 0xC2
38
+ );
39
+ if (sofIndex == -1) {
40
+ throw new Error("Illegal JPEG image without SOF");
41
+ }
42
+ let segments = this._segments.slice(0, sofIndex);
43
+ segments = segments.concat(this._quantTables.length ?
44
+ this._quantTables :
45
+ this._cachedQuantTables);
46
+ segments.push(this._segments[sofIndex]);
47
+ segments = segments.concat(this._huffmanTables.length ?
48
+ this._huffmanTables :
49
+ this._cachedHuffmanTables,
50
+ this._segments.slice(sofIndex + 1));
51
+ let length = 0;
52
+ for (let i = 0; i < segments.length; i++) {
53
+ length += segments[i].length;
54
+ }
55
+ const data = new Uint8Array(length);
56
+ length = 0;
57
+ for (let i = 0; i < segments.length; i++) {
58
+ data.set(segments[i], length);
59
+ length += segments[i].length;
60
+ }
61
+ display.imageRect(x, y, width, height, "image/jpeg", data);
62
+ return true;
63
+ }
64
+ }
65
+
66
+ _parseJPEG(buffer) {
67
+ if (this._quantTables.length != 0) {
68
+ this._cachedQuantTables = this._quantTables;
69
+ }
70
+ if (this._huffmanTables.length != 0) {
71
+ this._cachedHuffmanTables = this._huffmanTables;
72
+ }
73
+ this._quantTables = [];
74
+ this._huffmanTables = [];
75
+ this._segments = [];
76
+ let i = 0;
77
+ let bufferLength = buffer.length;
78
+ while (true) {
79
+ let j = i;
80
+ if (j + 2 > bufferLength) {
81
+ return false;
82
+ }
83
+ if (buffer[j] != 0xFF) {
84
+ throw new Error("Illegal JPEG marker received (byte: " +
85
+ buffer[j] + ")");
86
+ }
87
+ const type = buffer[j+1];
88
+ j += 2;
89
+ if (type == 0xD9) {
90
+ this._jpegLength = j;
91
+ this._segments.push(buffer.slice(i, j));
92
+ return true;
93
+ } else if (type == 0xDA) {
94
+ // start of scan
95
+ let hasFoundEndOfScan = false;
96
+ for (let k = j + 3; k + 1 < bufferLength; k++) {
97
+ if (buffer[k] == 0xFF && buffer[k+1] != 0x00 &&
98
+ !(buffer[k+1] >= 0xD0 && buffer[k+1] <= 0xD7)) {
99
+ j = k;
100
+ hasFoundEndOfScan = true;
101
+ break;
102
+ }
103
+ }
104
+ if (!hasFoundEndOfScan) {
105
+ return false;
106
+ }
107
+ this._segments.push(buffer.slice(i, j));
108
+ i = j;
109
+ continue;
110
+ } else if (type >= 0xD0 && type < 0xD9 || type == 0x01) {
111
+ // No length after marker
112
+ this._segments.push(buffer.slice(i, j));
113
+ i = j;
114
+ continue;
115
+ }
116
+ if (j + 2 > bufferLength) {
117
+ return false;
118
+ }
119
+ const length = (buffer[j] << 8) + buffer[j+1] - 2;
120
+ if (length < 0) {
121
+ throw new Error("Illegal JPEG length received (length: " +
122
+ length + ")");
123
+ }
124
+ j += 2;
125
+ if (j + length > bufferLength) {
126
+ return false;
127
+ }
128
+ j += length;
129
+ const segment = buffer.slice(i, j);
130
+ if (type == 0xC4) {
131
+ // Huffman tables
132
+ this._huffmanTables.push(segment);
133
+ } else if (type == 0xDB) {
134
+ // Quantization tables
135
+ this._quantTables.push(segment);
136
+ }
137
+ this._segments.push(segment);
138
+ i = j;
139
+ }
140
+ }
141
+ }
@@ -51,7 +51,7 @@ export default class RawDecoder {
51
51
 
52
52
  // Max sure the image is fully opaque
53
53
  for (let i = 0; i < pixels; i++) {
54
- data[i * 4 + 3] = 255;
54
+ data[index + i * 4 + 3] = 255;
55
55
  }
56
56
 
57
57
  display.blitImage(x, curY, width, currHeight, data, index);
@@ -0,0 +1,185 @@
1
+ /*
2
+ * noVNC: HTML5 VNC client
3
+ * Copyright (C) 2021 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
+
10
+ import Inflate from "../inflator.js";
11
+
12
+ const ZRLE_TILE_WIDTH = 64;
13
+ const ZRLE_TILE_HEIGHT = 64;
14
+
15
+ export default class ZRLEDecoder {
16
+ constructor() {
17
+ this._length = 0;
18
+ this._inflator = new Inflate();
19
+
20
+ this._pixelBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
21
+ this._tileBuffer = new Uint8Array(ZRLE_TILE_WIDTH * ZRLE_TILE_HEIGHT * 4);
22
+ }
23
+
24
+ decodeRect(x, y, width, height, sock, display, depth) {
25
+ if (this._length === 0) {
26
+ if (sock.rQwait("ZLib data length", 4)) {
27
+ return false;
28
+ }
29
+ this._length = sock.rQshift32();
30
+ }
31
+ if (sock.rQwait("Zlib data", this._length)) {
32
+ return false;
33
+ }
34
+
35
+ const data = sock.rQshiftBytes(this._length);
36
+
37
+ this._inflator.setInput(data);
38
+
39
+ for (let ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) {
40
+ let th = Math.min(ZRLE_TILE_HEIGHT, y + height - ty);
41
+
42
+ for (let tx = x; tx < x + width; tx += ZRLE_TILE_WIDTH) {
43
+ let tw = Math.min(ZRLE_TILE_WIDTH, x + width - tx);
44
+
45
+ const tileSize = tw * th;
46
+ const subencoding = this._inflator.inflate(1)[0];
47
+ if (subencoding === 0) {
48
+ // raw data
49
+ const data = this._readPixels(tileSize);
50
+ display.blitImage(tx, ty, tw, th, data, 0, false);
51
+ } else if (subencoding === 1) {
52
+ // solid
53
+ const background = this._readPixels(1);
54
+ display.fillRect(tx, ty, tw, th, [background[0], background[1], background[2]]);
55
+ } else if (subencoding >= 2 && subencoding <= 16) {
56
+ const data = this._decodePaletteTile(subencoding, tileSize, tw, th);
57
+ display.blitImage(tx, ty, tw, th, data, 0, false);
58
+ } else if (subencoding === 128) {
59
+ const data = this._decodeRLETile(tileSize);
60
+ display.blitImage(tx, ty, tw, th, data, 0, false);
61
+ } else if (subencoding >= 130 && subencoding <= 255) {
62
+ const data = this._decodeRLEPaletteTile(subencoding - 128, tileSize);
63
+ display.blitImage(tx, ty, tw, th, data, 0, false);
64
+ } else {
65
+ throw new Error('Unknown subencoding: ' + subencoding);
66
+ }
67
+ }
68
+ }
69
+ this._length = 0;
70
+ return true;
71
+ }
72
+
73
+ _getBitsPerPixelInPalette(paletteSize) {
74
+ if (paletteSize <= 2) {
75
+ return 1;
76
+ } else if (paletteSize <= 4) {
77
+ return 2;
78
+ } else if (paletteSize <= 16) {
79
+ return 4;
80
+ }
81
+ }
82
+
83
+ _readPixels(pixels) {
84
+ let data = this._pixelBuffer;
85
+ const buffer = this._inflator.inflate(3*pixels);
86
+ for (let i = 0, j = 0; i < pixels*4; i += 4, j += 3) {
87
+ data[i] = buffer[j];
88
+ data[i + 1] = buffer[j + 1];
89
+ data[i + 2] = buffer[j + 2];
90
+ data[i + 3] = 255; // Add the Alpha
91
+ }
92
+ return data;
93
+ }
94
+
95
+ _decodePaletteTile(paletteSize, tileSize, tilew, tileh) {
96
+ const data = this._tileBuffer;
97
+ const palette = this._readPixels(paletteSize);
98
+ const bitsPerPixel = this._getBitsPerPixelInPalette(paletteSize);
99
+ const mask = (1 << bitsPerPixel) - 1;
100
+
101
+ let offset = 0;
102
+ let encoded = this._inflator.inflate(1)[0];
103
+
104
+ for (let y=0; y<tileh; y++) {
105
+ let shift = 8-bitsPerPixel;
106
+ for (let x=0; x<tilew; x++) {
107
+ if (shift<0) {
108
+ shift=8-bitsPerPixel;
109
+ encoded = this._inflator.inflate(1)[0];
110
+ }
111
+ let indexInPalette = (encoded>>shift) & mask;
112
+
113
+ data[offset] = palette[indexInPalette * 4];
114
+ data[offset + 1] = palette[indexInPalette * 4 + 1];
115
+ data[offset + 2] = palette[indexInPalette * 4 + 2];
116
+ data[offset + 3] = palette[indexInPalette * 4 + 3];
117
+ offset += 4;
118
+ shift-=bitsPerPixel;
119
+ }
120
+ if (shift<8-bitsPerPixel && y<tileh-1) {
121
+ encoded = this._inflator.inflate(1)[0];
122
+ }
123
+ }
124
+ return data;
125
+ }
126
+
127
+ _decodeRLETile(tileSize) {
128
+ const data = this._tileBuffer;
129
+ let i = 0;
130
+ while (i < tileSize) {
131
+ const pixel = this._readPixels(1);
132
+ const length = this._readRLELength();
133
+ for (let j = 0; j < length; j++) {
134
+ data[i * 4] = pixel[0];
135
+ data[i * 4 + 1] = pixel[1];
136
+ data[i * 4 + 2] = pixel[2];
137
+ data[i * 4 + 3] = pixel[3];
138
+ i++;
139
+ }
140
+ }
141
+ return data;
142
+ }
143
+
144
+ _decodeRLEPaletteTile(paletteSize, tileSize) {
145
+ const data = this._tileBuffer;
146
+
147
+ // palette
148
+ const palette = this._readPixels(paletteSize);
149
+
150
+ let offset = 0;
151
+ while (offset < tileSize) {
152
+ let indexInPalette = this._inflator.inflate(1)[0];
153
+ let length = 1;
154
+ if (indexInPalette >= 128) {
155
+ indexInPalette -= 128;
156
+ length = this._readRLELength();
157
+ }
158
+ if (indexInPalette > paletteSize) {
159
+ throw new Error('Too big index in palette: ' + indexInPalette + ', palette size: ' + paletteSize);
160
+ }
161
+ if (offset + length > tileSize) {
162
+ throw new Error('Too big rle length in palette mode: ' + length + ', allowed length is: ' + (tileSize - offset));
163
+ }
164
+
165
+ for (let j = 0; j < length; j++) {
166
+ data[offset * 4] = palette[indexInPalette * 4];
167
+ data[offset * 4 + 1] = palette[indexInPalette * 4 + 1];
168
+ data[offset * 4 + 2] = palette[indexInPalette * 4 + 2];
169
+ data[offset * 4 + 3] = palette[indexInPalette * 4 + 3];
170
+ offset++;
171
+ }
172
+ }
173
+ return data;
174
+ }
175
+
176
+ _readRLELength() {
177
+ let length = 0;
178
+ let current = 0;
179
+ do {
180
+ current = this._inflator.inflate(1)[0];
181
+ length += current;
182
+ } while (current === 255);
183
+ return length + 1;
184
+ }
185
+ }
package/core/des.js CHANGED
@@ -81,7 +81,7 @@
81
81
  const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
82
82
  25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
83
83
  50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
84
- totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
84
+ totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
85
85
 
86
86
  const z = 0x0;
87
87
  let a,b,c,d,e,f;
package/core/display.js CHANGED
@@ -224,6 +224,18 @@ export default class Display {
224
224
  this.viewportChangePos(0, 0);
225
225
  }
226
226
 
227
+ getImageData() {
228
+ return this._drawCtx.getImageData(0, 0, this.width, this.height);
229
+ }
230
+
231
+ toDataURL(type, encoderOptions) {
232
+ return this._backbuffer.toDataURL(type, encoderOptions);
233
+ }
234
+
235
+ toBlob(callback, type, quality) {
236
+ return this._backbuffer.toBlob(callback, type, quality);
237
+ }
238
+
227
239
  // Track what parts of the visible canvas that need updating
228
240
  _damage(x, y, w, h) {
229
241
  if (x < this._damageBounds.left) {
package/core/encodings.js CHANGED
@@ -12,7 +12,9 @@ export const encodings = {
12
12
  encodingRRE: 2,
13
13
  encodingHextile: 5,
14
14
  encodingTight: 7,
15
+ encodingZRLE: 16,
15
16
  encodingTightPNG: -260,
17
+ encodingJPEG: 21,
16
18
 
17
19
  pseudoEncodingQualityLevel9: -23,
18
20
  pseudoEncodingQualityLevel0: -32,
@@ -38,7 +40,9 @@ export function encodingName(num) {
38
40
  case encodings.encodingRRE: return "RRE";
39
41
  case encodings.encodingHextile: return "Hextile";
40
42
  case encodings.encodingTight: return "Tight";
43
+ case encodings.encodingZRLE: return "ZRLE";
41
44
  case encodings.encodingTightPNG: return "TightPNG";
45
+ case encodings.encodingJPEG: return "JPEG";
42
46
  default: return "[unknown encoding " + num + "]";
43
47
  }
44
48
  }
@@ -153,6 +153,16 @@ export default class Keyboard {
153
153
  keysym = this._keyDownList[code];
154
154
  }
155
155
 
156
+ // macOS doesn't send proper key releases if a key is pressed
157
+ // while meta is held down
158
+ if ((browser.isMac() || browser.isIOS()) &&
159
+ (e.metaKey && code !== 'MetaLeft' && code !== 'MetaRight')) {
160
+ this._sendKeyEvent(keysym, code, true);
161
+ this._sendKeyEvent(keysym, code, false);
162
+ stopEvent(e);
163
+ return;
164
+ }
165
+
156
166
  // macOS doesn't send proper key events for modifiers, only
157
167
  // state change events. That gets extra confusing for CapsLock
158
168
  // which toggles on each press, but not on release. So pretend