/* Phaneron - Clustered, accelerated and cloud-fit video server, pre-assembled and in kit form. Copyright (C) 2020 Streampunk Media Ltd. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . https://www.streampunk.media/ mailto:furnace@streampunk.media 14 Ormiscaig, Aultbea, Achnasheen, IV22 2JJ U.K. */ import * as net from 'net' import { Responses, responses207, responses218, responses220 } from './cmdResponses' import { Commands } from './commands' let cmds: Commands let ccgResponses = responses218 export async function processCommand(command: string[] | null, token = ''): Promise { if (!command) { return '400 ERROR' } if (command[0] === 'REQ') { if (command[2] !== 'PING') { return processCommand(command.slice(2), command[1]) } else { token = command[1] } } if (command[0] === 'SWITCH') { if (command[1] === '207') { ccgResponses = responses207 return '202 SWITCH 207 OK' } if (command[1] === '218') { ccgResponses = responses218 return '202 SWITCH 218 OK' } if (command[1] === '220') { ccgResponses = responses220 return '202 SWITCH 220 OK' } return '400 SWITCH ERROR' } if (command[0] === 'BYE') { return '***BYE***' } if (ccgResponses[command[0]]) { if (!(await cmds?.process(command))) { return `400 ERROR\r\n${command.join(' ')} NOT IMPLEMENTED` } const responseFn = ccgResponses[command[0]] let response: string | null = null if (typeof responseFn === 'function') { response = responseFn(command) } else { if (responseFn.none && command.length === 1) { response = (responseFn.none as (req: string[]) => string | null)(command) } else if (responseFn.number && command.length >= 2) { response = (responseFn.number as (req: string[]) => string | null)(command) } else if (responseFn.layer && command.length >= 3) { response = ((responseFn.layer as Responses)[command[2]] as ( req: string[] ) => string | null)(command) } else if (command.length >= 2 && responseFn[command[1]]) { response = (responseFn[command[1]] as (req: string[]) => string | null)(command) } if (response === null && responseFn.string && command.length >= 2) { response = (responseFn.string as (req: string[]) => string | null)(command) } } if (response) return token ? `RES ${token} ${response}` : response } return token ? `RES ${token} 400 ERROR\r\n${command.join(' ')}` : `400 ERROR\r\n${command.join(' ')}` } const server = net.createServer((c) => { console.log('client connected') c.on('end', () => { console.log('client disconnected') }) }) server.on('error', (err) => { throw err }) export async function start(commands?: Commands): Promise { if (commands) cmds = commands return new Promise((resolve, reject) => { let resolved = false server.once('error', (e) => { if (!resolved) reject(e) }) server.listen(5250, () => { resolved = true resolve('Phaneron server AMCP protocol running on port 5250') }) }) } export async function stop(): Promise { return new Promise((resolve, reject) => { let resolved = false server.once('error', (err) => { if (!resolved) reject(err) }) server.close((e) => { if (e) return reject(e) resolved = true resolve('Phaneron server closed') }) }) } server.on('listening', () => { // console.log('Phaneron server AMCP protocol running on port 5250') }) server.on('connection', (sock) => { let chunk = '' sock.on('data', async (input) => { chunk += input.toString() let eol = chunk.indexOf('\r\n') while (eol > -1) { const command = chunk.substring(0, eol) console.log(command) const result = await processCommand(command.toUpperCase().match(/"[^"]+"|""|\S+/g)) if (result === '***BYE***') { sock.destroy() break } sock.write(result.toString() + '\r\n') console.log(result) if (result === '202 KILL OK') { sock.destroy() stop().catch(console.error) break } chunk = chunk.substring(eol + 2) eol = chunk.indexOf('\r\n') } }) sock.on('error', console.error) sock.on('close', () => { console.log('client disconnect') }) }) export function version(version: string): void { if (version === '207') { ccgResponses = responses207 } if (version === '218') { ccgResponses = responses218 } if (version === '220') { ccgResponses = responses220 } } if (require.main === module) { start().then(console.log, console.error) }