diff --git a/cvmts/src/BinRectsProtocol.ts b/cvmts/src/BinRectsProtocol.ts index f4a8d57..21bb76a 100644 --- a/cvmts/src/BinRectsProtocol.ts +++ b/cvmts/src/BinRectsProtocol.ts @@ -1,6 +1,16 @@ import * as msgpack from 'msgpackr'; import { CollabVMProtocolMessage, CollabVMProtocolMessageType } from '@cvmts/collab-vm-1.2-binary-protocol'; +import { GuacamoleProtocol } from './GuacamoleProtocol.js'; -// TODO: reimplement binrects protocol -// we can just create/proxy a GuacamoleProtocol manually, -// and for the rects do our own thing +import { ScreenRect } from './Protocol'; + +export class BinRectsProtocol extends GuacamoleProtocol { + sendScreenUpdate(rect: ScreenRect): void { + let bmsg: CollabVMProtocolMessage = { + type: CollabVMProtocolMessageType.rect, + rect: rect + }; + + this.user?.socket.sendBinary(msgpack.encode(bmsg)); + } +} diff --git a/cvmts/src/CollabVMServer.ts b/cvmts/src/CollabVMServer.ts index d0b238e..7d9bea5 100644 --- a/cvmts/src/CollabVMServer.ts +++ b/cvmts/src/CollabVMServer.ts @@ -211,6 +211,8 @@ export default class CollabVMServer implements IProtocolHandlers { this.clients.splice(clientIndex, 1); + user.protocol.dispose(); + this.logger.info(`Disconnect From ${user.IP.address}${user.username ? ` with username ${user.username}` : ''}`); if (!user.username) return; if (this.TurnQueue.toArray().indexOf(user) !== -1) { @@ -273,9 +275,9 @@ export default class CollabVMServer implements IProtocolHandlers { // Set rank user.rank = res.rank; if (user.rank === Rank.Admin) { - user.sendMsg(cvm.guacEncode('admin', '0', '1')); + user.protocol.sendAdminLoginResponse(true, undefined); } else if (user.rank === Rank.Moderator) { - user.sendMsg(cvm.guacEncode('admin', '0', '3', this.ModPerms.toString())); + user.protocol.sendAdminLoginResponse(true, this.ModPerms); } this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', user.username!, user.rank.toString()))); } else { @@ -303,12 +305,11 @@ export default class CollabVMServer implements IProtocolHandlers { for (let cap of capability) { switch (cap) { // binary 1.0 (msgpack rects) - // TODO: re-enable once binary1.0 is enabled case 'bin': - this.logger.info('Binary 1.0 protocol is currently disabled for refactoring'); - //user.Capabilities.bin = true; - //user.protocol = TheProtocolManager.createProtocol('binary1', user); - //user.protocol.setHandler(this as IProtocolHandlers); + user.Capabilities.bin = true; + user.protocol.dispose(); + user.protocol = TheProtocolManager.createProtocol('binary1', user); + user.protocol.setHandler(this as IProtocolHandlers); break; default: break; @@ -513,12 +514,12 @@ export default class CollabVMServer implements IProtocolHandlers { if (pwdHash === this.Config.collabvm.adminpass) { user.rank = Rank.Admin; - user.sendMsg(cvm.guacEncode('admin', '0', '1')); + user.protocol.sendAdminLoginResponse(true, undefined); } else if (this.Config.collabvm.moderatorEnabled && pwdHash === this.Config.collabvm.modpass) { user.rank = Rank.Moderator; - user.sendMsg(cvm.guacEncode('admin', '0', '3', this.ModPerms.toString())); + user.protocol.sendAdminLoginResponse(true, this.ModPerms); } else { - user.sendMsg(cvm.guacEncode('admin', '0', '0')); + user.protocol.sendAdminLoginResponse(false, undefined); return; } @@ -526,14 +527,22 @@ export default class CollabVMServer implements IProtocolHandlers { await this.SendFullScreenWithSize(user); } - this.clients.forEach((c) => c.sendMsg(cvm.guacEncode('adduser', '1', user.username!, user.rank.toString()))); + // Update rank + this.clients.forEach((c) => + c.protocol.sendAddUser([ + { + username: user.username!, + rank: user.rank + } + ]) + ); } async onAdminMonitor(user: User, node: string, command: string) { if (user.rank !== Rank.Admin) return; if (node !== this.Config.collabvm.node) return; let output = await this.VM.MonitorCommand(command); - user.sendMsg(cvm.guacEncode('admin', '2', String(output))); + user.protocol.sendAdminMonitorResponse(String(output)); } onAdminRestore(user: User, node: string): void { @@ -605,7 +614,7 @@ export default class CollabVMServer implements IProtocolHandlers { if (user.rank !== Rank.Admin && (user.rank !== Rank.Moderator || !this.Config.collabvm.moderatorPermissions.grabip)) return; let target = this.clients.find((c) => c.username === username); if (!target) return; - user.sendMsg(cvm.guacEncode('admin', '19', username, target.IP.address)); + user.protocol.sendAdminIPResponse(username, target.IP.address); } onAdminBypassTurn(user: User): void { diff --git a/cvmts/src/GuacamoleProtocol.ts b/cvmts/src/GuacamoleProtocol.ts index 258c863..f928455 100644 --- a/cvmts/src/GuacamoleProtocol.ts +++ b/cvmts/src/GuacamoleProtocol.ts @@ -11,12 +11,17 @@ export class GuacamoleProtocol implements IProtocol { name: 'CVMTS.GuacamoleProtocol' }); - private user: User | null = null; + protected user: User | null = null; init(u: User): void { this.user = u; } + dispose(): void { + this.user = null; + this.handlers = null; + } + setHandler(handlers: IProtocolHandlers): void { this.handlers = handlers; } @@ -237,6 +242,26 @@ export class GuacamoleProtocol implements IProtocol { } } + sendAdminLoginResponse(ok: boolean, modPerms: number | undefined): void { + if (ok) { + if (modPerms == undefined) { + this.user?.sendMsg(cvm.guacEncode('admin', '0', '1')); + } else { + this.user?.sendMsg(cvm.guacEncode('admin', '0', '3', modPerms.toString())); + } + } else { + this.user?.sendMsg(cvm.guacEncode('admin', '0', '0')); + } + } + + sendAdminMonitorResponse(output: string): void { + this.user?.sendMsg(cvm.guacEncode('admin', '2', output)); + } + + sendAdminIPResponse(username: string, ip: string): void { + this.user?.sendMsg(cvm.guacEncode('admin', '19', username, ip)); + } + sendChatMessage(username: string, message: string): void { this.user?.sendMsg(cvm.guacEncode('chat', username, message)); } diff --git a/cvmts/src/Protocol.ts b/cvmts/src/Protocol.ts index 1883772..ab5bc1f 100644 --- a/cvmts/src/Protocol.ts +++ b/cvmts/src/Protocol.ts @@ -77,6 +77,7 @@ export interface IProtocolHandlers { // Abstracts away all of the CollabVM protocol details export interface IProtocol { init(u: User): void; + dispose(): void; // Sets handler object. setHandler(handlers: IProtocolHandlers): void; @@ -98,6 +99,10 @@ export interface IProtocol { sendLoginResponse(ok: boolean, message: string | undefined): void; + sendAdminLoginResponse(ok: boolean, modPerms: number | undefined): void; + sendAdminMonitorResponse(output: string): void; + sendAdminIPResponse(username: string, ip: string): void; + sendChatMessage(username: '' | string, message: string): void; sendChatHistoryMessage(history: ProtocolChatHistory[]): void; diff --git a/cvmts/src/index.ts b/cvmts/src/index.ts index 1769dd7..d24541a 100644 --- a/cvmts/src/index.ts +++ b/cvmts/src/index.ts @@ -18,6 +18,7 @@ import { BanManager } from './BanManager.js'; import { QemuVMShim } from './vm/qemu.js'; import { TheProtocolManager } from './Protocol.js'; import { GuacamoleProtocol } from './GuacamoleProtocol.js'; +import { BinRectsProtocol } from './BinRectsProtocol.js'; let logger = pino(); @@ -99,8 +100,9 @@ async function start() { process.on('SIGINT', async () => await stop()); process.on('SIGTERM', async () => await stop()); - // Register protocol(s) + // Register protocol(s) that the server supports TheProtocolManager.registerProtocol("guacamole", () => new GuacamoleProtocol); + TheProtocolManager.registerProtocol("binary1", () => new BinRectsProtocol); await VM.Start(); // Start up the server