@novnc/novnc
Advanced tools
Comparing version 1.4.0-gc2d6a06 to 1.4.0-gca6527c
@@ -34,6 +34,3 @@ /* | ||
let rQ = sock.rQ; | ||
let rQi = sock.rQi; | ||
let subencoding = rQ[rQi]; // Peek | ||
let subencoding = sock.rQpeek8(); | ||
if (subencoding > 30) { // Raw | ||
@@ -69,3 +66,3 @@ throw new Error("Illegal hextile subencoding (subencoding: " + | ||
let subrects = rQ[rQi + bytes - 1]; // Peek | ||
let subrects = sock.rQpeekBytes(bytes).at(-1); | ||
if (subencoding & 0x10) { // SubrectsColoured | ||
@@ -84,3 +81,3 @@ bytes += subrects * (4 + 2); | ||
// We know the encoding and have a whole tile | ||
rQi++; | ||
sock.rQshift8(); | ||
if (subencoding === 0) { | ||
@@ -95,16 +92,14 @@ if (this._lastsubencoding & 0x01) { | ||
let pixels = tw * th; | ||
let data = sock.rQshiftBytes(pixels * 4, false); | ||
// Max sure the image is fully opaque | ||
for (let i = 0;i < pixels;i++) { | ||
rQ[rQi + i * 4 + 3] = 255; | ||
data[i * 4 + 3] = 255; | ||
} | ||
display.blitImage(tx, ty, tw, th, rQ, rQi); | ||
rQi += bytes - 1; | ||
display.blitImage(tx, ty, tw, th, data, 0); | ||
} else { | ||
if (subencoding & 0x02) { // Background | ||
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
this._background = new Uint8Array(sock.rQshiftBytes(4)); | ||
} | ||
if (subencoding & 0x04) { // Foreground | ||
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
this._foreground = new Uint8Array(sock.rQshiftBytes(4)); | ||
} | ||
@@ -114,4 +109,3 @@ | ||
if (subencoding & 0x08) { // AnySubrects | ||
let subrects = rQ[rQi]; | ||
rQi++; | ||
let subrects = sock.rQshift8(); | ||
@@ -121,14 +115,11 @@ for (let s = 0; s < subrects; s++) { | ||
if (subencoding & 0x10) { // SubrectsColoured | ||
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
color = sock.rQshiftBytes(4); | ||
} else { | ||
color = this._foreground; | ||
} | ||
const xy = rQ[rQi]; | ||
rQi++; | ||
const xy = sock.rQshift8(); | ||
const sx = (xy >> 4); | ||
const sy = (xy & 0x0f); | ||
const wh = rQ[rQi]; | ||
rQi++; | ||
const wh = sock.rQshift8(); | ||
const sw = (wh >> 4) + 1; | ||
@@ -142,3 +133,2 @@ const sh = (wh & 0x0f) + 1; | ||
} | ||
sock.rQi = rQi; | ||
this._lastsubencoding = subencoding; | ||
@@ -145,0 +135,0 @@ this._tiles--; |
@@ -14,8 +14,5 @@ /* | ||
// and Huffman tables, so we need to cache them. | ||
this._quantTables = []; | ||
this._huffmanTables = []; | ||
this._cachedQuantTables = []; | ||
this._cachedHuffmanTables = []; | ||
this._jpegLength = 0; | ||
this._segments = []; | ||
@@ -26,118 +23,126 @@ } | ||
// A rect of JPEG encodings is simply a JPEG file | ||
if (!this._parseJPEG(sock.rQslice(0))) { | ||
return false; | ||
} | ||
const data = sock.rQshiftBytes(this._jpegLength); | ||
if (this._quantTables.length != 0 && this._huffmanTables.length != 0) { | ||
// If there are quantization tables and Huffman tables in the JPEG | ||
// image, we can directly render it. | ||
display.imageRect(x, y, width, height, "image/jpeg", data); | ||
return true; | ||
} else { | ||
// Otherwise we need to insert cached tables. | ||
const sofIndex = this._segments.findIndex( | ||
x => x[1] == 0xC0 || x[1] == 0xC2 | ||
); | ||
if (sofIndex == -1) { | ||
throw new Error("Illegal JPEG image without SOF"); | ||
while (true) { | ||
let segment = this._readSegment(sock); | ||
if (segment === null) { | ||
return false; | ||
} | ||
let segments = this._segments.slice(0, sofIndex); | ||
segments = segments.concat(this._quantTables.length ? | ||
this._quantTables : | ||
this._cachedQuantTables); | ||
segments.push(this._segments[sofIndex]); | ||
segments = segments.concat(this._huffmanTables.length ? | ||
this._huffmanTables : | ||
this._cachedHuffmanTables, | ||
this._segments.slice(sofIndex + 1)); | ||
let length = 0; | ||
for (let i = 0; i < segments.length; i++) { | ||
length += segments[i].length; | ||
this._segments.push(segment); | ||
// End of image? | ||
if (segment[1] === 0xD9) { | ||
break; | ||
} | ||
const data = new Uint8Array(length); | ||
length = 0; | ||
for (let i = 0; i < segments.length; i++) { | ||
data.set(segments[i], length); | ||
length += segments[i].length; | ||
} | ||
let huffmanTables = []; | ||
let quantTables = []; | ||
for (let segment of this._segments) { | ||
let type = segment[1]; | ||
if (type === 0xC4) { | ||
// Huffman tables | ||
huffmanTables.push(segment); | ||
} else if (type === 0xDB) { | ||
// Quantization tables | ||
quantTables.push(segment); | ||
} | ||
display.imageRect(x, y, width, height, "image/jpeg", data); | ||
return true; | ||
} | ||
const sofIndex = this._segments.findIndex( | ||
x => x[1] == 0xC0 || x[1] == 0xC2 | ||
); | ||
if (sofIndex == -1) { | ||
throw new Error("Illegal JPEG image without SOF"); | ||
} | ||
if (quantTables.length === 0) { | ||
this._segments.splice(sofIndex+1, 0, | ||
...this._cachedQuantTables); | ||
} | ||
if (huffmanTables.length === 0) { | ||
this._segments.splice(sofIndex+1, 0, | ||
...this._cachedHuffmanTables); | ||
} | ||
let length = 0; | ||
for (let segment of this._segments) { | ||
length += segment.length; | ||
} | ||
let data = new Uint8Array(length); | ||
length = 0; | ||
for (let segment of this._segments) { | ||
data.set(segment, length); | ||
length += segment.length; | ||
} | ||
display.imageRect(x, y, width, height, "image/jpeg", data); | ||
if (huffmanTables.length !== 0) { | ||
this._cachedHuffmanTables = huffmanTables; | ||
} | ||
if (quantTables.length !== 0) { | ||
this._cachedQuantTables = quantTables; | ||
} | ||
this._segments = []; | ||
return true; | ||
} | ||
_parseJPEG(buffer) { | ||
if (this._quantTables.length != 0) { | ||
this._cachedQuantTables = this._quantTables; | ||
_readSegment(sock) { | ||
if (sock.rQwait("JPEG", 2)) { | ||
return null; | ||
} | ||
if (this._huffmanTables.length != 0) { | ||
this._cachedHuffmanTables = this._huffmanTables; | ||
let marker = sock.rQshift8(); | ||
if (marker != 0xFF) { | ||
throw new Error("Illegal JPEG marker received (byte: " + | ||
marker + ")"); | ||
} | ||
this._quantTables = []; | ||
this._huffmanTables = []; | ||
this._segments = []; | ||
let i = 0; | ||
let bufferLength = buffer.length; | ||
while (true) { | ||
let j = i; | ||
if (j + 2 > bufferLength) { | ||
return false; | ||
} | ||
if (buffer[j] != 0xFF) { | ||
throw new Error("Illegal JPEG marker received (byte: " + | ||
buffer[j] + ")"); | ||
} | ||
const type = buffer[j+1]; | ||
j += 2; | ||
if (type == 0xD9) { | ||
this._jpegLength = j; | ||
this._segments.push(buffer.slice(i, j)); | ||
return true; | ||
} else if (type == 0xDA) { | ||
// start of scan | ||
let hasFoundEndOfScan = false; | ||
for (let k = j + 3; k + 1 < bufferLength; k++) { | ||
if (buffer[k] == 0xFF && buffer[k+1] != 0x00 && | ||
!(buffer[k+1] >= 0xD0 && buffer[k+1] <= 0xD7)) { | ||
j = k; | ||
hasFoundEndOfScan = true; | ||
break; | ||
} | ||
let type = sock.rQshift8(); | ||
if (type >= 0xD0 && type <= 0xD9 || type == 0x01) { | ||
// No length after marker | ||
return new Uint8Array([marker, type]); | ||
} | ||
if (sock.rQwait("JPEG", 2, 2)) { | ||
return null; | ||
} | ||
let length = sock.rQshift16(); | ||
if (length < 2) { | ||
throw new Error("Illegal JPEG length received (length: " + | ||
length + ")"); | ||
} | ||
if (sock.rQwait("JPEG", length-2, 4)) { | ||
return null; | ||
} | ||
let extra = 0; | ||
if (type === 0xDA) { | ||
// start of scan | ||
extra += 2; | ||
while (true) { | ||
if (sock.rQwait("JPEG", length-2+extra, 4)) { | ||
return null; | ||
} | ||
if (!hasFoundEndOfScan) { | ||
return false; | ||
let data = sock.rQpeekBytes(length-2+extra, false); | ||
if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 && | ||
!(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) { | ||
extra -= 2; | ||
break; | ||
} | ||
this._segments.push(buffer.slice(i, j)); | ||
i = j; | ||
continue; | ||
} else if (type >= 0xD0 && type < 0xD9 || type == 0x01) { | ||
// No length after marker | ||
this._segments.push(buffer.slice(i, j)); | ||
i = j; | ||
continue; | ||
extra++; | ||
} | ||
if (j + 2 > bufferLength) { | ||
return false; | ||
} | ||
const length = (buffer[j] << 8) + buffer[j+1] - 2; | ||
if (length < 0) { | ||
throw new Error("Illegal JPEG length received (length: " + | ||
length + ")"); | ||
} | ||
j += 2; | ||
if (j + length > bufferLength) { | ||
return false; | ||
} | ||
j += length; | ||
const segment = buffer.slice(i, j); | ||
if (type == 0xC4) { | ||
// Huffman tables | ||
this._huffmanTables.push(segment); | ||
} else if (type == 0xDB) { | ||
// Quantization tables | ||
this._quantTables.push(segment); | ||
} | ||
this._segments.push(segment); | ||
i = j; | ||
} | ||
let segment = new Uint8Array(2 + length + extra); | ||
segment[0] = marker; | ||
segment[1] = type; | ||
segment[2] = length >> 8; | ||
segment[3] = length; | ||
segment.set(sock.rQshiftBytes(length-2+extra, false), 4); | ||
return segment; | ||
} | ||
} |
@@ -27,37 +27,30 @@ /* | ||
if (sock.rQwait("RAW", bytesPerLine)) { | ||
return false; | ||
} | ||
while (this._lines > 0) { | ||
if (sock.rQwait("RAW", bytesPerLine)) { | ||
return false; | ||
} | ||
const curY = y + (height - this._lines); | ||
const currHeight = Math.min(this._lines, | ||
Math.floor(sock.rQlen / bytesPerLine)); | ||
const pixels = width * currHeight; | ||
const curY = y + (height - this._lines); | ||
let data = sock.rQ; | ||
let index = sock.rQi; | ||
let data = sock.rQshiftBytes(bytesPerLine, false); | ||
// Convert data if needed | ||
if (depth == 8) { | ||
const newdata = new Uint8Array(pixels * 4); | ||
for (let i = 0; i < pixels; i++) { | ||
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 3] = 255; | ||
// Convert data if needed | ||
if (depth == 8) { | ||
const newdata = new Uint8Array(width * 4); | ||
for (let i = 0; i < width; i++) { | ||
newdata[i * 4 + 0] = ((data[i] >> 0) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 1] = ((data[i] >> 2) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 2] = ((data[i] >> 4) & 0x3) * 255 / 3; | ||
newdata[i * 4 + 3] = 255; | ||
} | ||
data = newdata; | ||
} | ||
data = newdata; | ||
index = 0; | ||
} | ||
// Max sure the image is fully opaque | ||
for (let i = 0; i < pixels; i++) { | ||
data[index + i * 4 + 3] = 255; | ||
} | ||
// Max sure the image is fully opaque | ||
for (let i = 0; i < width; i++) { | ||
data[i * 4 + 3] = 255; | ||
} | ||
display.blitImage(x, curY, width, currHeight, data, index); | ||
sock.rQskipBytes(currHeight * bytesPerLine); | ||
this._lines -= currHeight; | ||
if (this._lines > 0) { | ||
return false; | ||
display.blitImage(x, curY, width, 1, data, 0); | ||
this._lines--; | ||
} | ||
@@ -64,0 +57,0 @@ |
@@ -79,9 +79,5 @@ /* | ||
const rQi = sock.rQi; | ||
const rQ = sock.rQ; | ||
let pixel = sock.rQshiftBytes(3); | ||
display.fillRect(x, y, width, height, pixel, false); | ||
display.fillRect(x, y, width, height, | ||
[rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false); | ||
sock.rQskipBytes(3); | ||
return true; | ||
@@ -320,3 +316,3 @@ } | ||
let data = sock.rQshiftBytes(this._len); | ||
let data = sock.rQshiftBytes(this._len, false); | ||
this._len = 0; | ||
@@ -323,0 +319,0 @@ |
@@ -35,3 +35,3 @@ /* | ||
const data = sock.rQshiftBytes(this._length); | ||
const data = sock.rQshiftBytes(this._length, false); | ||
@@ -38,0 +38,0 @@ this._inflator.setInput(data); |
@@ -18,3 +18,3 @@ /* | ||
this._renderQ = []; // queue drawing actions for in-oder rendering | ||
this._flushing = false; | ||
this._flushPromise = null; | ||
@@ -65,6 +65,2 @@ // the full frame buffer (logical canvas) size | ||
this._clipViewport = false; | ||
// ===== EVENT HANDLERS ===== | ||
this.onflush = () => {}; // A flush request has finished | ||
} | ||
@@ -311,5 +307,10 @@ | ||
if (this._renderQ.length === 0) { | ||
this.onflush(); | ||
return Promise.resolve(); | ||
} else { | ||
this._flushing = true; | ||
if (this._flushPromise === null) { | ||
this._flushPromise = new Promise((resolve) => { | ||
this._flushResolve = resolve; | ||
}); | ||
} | ||
return this._flushPromise; | ||
} | ||
@@ -523,7 +524,9 @@ } | ||
if (this._renderQ.length === 0 && this._flushing) { | ||
this._flushing = false; | ||
this.onflush(); | ||
if (this._renderQ.length === 0 && | ||
this._flushPromise !== null) { | ||
this._flushResolve(); | ||
this._flushPromise = null; | ||
this._flushResolve = null; | ||
} | ||
} | ||
} |
@@ -142,3 +142,3 @@ import { encodeUTF8 } from './util/strings.js'; | ||
await this._waitSockAsync(4); | ||
const serverKeyLengthBuffer = this._sock.rQslice(0, 4); | ||
const serverKeyLengthBuffer = this._sock.rQpeekBytes(4); | ||
const serverKeyLength = this._sock.rQshift32(); | ||
@@ -162,6 +162,7 @@ if (serverKeyLength < 1024) { | ||
// verify server public key | ||
let approveKey = this._waitApproveKeyAsync(); | ||
this.dispatchEvent(new CustomEvent("serververification", { | ||
detail: { type: "RSA", publickey: serverPublickey } | ||
})); | ||
await this._waitApproveKeyAsync(); | ||
await approveKey; | ||
@@ -186,3 +187,4 @@ // 2: Send client public key | ||
clientPublicKey.set(clientE, 4 + clientKeyBytes); | ||
this._sock.send(clientPublicKey); | ||
this._sock.sQpushBytes(clientPublicKey); | ||
this._sock.flush(); | ||
@@ -198,3 +200,4 @@ // 3: Send client random | ||
clientRandomMessage.set(clientEncryptedRandom, 2); | ||
this._sock.send(clientRandomMessage); | ||
this._sock.sQpushBytes(clientRandomMessage); | ||
this._sock.flush(); | ||
@@ -240,3 +243,4 @@ // 4: Receive server random | ||
clientHash = new Uint8Array(clientHash); | ||
this._sock.send(await clientCipher.makeMessage(clientHash)); | ||
this._sock.sQpushBytes(await clientCipher.makeMessage(clientHash)); | ||
this._sock.flush(); | ||
await this._waitSockAsync(2 + 20 + 16); | ||
@@ -268,2 +272,3 @@ if (this._sock.rQshift16() !== 20) { | ||
subtype = subtype[0]; | ||
let waitCredentials = this._waitCredentialsAsync(subtype); | ||
if (subtype === 1) { | ||
@@ -285,3 +290,3 @@ if (this._getCredentials().username === undefined || | ||
} | ||
await this._waitCredentialsAsync(subtype); | ||
await waitCredentials; | ||
let username; | ||
@@ -303,3 +308,4 @@ if (subtype === 1) { | ||
} | ||
this._sock.send(await clientCipher.makeMessage(credentials)); | ||
this._sock.sQpushBytes(await clientCipher.makeMessage(credentials)); | ||
this._sock.flush(); | ||
} | ||
@@ -306,0 +312,0 @@ |
@@ -97,23 +97,3 @@ /* | ||
get sQ() { | ||
return this._sQ; | ||
} | ||
get rQ() { | ||
return this._rQ; | ||
} | ||
get rQi() { | ||
return this._rQi; | ||
} | ||
set rQi(val) { | ||
this._rQi = val; | ||
} | ||
// Receive Queue | ||
get rQlen() { | ||
return this._rQlen - this._rQi; | ||
} | ||
rQpeek8() { | ||
@@ -145,11 +125,10 @@ return this._rQ[this._rQi]; | ||
} | ||
return res; | ||
return res >>> 0; | ||
} | ||
rQshiftStr(len) { | ||
if (typeof(len) === 'undefined') { len = this.rQlen; } | ||
let str = ""; | ||
// Handle large arrays in steps to avoid long strings on the stack | ||
for (let i = 0; i < len; i += 4096) { | ||
let part = this.rQshiftBytes(Math.min(4096, len - i)); | ||
let part = this.rQshiftBytes(Math.min(4096, len - i), false); | ||
str += String.fromCharCode.apply(null, part); | ||
@@ -160,10 +139,12 @@ } | ||
rQshiftBytes(len) { | ||
if (typeof(len) === 'undefined') { len = this.rQlen; } | ||
rQshiftBytes(len, copy=true) { | ||
this._rQi += len; | ||
return new Uint8Array(this._rQ.buffer, this._rQi - len, len); | ||
if (copy) { | ||
return this._rQ.slice(this._rQi - len, this._rQi); | ||
} else { | ||
return this._rQ.subarray(this._rQi - len, this._rQi); | ||
} | ||
} | ||
rQshiftTo(target, len) { | ||
if (len === undefined) { len = this.rQlen; } | ||
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ | ||
@@ -174,4 +155,8 @@ target.set(new Uint8Array(this._rQ.buffer, this._rQi, len)); | ||
rQslice(start, end = this.rQlen) { | ||
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start); | ||
rQpeekBytes(len, copy=true) { | ||
if (copy) { | ||
return this._rQ.slice(this._rQi, this._rQi + len); | ||
} else { | ||
return this._rQ.subarray(this._rQi, this._rQi + len); | ||
} | ||
} | ||
@@ -183,3 +168,3 @@ | ||
rQwait(msg, num, goback) { | ||
if (this.rQlen < num) { | ||
if (this._rQlen - this._rQi < num) { | ||
if (goback) { | ||
@@ -198,5 +183,44 @@ if (this._rQi < goback) { | ||
sQpush8(num) { | ||
this._sQensureSpace(1); | ||
this._sQ[this._sQlen++] = num; | ||
} | ||
sQpush16(num) { | ||
this._sQensureSpace(2); | ||
this._sQ[this._sQlen++] = (num >> 8) & 0xff; | ||
this._sQ[this._sQlen++] = (num >> 0) & 0xff; | ||
} | ||
sQpush32(num) { | ||
this._sQensureSpace(4); | ||
this._sQ[this._sQlen++] = (num >> 24) & 0xff; | ||
this._sQ[this._sQlen++] = (num >> 16) & 0xff; | ||
this._sQ[this._sQlen++] = (num >> 8) & 0xff; | ||
this._sQ[this._sQlen++] = (num >> 0) & 0xff; | ||
} | ||
sQpushString(str) { | ||
let bytes = str.split('').map(chr => chr.charCodeAt(0)); | ||
this.sQpushBytes(new Uint8Array(bytes)); | ||
} | ||
sQpushBytes(bytes) { | ||
for (let offset = 0;offset < bytes.length;) { | ||
this._sQensureSpace(1); | ||
let chunkSize = this._sQbufferSize - this._sQlen; | ||
if (chunkSize > bytes.length - offset) { | ||
chunkSize = bytes.length - offset; | ||
} | ||
this._sQ.set(bytes.subarray(offset, chunkSize), this._sQlen); | ||
this._sQlen += chunkSize; | ||
offset += chunkSize; | ||
} | ||
} | ||
flush() { | ||
if (this._sQlen > 0 && this.readyState === 'open') { | ||
this._websocket.send(this._encodeMessage()); | ||
this._websocket.send(new Uint8Array(this._sQ.buffer, 0, this._sQlen)); | ||
this._sQlen = 0; | ||
@@ -206,12 +230,8 @@ } | ||
send(arr) { | ||
this._sQ.set(arr, this._sQlen); | ||
this._sQlen += arr.length; | ||
this.flush(); | ||
_sQensureSpace(bytes) { | ||
if (this._sQbufferSize - this._sQlen < bytes) { | ||
this.flush(); | ||
} | ||
} | ||
sendString(str) { | ||
this.send(str.split('').map(chr => chr.charCodeAt(0))); | ||
} | ||
// Event Handlers | ||
@@ -293,7 +313,2 @@ off(evt) { | ||
// private methods | ||
_encodeMessage() { | ||
// Put in a binary arraybuffer | ||
// according to the spec, you can send ArrayBufferViews with the send method | ||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen); | ||
} | ||
@@ -320,3 +335,3 @@ // We want to move all the unread data to the start of the queue, | ||
this._rQbufferSize = MAX_RQ_GROW_SIZE; | ||
if (this._rQbufferSize - this.rQlen < minFit) { | ||
if (this._rQbufferSize - (this._rQlen - this._rQi) < minFit) { | ||
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); | ||
@@ -339,4 +354,10 @@ } | ||
// push arraybuffer values onto the end of the receive que | ||
_DecodeMessage(data) { | ||
const u8 = new Uint8Array(data); | ||
_recvMessage(e) { | ||
if (this._rQlen == this._rQi) { | ||
// All data has now been processed, this means we | ||
// can reset the receive queue. | ||
this._rQlen = 0; | ||
this._rQi = 0; | ||
} | ||
const u8 = new Uint8Array(e.data); | ||
if (u8.length > this._rQbufferSize - this._rQlen) { | ||
@@ -347,14 +368,5 @@ this._expandCompactRQ(u8.length); | ||
this._rQlen += u8.length; | ||
} | ||
_recvMessage(e) { | ||
this._DecodeMessage(e.data); | ||
if (this.rQlen > 0) { | ||
if (this._rQlen - this._rQi > 0) { | ||
this._eventHandlers.message(); | ||
if (this._rQlen == this._rQi) { | ||
// All data has now been processed, this means we | ||
// can reset the receive queue. | ||
this._rQlen = 0; | ||
this._rQi = 0; | ||
} | ||
} else { | ||
@@ -361,0 +373,0 @@ Log.Debug("Ignoring empty message"); |
@@ -44,5 +44,3 @@ "use strict"; | ||
} | ||
var rQ = sock.rQ; | ||
var rQi = sock.rQi; | ||
var subencoding = rQ[rQi]; // Peek | ||
var subencoding = sock.rQpeek8(); | ||
if (subencoding > 30) { | ||
@@ -80,3 +78,3 @@ // Raw | ||
} | ||
var subrects = rQ[rQi + bytes - 1]; // Peek | ||
var subrects = sock.rQpeekBytes(bytes).at(-1); | ||
if (subencoding & 0x10) { | ||
@@ -95,3 +93,3 @@ // SubrectsColoured | ||
// We know the encoding and have a whole tile | ||
rQi++; | ||
sock.rQshift8(); | ||
if (subencoding === 0) { | ||
@@ -107,18 +105,16 @@ if (this._lastsubencoding & 0x01) { | ||
var pixels = tw * th; | ||
var data = sock.rQshiftBytes(pixels * 4, false); | ||
// Max sure the image is fully opaque | ||
for (var i = 0; i < pixels; i++) { | ||
rQ[rQi + i * 4 + 3] = 255; | ||
data[i * 4 + 3] = 255; | ||
} | ||
display.blitImage(tx, ty, tw, th, rQ, rQi); | ||
rQi += bytes - 1; | ||
display.blitImage(tx, ty, tw, th, data, 0); | ||
} else { | ||
if (subencoding & 0x02) { | ||
// Background | ||
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
this._background = new Uint8Array(sock.rQshiftBytes(4)); | ||
} | ||
if (subencoding & 0x04) { | ||
// Foreground | ||
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
this._foreground = new Uint8Array(sock.rQshiftBytes(4)); | ||
} | ||
@@ -128,4 +124,3 @@ this._startTile(tx, ty, tw, th, this._background); | ||
// AnySubrects | ||
var _subrects = rQ[rQi]; | ||
rQi++; | ||
var _subrects = sock.rQshift8(); | ||
for (var s = 0; s < _subrects; s++) { | ||
@@ -135,13 +130,10 @@ var color = void 0; | ||
// SubrectsColoured | ||
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]]; | ||
rQi += 4; | ||
color = sock.rQshiftBytes(4); | ||
} else { | ||
color = this._foreground; | ||
} | ||
var xy = rQ[rQi]; | ||
rQi++; | ||
var xy = sock.rQshift8(); | ||
var sx = xy >> 4; | ||
var sy = xy & 0x0f; | ||
var wh = rQ[rQi]; | ||
rQi++; | ||
var wh = sock.rQshift8(); | ||
var sw = (wh >> 4) + 1; | ||
@@ -154,3 +146,2 @@ var sh = (wh & 0x0f) + 1; | ||
} | ||
sock.rQi = rQi; | ||
this._lastsubencoding = subencoding; | ||
@@ -157,0 +148,0 @@ this._tiles--; |
@@ -8,2 +8,9 @@ "use strict"; | ||
function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } | ||
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } | ||
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } | ||
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } | ||
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } | ||
function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } | ||
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } | ||
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
@@ -27,7 +34,4 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } } | ||
// and Huffman tables, so we need to cache them. | ||
this._quantTables = []; | ||
this._huffmanTables = []; | ||
this._cachedQuantTables = []; | ||
this._cachedHuffmanTables = []; | ||
this._jpegLength = 0; | ||
this._segments = []; | ||
@@ -39,110 +43,134 @@ } | ||
// A rect of JPEG encodings is simply a JPEG file | ||
if (!this._parseJPEG(sock.rQslice(0))) { | ||
return false; | ||
while (true) { | ||
var segment = this._readSegment(sock); | ||
if (segment === null) { | ||
return false; | ||
} | ||
this._segments.push(segment); | ||
// End of image? | ||
if (segment[1] === 0xD9) { | ||
break; | ||
} | ||
} | ||
var data = sock.rQshiftBytes(this._jpegLength); | ||
if (this._quantTables.length != 0 && this._huffmanTables.length != 0) { | ||
// If there are quantization tables and Huffman tables in the JPEG | ||
// image, we can directly render it. | ||
display.imageRect(x, y, width, height, "image/jpeg", data); | ||
return true; | ||
} else { | ||
// Otherwise we need to insert cached tables. | ||
var sofIndex = this._segments.findIndex(function (x) { | ||
return x[1] == 0xC0 || x[1] == 0xC2; | ||
}); | ||
if (sofIndex == -1) { | ||
throw new Error("Illegal JPEG image without SOF"); | ||
var huffmanTables = []; | ||
var quantTables = []; | ||
var _iterator = _createForOfIteratorHelper(this._segments), | ||
_step; | ||
try { | ||
for (_iterator.s(); !(_step = _iterator.n()).done;) { | ||
var _segment = _step.value; | ||
var type = _segment[1]; | ||
if (type === 0xC4) { | ||
// Huffman tables | ||
huffmanTables.push(_segment); | ||
} else if (type === 0xDB) { | ||
// Quantization tables | ||
quantTables.push(_segment); | ||
} | ||
} | ||
var segments = this._segments.slice(0, sofIndex); | ||
segments = segments.concat(this._quantTables.length ? this._quantTables : this._cachedQuantTables); | ||
segments.push(this._segments[sofIndex]); | ||
segments = segments.concat(this._huffmanTables.length ? this._huffmanTables : this._cachedHuffmanTables, this._segments.slice(sofIndex + 1)); | ||
var length = 0; | ||
for (var i = 0; i < segments.length; i++) { | ||
length += segments[i].length; | ||
} catch (err) { | ||
_iterator.e(err); | ||
} finally { | ||
_iterator.f(); | ||
} | ||
var sofIndex = this._segments.findIndex(function (x) { | ||
return x[1] == 0xC0 || x[1] == 0xC2; | ||
}); | ||
if (sofIndex == -1) { | ||
throw new Error("Illegal JPEG image without SOF"); | ||
} | ||
if (quantTables.length === 0) { | ||
var _this$_segments; | ||
(_this$_segments = this._segments).splice.apply(_this$_segments, [sofIndex + 1, 0].concat(_toConsumableArray(this._cachedQuantTables))); | ||
} | ||
if (huffmanTables.length === 0) { | ||
var _this$_segments2; | ||
(_this$_segments2 = this._segments).splice.apply(_this$_segments2, [sofIndex + 1, 0].concat(_toConsumableArray(this._cachedHuffmanTables))); | ||
} | ||
var length = 0; | ||
var _iterator2 = _createForOfIteratorHelper(this._segments), | ||
_step2; | ||
try { | ||
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { | ||
var _segment2 = _step2.value; | ||
length += _segment2.length; | ||
} | ||
var _data = new Uint8Array(length); | ||
length = 0; | ||
for (var _i = 0; _i < segments.length; _i++) { | ||
_data.set(segments[_i], length); | ||
length += segments[_i].length; | ||
} catch (err) { | ||
_iterator2.e(err); | ||
} finally { | ||
_iterator2.f(); | ||
} | ||
var data = new Uint8Array(length); | ||
length = 0; | ||
var _iterator3 = _createForOfIteratorHelper(this._segments), | ||
_step3; | ||
try { | ||
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { | ||
var _segment3 = _step3.value; | ||
data.set(_segment3, length); | ||
length += _segment3.length; | ||
} | ||
display.imageRect(x, y, width, height, "image/jpeg", _data); | ||
return true; | ||
} catch (err) { | ||
_iterator3.e(err); | ||
} finally { | ||
_iterator3.f(); | ||
} | ||
display.imageRect(x, y, width, height, "image/jpeg", data); | ||
if (huffmanTables.length !== 0) { | ||
this._cachedHuffmanTables = huffmanTables; | ||
} | ||
if (quantTables.length !== 0) { | ||
this._cachedQuantTables = quantTables; | ||
} | ||
this._segments = []; | ||
return true; | ||
} | ||
}, { | ||
key: "_parseJPEG", | ||
value: function _parseJPEG(buffer) { | ||
if (this._quantTables.length != 0) { | ||
this._cachedQuantTables = this._quantTables; | ||
key: "_readSegment", | ||
value: function _readSegment(sock) { | ||
if (sock.rQwait("JPEG", 2)) { | ||
return null; | ||
} | ||
if (this._huffmanTables.length != 0) { | ||
this._cachedHuffmanTables = this._huffmanTables; | ||
var marker = sock.rQshift8(); | ||
if (marker != 0xFF) { | ||
throw new Error("Illegal JPEG marker received (byte: " + marker + ")"); | ||
} | ||
this._quantTables = []; | ||
this._huffmanTables = []; | ||
this._segments = []; | ||
var i = 0; | ||
var bufferLength = buffer.length; | ||
while (true) { | ||
var j = i; | ||
if (j + 2 > bufferLength) { | ||
return false; | ||
} | ||
if (buffer[j] != 0xFF) { | ||
throw new Error("Illegal JPEG marker received (byte: " + buffer[j] + ")"); | ||
} | ||
var type = buffer[j + 1]; | ||
j += 2; | ||
if (type == 0xD9) { | ||
this._jpegLength = j; | ||
this._segments.push(buffer.slice(i, j)); | ||
return true; | ||
} else if (type == 0xDA) { | ||
// start of scan | ||
var hasFoundEndOfScan = false; | ||
for (var k = j + 3; k + 1 < bufferLength; k++) { | ||
if (buffer[k] == 0xFF && buffer[k + 1] != 0x00 && !(buffer[k + 1] >= 0xD0 && buffer[k + 1] <= 0xD7)) { | ||
j = k; | ||
hasFoundEndOfScan = true; | ||
break; | ||
} | ||
var type = sock.rQshift8(); | ||
if (type >= 0xD0 && type <= 0xD9 || type == 0x01) { | ||
// No length after marker | ||
return new Uint8Array([marker, type]); | ||
} | ||
if (sock.rQwait("JPEG", 2, 2)) { | ||
return null; | ||
} | ||
var length = sock.rQshift16(); | ||
if (length < 2) { | ||
throw new Error("Illegal JPEG length received (length: " + length + ")"); | ||
} | ||
if (sock.rQwait("JPEG", length - 2, 4)) { | ||
return null; | ||
} | ||
var extra = 0; | ||
if (type === 0xDA) { | ||
// start of scan | ||
extra += 2; | ||
while (true) { | ||
if (sock.rQwait("JPEG", length - 2 + extra, 4)) { | ||
return null; | ||
} | ||
if (!hasFoundEndOfScan) { | ||
return false; | ||
var data = sock.rQpeekBytes(length - 2 + extra, false); | ||
if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 && !(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) { | ||
extra -= 2; | ||
break; | ||
} | ||
this._segments.push(buffer.slice(i, j)); | ||
i = j; | ||
continue; | ||
} else if (type >= 0xD0 && type < 0xD9 || type == 0x01) { | ||
// No length after marker | ||
this._segments.push(buffer.slice(i, j)); | ||
i = j; | ||
continue; | ||
extra++; | ||
} | ||
if (j + 2 > bufferLength) { | ||
return false; | ||
} | ||
var length = (buffer[j] << 8) + buffer[j + 1] - 2; | ||
if (length < 0) { | ||
throw new Error("Illegal JPEG length received (length: " + length + ")"); | ||
} | ||
j += 2; | ||
if (j + length > bufferLength) { | ||
return false; | ||
} | ||
j += length; | ||
var segment = buffer.slice(i, j); | ||
if (type == 0xC4) { | ||
// Huffman tables | ||
this._huffmanTables.push(segment); | ||
} else if (type == 0xDB) { | ||
// Quantization tables | ||
this._quantTables.push(segment); | ||
} | ||
this._segments.push(segment); | ||
i = j; | ||
} | ||
var segment = new Uint8Array(2 + length + extra); | ||
segment[0] = marker; | ||
segment[1] = type; | ||
segment[2] = length >> 8; | ||
segment[3] = length; | ||
segment.set(sock.rQshiftBytes(length - 2 + extra, false), 4); | ||
return segment; | ||
} | ||
@@ -149,0 +177,0 @@ }]); |
@@ -37,34 +37,28 @@ "use strict"; | ||
var bytesPerLine = width * pixelSize; | ||
if (sock.rQwait("RAW", bytesPerLine)) { | ||
return false; | ||
} | ||
var curY = y + (height - this._lines); | ||
var currHeight = Math.min(this._lines, Math.floor(sock.rQlen / bytesPerLine)); | ||
var pixels = width * currHeight; | ||
var data = sock.rQ; | ||
var index = sock.rQi; | ||
while (this._lines > 0) { | ||
if (sock.rQwait("RAW", bytesPerLine)) { | ||
return false; | ||
} | ||
var curY = y + (height - this._lines); | ||
var data = sock.rQshiftBytes(bytesPerLine, false); | ||
// Convert data if needed | ||
if (depth == 8) { | ||
var newdata = new Uint8Array(pixels * 4); | ||
for (var i = 0; i < pixels; i++) { | ||
newdata[i * 4 + 0] = (data[index + i] >> 0 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 1] = (data[index + i] >> 2 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 2] = (data[index + i] >> 4 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 3] = 255; | ||
// Convert data if needed | ||
if (depth == 8) { | ||
var newdata = new Uint8Array(width * 4); | ||
for (var i = 0; i < width; i++) { | ||
newdata[i * 4 + 0] = (data[i] >> 0 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 1] = (data[i] >> 2 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 2] = (data[i] >> 4 & 0x3) * 255 / 3; | ||
newdata[i * 4 + 3] = 255; | ||
} | ||
data = newdata; | ||
} | ||
data = newdata; | ||
index = 0; | ||
} | ||
// Max sure the image is fully opaque | ||
for (var _i = 0; _i < pixels; _i++) { | ||
data[index + _i * 4 + 3] = 255; | ||
// Max sure the image is fully opaque | ||
for (var _i = 0; _i < width; _i++) { | ||
data[_i * 4 + 3] = 255; | ||
} | ||
display.blitImage(x, curY, width, 1, data, 0); | ||
this._lines--; | ||
} | ||
display.blitImage(x, curY, width, currHeight, data, index); | ||
sock.rQskipBytes(currHeight * bytesPerLine); | ||
this._lines -= currHeight; | ||
if (this._lines > 0) { | ||
return false; | ||
} | ||
return true; | ||
@@ -71,0 +65,0 @@ } |
@@ -82,6 +82,4 @@ "use strict"; | ||
} | ||
var rQi = sock.rQi; | ||
var rQ = sock.rQ; | ||
display.fillRect(x, y, width, height, [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false); | ||
sock.rQskipBytes(3); | ||
var pixel = sock.rQshiftBytes(3); | ||
display.fillRect(x, y, width, height, pixel, false); | ||
return true; | ||
@@ -296,3 +294,3 @@ } | ||
} | ||
var data = sock.rQshiftBytes(this._len); | ||
var data = sock.rQshiftBytes(this._len, false); | ||
this._len = 0; | ||
@@ -299,0 +297,0 @@ return data; |
@@ -44,3 +44,3 @@ "use strict"; | ||
} | ||
var data = sock.rQshiftBytes(this._length); | ||
var data = sock.rQshiftBytes(this._length, false); | ||
this._inflator.setInput(data); | ||
@@ -47,0 +47,0 @@ for (var ty = y; ty < y + height; ty += ZRLE_TILE_HEIGHT) { |
@@ -30,3 +30,3 @@ "use strict"; | ||
this._renderQ = []; // queue drawing actions for in-oder rendering | ||
this._flushing = false; | ||
this._flushPromise = null; | ||
@@ -76,6 +76,2 @@ // the full frame buffer (logical canvas) size | ||
this._clipViewport = false; | ||
// ===== EVENT HANDLERS ===== | ||
this.onflush = function () {}; // A flush request has finished | ||
} | ||
@@ -315,6 +311,12 @@ | ||
value: function flush() { | ||
var _this = this; | ||
if (this._renderQ.length === 0) { | ||
this.onflush(); | ||
return Promise.resolve(); | ||
} else { | ||
this._flushing = true; | ||
if (this._flushPromise === null) { | ||
this._flushPromise = new Promise(function (resolve) { | ||
_this._flushResolve = resolve; | ||
}); | ||
} | ||
return this._flushPromise; | ||
} | ||
@@ -521,5 +523,6 @@ } | ||
} | ||
if (this._renderQ.length === 0 && this._flushing) { | ||
this._flushing = false; | ||
this.onflush(); | ||
if (this._renderQ.length === 0 && this._flushPromise !== null) { | ||
this._flushResolve(); | ||
this._flushPromise = null; | ||
this._flushResolve = null; | ||
} | ||
@@ -526,0 +529,0 @@ } |
150
lib/ra2.js
@@ -237,3 +237,3 @@ "use strict"; | ||
var _negotiateRA2neAuthAsync = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee4() { | ||
var serverKeyLengthBuffer, serverKeyLength, serverKeyBytes, serverN, serverE, serverRSACipher, serverPublickey, clientKeyLength, clientKeyBytes, clientRSACipher, clientExportedRSAKey, clientN, clientE, clientPublicKey, clientRandom, clientEncryptedRandom, clientRandomMessage, serverEncryptedRandom, serverRandom, clientSessionKey, serverSessionKey, clientCipher, serverCipher, serverHash, clientHash, serverHashReceived, i, subtype, username, password, credentials, _i, _i2; | ||
var serverKeyLengthBuffer, serverKeyLength, serverKeyBytes, serverN, serverE, serverRSACipher, serverPublickey, approveKey, clientKeyLength, clientKeyBytes, clientRSACipher, clientExportedRSAKey, clientN, clientE, clientPublicKey, clientRandom, clientEncryptedRandom, clientRandomMessage, serverEncryptedRandom, serverRandom, clientSessionKey, serverSessionKey, clientCipher, serverCipher, serverHash, clientHash, serverHashReceived, i, subtype, waitCredentials, username, password, credentials, _i, _i2; | ||
return _regeneratorRuntime().wrap(function _callee4$(_context4) { | ||
@@ -247,3 +247,3 @@ while (1) switch (_context4.prev = _context4.next) { | ||
case 3: | ||
serverKeyLengthBuffer = this._sock.rQslice(0, 4); | ||
serverKeyLengthBuffer = this._sock.rQpeekBytes(4); | ||
serverKeyLength = this._sock.rQshift32(); | ||
@@ -283,2 +283,3 @@ if (!(serverKeyLength < 1024)) { | ||
// verify server public key | ||
approveKey = this._waitApproveKeyAsync(); | ||
this.dispatchEvent(new CustomEvent("serververification", { | ||
@@ -290,9 +291,9 @@ detail: { | ||
})); | ||
_context4.next = 26; | ||
return this._waitApproveKeyAsync(); | ||
case 26: | ||
_context4.next = 27; | ||
return approveKey; | ||
case 27: | ||
// 2: Send client public key | ||
clientKeyLength = 2048; | ||
clientKeyBytes = Math.ceil(clientKeyLength / 8); | ||
_context4.next = 30; | ||
_context4.next = 31; | ||
return _crypto["default"].generateKey({ | ||
@@ -303,7 +304,7 @@ name: "RSA-PKCS1-v1_5", | ||
}, true, ["encrypt"]); | ||
case 30: | ||
case 31: | ||
clientRSACipher = _context4.sent.privateKey; | ||
_context4.next = 33; | ||
_context4.next = 34; | ||
return _crypto["default"].exportKey("raw", clientRSACipher); | ||
case 33: | ||
case 34: | ||
clientExportedRSAKey = _context4.sent; | ||
@@ -319,3 +320,4 @@ clientN = clientExportedRSAKey.n; | ||
clientPublicKey.set(clientE, 4 + clientKeyBytes); | ||
this._sock.send(clientPublicKey); | ||
this._sock.sQpushBytes(clientPublicKey); | ||
this._sock.flush(); | ||
@@ -325,7 +327,7 @@ // 3: Send client random | ||
window.crypto.getRandomValues(clientRandom); | ||
_context4.next = 48; | ||
_context4.next = 50; | ||
return _crypto["default"].encrypt({ | ||
name: "RSA-PKCS1-v1_5" | ||
}, serverRSACipher, clientRandom); | ||
case 48: | ||
case 50: | ||
clientEncryptedRandom = _context4.sent; | ||
@@ -336,27 +338,28 @@ clientRandomMessage = new Uint8Array(2 + serverKeyBytes); | ||
clientRandomMessage.set(clientEncryptedRandom, 2); | ||
this._sock.send(clientRandomMessage); | ||
this._sock.sQpushBytes(clientRandomMessage); | ||
this._sock.flush(); | ||
// 4: Receive server random | ||
_context4.next = 56; | ||
_context4.next = 59; | ||
return this._waitSockAsync(2); | ||
case 56: | ||
case 59: | ||
if (!(this._sock.rQshift16() !== clientKeyBytes)) { | ||
_context4.next = 58; | ||
_context4.next = 61; | ||
break; | ||
} | ||
throw new Error("RA2: wrong encrypted message length"); | ||
case 58: | ||
case 61: | ||
serverEncryptedRandom = this._sock.rQshiftBytes(clientKeyBytes); | ||
_context4.next = 61; | ||
_context4.next = 64; | ||
return _crypto["default"].decrypt({ | ||
name: "RSA-PKCS1-v1_5" | ||
}, clientRSACipher, serverEncryptedRandom); | ||
case 61: | ||
case 64: | ||
serverRandom = _context4.sent; | ||
if (!(serverRandom === null || serverRandom.length !== 16)) { | ||
_context4.next = 64; | ||
_context4.next = 67; | ||
break; | ||
} | ||
throw new Error("RA2: corrupted server encrypted random"); | ||
case 64: | ||
case 67: | ||
// 5: Compute session keys and set ciphers | ||
@@ -369,20 +372,20 @@ clientSessionKey = new Uint8Array(32); | ||
serverSessionKey.set(serverRandom, 16); | ||
_context4.next = 72; | ||
_context4.next = 75; | ||
return window.crypto.subtle.digest("SHA-1", clientSessionKey); | ||
case 72: | ||
case 75: | ||
clientSessionKey = _context4.sent; | ||
clientSessionKey = new Uint8Array(clientSessionKey).slice(0, 16); | ||
_context4.next = 76; | ||
_context4.next = 79; | ||
return window.crypto.subtle.digest("SHA-1", serverSessionKey); | ||
case 76: | ||
case 79: | ||
serverSessionKey = _context4.sent; | ||
serverSessionKey = new Uint8Array(serverSessionKey).slice(0, 16); | ||
clientCipher = new RA2Cipher(); | ||
_context4.next = 81; | ||
_context4.next = 84; | ||
return clientCipher.setKey(clientSessionKey); | ||
case 81: | ||
case 84: | ||
serverCipher = new RA2Cipher(); | ||
_context4.next = 84; | ||
_context4.next = 87; | ||
return serverCipher.setKey(serverSessionKey); | ||
case 84: | ||
case 87: | ||
// 6: Compute and exchange hashes | ||
@@ -395,9 +398,9 @@ serverHash = new Uint8Array(8 + serverKeyBytes * 2 + clientKeyBytes * 2); | ||
clientHash.set(serverPublickey, 4 + clientKeyBytes * 2); | ||
_context4.next = 92; | ||
_context4.next = 95; | ||
return window.crypto.subtle.digest("SHA-1", serverHash); | ||
case 92: | ||
case 95: | ||
serverHash = _context4.sent; | ||
_context4.next = 95; | ||
_context4.next = 98; | ||
return window.crypto.subtle.digest("SHA-1", clientHash); | ||
case 95: | ||
case 98: | ||
clientHash = _context4.sent; | ||
@@ -407,64 +410,66 @@ serverHash = new Uint8Array(serverHash); | ||
_context4.t0 = this._sock; | ||
_context4.next = 101; | ||
_context4.next = 104; | ||
return clientCipher.makeMessage(clientHash); | ||
case 101: | ||
case 104: | ||
_context4.t1 = _context4.sent; | ||
_context4.t0.send.call(_context4.t0, _context4.t1); | ||
_context4.next = 105; | ||
_context4.t0.sQpushBytes.call(_context4.t0, _context4.t1); | ||
this._sock.flush(); | ||
_context4.next = 109; | ||
return this._waitSockAsync(2 + 20 + 16); | ||
case 105: | ||
case 109: | ||
if (!(this._sock.rQshift16() !== 20)) { | ||
_context4.next = 107; | ||
_context4.next = 111; | ||
break; | ||
} | ||
throw new Error("RA2: wrong server hash"); | ||
case 107: | ||
_context4.next = 109; | ||
case 111: | ||
_context4.next = 113; | ||
return serverCipher.receiveMessage(20, this._sock.rQshiftBytes(20 + 16)); | ||
case 109: | ||
case 113: | ||
serverHashReceived = _context4.sent; | ||
if (!(serverHashReceived === null)) { | ||
_context4.next = 112; | ||
_context4.next = 116; | ||
break; | ||
} | ||
throw new Error("RA2: failed to authenticate the message"); | ||
case 112: | ||
case 116: | ||
i = 0; | ||
case 113: | ||
case 117: | ||
if (!(i < 20)) { | ||
_context4.next = 119; | ||
_context4.next = 123; | ||
break; | ||
} | ||
if (!(serverHashReceived[i] !== serverHash[i])) { | ||
_context4.next = 116; | ||
_context4.next = 120; | ||
break; | ||
} | ||
throw new Error("RA2: wrong server hash"); | ||
case 116: | ||
case 120: | ||
i++; | ||
_context4.next = 113; | ||
_context4.next = 117; | ||
break; | ||
case 119: | ||
_context4.next = 121; | ||
case 123: | ||
_context4.next = 125; | ||
return this._waitSockAsync(2 + 1 + 16); | ||
case 121: | ||
case 125: | ||
if (!(this._sock.rQshift16() !== 1)) { | ||
_context4.next = 123; | ||
_context4.next = 127; | ||
break; | ||
} | ||
throw new Error("RA2: wrong subtype"); | ||
case 123: | ||
_context4.next = 125; | ||
case 127: | ||
_context4.next = 129; | ||
return serverCipher.receiveMessage(1, this._sock.rQshiftBytes(1 + 16)); | ||
case 125: | ||
case 129: | ||
subtype = _context4.sent; | ||
if (!(subtype === null)) { | ||
_context4.next = 128; | ||
_context4.next = 132; | ||
break; | ||
} | ||
throw new Error("RA2: failed to authenticate the message"); | ||
case 128: | ||
case 132: | ||
subtype = subtype[0]; | ||
waitCredentials = this._waitCredentialsAsync(subtype); | ||
if (!(subtype === 1)) { | ||
_context4.next = 133; | ||
_context4.next = 138; | ||
break; | ||
@@ -479,7 +484,7 @@ } | ||
} | ||
_context4.next = 138; | ||
_context4.next = 143; | ||
break; | ||
case 133: | ||
case 138: | ||
if (!(subtype === 2)) { | ||
_context4.next = 137; | ||
_context4.next = 142; | ||
break; | ||
@@ -494,10 +499,10 @@ } | ||
} | ||
_context4.next = 138; | ||
_context4.next = 143; | ||
break; | ||
case 137: | ||
case 142: | ||
throw new Error("RA2: wrong subtype"); | ||
case 138: | ||
_context4.next = 140; | ||
return this._waitCredentialsAsync(subtype); | ||
case 140: | ||
case 143: | ||
_context4.next = 145; | ||
return waitCredentials; | ||
case 145: | ||
if (subtype === 1) { | ||
@@ -519,8 +524,9 @@ username = (0, _strings.encodeUTF8)(this._getCredentials().username).slice(0, 255); | ||
_context4.t2 = this._sock; | ||
_context4.next = 150; | ||
_context4.next = 155; | ||
return clientCipher.makeMessage(credentials); | ||
case 150: | ||
case 155: | ||
_context4.t3 = _context4.sent; | ||
_context4.t2.send.call(_context4.t2, _context4.t3); | ||
case 152: | ||
_context4.t2.sQpushBytes.call(_context4.t2, _context4.t3); | ||
this._sock.flush(); | ||
case 158: | ||
case "end": | ||
@@ -527,0 +533,0 @@ return _context4.stop(); |
@@ -100,28 +100,5 @@ "use strict"; | ||
} | ||
}, { | ||
key: "sQ", | ||
get: function get() { | ||
return this._sQ; | ||
} | ||
}, { | ||
key: "rQ", | ||
get: function get() { | ||
return this._rQ; | ||
} | ||
}, { | ||
key: "rQi", | ||
get: function get() { | ||
return this._rQi; | ||
}, | ||
set: function set(val) { | ||
this._rQi = val; | ||
} | ||
// Receive Queue | ||
}, { | ||
key: "rQlen", | ||
get: function get() { | ||
return this._rQlen - this._rQi; | ||
} | ||
}, { | ||
key: "rQpeek8", | ||
@@ -160,3 +137,3 @@ value: function rQpeek8() { | ||
} | ||
return res; | ||
return res >>> 0; | ||
} | ||
@@ -166,9 +143,6 @@ }, { | ||
value: function rQshiftStr(len) { | ||
if (typeof len === 'undefined') { | ||
len = this.rQlen; | ||
} | ||
var str = ""; | ||
// Handle large arrays in steps to avoid long strings on the stack | ||
for (var i = 0; i < len; i += 4096) { | ||
var part = this.rQshiftBytes(Math.min(4096, len - i)); | ||
var part = this.rQshiftBytes(Math.min(4096, len - i), false); | ||
str += String.fromCharCode.apply(null, part); | ||
@@ -181,7 +155,9 @@ } | ||
value: function rQshiftBytes(len) { | ||
if (typeof len === 'undefined') { | ||
len = this.rQlen; | ||
var copy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
this._rQi += len; | ||
if (copy) { | ||
return this._rQ.slice(this._rQi - len, this._rQi); | ||
} else { | ||
return this._rQ.subarray(this._rQi - len, this._rQi); | ||
} | ||
this._rQi += len; | ||
return new Uint8Array(this._rQ.buffer, this._rQi - len, len); | ||
} | ||
@@ -191,5 +167,2 @@ }, { | ||
value: function rQshiftTo(target, len) { | ||
if (len === undefined) { | ||
len = this.rQlen; | ||
} | ||
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ | ||
@@ -200,6 +173,10 @@ target.set(new Uint8Array(this._rQ.buffer, this._rQi, len)); | ||
}, { | ||
key: "rQslice", | ||
value: function rQslice(start) { | ||
var end = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.rQlen; | ||
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start); | ||
key: "rQpeekBytes", | ||
value: function rQpeekBytes(len) { | ||
var copy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; | ||
if (copy) { | ||
return this._rQ.slice(this._rQi, this._rQi + len); | ||
} else { | ||
return this._rQ.subarray(this._rQi, this._rQi + len); | ||
} | ||
} | ||
@@ -213,3 +190,3 @@ | ||
value: function rQwait(msg, num, goback) { | ||
if (this.rQlen < num) { | ||
if (this._rQlen - this._rQi < num) { | ||
if (goback) { | ||
@@ -229,6 +206,50 @@ if (this._rQi < goback) { | ||
}, { | ||
key: "sQpush8", | ||
value: function sQpush8(num) { | ||
this._sQensureSpace(1); | ||
this._sQ[this._sQlen++] = num; | ||
} | ||
}, { | ||
key: "sQpush16", | ||
value: function sQpush16(num) { | ||
this._sQensureSpace(2); | ||
this._sQ[this._sQlen++] = num >> 8 & 0xff; | ||
this._sQ[this._sQlen++] = num >> 0 & 0xff; | ||
} | ||
}, { | ||
key: "sQpush32", | ||
value: function sQpush32(num) { | ||
this._sQensureSpace(4); | ||
this._sQ[this._sQlen++] = num >> 24 & 0xff; | ||
this._sQ[this._sQlen++] = num >> 16 & 0xff; | ||
this._sQ[this._sQlen++] = num >> 8 & 0xff; | ||
this._sQ[this._sQlen++] = num >> 0 & 0xff; | ||
} | ||
}, { | ||
key: "sQpushString", | ||
value: function sQpushString(str) { | ||
var bytes = str.split('').map(function (chr) { | ||
return chr.charCodeAt(0); | ||
}); | ||
this.sQpushBytes(new Uint8Array(bytes)); | ||
} | ||
}, { | ||
key: "sQpushBytes", | ||
value: function sQpushBytes(bytes) { | ||
for (var offset = 0; offset < bytes.length;) { | ||
this._sQensureSpace(1); | ||
var chunkSize = this._sQbufferSize - this._sQlen; | ||
if (chunkSize > bytes.length - offset) { | ||
chunkSize = bytes.length - offset; | ||
} | ||
this._sQ.set(bytes.subarray(offset, chunkSize), this._sQlen); | ||
this._sQlen += chunkSize; | ||
offset += chunkSize; | ||
} | ||
} | ||
}, { | ||
key: "flush", | ||
value: function flush() { | ||
if (this._sQlen > 0 && this.readyState === 'open') { | ||
this._websocket.send(this._encodeMessage()); | ||
this._websocket.send(new Uint8Array(this._sQ.buffer, 0, this._sQlen)); | ||
this._sQlen = 0; | ||
@@ -238,15 +259,8 @@ } | ||
}, { | ||
key: "send", | ||
value: function send(arr) { | ||
this._sQ.set(arr, this._sQlen); | ||
this._sQlen += arr.length; | ||
this.flush(); | ||
key: "_sQensureSpace", | ||
value: function _sQensureSpace(bytes) { | ||
if (this._sQbufferSize - this._sQlen < bytes) { | ||
this.flush(); | ||
} | ||
} | ||
}, { | ||
key: "sendString", | ||
value: function sendString(str) { | ||
this.send(str.split('').map(function (chr) { | ||
return chr.charCodeAt(0); | ||
})); | ||
} | ||
@@ -331,9 +345,2 @@ // Event Handlers | ||
// private methods | ||
}, { | ||
key: "_encodeMessage", | ||
value: function _encodeMessage() { | ||
// Put in a binary arraybuffer | ||
// according to the spec, you can send ArrayBufferViews with the send method | ||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen); | ||
} | ||
@@ -361,3 +368,3 @@ // We want to move all the unread data to the start of the queue, | ||
this._rQbufferSize = MAX_RQ_GROW_SIZE; | ||
if (this._rQbufferSize - this.rQlen < minFit) { | ||
if (this._rQbufferSize - (this._rQlen - this._rQi) < minFit) { | ||
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); | ||
@@ -379,5 +386,11 @@ } | ||
}, { | ||
key: "_DecodeMessage", | ||
value: function _DecodeMessage(data) { | ||
var u8 = new Uint8Array(data); | ||
key: "_recvMessage", | ||
value: function _recvMessage(e) { | ||
if (this._rQlen == this._rQi) { | ||
// All data has now been processed, this means we | ||
// can reset the receive queue. | ||
this._rQlen = 0; | ||
this._rQi = 0; | ||
} | ||
var u8 = new Uint8Array(e.data); | ||
if (u8.length > this._rQbufferSize - this._rQlen) { | ||
@@ -388,15 +401,4 @@ this._expandCompactRQ(u8.length); | ||
this._rQlen += u8.length; | ||
} | ||
}, { | ||
key: "_recvMessage", | ||
value: function _recvMessage(e) { | ||
this._DecodeMessage(e.data); | ||
if (this.rQlen > 0) { | ||
if (this._rQlen - this._rQi > 0) { | ||
this._eventHandlers.message(); | ||
if (this._rQlen == this._rQi) { | ||
// All data has now been processed, this means we | ||
// can reset the receive queue. | ||
this._rQlen = 0; | ||
this._rQi = 0; | ||
} | ||
} else { | ||
@@ -403,0 +405,0 @@ Log.Debug("Ignoring empty message"); |
{ | ||
"name": "@novnc/novnc", | ||
"version": "1.4.0-gc2d6a06", | ||
"version": "1.4.0-gca6527c", | ||
"description": "An HTML5 VNC client", | ||
@@ -5,0 +5,0 @@ "browser": "lib/rfb", |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1342752
31263