/* Copyright 2020-2021 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. @description MeshCentral Server IDER handler @author Ylian Saint-Hilaire @version v0.3.0 */ /*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; };