/** * @description MeshCentral Server IDER handler * @author Ylian Saint-Hilaire & Bryan Roe * @copyright Intel Corporation 2018-2019 * @license Apache-2.0 * @version v0.0.1 */ /*jslint node: true */ /*jshint node: true */ /*jshint strict:false */ /*jshint -W097 */ /*jshint esversion: 6 */ "use strict"; // Construct a MeshAgent object, called upon connection module.exports.CreateAmtIderSession = function (parent, db, ws, req, args, domain, user) { const fs = require('fs'); const path = require('path'); const common = parent.common; const amtMeshRedirModule = require('./amt-redir-mesh.js'); const amtMeshIderModule = require('./amt-ider-module.js'); //console.log('New Server IDER session from ' + user.name); var obj = {}; obj.user = user; obj.domain = domain; obj.ider = null; // Disconnect this user obj.close = function (arg) { if ((arg == 1) || (arg == null)) { try { ws.close(); parent.parent.debug(1, 'Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket if (arg == 2) { try { ws._socket._parent.end(); parent.parent.debug(1, 'Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket }; try { // Check if the user is logged in if (user == null) { try { ws.close(); } catch (e) { } return; } // When data is received from the web socket ws.on('message', processWebSocketData); // If error, do nothing ws.on('error', function (err) { console.log(err); obj.close(0); }); // If the web socket is closed ws.on('close', function (req) { // Close the IDER session if (obj.ider) { obj.ider.Stop(); delete obj.ider; } }); // We are all set, start receiving data ws._socket.resume(); } catch (e) { console.log(e); } // Process incoming web socket data from the browser function processWebSocketData(msg) { var command, i = 0, mesh = null, meshid = null, nodeid = null, meshlinks = null, change = 0; try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; } if (common.validateString(command.action, 3, 32) == false) return; // Action must be a string between 3 and 32 chars switch (command.action) { case 'ping': { try { ws.send(JSON.stringify({ action: 'pong' })); } catch (ex) { } break; } case 'start': { // Get the list of disk images var domainx = 'domain' + ((domain.id == '') ? '' : ('-' + domain.id)); var useridx = user._id.split('/')[2]; var userPath = parent.parent.path.join(parent.parent.filespath, domainx, 'user-' + useridx); // Look for a list of disk images for the user to select. if (fs.existsSync(userPath)) { // Do something readFsRec(userPath, function (err, results) { var floppyImages = [], cdromImages = []; for (var i in results) { if (results[i].toLowerCase().endsWith('.img')) { floppyImages.push(results[i].substring(userPath.length + 1)); } else if (results[i].toLowerCase().endsWith('.iso')) { cdromImages.push(results[i].substring(userPath.length + 1)); } } var xx, sel = true, html = "
Select disk images & start type.
"; // Floppy image selection xx = ""; html += "
" + addHtmlValue("Floppy Image", xx) + "
"; // CDROM image selection sel = true; xx = ""; html += "
" + addHtmlValue("CDROM Image", xx) + "
"; // Start type xx = ""; html += "
" + addHtmlValue("Session Start", xx) + "
"; var js = "function iderServerCall() { return { ider: 1, floppyPath: Q('xxFloppyImagesSelect').value, cdromPath: Q('xxCdromImagesSelect').value, iderStart: Q('xxIderStartType').value }; }"; try { ws.send(JSON.stringify({ action: 'dialog', args: { html: html, js: js }, buttons: 3 })); } catch (ex) { } }); } else { // No user folder try { ws.send(JSON.stringify({ action: 'dialog', args: { html: 'No disk images found on remote server. Upload .img and .iso files to server "My Files" folder to enable this feature.' }, buttons: 2 })); } catch (ex) { } } break; } case 'dialogResponse': { if (command.args.ider == 1) { // Start IDER Session // Decode and validate file paths if ((command.args.floppyPath != null) && (typeof command.args.floppyPath != 'string')) { command.args.floppyPath = null; } else { command.args.floppyPath = decodeURIComponent(command.args.floppyPath); } if ((command.args.cdromPath != null) && (typeof command.args.cdromPath != 'string')) { command.args.cdromPath = null; } else { command.args.cdromPath = decodeURIComponent(command.args.cdromPath); } // TODO: Double check that "." or ".." are not used. if ((command.args.floppyPath != null) && (command.args.floppyPath.indexOf("..") >= 0)) { delete command.args.floppyPath; } if ((command.args.cdromPath != null) && (command.args.cdromPath.indexOf("..") >= 0)) { delete command.args.cdromPath; } // Get the disk image paths var domainx = 'domain' + ((domain.id == '') ? '' : ('-' + domain.id)); var useridx = user._id.split('/')[2]; var floppyPath = null, cdromPath = null; if (command.args.floppyPath) { floppyPath = parent.parent.path.join(parent.parent.filespath, domainx, 'user-' + useridx, command.args.floppyPath); } if (command.args.cdromPath) { cdromPath = parent.parent.path.join(parent.parent.filespath, domainx, 'user-' + useridx, command.args.cdromPath); } // Setup the IDER session obj.ider = amtMeshRedirModule.CreateAmtRedirect(amtMeshIderModule.CreateAmtRemoteIder(parent, parent.parent), domain, user, parent, parent.parent); obj.ider.onStateChanged = onIderStateChange; obj.ider.m.iderStart = command.args.iderStart; obj.ider.m.sectorStats = iderSectorStats; obj.ider.tlsv1only = req.query.tlsv1only; // Setup disk images var iderError = obj.ider.m.diskSetup(floppyPath, cdromPath); // Error with the disk images, unable to start IDER if (iderError != 0) { try { ws.send(JSON.stringify({ action: "error", code: iderError })); } catch (ex) { } break; } // Start the IDER session obj.ider.Start(req.query.host, req.query.port, req.query.tls); } break; } default: { // Unknown user action console.log('Unknown IDER action from user ' + user.name + ': ' + command.action + '.'); break; } } } function onIderStateChange(sender, state) { try { ws.send(JSON.stringify({ action: 'state', state: state })); } catch (ex) { } switch (state) { case 0: // Close the websocket connection and clean up. obj.ider.onStateChanged = null; obj.ider.m.sectorStats = null; obj.ider = null; obj.close(); break; } } function iderSectorStats(mode, dev, total, start, len) { try { ws.send(JSON.stringify({ action: 'stats', mode: mode, dev: dev, total: total, start: start, len: len, toAmt: obj.ider.m.bytesToAmt, fromAmt: obj.ider.m.bytesFromAmt })); } catch (ex) { } } // Recursivly read all of the files in a fonder function readFsRec(dir, func) { var results = []; fs.readdir(dir, function (err, list) { if (err) return func(err); var pending = list.length; if (!pending) return func(null, results); list.forEach(function (file) { file = path.resolve(dir, file); fs.stat(file, function (err, stat) { if (stat && stat.isDirectory()) { readFsRec(file, function (err, res) { results = results.concat(res); if (!--pending) func(null, results); }); } else { results.push(file); if (!--pending) func(null, results); } }); }); }); }; function addHtmlValue(t, v) { return '
' + v + '
' + t + '
'; } return obj; };