Feature/kasm 4608 linux printing (#74)
* Add support for relaying unix sockets * KASM-4608 Add document printing using the printer socket relay --------- Co-authored-by: Lauri Kasanen <cand@gmx.com> Co-authored-by: mattmcclaskey <matt@kasmweb.com>
This commit is contained in:
parent
2e10cdf12d
commit
d135f05932
|
@ -0,0 +1,63 @@
|
|||
const PACKETS = {
|
||||
DOCUMENT_START: 0,
|
||||
DOCUMENT_CHUNK: 1,
|
||||
DOCUMENT_END: 2
|
||||
};
|
||||
|
||||
const printDocument = async (data) => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.style.display = "none";
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
iframe.onload = () => {
|
||||
setTimeout(() => {
|
||||
iframe.focus();
|
||||
iframe.contentWindow.print();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
const blob = new Blob([new Uint8Array(data)], { type: "application/pdf" });
|
||||
iframe.src = URL.createObjectURL(blob);
|
||||
}
|
||||
|
||||
export default (rfb) => {
|
||||
let documentSize = 0;
|
||||
let downloadedSize = 0;
|
||||
let documentData = [];
|
||||
|
||||
const processRelayData = (payload) => {
|
||||
const array = Array.from(payload);
|
||||
const buffer = new Uint8Array(array).buffer;
|
||||
const packetData = new DataView(buffer);
|
||||
const packetId = packetData.getUint32(0, false);
|
||||
|
||||
switch (packetId) {
|
||||
case PACKETS.DOCUMENT_START:
|
||||
documentSize = packetData.getUint32(4, false);
|
||||
downloadedSize = 0;
|
||||
console.log(`Downloading document for printing (${documentSize}B)`);
|
||||
break;
|
||||
|
||||
case PACKETS.DOCUMENT_CHUNK:
|
||||
let chunkSize = packetData.getUint32(4, false);
|
||||
let chunkData = new Uint8Array(buffer, 8);
|
||||
downloadedSize += chunkSize;
|
||||
documentData.push(...chunkData);
|
||||
console.log(`Downloading document for printing (${downloadedSize}/${documentSize}B)`);
|
||||
break;
|
||||
|
||||
case PACKETS.DOCUMENT_END:
|
||||
console.log(`Downloaded document for printing (${downloadedSize}/${documentSize}B)`);
|
||||
printDocument(documentData);
|
||||
downloadedSize = 0;
|
||||
documentSize = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.error(`Unknown packet id: ${packetId}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
rfb.subscribeUnixRelay("printer", processRelayData);
|
||||
}
|
106
core/rfb.js
106
core/rfb.js
|
@ -20,6 +20,7 @@ import Display from "./display.js";
|
|||
import Inflator from "./inflator.js";
|
||||
import Deflator from "./deflator.js";
|
||||
import Keyboard from "./input/keyboard.js";
|
||||
import initializePrinterRelay from "./output/printer.js";
|
||||
import GestureHandler from "./input/gesturehandler.js";
|
||||
import Cursor from "./util/cursor.js";
|
||||
import Websock from "./websock.js";
|
||||
|
@ -981,6 +982,16 @@ export default class RFB extends EventTargetMixin {
|
|||
RFB.messages.requestStats(this._sock);
|
||||
}
|
||||
|
||||
subscribeUnixRelay(name, processRelayFn) {
|
||||
this._unixRelays = this._unixRelays || {};
|
||||
this._unixRelays[name] = processRelayFn;
|
||||
RFB.messages.sendSubscribeUnixRelay(this._sock, name);
|
||||
}
|
||||
|
||||
sendUnixRelayData(name, payload) {
|
||||
RFB.messages.sendUnixRelay(this._sock, name, payload);
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_setLastActive() {
|
||||
|
@ -2536,6 +2547,10 @@ export default class RFB extends EventTargetMixin {
|
|||
RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight);
|
||||
|
||||
this._updateConnectionState('connected');
|
||||
|
||||
//Register pipe based extensions
|
||||
initializePrinterRelay(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3083,6 +3098,12 @@ export default class RFB extends EventTargetMixin {
|
|||
case 181: // KASM UDP upgrade
|
||||
return this._handleUdpUpgrade();
|
||||
|
||||
case 182: // KASM unix relay subscription
|
||||
return this._handleSubscribeUnixRelay();
|
||||
|
||||
case 183: // KASM unix relay data
|
||||
return this._handleUnixRelay();
|
||||
|
||||
case 248: // ServerFence
|
||||
return this._handleServerFenceMsg();
|
||||
|
||||
|
@ -3211,6 +3232,36 @@ export default class RFB extends EventTargetMixin {
|
|||
}.bind(this));
|
||||
}
|
||||
|
||||
_handleSubscribeUnixRelay() {
|
||||
if (this._sock.rQwait("SubscribeUnixRelay header", 2, 1)) { return false; }
|
||||
let status = this._sock.rQshift8();
|
||||
let len = this._sock.rQshift8();
|
||||
if (this._sock.rQwait("SubscribeUnixRelay message", len, 3)) { return false; }
|
||||
|
||||
const payload = this._sock.rQshiftStr(len);
|
||||
|
||||
if (status) {
|
||||
console.log("Unix relay subscription succeeded");
|
||||
} else {
|
||||
console.log("Unix relay subscription failed, " + payload);
|
||||
}
|
||||
}
|
||||
|
||||
_handleUnixRelay() {
|
||||
if (this._sock.rQwait("UnixRelay header", 1, 1)) { return false; }
|
||||
let namelen = this._sock.rQshift8();
|
||||
if (this._sock.rQwait("UnixRelay name", namelen, 2)) { return false; }
|
||||
const name = this._sock.rQshiftStr(namelen);
|
||||
|
||||
if (this._sock.rQwait("UnixRelay len", 4, 2 + namelen)) { return false; }
|
||||
let len = this._sock.rQshift32();
|
||||
if (this._sock.rQwait("UnixRelay data", len, 6 + namelen)) { return false; }
|
||||
|
||||
const payload = this._sock.rQshiftBytes(len);
|
||||
const processRelay = this._unixRelays[name];
|
||||
processRelay && processRelay(payload);
|
||||
}
|
||||
|
||||
_framebufferUpdate() {
|
||||
if (this._FBU.rects === 0) {
|
||||
if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
|
||||
|
@ -3952,6 +4003,61 @@ RFB.messages = {
|
|||
}
|
||||
},
|
||||
|
||||
sendSubscribeUnixRelay(sock, name) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
buff[offset] = 182; // msg-type
|
||||
buff[offset + 1] = name.length; // len
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
buff[offset + 2 + i] = name.charCodeAt(i);
|
||||
}
|
||||
|
||||
sock._sQlen += 2 + name.length;
|
||||
sock.flush();
|
||||
},
|
||||
|
||||
sendUnixRelay(sock, name, data) {
|
||||
const buff = sock._sQ;
|
||||
let offset = sock._sQlen;
|
||||
|
||||
buff[offset++] = 183; // msg-type
|
||||
buff[offset++] = name.length; // len
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
buff[offset++] = name.charCodeAt(i);
|
||||
}
|
||||
|
||||
let length = data.length;
|
||||
|
||||
Log.Info('Sent unix relay data len ' + length);
|
||||
|
||||
buff[offset++] = length >> 24;
|
||||
buff[offset++] = length >> 16;
|
||||
buff[offset++] = length >> 8;
|
||||
buff[offset++] = length;
|
||||
|
||||
sock._sQlen += 2 + name.length + 4;
|
||||
|
||||
// We have to keep track of from where in the data we begin creating the
|
||||
// buffer for the flush in the next iteration.
|
||||
let dataOffset = 0;
|
||||
|
||||
let remaining = data.length;
|
||||
while (remaining > 0) {
|
||||
|
||||
let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
|
||||
for (let i = 0; i < flushSize; i++) {
|
||||
buff[sock._sQlen + i] = data[dataOffset + i];
|
||||
}
|
||||
|
||||
sock._sQlen += flushSize;
|
||||
sock.flush();
|
||||
|
||||
remaining -= flushSize;
|
||||
dataOffset += flushSize;
|
||||
}
|
||||
},
|
||||
|
||||
setDesktopSize(sock, width, height, id, flags) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
|
Loading…
Reference in New Issue