diff --git a/agents/MeshCmd-signed.exe b/agents/MeshCmd-signed.exe index 728078b3..83a6c917 100644 Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ diff --git a/agents/MeshCmd64-signed.exe b/agents/MeshCmd64-signed.exe index 1539f1bf..bb44b058 100644 Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ diff --git a/agents/MeshService-signed.exe b/agents/MeshService-signed.exe index 7ed930ce..8fb8cd18 100644 Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ diff --git a/agents/MeshService.exe b/agents/MeshService.exe index bfe38e5c..703d64c0 100644 Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ diff --git a/agents/MeshService64-signed.exe b/agents/MeshService64-signed.exe index 245e2595..33bc7312 100644 Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ diff --git a/agents/MeshService64.exe b/agents/MeshService64.exe index 9a661d42..7ffdd59a 100644 Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ diff --git a/agents/modules_meshcmd/promise.js b/agents/modules_meshcmd/promise.js new file mode 100644 index 00000000..e764a630 --- /dev/null +++ b/agents/modules_meshcmd/promise.js @@ -0,0 +1,185 @@ +/* +Copyright 2018 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. +*/ + +var refTable = {}; + +function Promise(promiseFunc) +{ + this._ObjectID = 'promise'; + this._internal = { promise: this, func: promiseFunc, completed: false, errors: false, completedArgs: [] }; + require('events').EventEmitter.call(this._internal); + this._internal.on('_eventHook', function (eventName, eventCallback) + { + //console.log('hook', eventName, 'errors/' + this.errors + ' completed/' + this.completed); + var r = null; + + if (eventName == 'resolved' && !this.errors && this.completed) + { + r = eventCallback.apply(this, this.completedArgs); + if(r!=null) + { + this.emit_returnValue('resolved', r); + } + } + if (eventName == 'rejected' && this.errors && this.completed) + { + eventCallback.apply(this, this.completedArgs); + } + if (eventName == 'settled' && this.completed) + { + eventCallback.apply(this, []); + } + }); + this._internal.resolver = function _resolver() + { + _resolver._self.errors = false; + _resolver._self.completed = true; + _resolver._self.completedArgs = []; + var args = ['resolved']; + if (this.emit_returnValue && this.emit_returnValue('resolved') != null) + { + _resolver._self.completedArgs.push(this.emit_returnValue('resolved')); + args.push(this.emit_returnValue('resolved')); + } + else + { + for (var a in arguments) + { + _resolver._self.completedArgs.push(arguments[a]); + args.push(arguments[a]); + } + } + _resolver._self.emit.apply(_resolver._self, args); + _resolver._self.emit('settled'); + }; + this._internal.rejector = function _rejector() + { + _rejector._self.errors = true; + _rejector._self.completed = true; + _rejector._self.completedArgs = []; + var args = ['rejected']; + for (var a in arguments) + { + _rejector._self.completedArgs.push(arguments[a]); + args.push(arguments[a]); + } + + _rejector._self.emit.apply(_rejector._self, args); + _rejector._self.emit('settled'); + }; + this.catch = function(func) + { + this._internal.once('settled', func); + } + this.finally = function (func) + { + this._internal.once('settled', func); + }; + this.then = function (resolved, rejected) + { + if (resolved) { this._internal.once('resolved', resolved); } + if (rejected) { this._internal.once('rejected', rejected); } + + var retVal = new Promise(function (r, j) { }); + + this._internal.once('resolved', retVal._internal.resolver); + this._internal.once('rejected', retVal._internal.rejector); + return (retVal); + }; + + this._internal.resolver._self = this._internal; + this._internal.rejector._self = this._internal;; + + try + { + promiseFunc.call(this, this._internal.resolver, this._internal.rejector); + } + catch(e) + { + this._internal.errors = true; + this._internal.completed = true; + this._internal.completedArgs = [e]; + this._internal.emit('rejected', e); + this._internal.emit('settled'); + } + + if(!this._internal.completed) + { + // Save reference of this object + refTable[this._internal._hashCode()] = this._internal; + this._internal.once('settled', function () { refTable[this._hashCode()] = null; }); + } +} + +Promise.resolve = function resolve() +{ + var retVal = new Promise(function (r, j) { }); + var args = []; + for (var i in arguments) + { + args.push(arguments[i]); + } + retVal._internal.resolver.apply(retVal._internal, args); + return (retVal); +}; +Promise.reject = function reject() { + var retVal = new Promise(function (r, j) { }); + var args = []; + for (var i in arguments) { + args.push(arguments[i]); + } + retVal._internal.rejector.apply(retVal._internal, args); + return (retVal); +}; +Promise.all = function all(promiseList) +{ + var ret = new Promise(function (res, rej) + { + this.__rejector = rej; + this.__resolver = res; + this.__promiseList = promiseList; + this.__done = false; + this.__count = 0; + }); + + for (var i in promiseList) + { + promiseList[i].then(function () + { + // Success + if(++ret.__count == ret.__promiseList.length) + { + ret.__done = true; + ret.__resolver(ret.__promiseList); + } + }, function (arg) + { + // Failure + if(!ret.__done) + { + ret.__done = true; + ret.__rejector(arg); + } + }); + } + if (promiseList.length == 0) + { + ret.__resolver(promiseList); + } + return (ret); +}; + +module.exports = Promise; \ No newline at end of file diff --git a/agents/modules_meshcmd/service-manager.js b/agents/modules_meshcmd/service-manager.js index f6eb9e25..729beea6 100644 --- a/agents/modules_meshcmd/service-manager.js +++ b/agents/modules_meshcmd/service-manager.js @@ -109,29 +109,16 @@ function serviceManager() } return admin; }; + this.getProgramFolder = function getProgramFolder() { + if (require('os').arch() == 'x64') { // 64 bit Windows + if (this.GM.PointerSize == 4) { return process.env['ProgramFiles(x86)']; } // 32 Bit App + return process.env['ProgramFiles']; // 64 bit App + } + return process.env['ProgramFiles']; // 32 bit Windows + }; this.getServiceFolder = function getServiceFolder() { - var destinationFolder = null; - if (require('os').arch() == 'x64') - { - // 64 bit Windows - if (this.GM.PointerSize == 4) - { - // 32 Bit App - destinationFolder = process.env['ProgramFiles(x86)']; - } - else - { - // 64 bit App - destinationFolder = process.env['ProgramFiles']; - } - } - else - { - // 32 bit Windows - destinationFolder = process.env['ProgramFiles']; - } - return (destinationFolder + '\\mesh'); + return this.getProgramFolder() + '\\mesh'; }; this.enumerateService = function () { diff --git a/meshcentral.js b/meshcentral.js index 964bd410..91a2cc33 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -6,23 +6,29 @@ * @version v0.0.1 */ -'use strict'; +/*xjslint node: true */ +/*xjslint plusplus: true */ +/*xjslint maxlen: 256 */ +/*jshint node: true */ +/*jshint strict: false */ +/*jshint esversion: 6 */ +"use strict"; // If app metrics is available if (process.argv[2] == '--launch') { try { require('appmetrics-dash').monitor({ url: '/', title: 'MeshCentral', port: 88, host: '127.0.0.1' }); } catch (e) { } } function CreateMeshCentralServer(config, args) { var obj = {}; - obj.db; - obj.webserver; - obj.redirserver; - obj.mpsserver; - obj.swarmserver; - obj.mailserver; - obj.amtEventHandler; - obj.amtScanner; - obj.meshScanner; - obj.letsencrypt; + obj.db = null; + obj.webserver = null; + obj.redirserver = null; + obj.mpsserver = null; + obj.swarmserver = null; + obj.mailserver = null; + obj.amtEventHandler = null; + obj.amtScanner = null; + obj.meshScanner = null; + obj.letsencrypt = null; obj.eventsDispatch = {}; obj.fs = require('fs'); obj.path = require('path'); @@ -78,13 +84,14 @@ function CreateMeshCentralServer(config, args) { // Start the Meshcentral server obj.Start = function () { + var i; try { require('./pass').hash('test', function () { }); } catch (e) { console.log('Old version of node, must upgrade.'); return; } // TODO: Not sure if this test works or not. // Check for invalid arguments var validArguments = ['_', 'notls', 'user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'cert', 'mpscert', 'deletedomain', 'deletedefaultdomain', 'showall', 'showusers', 'shownodes', 'showmeshes', 'showevents', 'showpower', 'clearpower', 'showiplocations', 'help', 'exactports', 'install', 'uninstall', 'start', 'stop', 'restart', 'debug', 'filespath', 'datapath', 'noagentupdate', 'launch', 'noserverbackup', 'mongodb', 'mongodbcol', 'wanonly', 'lanonly', 'nousers', 'mpsdebug', 'mpspass', 'ciralocalfqdn', 'dbexport', 'dbimport', 'selfupdate', 'tlsoffload', 'userallowedip', 'fastcert', 'swarmport', 'swarmdebug', 'logintoken', 'logintokenkey', 'logintokengen', 'logintokengen', 'mailtokengen', 'admin', 'unadmin', 'sessionkey', 'sessiontime', 'minify']; for (var arg in obj.args) { obj.args[arg.toLocaleLowerCase()] = obj.args[arg]; if (validArguments.indexOf(arg.toLocaleLowerCase()) == -1) { console.log('Invalid argument "' + arg + '", use --help.'); return; } } if (obj.args.mongodb == true) { console.log('Must specify: --mongodb [connectionstring] \r\nSee https://docs.mongodb.com/manual/reference/connection-string/ for MongoDB connection string.'); return; } - for (var i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence. + for (i in obj.config.settings) { obj.args[i] = obj.config.settings[i]; } // Place all settings into arguments, arguments have already been placed into settings so arguments take precedence. if ((obj.args.help == true) || (obj.args['?'] == true)) { console.log('MeshCentral2 Beta 2, a web-based remote computer management web portal.\r\n'); @@ -106,19 +113,19 @@ function CreateMeshCentralServer(config, args) { console.log(' country and organization can optionaly be set.'); return; } - + // Check if we need to install, start, stop, remove ourself as a background service if ((obj.service != null) && ((obj.args.install == true) || (obj.args.uninstall == true) || (obj.args.start == true) || (obj.args.stop == true) || (obj.args.restart == true))) { var env = [], xenv = ['user', 'port', 'aliasport', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug']; - for (var i in xenv) { if (obj.args[xenv[i]] != null) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables. - var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: obj.path.join(__dirname, 'winservice.js'), env: env, wait: 2, grow: .5 }); + for (i in xenv) { if (obj.args[xenv[i]] != null) { env.push({ name: 'mesh' + xenv[i], value: obj.args[xenv[i]] }); } } // Set some args as service environement variables. + var svc = new obj.service({ name: 'MeshCentral', description: 'MeshCentral Remote Management Server', script: obj.path.join(__dirname, 'winservice.js'), env: env, wait: 2, grow: 0.5 }); svc.on('install', function () { console.log('MeshCentral service installed.'); svc.start(); }); svc.on('uninstall', function () { console.log('MeshCentral service uninstalled.'); process.exit(); }); svc.on('start', function () { console.log('MeshCentral service started.'); process.exit(); }); svc.on('stop', function () { console.log('MeshCentral service stopped.'); if (obj.args.stop) { process.exit(); } if (obj.args.restart) { console.log('Holding 5 seconds...'); setTimeout(function () { svc.start(); }, 5000); } }); svc.on('alreadyinstalled', function () { console.log('MeshCentral service already installed.'); process.exit(); }); svc.on('invalidinstallation', function () { console.log('Invalid MeshCentral service installation.'); process.exit(); }); - + if (obj.args.install == true) { try { svc.install(); } catch (e) { logException(e); } } if (obj.args.stop == true || obj.args.restart == true) { try { svc.stop(); } catch (e) { logException(e); } } if (obj.args.start == true || obj.args.restart == true) { try { svc.start(); } catch (e) { logException(e); } } @@ -132,7 +139,7 @@ function CreateMeshCentralServer(config, args) { } else { // if "--launch" is not specified, launch the server as a child process. var startLine = ''; - for (var i in process.argv) { + for (i in process.argv) { var arg = process.argv[i]; if (arg.length > 0) { if (startLine.length > 0) startLine += ' '; @@ -141,7 +148,7 @@ function CreateMeshCentralServer(config, args) { } obj.launchChildServer(startLine); } - } + }; // Launch MeshCentral as a child server and monitor it. obj.launchChildServer = function (startLine) { @@ -166,7 +173,7 @@ function CreateMeshCentralServer(config, args) { console.log(error); console.log('ERROR: MeshCentral failed with critical error, check MeshErrors.txt. Restarting in 5 seconds...'); setTimeout(function () { obj.launchChildServer(startLine); }, 5000); - } + } } }); xprocess.stdout.on('data', function (data) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } if (data.indexOf('Updating settings folder...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Updating server certificates...') >= 0) { xprocess.xrestart = 1; } else if (data.indexOf('Server Ctrl-C exit...') >= 0) { xprocess.xrestart = 2; } else if (data.indexOf('Starting self upgrade...') >= 0) { xprocess.xrestart = 3; } console.log(data); }); @@ -175,7 +182,7 @@ function CreateMeshCentralServer(config, args) { if (data[data.length - 1] == '\n') { data = data.substring(0, data.length - 1); } obj.fs.appendFileSync(obj.getConfigFilePath('mesherrors.txt'), '-------- ' + new Date().toLocaleString() + ' --------\r\n\r\n' + data + '\r\n\r\n\r\n'); }); xprocess.on('close', function (code) { if ((code != 0) && (code != 123)) { /* console.log("Exited with code " + code); */ } }); - } + }; // Get current and latest MeshCentral server versions using NPM obj.getLatestServerVersion = function (callback) { @@ -190,44 +197,45 @@ function CreateMeshCentralServer(config, args) { if (code == 0) { try { latestVer = xprocess.data.split(' ').join('').split('\r').join('').split('\n').join(''); } catch (e) { } } callback(obj.currentVer, latestVer); }); - } + }; // Initiate server self-update - obj.performServerUpdate = function () { console.log('Starting self upgrade...'); process.exit(200); } + obj.performServerUpdate = function () { console.log('Starting self upgrade...'); process.exit(200); }; // Initiate server self-update - obj.performServerCertUpdate = function () { console.log('Updating server certificates...'); process.exit(200); } + obj.performServerCertUpdate = function () { console.log('Updating server certificates...'); process.exit(200); }; obj.StartEx = function () { + var i; //var wincmd = require('node-windows'); //wincmd.list(function (svc) { console.log(svc); }, true); - + // Write the server state obj.updateServerState('state', 'starting'); // Look to see if data and/or file path is specified if (obj.args.datapath) { obj.datapath = obj.args.datapath; } if (obj.args.filespath) { obj.filespath = obj.args.filespath; } - + // Read environment variables. For a subset of arguments, we allow them to be read from environment variables. var xenv = ['user', 'port', 'mpsport', 'mpsaliasport', 'redirport', 'exactport', 'debug']; - for (var i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } } - + for (i in xenv) { if ((obj.args[xenv[i]] == null) && (process.env['mesh' + xenv[i]])) { obj.args[xenv[i]] = obj.common.toNumber(process.env['mesh' + xenv[i]]); } } + // Validate the domains, this is used for multi-hosting if (obj.config.domains == null) { obj.config.domains = {}; } if (obj.config.domains[''] == null) { obj.config.domains[''] = {}; } if (obj.config.domains[''].dns != null) { console.log("ERROR: Default domain can't have a DNS name."); return; } - var xdomains = {}; for (var i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 2'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains; + var xdomains = {}; for (i in obj.config.domains) { if (!obj.config.domains[i].title) { obj.config.domains[i].title = 'MeshCentral'; } if (!obj.config.domains[i].title2) { obj.config.domains[i].title2 = '2.0 Beta 2'; } xdomains[i.toLowerCase()] = obj.config.domains[i]; } obj.config.domains = xdomains; var bannedDomains = ['public', 'private', 'images', 'scripts', 'styles', 'views']; // List of banned domains - for (var i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } } - for (var i in obj.config.domains) { + for (i in obj.config.domains) { for (var j in bannedDomains) { if (i == bannedDomains[j]) { console.log("ERROR: Domain '" + i + "' is not allowed domain name in ./data/config.json."); return; } } } + for (i in obj.config.domains) { if (obj.config.domains[i].dns == null) { obj.config.domains[i].url = (i == '') ? '/' : ('/' + i + '/'); } else { obj.config.domains[i].url = '/'; } obj.config.domains[i].id = i; if (typeof obj.config.domains[i].userallowedip == 'string') { obj.config.domains[i].userallowedip = null; if (obj.config.domains[i].userallowedip != "") { obj.config.domains[i].userallowedip = obj.config.domains[i].userallowedip.split(','); } } } // Log passed arguments into Windows Service Log - //if (obj.servicelog != null) { var s = ''; for (var i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); } + //if (obj.servicelog != null) { var s = ''; for (i in obj.args) { if (i != '_') { if (s.length > 0) { s += ', '; } s += i + "=" + obj.args[i]; } } logInfoEvent('MeshServer started with arguments: ' + s); } // Look at passed in arguments if ((obj.args.user != null) && (typeof obj.args.user != 'string')) { delete obj.args.user; } @@ -269,12 +277,12 @@ function CreateMeshCentralServer(config, args) { if (obj.args.dbimport == true) { obj.args.dbimport = obj.getConfigFilePath('meshcentral.db.json'); } var json = null, json2 = "", badCharCount = 0; try { json = obj.fs.readFileSync(obj.args.dbimport, { encoding: 'utf8' }); } catch (e) { console.log('Invalid JSON file: ' + obj.args.dbimport + '.'); process.exit(); } - for (var i = 0; i < json.length; i++) { if (json.charCodeAt(i) >= 32) { json2 += json[i]; } else { var tt = json.charCodeAt(i); if (tt != 10 && tt != 13) { badCharCount++; } } } // Remove all bad chars + for (i = 0; i < json.length; i++) { if (json.charCodeAt(i) >= 32) { json2 += json[i]; } else { var tt = json.charCodeAt(i); if (tt != 10 && tt != 13) { badCharCount++; } } } // Remove all bad chars if (badCharCount > 0) { console.log(badCharCount + ' invalid character(s) where removed.'); } try { json = JSON.parse(json2); } catch (e) { console.log('Invalid JSON format: ' + obj.args.dbimport + ': ' + e); process.exit(); } if ((json == null) || (typeof json.length != 'number') || (json.length < 1)) { console.log('Invalid JSON format: ' + obj.args.dbimport + '.'); } - for (var i in json) { if ((json[i].type == "mesh") && (json[i].links != null)) { for (var j in json[i].links) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].links[esc] = json[i].links[j]; delete json[i].links[j]; } } } } // Escape MongoDB invalid field chars - //for (var i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname + for (i in json) { if ((json[i].type == "mesh") && (json[i].links != null)) { for (var j in json[i].links) { var esc = obj.common.escapeFieldName(j); if (esc !== j) { json[i].links[esc] = json[i].links[j]; delete json[i].links[j]; } } } } // Escape MongoDB invalid field chars + //for (i in json) { if ((json[i].type == "node") && (json[i].host != null)) { json[i].rname = json[i].host; delete json[i].host; } } // DEBUG: Change host to rname obj.db.RemoveAll(function () { obj.db.InsertMany(json, function (err) { if (err != null) { console.log(err); } else { console.log('Imported ' + json.length + ' objects(s) from ' + obj.args.dbimport + '.'); } process.exit(); }); }); return; } @@ -333,7 +341,7 @@ function CreateMeshCentralServer(config, args) { obj.db.Get('dbconfig', function (err, dbconfig) { if (dbconfig.length == 1) { obj.dbconfig = dbconfig[0]; } else { obj.dbconfig = { _id: 'dbconfig', version: 1 }; } if (obj.dbconfig.amtWsEventSecret == null) { require('crypto').randomBytes(32, function (err, buf) { obj.dbconfig.amtWsEventSecret = buf.toString('hex'); obj.db.Set(obj.dbconfig); }); } - + // This is used by the user to create a username/password for a Intel AMT WSMAN event subscription if (obj.args.getwspass) { if (obj.args.getwspass.length == 64) { @@ -367,12 +375,12 @@ function CreateMeshCentralServer(config, args) { } }); }); - } + }; // Done starting the redirection server, go on to load the server certificates obj.StartEx2 = function () { // Load server certificates - obj.certificateOperations = require('./certoperations.js').CertificateOperations() + obj.certificateOperations = require('./certoperations.js').CertificateOperations(); obj.certificateOperations.GetMeshServerCertificate(obj, obj.args, obj.config, function (certs) { if (obj.config.letsencrypt == null) { obj.StartEx3(certs); // Just use the configured certificates @@ -387,10 +395,11 @@ function CreateMeshCentralServer(config, args) { } } }); - } + }; // Start the server with the given certificates obj.StartEx3 = function (certs) { + var i; obj.certificates = certs; obj.certificateOperations.acceleratorStart(certs); // Set the state of the accelerators @@ -398,14 +407,14 @@ function CreateMeshCentralServer(config, args) { if (obj.certificates.CommonName == 'un-configured') { console.log('Server name not configured, running in LAN-only mode.'); obj.args.lanonly = true; } // Check that no sub-domains have the same DNS as the parent - for (var i in obj.config.domains) { + for (i in obj.config.domains) { if ((obj.config.domains[i].dns != null) && (obj.certificates.CommonName.toLowerCase() === obj.config.domains[i].dns.toLowerCase())) { console.log("ERROR: Server sub-domain can't have same DNS name as the parent."); process.exit(0); return; } } // Load the list of mesh agents and install scripts - if (obj.args.noagentupdate == 1) { for (var i in obj.meshAgentsArchitectureNumbers) { obj.meshAgentsArchitectureNumbers[i].update = false; } } + if (obj.args.noagentupdate == 1) { for (i in obj.meshAgentsArchitectureNumbers) { obj.meshAgentsArchitectureNumbers[i].update = false; } } obj.updateMeshAgentsTable(function () { obj.updateMeshAgentInstallScripts(); @@ -460,7 +469,7 @@ function CreateMeshCentralServer(config, args) { obj.maintenanceTimer = setInterval(obj.maintenanceActions, 1000 * 60 * 60); // Run this every hour // Dispatch an event that the server is now running - obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' }) + obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' }); // Load the login cookie encryption key from the database if allowed if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) { @@ -473,12 +482,12 @@ function CreateMeshCentralServer(config, args) { }); } - obj.debug(1, 'Server started'); + //obj.debug(1, 'Server started'); if (obj.args.nousers == true) { obj.updateServerState('nousers', '1'); } obj.updateServerState('state', 'running'); }); }); - } + }; // Perform maintenance operations (called every hour) obj.maintenanceActions = function () { @@ -499,7 +508,7 @@ function CreateMeshCentralServer(config, args) { // Perform other database cleanup obj.db.cleanup(); - } + }; // Stop the Meshcentral server obj.Stop = function (restoreFile) { @@ -507,7 +516,7 @@ function CreateMeshCentralServer(config, args) { if (!obj.db) return; // Dispatch an event saying the server is now stopping - obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' }) + obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'stopped', msg: 'Server stopped' }); // Set all nodes to power state of unknown (0) var record = { type: 'power', time: Date.now(), node: '*', power: 0, s: 2 }; @@ -543,7 +552,7 @@ function CreateMeshCentralServer(config, args) { }); } }); - zipfile.on("end", function () { setTimeout(function () { fs.unlinkSync(restoreFile); process.exit(123); }); }); + zipfile.on("end", function () { setTimeout(function () { obj.fs.unlinkSync(restoreFile); process.exit(123); }); }); }); } else { obj.debug(1, 'Server stopped'); @@ -553,25 +562,25 @@ function CreateMeshCentralServer(config, args) { // Update the server state obj.updateServerState('state', 'stopped'); - } + }; // Event Dispatch obj.AddEventDispatch = function (ids, target) { obj.debug(3, 'AddEventDispatch', ids); for (var i in ids) { var id = ids[i]; if (!obj.eventsDispatch[id]) { obj.eventsDispatch[id] = [target]; } else { obj.eventsDispatch[id].push(target); } } - } + }; obj.RemoveEventDispatch = function (ids, target) { obj.debug(3, 'RemoveEventDispatch', id); - for (var i in ids) { var id = ids[i]; if (obj.eventsDispatch[id]) { var j = obj.eventsDispatch[id].indexOf(target); if (j >= 0) { array.splice(j, 1); } } } - } + for (var i in ids) { var id = ids[i]; if (obj.eventsDispatch[id]) { var j = obj.eventsDispatch[id].indexOf(target); if (j >= 0) { obj.eventsDispatch[id].splice(j, 1); } } } + }; obj.RemoveEventDispatchId = function (id) { obj.debug(3, 'RemoveEventDispatchId', id); if (obj.eventsDispatch[id] != null) { delete obj.eventsDispatch[id]; } - } + }; obj.RemoveAllEventDispatch = function (target) { obj.debug(3, 'RemoveAllEventDispatch'); for (var i in obj.eventsDispatch) { var j = obj.eventsDispatch[i].indexOf(target); if (j >= 0) { obj.eventsDispatch[i].splice(j, 1); } } - } + }; obj.DispatchEvent = function (ids, source, event, fromPeerServer) { // If the database is not setup, exit now. if (!obj.db) return; @@ -596,21 +605,21 @@ function CreateMeshCentralServer(config, args) { } } if ((fromPeerServer == null) && (obj.multiServer != null) && ((typeof event != 'object') || (event.nopeers != 1))) { obj.multiServer.DispatchEvent(ids, source, event); } - } + }; // Get the connection state of a node - obj.GetConnectivityState = function (nodeid) { return obj.connectivityByNode[nodeid]; } + obj.GetConnectivityState = function (nodeid) { return obj.connectivityByNode[nodeid]; }; // Get the routing server id for a given node and connection type, can never be self. obj.GetRoutingServerId = function (nodeid, connectType) { if (obj.multiServer == null) return null; - for (serverid in obj.peerConnectivityByNode) { + for (var serverid in obj.peerConnectivityByNode) { if (serverid == obj.serverId) continue; var state = obj.peerConnectivityByNode[serverid][nodeid]; if ((state != null) && ((state.connectivity & connectType) != 0)) { return { serverid: serverid, meshid: state.meshid }; } } return null; - } + }; // Update the connection state of a node when in multi-server mode // Update obj.connectivityByNode using obj.peerConnectivityByNode for the list of nodes in argument @@ -618,8 +627,8 @@ function CreateMeshCentralServer(config, args) { for (var nodeid in nodeids) { var meshid = null, state = null, oldConnectivity = 0, oldPowerState = 0, newConnectivity = 0, newPowerState = 0; var oldState = obj.connectivityByNode[nodeid]; - if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; } - for (serverid in obj.peerConnectivityByNode) { + if (oldState != null) { meshid = oldState.meshid; oldConnectivity = oldState.connectivity; oldPowerState = oldState.powerState; } + for (var serverid in obj.peerConnectivityByNode) { var peerState = obj.peerConnectivityByNode[serverid][nodeid]; if (peerState != null) { if (state == null) { @@ -652,7 +661,7 @@ function CreateMeshCentralServer(config, args) { obj.DispatchEvent(['*', meshid], obj, { action: 'nodeconnect', meshid: meshid, nodeid: nodeid, conn: newConnectivity, pwr: newPowerState, nolog: 1, nopeers: 1 }); } } - } + }; // Set the connectivity state of a node and setup the server so that messages can be routed correctly. // meshId: mesh identifier of format mesh/domain/meshidhex @@ -660,8 +669,8 @@ function CreateMeshCentralServer(config, args) { // connectTime: time of connection, milliseconds elapsed since the UNIX epoch. // connectType: Bitmask, 1 = MeshAgent, 2 = Intel AMT CIRA, 4 = Intel AMT local. // powerState: Value, 0 = Unknown, 1 = S0 power on, 2 = S1 Sleep, 3 = S2 Sleep, 4 = S3 Sleep, 5 = S4 Hibernate, 6 = S5 Soft-Off, 7 = Present - var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local']; - var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; + //var connectTypeStrings = ['', 'MeshAgent', 'Intel AMT CIRA', '', 'Intel AMT local']; + //var powerStateStrings = ['Unknown', 'Powered', 'Sleep', 'Sleep', 'Deep Sleep', 'Hibernating', 'Soft-Off', 'Present']; obj.SetConnectivityState = function (meshid, nodeid, connectTime, connectType, powerState, serverid) { //console.log('SetConnectivity for ' + nodeid.substring(0, 16) + ', Type: ' + connectTypeStrings[connectType] + ', Power: ' + powerStateStrings[powerState] + (serverid == null ? ('') : (', ServerId: ' + serverid))); if ((serverid == null) && (obj.multiServer != null)) { obj.multiServer.DispatchMessage({ action: 'SetConnectivityState', meshid: meshid, nodeid: nodeid, connectTime: connectTime, connectType: connectType, powerState: powerState }); } @@ -716,7 +725,7 @@ function CreateMeshCentralServer(config, args) { // Set node power state if (connectType == 1) { state.agentPower = powerState; } else if (connectType == 2) { state.ciraPower = powerState; } else if (connectType == 4) { state.amtPower = powerState; } - var powerState = 0; + var powerState = 0, oldPowerState = state.powerState; if ((state.connectivity & 1) != 0) { powerState = state.agentPower; } else if ((state.connectivity & 2) != 0) { powerState = state.ciraPower; } else if ((state.connectivity & 4) != 0) { powerState = state.amtPower; } if ((state.powerState == null) || (state.powerState != powerState)) { state.powerState = powerState; @@ -731,7 +740,7 @@ function CreateMeshCentralServer(config, args) { var x = {}; x[nodeid] = 1; obj.UpdateConnectivityState(x); } - } + }; // Clear the connectivity state of a node and setup the server so that messages can be routed correctly. // meshId: mesh identifier of format mesh/domain/meshidhex @@ -798,7 +807,7 @@ function CreateMeshCentralServer(config, args) { var x = {}; x[nodeid] = 1; obj.UpdateConnectivityState(x); } - } + }; // Update the default mesh core obj.updateMeshCoreTimer = 'notset'; @@ -845,9 +854,9 @@ function CreateMeshCentralServer(config, args) { obj.fs.watch(obj.path.join(meshcorePath, 'meshcore.js'), function (eventType, filename) { if (obj.updateMeshCoreTimer != null) { clearTimeout(obj.updateMeshCoreTimer); obj.updateMeshCoreTimer = null; } obj.updateMeshCoreTimer = setTimeout(function () { obj.updateMeshCore(); console.log('Updated meshcore.js.'); }, 5000); - }) + }); } - } + }; // Update the default meshcmd obj.updateMeshCmdTimer = 'notset'; @@ -860,7 +869,7 @@ function CreateMeshCentralServer(config, args) { obj.defaultMeshCmd = null; if (func != null) { func(false); } // meshcmd.js not found } } - + // Read meshcore.js and all .js files in the modules folder. var moduleAdditions = 'var addedModules = [];', modulesDir = null; var meshCmd = obj.fs.readFileSync(obj.path.join(meshcmdPath, 'meshcmd.js')).toString().replace("'***Mesh*Cmd*Version***'", '\'' + obj.currentVer + '\''); @@ -886,9 +895,9 @@ function CreateMeshCentralServer(config, args) { obj.fs.watch(obj.path.join(meshcmdPath, 'meshcmd.js'), function (eventType, filename) { if (obj.updateMeshCmdTimer != null) { clearTimeout(obj.updateMeshCmdTimer); obj.updateMeshCmdTimer = null; } obj.updateMeshCmdTimer = setTimeout(function () { obj.updateMeshCmd(); console.log('Updated meshcmd.js.'); }, 5000); - }) + }); } - } + }; // List of possible mesh agent install scripts var meshAgentsInstallScriptList = { @@ -903,7 +912,7 @@ function CreateMeshCentralServer(config, args) { var stream = null; try { stream = obj.fs.createReadStream(scriptpath); - stream.on('data', function (data) { this.hash.update(data, 'binary') }); + stream.on('data', function (data) { this.hash.update(data, 'binary'); }); stream.on('error', function (data) { // If there is an error reading this file, make sure this agent is not in the agent table if (obj.meshAgentInstallScripts[this.info.id] != null) { delete obj.meshAgentInstallScripts[this.info.id]; } @@ -915,7 +924,7 @@ function CreateMeshCentralServer(config, args) { obj.meshAgentInstallScripts[this.info.id].path = this.agentpath; obj.meshAgentInstallScripts[this.info.id].url = ((obj.args.notls == true) ? 'http://' : 'https://') + obj.certificates.CommonName + ':' + obj.args.port + '/meshagents?script=' + this.info.id; var stats = null; - try { stats = obj.fs.statSync(this.agentpath) } catch (e) { } + try { stats = obj.fs.statSync(this.agentpath); } catch (e) { } if (stats != null) { obj.meshAgentInstallScripts[this.info.id].size = stats.size; } }); stream.info = meshAgentsInstallScriptList[scriptid]; @@ -923,7 +932,7 @@ function CreateMeshCentralServer(config, args) { stream.hash = obj.crypto.createHash('sha384', stream); } catch (e) { } } - } + }; // List of possible mesh agents obj.meshAgentsArchitectureNumbers = { @@ -962,10 +971,10 @@ function CreateMeshCentralServer(config, args) { var archcount = 0; for (var archid in obj.meshAgentsArchitectureNumbers) { var agentpath = obj.path.join(__dirname, 'agents', obj.meshAgentsArchitectureNumbers[archid].localname); - + // Fetch all the agent binary information var stats = null; - try { stats = obj.fs.statSync(agentpath) } catch (e) { } + try { stats = obj.fs.statSync(agentpath); } catch (e) { } if ((stats != null)) { // If file exists archcount++; @@ -991,7 +1000,7 @@ function CreateMeshCentralServer(config, args) { } if ((obj.meshAgentBinaries[3] == null) && (obj.meshAgentBinaries[10003] != null)) { obj.meshAgentBinaries[3] = obj.meshAgentBinaries[10003]; } // If only the unsigned windows binaries are present, use them. if ((obj.meshAgentBinaries[4] == null) && (obj.meshAgentBinaries[10004] != null)) { obj.meshAgentBinaries[4] = obj.meshAgentBinaries[10004]; } // If only the unsigned windows binaries are present, use them. - } + }; // Generate a time limited user login token obj.getLoginToken = function (userid, func) { @@ -1016,7 +1025,7 @@ function CreateMeshCentralServer(config, args) { }); } }); - } + }; // Show the yser login token generation key obj.showLoginTokenKey = function (func) { @@ -1031,13 +1040,13 @@ function CreateMeshCentralServer(config, args) { obj.db.Set({ _id: 'LoginCookieEncryptionKey', key: obj.loginCookieEncryptionKey.toString('hex'), time: Date.now() }, function () { func(obj.loginCookieEncryptionKey.toString('hex')); }); } }); - } + }; // Generate a cryptographic key used to encode and decode cookies obj.generateCookieKey = function () { return new Buffer(obj.crypto.randomBytes(32), 'binary'); //return Buffer.alloc(32, 0); // Sets the key to zeros, debug only. - } + }; // Encode an object as a cookie using a key. (key must be 32 bytes long) obj.encodeCookie = function (o, key) { @@ -1048,7 +1057,7 @@ function CreateMeshCentralServer(config, args) { var crypted = Buffer.concat([cipher.update(JSON.stringify(o), 'utf8'), cipher.final()]); return Buffer.concat([iv, cipher.getAuthTag(), crypted]).toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); } catch (e) { return null; } - } + }; // Decode a cookie back into an object using a key. Return null if it's not a valid cookie. (key must be 32 bytes long) obj.decodeCookie = function (cookie, key, timeout) { @@ -1065,7 +1074,7 @@ function CreateMeshCentralServer(config, args) { if ((o.dtime > (timeout * 60000)) || (o.dtime < -30000)) return null; // The cookie is only valid 120 seconds, or 30 seconds back in time (in case other server's clock is not quite right) return o; } catch (e) { return null; } - } + }; // Debug obj.debug = function (lvl) { @@ -1074,11 +1083,11 @@ function CreateMeshCentralServer(config, args) { else if (arguments.length == 3) { console.log(arguments[1], arguments[2]); } else if (arguments.length == 4) { console.log(arguments[1], arguments[2], arguments[3]); } else if (arguments.length == 5) { console.log(arguments[1], arguments[2], arguments[3], arguments[4]); } - } + }; // Update server state. Writes a server state file. var meshServerState = {}; - obj.updateServerState = function(name, val) { + obj.updateServerState = function (name, val) { if ((name != null) && (val != null)) { var changed = false; if ((name != null) && (meshServerState[name] != val)) { if ((val == null) && (meshServerState[name] != null)) { delete meshServerState[name]; changed = true; } else { if (meshServerState[name] != val) { meshServerState[name] = val; changed = true; } } } @@ -1087,7 +1096,7 @@ function CreateMeshCentralServer(config, args) { var r = 'time=' + Date.now() + '\r\n'; for (var i in meshServerState) { r += (i + '=' + meshServerState[i] + '\r\n'); } obj.fs.writeFileSync(obj.getConfigFilePath('serverstate.txt'), r); - } + }; // Logging funtions function logException(e) { e += ''; logErrorEvent(e); } @@ -1124,7 +1133,7 @@ function CreateMeshCentralServer(config, args) { } //console.log('getConfigFilePath(\"' + filename + '\") = ' + obj.path.join(obj.datapath, filename)); return obj.path.join(obj.datapath, filename); - } + }; return obj; } @@ -1132,6 +1141,7 @@ function CreateMeshCentralServer(config, args) { // Return the server configuration function getConfig() { // Figure out the datapath location + var i; var fs = require('fs'); var path = require('path'); var datapath = null; @@ -1150,7 +1160,7 @@ function getConfig() { // Load and validate the configuration file try { config = require(configFilePath); } catch (e) { console.log('ERROR: Unable to parse ' + configFilePath + '.'); return null; } if (config.domains == null) { config.domains = {}; } - for (var i in config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return null; } } + for (i in config.domains) { if ((i.split('/').length > 1) || (i.split(' ').length > 1)) { console.log("ERROR: Error in config.json, domain names can't have spaces or /."); return null; } } } else { // Copy the "sample-config.json" to give users a starting point var sampleConfigPath = path.join(__dirname, 'sample-config.json'); @@ -1159,7 +1169,7 @@ function getConfig() { // Set the command line arguments to the config file if they are not present if (!config.settings) { config.settings = {}; } - for (var i in args) { config.settings[i] = args[i]; } + for (i in args) { config.settings[i] = args[i]; } // Lower case all keys in the config file require('./common.js').objKeysToLower(config); diff --git a/meshrelay.js b/meshrelay.js index 5f535a47..f6c93ccf 100644 --- a/meshrelay.js +++ b/meshrelay.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; module.exports.CreateMeshRelay = function (parent, ws, req, domain) { var obj = {}; @@ -23,9 +28,10 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { obj.close = function (arg) { if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Relay: Soft disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Relay: Hard disconnect (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket - } + }; obj.sendAgentMessage = function (command, userid, domainid) { + var rights; if (command.nodeid == null) return false; var user = obj.parent.users[userid]; if (user == null) return false; @@ -37,7 +43,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { var agent = obj.parent.wsagents[command.nodeid]; if (agent != null) { // Check if we have permission to send a message to that node - var rights = user.links[agent.dbMeshKey]; + rights = user.links[agent.dbMeshKey]; if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking command.sessionid = ws.sessionId; // Set the session id, required for responses. command.rights = rights.rights; // Add user rights flags to the message @@ -50,7 +56,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { var routing = obj.parent.parent.GetRoutingServerId(command.nodeid, 1); // 1 = MeshAgent routing type if (routing != null) { // Check if we have permission to send a message to that node - var rights = user.links[routing.meshid]; + rights = user.links[routing.meshid]; if (rights != null || ((rights & 16) != 0)) { // TODO: 16 is console permission, may need more gradular permission checking command.fromSessionid = ws.sessionId; // Set the session id, required for responses. command.rights = rights.rights; // Add user rights flags to the message @@ -61,7 +67,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { } } return false; - } + }; if (req.query.auth == null) { // Use ExpressJS session, check if this session is a logged in user, at least one of the two connections will need to be authenticated. @@ -160,7 +166,7 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { // Wait for other relay connection ws.pause(); // Hold traffic until the other connection parent.wsrelays[obj.id] = { peer1: obj, state: 1 }; - obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ') ' + (obj.authenticated?'Authenticated':'') ); + obj.parent.parent.debug(1, 'Relay holding: ' + obj.id + ' (' + obj.remoteaddr + ') ' + (obj.authenticated ? 'Authenticated' : '')); // Check if a peer server has this connection if (parent.parent.multiServer != null) { @@ -213,6 +219,6 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain) { obj.id = null; } }); - + return obj; -} +}; diff --git a/meshscanner.js b/meshscanner.js index d243c400..7aa25aa7 100644 --- a/meshscanner.js +++ b/meshscanner.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; // Construct a Mesh Scanner object // TODO: We need once "server4" and "server6" per interface, or change the default multicast interface as we send. @@ -26,9 +31,10 @@ module.exports.CreateMeshScanner = function (parent) { // Get a list of IPv4 and IPv6 interface addresses function getInterfaceList() { + var i; var ipv4 = ['*'], ipv6 = ['*']; // Bind to IN_ADDR_ANY always var interfaces = require('os').networkInterfaces(); - for (var i in interfaces) { + for (i in interfaces) { var xinterface = interfaces[i]; for (var j in xinterface) { var interface2 = xinterface[j]; @@ -43,11 +49,11 @@ module.exports.CreateMeshScanner = function (parent) { // Setup all IPv4 and IPv6 servers function setupServers() { - var addresses = getInterfaceList(); - for (var i in obj.servers4) { obj.servers4[i].xxclear = true; } - for (var i in obj.servers6) { obj.servers6[i].xxclear = true; } - for (var i in addresses.ipv4) { - var localAddress = addresses.ipv4[i]; + var addresses = getInterfaceList(), i, localAddress, bindOptions; + for (i in obj.servers4) { obj.servers4[i].xxclear = true; } + for (i in obj.servers6) { obj.servers6[i].xxclear = true; } + for (i in addresses.ipv4) { + localAddress = addresses.ipv4[i]; if (obj.servers4[localAddress] != null) { // Server already exists obj.servers4[localAddress].xxclear = false; @@ -59,7 +65,7 @@ module.exports.CreateMeshScanner = function (parent) { server4.xxtype = 4; server4.xxlocal = localAddress; server4.on('error', function (err) { if (this.xxlocal == '*') { console.log("ERROR: Server port 16989 not available, check if server is running twice."); } this.close(); delete obj.servers6[this.xxlocal]; }); - var bindOptions = { port: 16989, exclusive: true }; + bindOptions = { port: 16989, exclusive: true }; if (server4.xxlocal != '*') { bindOptions.address = server4.xxlocal; } server4.bind(bindOptions, function () { try { @@ -77,8 +83,8 @@ module.exports.CreateMeshScanner = function (parent) { } } - for (var i in addresses.ipv6) { - var localAddress = addresses.ipv6[i]; + for (i in addresses.ipv6) { + localAddress = addresses.ipv6[i]; if (obj.servers6[localAddress] != null) { // Server already exists obj.servers6[localAddress].xxclear = false; @@ -90,7 +96,7 @@ module.exports.CreateMeshScanner = function (parent) { server6.xxtype = 6; server6.xxlocal = localAddress; server6.on('error', function (err) { this.close(); delete obj.servers6[this.xxlocal]; }); - var bindOptions = { port: 16989, exclusive: true }; + bindOptions = { port: 16989, exclusive: true }; if (server6.xxlocal != '*') { bindOptions.address = server6.xxlocal; } server6.bind(bindOptions, function () { try { @@ -108,14 +114,15 @@ module.exports.CreateMeshScanner = function (parent) { } } - for (var i in obj.servers4) { if (obj.servers4[i].xxclear == true) { obj.servers4[i].close(); delete obj.servers4[i]; }; } - for (var i in obj.servers6) { if (obj.servers6[i].xxclear == true) { obj.servers6[i].close(); delete obj.servers6[i]; }; } + for (i in obj.servers4) { if (obj.servers4[i].xxclear == true) { obj.servers4[i].close(); delete obj.servers4[i]; } } + for (i in obj.servers6) { if (obj.servers6[i].xxclear == true) { obj.servers6[i].close(); delete obj.servers6[i]; } } } // Clear all IPv4 and IPv6 servers function clearServers() { - for (var i in obj.servers4) { obj.servers4[i].close(); delete obj.servers4[i]; } - for (var i in obj.servers6) { obj.servers6[i].close(); delete obj.servers6[i]; } + var i; + for (i in obj.servers4) { obj.servers4[i].close(); delete obj.servers4[i]; } + for (i in obj.servers6) { obj.servers6[i].close(); delete obj.servers6[i]; } } // Start scanning for local network Mesh Agents @@ -128,27 +135,28 @@ module.exports.CreateMeshScanner = function (parent) { setupServers(); obj.mainTimer = setInterval(obj.performScan, periodicScanTime); return obj; - } + }; // Stop scanning for local network Mesh Agents obj.stop = function () { if (obj.mainTimer != null) { clearInterval(obj.mainTimer); obj.mainTimer = null; } clearServers(); - } + }; // Look for all Mesh Agents that may be locally reachable, indicating the presense of this server. obj.performScan = function (server) { + var i; if (server != null) { if (server.xxtype == 4) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } if (server.xxtype == 6) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } if ((server.xxtype == 4) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '127.0.0.1'); } catch (e) { } try { server.send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, '255.255.255.255'); } catch (e) { } } if ((server.xxtype == 6) && (server.xxlocal == '*')) { try { server.send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, '::1'); } catch (e) { } } } else { - for (var i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } - for (var i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } + for (i in obj.servers4) { try { obj.servers4[i].send(obj.multicastPacket4, 0, obj.multicastPacket4.length, 16990, membershipIPv4); } catch (e) { } } + for (i in obj.servers6) { try { obj.servers6[i].send(obj.multicastPacket6, 0, obj.multicastPacket6.length, 16990, membershipIPv6); } catch (e) { } } setupServers(); // Check if any network interfaces where added or removed } - } + }; // Called when a UDP packet is received from an agent. function onUdpPacket(msg, info, server) { @@ -161,26 +169,27 @@ module.exports.CreateMeshScanner = function (parent) { // As a side job, we also send server wake-on-lan packets obj.wakeOnLan = function (macs) { - for (var i in macs) { + var i, j; + for (i in macs) { var mac = macs[i]; var hexpacket = 'FFFFFFFFFFFF'; - for (var i = 0; i < 16; i++) { hexpacket += mac; } + for (j = 0; j < 16; j++) { hexpacket += mac; } var wakepacket = Buffer.from(hexpacket, 'hex'); //console.log(wakepacket.toString('hex')); // Send the wake packet 3 times with small time intervals - for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } - for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } + for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } + for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } setTimeout(function () { - for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } - for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } + for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } + for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } }, 200); setTimeout(function () { - for (var i in obj.servers4) { obj.servers4[i].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } - for (var i in obj.servers6) { obj.servers6[i].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } + for (j in obj.servers4) { obj.servers4[j].send(wakepacket, 0, wakepacket.length, 7, "255.255.255.255"); obj.servers4[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv4); } + for (j in obj.servers6) { obj.servers6[j].send(wakepacket, 0, wakepacket.length, 16990, membershipIPv6); } }, 500); } - } - + }; + return obj; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/meshuser.js b/meshuser.js index 94b14871..e13a3427 100644 --- a/meshuser.js +++ b/meshuser.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*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.CreateMeshUser = function (parent, db, ws, req, args, domain) { @@ -27,7 +32,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.close = function (arg) { if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'Soft disconnect'); } catch (e) { console.log(e); } } // Soft close, close the websocket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'Hard disconnect'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket - } + }; // Convert a mesh path array into a real path on the server side function meshPathToRealPath(meshpath, user) { @@ -52,22 +57,22 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // function copyFile(src, dest, func, tag) { - //var ss = obj.fs.createReadStream(src, { flags: 'rb' }); + //var ss = obj.fs.createReadStream(src, { flags: 'rb' }); //var ds = obj.fs.createWriteStream(dest, { flags: 'wb' }); var ss = obj.fs.createReadStream(src); var ds = obj.fs.createWriteStream(dest); ss.fs = obj.fs; - ss.pipe(ds); + ss.pipe(ds); ds.ss = ss; /* if (!this._copyStreams) { this._copyStreams = {}; this._copyStreamID = 0; } ss.id = this._copyStreamID++; this._copyStreams[ss.id] = ss; */ - if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); } - else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); } - ds.on('close', function() { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); }); - }; + if (arguments.length == 3 && typeof arguments[2] === 'function') { ds.on('close', arguments[2]); } + else if (arguments.length == 4 && typeof arguments[3] === 'function') { ds.on('close', arguments[3]); } + ds.on('close', function () { /*delete this.ss.fs._copyStreams[this.ss.id];*/ func(tag); }); + } try { // Check if the user is logged in @@ -83,7 +88,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.parent.wssessions2[ws.sessionId] = obj.ws; if (!obj.parent.wssessions[user._id]) { obj.parent.wssessions[user._id] = [ws]; } else { obj.parent.wssessions[user._id].push(obj.ws); } if (obj.parent.parent.multiServer == null) { - obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[user._id].length, nolog: 1, domain: obj.domain.id }) + obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[user._id].length, nolog: 1, domain: obj.domain.id }); } else { obj.parent.recountSessions(obj.ws.sessionId); // Recount sessions } @@ -101,14 +106,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { else { ws.send(JSON.stringify({ action: 'event', event: event })); } } catch (e) { } } - } + }; user.subscriptions = obj.parent.subscribe(user._id, ws); // Subscribe to events obj.ws._socket.setKeepAlive(true, 240000); // Set TCP keep alive // When data is received from the web socket ws.on('message', function (msg) { - var command, user = obj.parent.users[req.session.userid]; + var command, user = obj.parent.users[req.session.userid], i = 0, mesh = null, meshid = null, nodeid = null, meshlinks = null, change = 0; try { command = JSON.parse(msg.toString('utf8')); } catch (e) { return; } if ((user == null) || (obj.common.validateString(command.action, 3, 32) == false)) return; // User must be set and action must be a string between 3 and 32 chars @@ -118,7 +123,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { { // Request a list of all meshes this user as rights to var docs = []; - for (var i in user.links) { if (obj.parent.meshes[i]) { docs.push(obj.parent.meshes[i]); } } + for (i in user.links) { if (obj.parent.meshes[i]) { docs.push(obj.parent.meshes[i]); } } ws.send(JSON.stringify({ action: 'meshes', meshes: docs, tag: command.tag })); break; } @@ -127,10 +132,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var links = []; if (command.meshid == null) { // Request a list of all meshes this user as rights to - for (var i in user.links) { links.push(i); } + for (i in user.links) { links.push(i); } } else { // Request list of all nodes for one specific meshid - var meshid = command.meshid; + meshid = command.meshid; if (obj.common.validateString(meshid, 0, 128) == false) return; if (meshid.split('/').length == 0) { meshid = 'mesh/' + domain.id + '/' + command.meshid; } if (user.links[meshid] != null) { links.push(meshid); } @@ -139,7 +144,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Request a list of all nodes obj.db.GetAllTypeNoTypeFieldMeshFiltered(links, domain.id, 'node', function (err, docs) { var r = {}; - for (var i in docs) { + for (i in docs) { // Add the connection state var state = obj.parent.parent.GetConnectivityState(docs[i]._id); if (state) { @@ -150,7 +155,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { } // Compress the meshid's - var meshid = docs[i].meshid; + meshid = docs[i].meshid; if (!r[meshid]) { r[meshid] = []; } delete docs[i].meshid; @@ -171,7 +176,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.db.getPowerTimeline(command.nodeid, function (err, docs) { if (err == null && docs.length > 0) { var timeline = [], time = null, previousPower; - for (var i in docs) { + for (i in docs) { var doc = docs[i]; if (time == null) { // First element @@ -228,14 +233,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (path == null) break; if ((command.fileop == 'createfolder') && (obj.common.IsFilenameValid(command.newfolder) == true)) { try { obj.fs.mkdirSync(path + "/" + command.newfolder); } catch (e) { } } // Create a new folder - else if (command.fileop == 'delete') { if (obj.common.validateArray(command.delfiles, 1) == false) return; for (var i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete + else if (command.fileop == 'delete') { if (obj.common.validateArray(command.delfiles, 1) == false) return; for (i in command.delfiles) { if (obj.common.IsFilenameValid(command.delfiles[i]) == true) { var fullpath = path + "/" + command.delfiles[i]; try { obj.fs.rmdirSync(fullpath); } catch (e) { try { obj.fs.unlinkSync(fullpath); } catch (e) { } } } } } // Delete else if ((command.fileop == 'rename') && (obj.common.IsFilenameValid(command.oldname) == true) && (obj.common.IsFilenameValid(command.newname) == true)) { try { obj.fs.renameSync(path + "/" + command.oldname, path + "/" + command.newname); } catch (e) { } } // Rename else if ((command.fileop == 'copy') || (command.fileop == 'move')) { if (obj.common.validateArray(command.names, 1) == false) return; var scpath = meshPathToRealPath(command.scpath, user); // This will also check access rights if (scpath == null) break; // TODO: Check quota if this is a copy!!!!!!!!!!!!!!!! - for (var i in command.names) { + for (i in command.names) { var s = obj.path.join(scpath, command.names[i]), d = obj.path.join(path, command.names[i]); sendUpdate = false; copyFile(s, d, function (op) { if (op != null) { obj.fs.unlink(op, function () { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); }); } else { obj.parent.parent.DispatchEvent([user._id], obj, 'updatefiles'); } }, ((command.fileop == 'move') ? s : null)); @@ -319,7 +324,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Delete all events if (user.siteadmin != 0xFFFFFFFF) break; obj.db.RemoveAllEvents(domain.id); - obj.parent.parent.DispatchEvent(['*', 'server-global'], obj, { action: 'clearevents', nolog: 1, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', 'server-global'], obj, { action: 'clearevents', nolog: 1, domain: domain.id }); break; } case 'users': @@ -327,7 +332,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Request a list of all users if ((user.siteadmin & 2) == 0) break; var docs = []; - for (var i in obj.parent.users) { + for (i in obj.parent.users) { if ((obj.parent.users[i].domain == domain.id) && (obj.parent.users[i].name != '~')) { var userinfo = obj.common.Clone(obj.parent.users[i]); delete userinfo.hash; @@ -403,10 +408,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((user.siteadmin & 2) == 0) break; if (obj.parent.parent.multiServer == null) { // No peering, use simple session counting - for (var i in obj.parent.wssessions) { if (obj.parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.parent.wssessions[i].length; } } + for (i in obj.parent.wssessions) { if (obj.parent.wssessions[i][0].domainid == domain.id) { wssessions[i] = obj.parent.wssessions[i].length; } } } else { // We have peer servers, use more complex session counting - for (var userid in obj.parent.sessionsCount) { if (userid.split('/')[1] == domain.id) { wssessions[userid] = obj.parent.sessionsCount[userid]; } } + for (i in obj.parent.sessionsCount) { if (i.split('/')[1] == domain.id) { wssessions[i] = obj.parent.sessionsCount[i]; } } } ws.send(JSON.stringify({ action: 'wssessioncount', wssessions: wssessions, tag: command.tag })); // wssessions is: userid --> count break; @@ -422,15 +427,15 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Remove all the mesh links to this user if (deluser.links != null) { - for (var meshid in deluser.links) { + for (meshid in deluser.links) { // Get the mesh - var mesh = obj.parent.meshes[meshid]; + mesh = obj.parent.meshes[meshid]; if (mesh) { // Remove user from the mesh if (mesh.links[deluser._id] != null) { delete mesh.links[deluser._id]; obj.parent.db.Set(mesh); } // Notify mesh change - var change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name; - obj.parent.parent.DispatchEvent(['*', mesh._id, deluser._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) + change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name; + obj.parent.parent.DispatchEvent(['*', mesh._id, deluser._id, user._id], obj, { etype: 'mesh', username: user.name, userid: user._id, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); } } } @@ -446,7 +451,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.db.Remove(deluserid); delete obj.parent.users[deluserid]; - obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', userid: deluserid, username: deluser.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }); obj.parent.parent.DispatchEvent([deluserid], obj, 'close'); break; @@ -474,7 +479,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (newuser2.subscriptions) { delete newuser2.subscriptions; } if (newuser2.salt) { delete newuser2.salt; } if (newuser2.hash) { delete newuser2.hash; } - obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: newuser2, action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: newusername, account: newuser2, action: 'accountcreate', msg: 'Account created, email is ' + command.email, domain: domain.id }); }); } break; @@ -483,12 +488,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { { // Edit a user account, may involve changing email or administrator permissions if (((user.siteadmin & 2) != 0) || (user.name == command.name)) { - var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = obj.parent.users[chguserid], change = 0; + var chguserid = 'user/' + domain.id + '/' + command.name.toLowerCase(), chguser = obj.parent.users[chguserid]; + change = 0; if (chguser) { if (obj.common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; } if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; } if (obj.common.validateInt(command.quota, 0) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; } - if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1 } + if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; } if (change == 1) { obj.db.SetUser(chguser); obj.parent.parent.DispatchEvent([chguser._id], obj, 'resubscribe'); @@ -500,7 +506,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { delete userinfo.domain; delete userinfo.subscriptions; delete userinfo.passtype; - obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: userinfo, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', 'server-users', user._id, chguser._id], obj, { etype: 'user', username: user.name, account: userinfo, action: 'accountchange', msg: 'Account changed: ' + command.name, domain: domain.id }); } if ((chguser.siteadmin) && (chguser.siteadmin != 0xFFFFFFFF) && (chguser.siteadmin & 32)) { obj.parent.parent.DispatchEvent([chguser._id], obj, 'close'); // Disconnect all this user's sessions @@ -534,11 +540,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Get the list of sessions for this user var sessions = obj.parent.wssessions[command.userid]; - if (sessions != null) { for (var i in sessions) { sessions[i].send(JSON.stringify(notification)); } } + if (sessions != null) { for (i in sessions) { sessions[i].send(JSON.stringify(notification)); } } if (obj.parent.parent.multiServer != null) { // TODO: Add multi-server support } + break; } case 'serverversion': { @@ -564,10 +571,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((command.meshtype == 1) || (command.meshtype == 2)) { // Create a type 1 agent-less Intel AMT mesh. obj.parent.crypto.randomBytes(48, function (err, buf) { - var meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); - var links = {} + meshid = 'mesh/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); + var links = {}; links[user._id] = { name: user.name, rights: 0xFFFFFFFF }; - var mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links }; + mesh = { type: 'mesh', _id: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, domain: domain.id, links: links }; obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.parent.meshes[meshid] = mesh; obj.parent.parent.AddEventDispatch([meshid], ws); @@ -575,7 +582,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { user.links[meshid] = { rights: 0xFFFFFFFF }; user.subscriptions = obj.parent.subscribe(user._id, ws); obj.db.SetUser(user); - obj.parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', meshid, user._id], obj, { etype: 'mesh', username: user.name, meshid: meshid, name: command.meshname, mtype: command.meshtype, desc: command.desc, action: 'createmesh', links: links, msg: 'Mesh created: ' + command.meshname, domain: domain.id }); }); } break; @@ -586,17 +593,17 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid obj.db.Get(command.meshid, function (err, meshes) { if (meshes.length != 1) return; - var mesh = obj.common.unEscapeLinksFieldName(meshes[0]); + mesh = obj.common.unEscapeLinksFieldName(meshes[0]); // Check if this user has rights to do this if (mesh.links[user._id] == null || mesh.links[user._id].rights != 0xFFFFFFFF) return; if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain // Fire the removal event first, because after this, the event will not route - obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'mesh', username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'mesh', username: user.name, meshid: command.meshid, name: command.meshname, action: 'deletemesh', msg: 'Mesh deleted: ' + command.meshname, domain: domain.id }); // Remove all user links to this mesh - for (var i in meshes) { + for (i in meshes) { var links = meshes[i].links; for (var j in links) { var xuser = obj.parent.users[j]; @@ -608,8 +615,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Delete all files on the server for this mesh try { - var meshpath = getServerRootFilePath(mesh); - if (meshpath != null) { deleteFolderRec(meshpath); } + var meshpath = obj.parent.getServerRootFilePath(mesh); + if (meshpath != null) { obj.parent.deleteFolderRec(meshpath); } } catch (e) { } obj.parent.parent.RemoveEventDispatchId(command.meshid); // Remove all subscriptions to this mesh @@ -622,7 +629,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { { // Change the name or description of a mesh if (obj.common.validateString(command.meshid, 1, 1024) == false) break; // Check the meshid - var mesh = obj.parent.meshes[command.meshid], change = ''; + mesh = obj.parent.meshes[command.meshid]; + change = ''; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 1) == 0)) return; @@ -630,7 +638,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((obj.common.validateString(command.meshname, 1, 64) == true) && (command.meshname != mesh.name)) { change = 'Mesh name changed from "' + mesh.name + '" to "' + command.meshname + '"'; mesh.name = command.meshname; } if ((obj.common.validateString(command.desc, 0, 1024) == true) && (command.desc != mesh.desc)) { if (change != '') change += ' and description changed'; else change += 'Mesh "' + mesh.name + '" description changed'; mesh.desc = command.desc; } - if (change != '') { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) } + if (change != '') { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); obj.parent.parent.DispatchEvent(['*', mesh._id, user._id], obj, { etype: 'mesh', username: user.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); } } break; } @@ -648,7 +656,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { } // Get the mesh - var mesh = obj.parent.meshes[command.meshid], change = ''; + mesh = obj.parent.meshes[command.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return; @@ -665,8 +673,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); // Notify mesh change - var change = 'Added user ' + newuser.name + ' to mesh ' + mesh.name; - obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, newuserid], obj, { etype: 'mesh', username: newuser.name, userid: command.userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Added user ' + newuser.name + ' to mesh ' + mesh.name, domain: domain.id }); } break; } @@ -677,7 +684,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((command.userid.split('/').length != 3) || (command.userid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain // Get the mesh - var mesh = obj.parent.meshes[command.meshid]; + mesh = obj.parent.meshes[command.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 2) == 0)) return; @@ -701,8 +708,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.db.Set(obj.common.escapeLinksFieldName(mesh)); // Notify mesh change - var change = 'Removed user ' + deluser.name + ' from mesh ' + mesh.name; - obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', mesh._id, user._id, command.userid], obj, { etype: 'mesh', username: user.name, userid: deluser.name, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: 'Removed user ' + deluser.name + ' from mesh ' + mesh.name, domain: domain.id }); } } break; @@ -723,7 +729,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((obj.parent.parent.args.wanonly == true) && (command.hostname)) { delete command.hostname; } // Get the mesh - var mesh = obj.parent.meshes[command.meshid]; + mesh = obj.parent.meshes[command.meshid]; if (mesh) { if (mesh.mtype != 1) return; // This operation is only allowed for mesh type 1, Intel AMT agentless mesh. @@ -733,15 +739,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Create a new nodeid obj.parent.crypto.randomBytes(48, function (err, buf) { // create the new node - var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');; + nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } }; obj.db.Set(device); // Event the new node var device2 = obj.common.Clone(device); delete device2.intelamt.pass; // Remove the Intel AMT password before eventing this. - var change = 'Added device ' + command.devicename + ' to mesh ' + mesh.name; - obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', username: user.name, action: 'addnode', node: device2, msg: change, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', command.meshid], obj, { etype: 'node', username: user.name, action: 'addnode', node: device2, msg: 'Added device ' + command.devicename + ' to mesh ' + mesh.name, domain: domain.id }); }); } break; @@ -763,8 +768,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { { if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's - for (var i in command.nodeids) { - var nodeid = command.nodeids[i]; + for (i in command.nodeids) { + nodeid = command.nodeids[i]; if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if ((nodeid.split('/').length != 3) || (nodeid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain @@ -774,7 +779,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; @@ -786,8 +791,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { obj.db.RemoveNode(node._id); // Remove all entries with node:id // Event node deletion - var change = 'Removed device ' + node.name + ' from mesh ' + mesh.name; - obj.parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: change, domain: domain.id }) + obj.parent.parent.DispatchEvent(['*', node.meshid], obj, { etype: 'node', username: user.name, action: 'removenode', nodeid: node._id, msg: 'Removed device ' + node.name + ' from mesh ' + mesh.name, domain: domain.id }); // Disconnect all connections if needed var state = obj.parent.parent.GetConnectivityState(nodeid); @@ -807,8 +811,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // TODO: We can optimize this a lot. // - We should get a full list of all MAC's to wake first. // - We should try to only have one agent per subnet (using Gateway MAC) send a wake-on-lan. - for (var i in command.nodeids) { - var nodeid = command.nodeids[i], wakeActions = 0; + for (i in command.nodeids) { + nodeid = command.nodeids[i]; + var wakeActions = 0; if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain // Get the device @@ -817,7 +822,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this @@ -835,10 +840,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Get the list of mesh this user as access to var targetMeshes = []; - for (var i in user.links) { targetMeshes.push(i); } + for (i in user.links) { targetMeshes.push(i); } // Go thru all the connected agents and send wake-on-lan on all the ones in the target mesh list - for (var i in obj.parent.wsagents) { + for (i in obj.parent.wsagents) { var agent = obj.parent.wsagents[i]; if ((targetMeshes.indexOf(agent.dbMeshKey) >= 0) && (agent.authenticated == 2)) { //console.log('Asking agent ' + agent.dbNodeKey + ' to wake ' + macs.join(',')); @@ -862,8 +867,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { case 'poweraction': { if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's - for (var i in command.nodeids) { - var nodeid = command.nodeids[i], powerActions = 0; + for (i in command.nodeids) { + nodeid = command.nodeids[i]; + var powerActions = 0; if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain // Get the device @@ -872,7 +878,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this @@ -899,8 +905,9 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's if (obj.common.validateString(command.title, 1, 512) == false) break; // Check title if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message - for (var i in command.nodeids) { - var nodeid = command.nodeids[i], powerActions = 0; + for (i in command.nodeids) { + nodeid = command.nodeids[i]; + var powerActions = 0; if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain // Get the device @@ -909,7 +916,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this @@ -940,7 +947,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { ws.send(JSON.stringify({ action: 'getnetworkinfo', nodeid: command.nodeid, netif: null })); return; } @@ -968,13 +975,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || ((mesh.links[user._id].rights & 4) == 0)) return; // Ready the node change event - var changes = [], change = 0, event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; + var changes = [], event = { etype: 'node', username: user.name, action: 'changenode', nodeid: node._id, domain: domain.id }; + change = 0; event.msg = ": "; // If we are in WAN-only mode, host is not used @@ -1041,7 +1049,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { data = obj.common.IntToStr(0) + data; // Add the 4 bytes encoding type & flags (Set to 0 for raw) obj.parent.sendMeshAgentCore(user, domain, command.nodeid, data); } - }) + }); } } } else { @@ -1071,7 +1079,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if (obj.common.validateString(command.nodeid, 1, 1024) == false) break; // Check nodeid obj.db.Get(command.nodeid, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???) if (nodes.length == 1) { - var meshlinks = user.links[nodes[0].meshid]; + meshlinks = user.links[nodes[0].meshid]; if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_REMOTECONTROL != 0)) { // Add a user authentication cookie to a url var cookieContent = { userid: user._id, domainid: user.domain }; @@ -1093,7 +1101,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain // Get the mesh - var mesh = obj.parent.meshes[command.meshid]; + mesh = obj.parent.meshes[command.meshid]; if (mesh) { if (mesh.mtype != 2) return; // This operation is only allowed for mesh type 2, agent mesh @@ -1118,7 +1126,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { // Check if this user has rights on this id to set notes obj.db.Get(command.id, function (err, nodes) { // TODO: Make a NodeRights(user) method that also does not do a db call if agent is connected (???) if (nodes.length == 1) { - var meshlinks = user.links[nodes[0].meshid]; + meshlinks = user.links[nodes[0].meshid]; if ((meshlinks) && (meshlinks.rights) && (meshlinks.rights & obj.parent.MESHRIGHT_SETNOTES != 0)) { // Set the id's notes if (obj.common.validateString(command.notes, 1) == false) { @@ -1131,7 +1139,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { }); } else if (idtype == 'mesh') { // Get the mesh for this device - var mesh = obj.parent.meshes[command.id]; + mesh = obj.parent.meshes[command.id]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } @@ -1170,7 +1178,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var node = nodes[0]; // Get the mesh for this device - var mesh = obj.parent.meshes[node.meshid]; + mesh = obj.parent.meshes[node.meshid]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } @@ -1184,7 +1192,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { }); } else if (idtype == 'mesh') { // Get the mesh for this device - var mesh = obj.parent.meshes[command.id]; + mesh = obj.parent.meshes[command.id]; if (mesh) { // Check if this user has rights to do this if (mesh.links[user._id] == null || (mesh.links[user._id].rights == 0)) { return; } @@ -1223,7 +1231,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var user = obj.parent.users[ws.userid]; if (user) { if (obj.parent.parent.multiServer == null) { - obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[ws.userid].length, nolog: 1, domain: obj.domain.id }) + obj.parent.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: user.name, count: obj.parent.wssessions[ws.userid].length, nolog: 1, domain: obj.domain.id }); } else { obj.parent.recountSessions(ws.sessionId); // Recount sessions } @@ -1241,7 +1249,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var httpport = ((obj.args.aliasport != null) ? obj.args.aliasport : obj.args.port); // Build server information object - var serverinfo = { name: obj.parent.certificates.CommonName, mpsname: obj.parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: obj.args.mpspass, port: httpport, emailcheck: obj.parent.parent.mailserver != null } + var serverinfo = { name: obj.parent.certificates.CommonName, mpsname: obj.parent.certificates.AmtMpsName, mpsport: mpsport, mpspass: obj.args.mpspass, port: httpport, emailcheck: obj.parent.parent.mailserver != null }; if (obj.args.notls == true) { serverinfo.https = false; } else { serverinfo.https = true; serverinfo.redirport = obj.args.redirport; } // Send server information @@ -1259,7 +1267,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { var r = {}, dir = obj.fs.readdirSync(path); for (var i in dir) { var f = { t: 3, d: 111 }; - var stat = obj.fs.statSync(path + '/' + dir[i]) + var stat = obj.fs.statSync(path + '/' + dir[i]); if ((stat.mode & 0x004000) == 0) { f.s = stat.size; f.d = stat.mtime.getTime(); } else { f.t = 2; f.f = readFilesRec(path + '/' + dir[i]); } r[dir[i]] = f; } @@ -1316,7 +1324,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) { } function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(//g, '>').replace(/').replace(/\n/g, '').replace(/\t/g, '  '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } + //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/').replace(/\n/g, '').replace(/\t/g, '  '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } return obj; -} +}; \ No newline at end of file diff --git a/multiserver.js b/multiserver.js index d99cf430..2f2bf586 100644 --- a/multiserver.js +++ b/multiserver.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; // Construct a Mesh Multi-Server object. This is used for MeshCentral-to-MeshCentral communication. module.exports.CreateMultiServer = function (parent, args) { @@ -46,7 +51,7 @@ module.exports.CreateMultiServer = function (parent, args) { obj.stop = function () { obj.connectionState = 0; disconnect(); - } + }; // Make one attempt at connecting to the server function connect() { @@ -169,13 +174,13 @@ module.exports.CreateMultiServer = function (parent, args) { if (typeof msg == 'object') { obj.ws.send(JSON.stringify(msg)); return; } if (typeof msg == 'string') { obj.ws.send(msg); return; } } catch (e) { } - } + }; // Process incoming peer server JSON data function processServerData(msg) { - var str = msg.toString('utf8'); + var str = msg.toString('utf8'), command = null; if (str[0] == '{') { - try { command = JSON.parse(str) } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. + try { command = JSON.parse(str); } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. if (command.action == 'info') { if (obj.authenticated != 3) { // We get the peer's serverid and database identifier. @@ -198,7 +203,7 @@ module.exports.CreateMultiServer = function (parent, args) { connect(); return obj; - } + }; // Create a mesh server module that received a connection to another server obj.CreatePeerInServer = function (parent, ws, req) { @@ -227,14 +232,14 @@ module.exports.CreateMultiServer = function (parent, args) { if (typeof data == 'object') { obj.ws.send(JSON.stringify(data)); return; } obj.ws.send(data); } catch (e) { } - } + }; // Disconnect this server obj.close = function (arg) { if ((arg == 1) || (arg == null)) { try { obj.ws.close(); obj.parent.parent.debug(1, 'InPeer: Soft disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Soft close, close the websocket if (arg == 2) { try { obj.ws._socket._parent.end(); obj.parent.parent.debug(1, 'InPeer: Hard disconnect ' + obj.peerServerId + ' (' + obj.remoteaddr + ')'); } catch (e) { console.log(e); } } // Hard close, close the TCP socket if (obj.authenticated == 3) { obj.parent.ClearPeerServer(obj, obj.peerServerId); obj.authenticated = 0; } - } + }; // When data is received from the peer server web socket ws.on('message', function (msg) { @@ -244,7 +249,7 @@ module.exports.CreateMultiServer = function (parent, args) { if (obj.authenticated >= 2) { // We are authenticated if (msg.charCodeAt(0) == 123) { processServerData(msg); } if (msg.length < 2) return; - var cmdid = obj.common.ReadShort(msg, 0); + //var cmdid = obj.common.ReadShort(msg, 0); // Process binary commands (if any). None right now. } else if (obj.authenticated < 2) { // We are not authenticated @@ -259,14 +264,14 @@ module.exports.CreateMultiServer = function (parent, args) { obj.peernonce = msg.substring(50); // Perform the hash signature using the server agent certificate - obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (signature) { + obj.parent.parent.certificateOperations.acceleratorPerformSignature(0, msg.substring(2) + obj.nonce, obj, function (obj2, signature) { // Send back our certificate + signature - obj2.send(obj2.common.ShortToStr(2) + obj.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature + obj2.send(obj2.common.ShortToStr(2) + obj2.common.ShortToStr(obj2.agentCertificateAsn1.length) + obj2.agentCertificateAsn1 + signature); // Command 2, certificate + signature }); // Check the peer server signature if we can if (obj.unauthsign != null) { - if (processPeerSignature(obj.unauthsign) == false) { disconnect(); return; } else { completePeerServerConnection(); } + if (processPeerSignature(obj.unauthsign) == false) { obj.close(); return; } else { completePeerServerConnection(); } } } else if (cmd == 2) { @@ -337,9 +342,9 @@ module.exports.CreateMultiServer = function (parent, args) { // Process incoming peer server JSON data function processServerData(msg) { - var str = msg.toString('utf8'); + var str = msg.toString('utf8'), command = null; if (str[0] == '{') { - try { command = JSON.parse(str) } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. + try { command = JSON.parse(str); } catch (e) { obj.parent.parent.debug(1, 'Unable to parse server JSON (' + obj.remoteaddr + ').'); return; } // If the command can't be parsed, ignore it. if (command.action == 'info') { if (obj.authenticated != 3) { // We get the peer's serverid and database identifier. @@ -362,7 +367,7 @@ module.exports.CreateMultiServer = function (parent, args) { } return obj; - } + }; // If we have no peering configuration, don't setup this object if (obj.peerConfig == null) { return null; } @@ -375,34 +380,34 @@ module.exports.CreateMultiServer = function (parent, args) { var server = obj.peerServers[serverid]; if (server && server.peerServerKey) return server.peerServerKey; return null; - } + }; // Dispatch an event to all other MeshCentral2 peer servers obj.DispatchEvent = function (ids, source, event) { var busmsg = JSON.stringify({ action: 'bus', ids: ids, event: event }); for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(busmsg); } - } + }; // Dispatch a message to other MeshCentral2 peer servers obj.DispatchMessage = function (msg) { for (var serverid in obj.peerServers) { obj.peerServers[serverid].send(msg); } - } + }; // Dispatch a message to other MeshCentral2 peer servers obj.DispatchMessageSingleServer = function (msg, serverid) { var server = obj.peerServers[serverid]; if (server != null) { server.send(msg); } - } + }; // Attempt to connect to all peers obj.ConnectToPeers = function () { - for (serverId in obj.peerConfig.servers) { + for (var serverId in obj.peerConfig.servers) { // We will only connect to names that are larger then ours. This way, eveyone has one connection to everyone else (no cross-connections). if ((serverId > obj.serverid) && (obj.peerConfig.servers[serverId].url != null) && (obj.outPeerServers[serverId] == null)) { obj.outPeerServers[serverId] = obj.CreatePeerOutServer(obj, serverId, obj.peerConfig.servers[serverId].url); } } - } + }; // We connected to a peer server, setup everything obj.SetupPeerServer = function (server, peerServerId) { @@ -414,7 +419,7 @@ module.exports.CreateMultiServer = function (parent, args) { // Send a list of user sessions to the peer server.send(JSON.stringify({ action: 'sessionsTable', sessionsTable: Object.keys(obj.parent.webserver.wssessions2) })); - } + }; // We disconnected to a peer server, clean up everything obj.ClearPeerServer = function (server, peerServerId) { @@ -431,10 +436,11 @@ module.exports.CreateMultiServer = function (parent, args) { delete obj.parent.webserver.wsPeerSessions[peerServerId]; delete obj.parent.webserver.wsPeerSessions3[peerServerId]; obj.parent.webserver.recountSessions(); // Recount all sessions - } + }; // Process a message coming from a peer server obj.ProcessPeerServerMessage = function (server, peerServerId, msg) { + var userid, i; //console.log('ProcessPeerServerMessage', peerServerId, msg); switch (msg.action) { case 'bus': { @@ -449,10 +455,10 @@ module.exports.CreateMultiServer = function (parent, args) { case 'sessionsTable': { obj.parent.webserver.wsPeerSessions[peerServerId] = msg.sessionsTable; var userToSession = {}; - for (var i in msg.sessionsTable) { + for (i in msg.sessionsTable) { var sessionid = msg.sessionsTable[i]; obj.parent.webserver.wsPeerSessions2[sessionid] = peerServerId; - var userid = sessionid.split('/').slice(0, 3).join('/'); // Take the sessionid and keep only the userid partion + userid = sessionid.split('/').slice(0, 3).join('/'); // Take the sessionid and keep only the userid partion if (userToSession[userid] == null) { userToSession[userid] = [sessionid]; } else { userToSession[userid].push(sessionid); } // UserId -> [ SessionId ] } obj.parent.webserver.wsPeerSessions3[peerServerId] = userToSession; // ServerId --> UserId --> SessionId @@ -462,17 +468,17 @@ module.exports.CreateMultiServer = function (parent, args) { case 'sessionStart': { obj.parent.webserver.wsPeerSessions[peerServerId].push(msg.sessionid); obj.parent.webserver.wsPeerSessions2[msg.sessionid] = peerServerId; - var userid = msg.sessionid.split('/').slice(0, 3).join('/'); + userid = msg.sessionid.split('/').slice(0, 3).join('/'); if (obj.parent.webserver.wsPeerSessions3[peerServerId] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId] = {}; } - if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId][userid] = [ msg.sessionid ]; } else { obj.parent.webserver.wsPeerSessions3[peerServerId][userid].push(msg.sessionid); } + if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] == null) { obj.parent.webserver.wsPeerSessions3[peerServerId][userid] = [msg.sessionid]; } else { obj.parent.webserver.wsPeerSessions3[peerServerId][userid].push(msg.sessionid); } obj.parent.webserver.recountSessions(msg.sessionid); // Recount a specific user break; } case 'sessionEnd': { - var i = obj.parent.webserver.wsPeerSessions[peerServerId].indexOf(msg.sessionid); + i = obj.parent.webserver.wsPeerSessions[peerServerId].indexOf(msg.sessionid); if (i >= 0) { obj.parent.webserver.wsPeerSessions[peerServerId].splice(i, 1); } delete obj.parent.webserver.wsPeerSessions2[msg.sessionid]; - var userid = msg.sessionid.split('/').slice(0, 3).join('/'); + userid = msg.sessionid.split('/').slice(0, 3).join('/'); if (obj.parent.webserver.wsPeerSessions3[peerServerId][userid] != null) { i = obj.parent.webserver.wsPeerSessions3[peerServerId][userid].indexOf(msg.sessionid); if (i >= 0) { @@ -498,7 +504,7 @@ module.exports.CreateMultiServer = function (parent, args) { // Yes, there is a waiting session, see if we must initiate. if (peerServerId > obj.parent.serverId) { // We must initiate the connection to the peer - var userid = null; + userid = null; if (rsession.peer1.req.session != null) { userid = rsession.peer1.req.session.userid; } obj.createPeerRelay(rsession.peer1.ws, rsession.peer1.req, peerServerId, userid); delete obj.parent.webserver.wsrelays[msg.id]; @@ -509,33 +515,33 @@ module.exports.CreateMultiServer = function (parent, args) { // Clear all relay sessions that are more than 1 minute var oneMinuteAgo = Date.now() - 60000; - for (var id in obj.parent.webserver.wsPeerRelays) { if (obj.parent.webserver.wsPeerRelays[id].time < oneMinuteAgo) { delete obj.parent.webserver.wsPeerRelays[id]; } } + for (i in obj.parent.webserver.wsPeerRelays) { if (obj.parent.webserver.wsPeerRelays[i].time < oneMinuteAgo) { delete obj.parent.webserver.wsPeerRelays[i]; } } } break; } case 'msg': { if (msg.sessionid != null) { // Route this message to a connected user session - if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; } - var ws = obj.parent.webserver.wssessions2[command.sessionid]; - if (ws != null) { ws.send(JSON.stringify(command)); } + if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; } + var ws = obj.parent.webserver.wssessions2[msg.sessionid]; + if (ws != null) { ws.send(JSON.stringify(msg)); } } else if (msg.nodeid != null) { // Route this message to a connected agent - if (command.fromSessionid != null) { command.sessionid = command.fromSessionid; delete command.fromSessionid; } + if (msg.fromSessionid != null) { msg.sessionid = msg.fromSessionid; delete msg.fromSessionid; } var agent = obj.parent.webserver.wsagents[msg.nodeid]; if (agent != null) { delete msg.nodeid; agent.send(JSON.stringify(msg)); } // Remove the nodeid since it's implyed and send the message to the agent } else if (msg.meshid != null) { // Route this message to all users of this mesh - if (command.fromNodeid != null) { command.nodeid = command.fromNodeid; delete command.fromNodeid; } - var cmdstr = JSON.stringify(command); - for (var userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message + if (msg.fromNodeid != null) { msg.nodeid = msg.fromNodeid; delete msg.fromNodeid; } + var cmdstr = JSON.stringify(msg); + for (userid in obj.parent.webserver.wssessions) { // Find all connected users for this mesh and send the message var user = obj.parent.webserver.users[userid]; if (user) { var rights = user.links[msg.meshid]; if (rights != null) { // TODO: Look at what rights are needed for message routing var sessions = obj.parent.webserver.wssessions[userid]; // Send the message to all users on this server - for (var i in sessions) { sessions[i].send(cmdstr); } + for (i in sessions) { sessions[i].send(cmdstr); } } } } @@ -543,7 +549,7 @@ module.exports.CreateMultiServer = function (parent, args) { break; } } - } + }; // Create a tunnel connection to a peer server obj.createPeerRelay = function (ws, req, serverid, user) { @@ -558,7 +564,7 @@ module.exports.CreateMultiServer = function (parent, args) { var path = req.path; if (path[0] == '/') path = path.substring(1); if (path.substring(path.length - 11) == '/.websocket') { path = path.substring(0, path.length - 11); } - var queryStr = '' + var queryStr = ''; for (var i in req.query) { queryStr += ((queryStr == '') ? '?' : '&') + i + '=' + req.query[i]; } if (user != null) { queryStr += ((queryStr == '') ? '?' : '&') + 'auth=' + obj.parent.encodeCookie({ userid: user._id, domainid: user.domain }, cookieKey); } var url = obj.peerConfig.servers[serverid].url + path + queryStr; @@ -566,7 +572,7 @@ module.exports.CreateMultiServer = function (parent, args) { // Setup an connect the web socket var tunnel = obj.createPeerRelayEx(ws, url, serverid); tunnel.connect(); - } + }; // Create a tunnel connection to a peer server // We assume that "ws" is paused already. @@ -610,7 +616,7 @@ module.exports.CreateMultiServer = function (parent, args) { // If the web socket is closed, close the associated TCP connection. peerTunnel.ws1.on('close', function (req) { peerTunnel.parent.parent.debug(1, 'FTunnel disconnect ' + peerTunnel.serverid); peerTunnel.close(); }); - } + }; // Disconnect both sides of the tunnel peerTunnel.close = function (arg) { @@ -623,11 +629,11 @@ module.exports.CreateMultiServer = function (parent, args) { if (peerTunnel.ws1 != null) { try { peerTunnel.ws1.close(); peerTunnel.parent.parent.debug(1, 'FTunnel1: Soft disconnect '); } catch (e) { console.log(e); } } if (peerTunnel.ws2 != null) { try { peerTunnel.ws2.close(); peerTunnel.parent.parent.debug(1, 'FTunnel2: Soft disconnect '); } catch (e) { console.log(e); } } } - } + }; return peerTunnel; - } + }; setTimeout(function () { obj.ConnectToPeers(); }, 1000); // Delay this a little to make sure we are ready on our side. return obj; -} +}; \ No newline at end of file diff --git a/pass.js b/pass.js index 9091e290..68d9f7e6 100644 --- a/pass.js +++ b/pass.js @@ -1,6 +1,11 @@ // check out https://github.com/tj/node-pwd -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; // Module dependencies. const crypto = require('crypto'); diff --git a/swarmserver.js b/swarmserver.js index bf601fdc..631775ce 100644 --- a/swarmserver.js +++ b/swarmserver.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; // Construct a legacy Swarm Server server object module.exports.CreateSwarmServer = function (parent, db, args, certificates) { @@ -18,7 +23,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { obj.legacyAgentConnections = {}; obj.migrationAgents = {}; const common = require('./common.js'); - const net = require('net'); + //const net = require('net'); const tls = require('tls'); const forge = require('node-forge'); @@ -115,7 +120,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { USERAUTH2: 1031, // Authenticate a user to the swarm server (Uses SHA1 SALT) GUESTREMOTEDESKTOP: 2001, // Guest usage: Remote Desktop GUESTWEBRTCMESH: 2002 // Guest usage: WebRTC Mesh - } + }; obj.server = tls.createServer({ key: certificates.swarmserver.key, cert: certificates.swarmserver.cert, requestCert: true }, onConnection); obj.server.listen(args.swarmport, function () { console.log('MeshCentral Legacy Swarm Server running on ' + certificates.CommonName + ':' + args.swarmport + '.'); obj.parent.updateServerState('swarm-port', args.swarmport); }).on('error', function (err) { console.error('ERROR: MeshCentral Swarm Server server port ' + args.swarmport + ' is not available.'); if (args.exactports) { process.exit(); } }); @@ -146,11 +151,11 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { socket.setEncoding('binary'); socket.pingTimer = setInterval(function () { obj.SendCommand(socket, LegacyMeshProtocol.PING); }, 20000); Debug(1, 'SWARM:New legacy agent connection'); - + socket.addListener("data", function (data) { if (args.swarmdebug) { var buf = new Buffer(data, "binary"); console.log('SWARM <-- (' + buf.length + '):' + buf.toString('hex')); } // Print out received bytes socket.tag.accumulator += data; - + // Detect if this is an HTTPS request, if it is, return a simple answer and disconnect. This is useful for debugging access to the MPS port. if (socket.tag.first == true) { if (socket.tag.accumulator.length < 3) return; @@ -214,7 +219,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { Debug(3, 'Swarm:GETSTATE'); if (len < 12) break; var statecmd = common.ReadInt(data, 0); - var statesync = common.ReadInt(data, 4); + //var statesync = common.ReadInt(data, 4); switch (statecmd) { case 6: { // Ask for agent block if (socket.tag.update != null) { @@ -247,14 +252,14 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { } return len; } - + socket.addListener("close", function () { Debug(1, 'Swarm:Connection closed'); try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); if (socket.pingTimer != null) { clearInterval(socket.pingTimer); delete socket.pingTimer; } }); - + socket.addListener("error", function () { //console.log("Swarm Error: " + socket.remoteAddress); }); @@ -311,19 +316,19 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { console.log(e); } return null; - } + }; // Disconnect legacy agent connection obj.close = function (socket) { try { socket.close(); } catch (e) { } try { delete obj.ciraConnections[socket.tag.nodeid]; } catch (e) { } obj.parent.ClearConnectivityState(socket.tag.meshid, socket.tag.nodeid, 2); - } + }; - obj.SendCommand = function(socket, cmdid, data) { + obj.SendCommand = function (socket, cmdid, data) { if (data == null) { data = ''; } Write(socket, common.ShortToStr(cmdid) + common.ShortToStr(data.length + 4) + data); - } + }; function Write(socket, data) { if (args.swarmdebug) { @@ -335,7 +340,7 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { socket.write(new Buffer(data, "binary")); } } - + // Debug function Debug(lvl) { if (lvl > obj.parent.debugLevel) return; @@ -348,4 +353,4 @@ module.exports.CreateSwarmServer = function (parent, db, args, certificates) { } return obj; -} +}; diff --git a/views/default.handlebars b/views/default.handlebars index 6c55dd95..7a78c957 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2251,9 +2251,8 @@ var boundingBox = null; for (var i in nodes) { try { - var loc = map_parseNodeLoc(nodes[i]); - var feature = xxmap.markersSource.getFeatureById(nodes[i]._id); - if ((typeof loc == 'object') && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations + var loc = map_parseNodeLoc(nodes[i]), feature = xxmap.markersSource.getFeatureById(nodes[i]._id); + if ((loc != null) && ((nodes[i].meshid == selectedMesh) || (selectedMesh == null))) { // Draw markers for devices with locations var lat = loc[0], lon = loc[1], type = loc[2]; if (boundingBox == null) { boundingBox = [ lat, lon, lat, lon, 0 ]; } else { if (lat < boundingBox[0]) { boundingBox[0] = lat; } if (lon < boundingBox[1]) { boundingBox[1] = lon; } if (lat > boundingBox[2]) { boundingBox[2] = lat; } if (lon > boundingBox[3]) { boundingBox[3] = lon; } } if (feature == null) { addFeature(nodes[i]); boundingBox[4] = 1; } else { updateFeature(nodes[i], feature); feature.setStyle(markerStyle(nodes[i], loc[2])); } // Update Feature @@ -2311,7 +2310,7 @@ if (node.wifiloc) { loc = node.wifiloc; t = 2; } if (node.gpsloc) { loc = node.gpsloc; t = 3; } if (node.userloc) { loc = node.userloc; t = 4; } - if ((loc == null) || (typeof loc != 'string')) return; + if ((loc == null) || (typeof loc != 'string')) return null; loc = loc.split(','); if (t == 1) { // If this is IP location, randomize the position a little. @@ -2450,12 +2449,14 @@ } // Since this is IP address location, add some fixed randomness to the location. Avoid pin pile-up. - var loc = map_parseNodeLoc(node); lat = loc[0]; lon = loc[1]; - - if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed - feature.set('lat', lat); feature.set('lon', lon); - var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326','EPSG:3857'); - feature.getGeometry().setCoordinates(modifiedCoordinates); + var loc = map_parseNodeLoc(node); + if (loc != null) { + var lat = loc[0], lon = loc[1]; + if ((lat != feature.get('lat')) || (lon != feature.get('lon'))) { // Update lat and lon if changed + feature.set('lat', lat); feature.set('lon', lon); + var modifiedCoordinates = ol.proj.transform([parseFloat(lon), parseFloat(lat)], 'EPSG:4326', 'EPSG:3857'); + feature.getGeometry().setCoordinates(modifiedCoordinates); + } } if (node.name != feature.get('name') ) { feature.set('name', node.name); } // Update name diff --git a/webserver.js b/webserver.js index 6e4eeded..666fc8cb 100644 --- a/webserver.js +++ b/webserver.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; /* class SerialTunnel extends require('stream').Duplex { @@ -22,9 +27,9 @@ class SerialTunnel extends require('stream').Duplex { function SerialTunnel(options) { var obj = new require('stream').Duplex(options); obj.forwardwrite = null; - obj.updateBuffer = function (chunk) { this.push(chunk); } - obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); } // Pass data written to forward - obj._read = function(size) { } // Push nothing, anything to read should be pushed from updateBuffer() + obj.updateBuffer = function (chunk) { this.push(chunk); }; + obj._write = function (chunk, encoding, callback) { if (obj.forwardwrite != null) { obj.forwardwrite(chunk); } else { console.err("Failed to fwd _write."); } if (callback) callback(); }; // Pass data written to forward + obj._read = function (size) { }; // Push nothing, anything to read should be pushed from updateBuffer() return obj; } @@ -37,7 +42,7 @@ if (!String.prototype.endsWith) { String.prototype.endsWith = function (searchSt // Construct a HTTP web server object module.exports.CreateWebServer = function (parent, db, args, certificates) { - var obj = {}; + var obj = {}, i = 0; // Modules obj.fs = require('fs'); @@ -52,8 +57,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.common = require('./common.js'); obj.express = require('express'); obj.meshAgentHandler = require('./meshagent.js'); - obj.meshRelayHandler = require('./meshrelay.js') - obj.meshUserHandler = require('./meshuser.js') + obj.meshRelayHandler = require('./meshrelay.js'); + obj.meshUserHandler = require('./meshuser.js'); obj.interceptor = require('./interceptor'); // Variables @@ -63,13 +68,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.app = obj.express(); obj.app.use(require('compression')()); obj.tlsServer = null; - obj.tcpServer; + obj.tcpServer = null; obj.certificates = certificates; obj.args = args; obj.users = {}; obj.meshes = {}; obj.userAllowedIp = args.userallowedip; // List of allowed IP addresses for users - obj.tlsSniCredentials; + obj.tlsSniCredentials = null; obj.dnsDomains = {}; //obj.agentConnCount = 0; @@ -93,13 +98,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Setup SSPI authentication if needed if ((obj.parent.platform == 'win32') && (obj.args.nousers != true) && (obj.parent.config != null) && (obj.parent.config.domains != null)) { - for (var i in obj.parent.config.domains) { if (obj.parent.config.domains[i].auth == 'sspi') { var nodeSSPI = require('node-sspi'); obj.parent.config.domains[i].sspi = new nodeSSPI({ retrieveGroups: true, offerBasic: false }); } } + for (i in obj.parent.config.domains) { if (obj.parent.config.domains[i].auth == 'sspi') { var nodeSSPI = require('node-sspi'); obj.parent.config.domains[i].sspi = new nodeSSPI({ retrieveGroups: true, offerBasic: false }); } } } // Perform hash on web certificate and agent certificate obj.webCertificateHash = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); obj.webCertificateHashs = { '': obj.webCertificateHash }; - for (var i in obj.parent.config.domains) { if (obj.parent.config.domains[i].dns != null) { obj.webCertificateHashs[i] = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); } } + for (i in obj.parent.config.domains) { if (obj.parent.config.domains[i].dns != null) { obj.webCertificateHashs[i] = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }); } } obj.webCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.web.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); obj.agentCertificateHashHex = parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'hex' }); obj.agentCertificateHashBase64 = new Buffer(parent.certificateOperations.forge.pki.getPublicKeyFingerprint(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.agent.cert).publicKey, { md: parent.certificateOperations.forge.md.sha384.create(), encoding: 'binary' }), 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'); @@ -130,13 +135,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { { var dnscount = 0; obj.tlsSniCredentials = {}; - for (var i in obj.certificates.dns) { if (obj.parent.config.domains[i].dns != null) { obj.dnsDomains[obj.parent.config.domains[i].dns.toLowerCase()] = obj.parent.config.domains[i]; obj.tlsSniCredentials[obj.parent.config.domains[i].dns] = obj.tls.createSecureContext(obj.certificates.dns[i]).context; dnscount++; } } + for (i in obj.certificates.dns) { if (obj.parent.config.domains[i].dns != null) { obj.dnsDomains[obj.parent.config.domains[i].dns.toLowerCase()] = obj.parent.config.domains[i]; obj.tlsSniCredentials[obj.parent.config.domains[i].dns] = obj.tls.createSecureContext(obj.certificates.dns[i]).context; dnscount++; } } if (dnscount > 0) { obj.tlsSniCredentials[''] = obj.tls.createSecureContext({ cert: obj.certificates.web.cert, key: obj.certificates.web.key, ca: obj.certificates.web.ca }).context; } else { obj.tlsSniCredentials = null; } } function TlsSniCallback(name, cb) { var c = obj.tlsSniCredentials[name]; if (c != null) { cb(null, c); } else { cb(null, obj.tlsSniCredentials['']); } } function EscapeHtml(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(//g, '>').replace(/').replace(/\n/g, '').replace(/\t/g, '  '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } + //function EscapeHtmlBreaks(x) { if (typeof x == "string") return x.replace(/&/g, '&').replace(/>/g, '>').replace(/').replace(/\n/g, '').replace(/\t/g, '  '); if (typeof x == "boolean") return x; if (typeof x == "number") return x; } if (obj.args.notls || obj.args.tlsoffload) { // Setup the HTTP server without TLS @@ -172,27 +177,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Session-persisted message middleware obj.app.use(function (req, res, next) { + var err = null, msg = null, passhint = null; if (req.session != null) { - var err = req.session.error; - var msg = req.session.success; - var passhint = req.session.passhint; + err = req.session.error; + msg = req.session.success; + passhint = req.session.passhint; delete req.session.error; delete req.session.success; delete req.session.passhint; } res.locals.message = ''; - if (err) res.locals.message = '

' + err + '

'; - if (msg) res.locals.message = '

' + msg + '

'; - if (passhint) res.locals.passhint = EscapeHtml(passhint); + if (err != null) res.locals.message = '

' + err + '

'; + if (msg != null) res.locals.message = '

' + msg + '

'; + if (passhint != null) res.locals.passhint = EscapeHtml(passhint); next(); }); // Fetch all users from the database, keep this in memory obj.db.GetAllType('user', function (err, docs) { - var domainUserCount = {}; - for (var i in parent.config.domains) { domainUserCount[i] = 0; } - for (var i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; } - for (var i in parent.config.domains) { + var domainUserCount = {}, i = 0; + for (i in parent.config.domains) { domainUserCount[i] = 0; } + for (i in docs) { var u = obj.users[docs[i]._id] = docs[i]; domainUserCount[u.domain]++; } + for (i in parent.config.domains) { if (domainUserCount[i] == 0) { if (parent.config.domains[i].newaccounts == 0) { parent.config.domains[i].newaccounts = 2; } console.log('Server ' + ((i == '') ? '' : (i + ' ')) + 'has no users, next new account will be site administrator.'); @@ -237,7 +243,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { }); } } - } + }; /* obj.restrict = function (req, res, next) { @@ -249,7 +255,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { req.session.error = 'Access denied!'; res.redirect(domain.url + 'login'); } - } + }; */ // Check if the source IP address is allowed for a given allowed list, return false if not @@ -287,8 +293,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (req.headers.host != null) { var d = obj.dnsDomains[req.headers.host.toLowerCase()]; if (d != null) return d; } // If this is a DNS name domain, return it here. var x = req.url.split('/'); if (x.length < 2) return parent.config.domains['']; - var d = parent.config.domains[x[1].toLowerCase()]; - if ((d != null) && (d.dns == null)) return parent.config.domains[x[1].toLowerCase()]; + var y = parent.config.domains[x[1].toLowerCase()]; + if ((y != null) && (y.dns == null)) return parent.config.domains[x[1].toLowerCase()]; return parent.config.domains['']; } @@ -298,8 +304,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); // Destroy the user's session to log them out will be re-created next request if (req.session.userid) { - var user = obj.users[req.session.userid] - obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id }) + var user = obj.users[req.session.userid]; + obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'logout', msg: 'Account logout', domain: domain.id }); } req.session = null; res.redirect(domain.url); @@ -319,35 +325,35 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Regenerate session when signing in to prevent fixation //req.session.regenerate(function () { - // Store the user's primary key in the session store to be retrieved, or in this case the entire user object - // req.session.success = 'Authenticated as ' + user.name + 'click to logout. You may now access /restricted.'; - delete req.session.loginmode; - req.session.userid = userid; - req.session.domainid = domain.id; - req.session.currentNode = ''; - if (req.session.passhint) { delete req.session.passhint; } - if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } - if (req.body.host) { - // TODO: This is a terrible search!!! FIX THIS. - /* - obj.db.GetAllType('node', function (err, docs) { - for (var i = 0; i < docs.length; i++) { - if (docs[i].name == req.body.host) { - req.session.currentNode = docs[i]._id; - break; - } + // Store the user's primary key in the session store to be retrieved, or in this case the entire user object + // req.session.success = 'Authenticated as ' + user.name + 'click to logout. You may now access /restricted.'; + delete req.session.loginmode; + req.session.userid = userid; + req.session.domainid = domain.id; + req.session.currentNode = ''; + if (req.session.passhint) { delete req.session.passhint; } + if (req.body.viewmode) { req.session.viewmode = req.body.viewmode; } + if (req.body.host) { + // TODO: This is a terrible search!!! FIX THIS. + /* + obj.db.GetAllType('node', function (err, docs) { + for (var i = 0; i < docs.length; i++) { + if (docs[i].name == req.body.host) { + req.session.currentNode = docs[i]._id; + break; } - console.log("CurrentNode: " + req.session.currentNode); - // This redirect happens after finding node is completed - res.redirect(domain.url); - }); - */ - } else { + } + console.log("CurrentNode: " + req.session.currentNode); + // This redirect happens after finding node is completed res.redirect(domain.url); - } + }); + */ + } else { + res.redirect(domain.url); + } //}); - obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }) + obj.parent.DispatchEvent(['*'], obj, { etype: 'user', username: user.name, action: 'login', msg: 'Account login', domain: domain.id }); } else { delete req.session.loginmode; if (err == 'locked') { req.session.error = 'Account locked.'; } else { req.session.error = 'Login failed, check username and password.'; } @@ -367,14 +373,14 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (domain.newaccounts == 0) { res.sendStatus(401); return; } if (!obj.common.validateUsername(req.body.username, 1, 64) || !obj.common.validateEmail(req.body.email, 1, 256) || !obj.common.validateString(req.body.password1, 1, 256) || !obj.common.validateString(req.body.password2, 1, 256) || (req.body.password1 != req.body.password2) || req.body.username == '~') { req.session.loginmode = 2; - req.session.error = 'Unable to create account.';; + req.session.error = 'Unable to create account.'; res.redirect(domain.url); } else { // Check if this email was already verified obj.db.GetUserWithVerifiedEmail(domain.id, req.body.email, function (err, docs) { if (docs.length > 0) { req.session.loginmode = 2; - req.session.error = 'Existing account with this email address.';; + req.session.error = 'Existing account with this email address.'; res.redirect(domain.url); } else { // Check if there is domain.newAccountToken, check if supplied token is valid @@ -406,7 +412,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.db.SetUser(user); if (obj.parent.mailserver != null) { obj.parent.mailserver.sendAccountCheckMail(domain, user.name, user.email); } }); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: user, action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id }) + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, account: user, action: 'accountcreate', msg: 'Account created, email is ' + req.body.email, domain: domain.id }); } res.redirect(domain.url); } @@ -459,7 +465,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else { obj.db.Get('user/' + cookie.u, function (err, docs) { if (docs.length == 0) { - res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid username \"' + EscapeHtml(user.name) + '\". Go to login page.' }); + res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'ERROR: Invalid username \"' + EscapeHtml(cookie.u) + '\". Go to login page.' }); } else { var user = docs[0]; if (user.email != cookie.e) { @@ -488,13 +494,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete userinfo.domain; delete userinfo.subscriptions; delete userinfo.passtype; - obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + EscapeHtml(userinfo.email) + ')', domain: domain.id }) + obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Verified email of user ' + EscapeHtml(user.name) + ' (' + EscapeHtml(userinfo.email) + ')', domain: domain.id }); // Send the confirmation page res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: 'Verified email ' + EscapeHtml(user.email) + ' for user account ' + EscapeHtml(user.name) + '. Go to login page.' }); // Send a notification - obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified:
' + EscapeHtml(userinfo.email) + '.', nolog: 1 }) + obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified:
' + EscapeHtml(userinfo.email) + '.', nolog: 1 }); } }); } @@ -507,10 +513,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.crypto.randomBytes(16, function (err, buf) { var newpass = buf.toString('base64').split('=').join('').split('/').join(''); require('./pass').hash(newpass, function (err, salt, hash) { + var userinfo = null; if (err) throw err; // Change the password - var userinfo = obj.users[user._id]; + userinfo = obj.users[user._id]; userinfo.salt = salt; userinfo.hash = hash; userinfo.passchange = Date.now(); @@ -518,7 +525,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.db.SetUser(userinfo); // Event the change - var userinfo = obj.common.Clone(userinfo); + userinfo = obj.common.Clone(userinfo); delete userinfo.hash; delete userinfo.passhint; delete userinfo.salt; @@ -526,7 +533,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete userinfo.domain; delete userinfo.subscriptions; delete userinfo.passtype; - obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Password reset for user ' + EscapeHtml(user.name), domain: domain.id }) + obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, { etype: 'user', username: userinfo.name, account: userinfo, action: 'accountchange', msg: 'Password reset for user ' + EscapeHtml(user.name), domain: domain.id }); // Send the new password res.render(obj.path.join(__dirname, 'views/message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', message: '
Password for account ' + EscapeHtml(user.name) + ' has been reset to:
' + EscapeHtml(newpass) + '
Login and go to the \"My Account\" tab to update your password. Go to login page.' }); @@ -569,7 +576,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (mesh.links[escUserId] != null) { delete mesh.links[escUserId]; obj.db.Set(mesh); } // Notify mesh change var change = 'Removed user ' + user.name + ' from mesh ' + mesh.name; - obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }) + obj.parent.DispatchEvent(['*', mesh._id, user._id, userid], obj, { etype: 'mesh', username: user.name, userid: userid, meshid: mesh._id, name: mesh.name, mtype: mesh.mtype, desc: mesh.desc, action: 'meshchange', links: mesh.links, msg: change, domain: domain.id }); } } } @@ -582,7 +589,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { delete obj.users[user._id]; req.session = null; res.redirect(domain.url); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }) + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'accountremove', msg: 'Account removed', domain: domain.id }); } else { res.redirect(domain.url); } @@ -609,7 +616,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.db.SetUser(user); req.session.viewmode = 2; res.redirect(domain.url); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id }) + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: user.name, action: 'passchange', msg: 'Account password changed: ' + user.name, domain: domain.id }); }); } @@ -618,11 +625,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var domain = checkUserIpAddress(req, res); if (domain == null) return; if (!obj.args) { res.sendStatus(500); return; } - var domain = getDomain(req); if ((domain.sspi != null) && ((req.query.login == null) || (obj.parent.loginCookieEncryptionKey == null))) { // Login using SSPI - domain.sspi.authenticate(req, res, function (err) { if ((err != null) || (req.connection.user == null)) { res.end('Authentication Required...'); } else { handleRootRequestEx(req, res, domain); } }) + domain.sspi.authenticate(req, res, function (err) { if ((err != null) || (req.connection.user == null)) { res.end('Authentication Required...'); } else { handleRootRequestEx(req, res, domain); } }); } else { // Login using a different system handleRootRequestEx(req, res, domain); @@ -630,7 +636,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } function handleRootRequestEx(req, res, domain) { - var nologout = false; + var nologout = false, user = null, features = 0; res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' }); // Check if we have an incomplete domain name in the path @@ -676,15 +682,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { req.session.currentNode = ''; // Check if this user exists, create it if not. - var user = obj.users[req.session.userid]; + user = obj.users[req.session.userid]; if ((user == null) || (user.sid != req.session.usersid)) { // Create the domain user - var usercount = 0, user = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid }; + var usercount = 0, user2 = { type: 'user', _id: req.session.userid, name: req.connection.user, domain: domain.id, sid: req.session.usersid }; for (var i in obj.users) { if (obj.users[i].domain == domain.id) { usercount++; } } - if (usercount == 0) { user.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin. - obj.users[req.session.userid] = user; - obj.db.SetUser(user); - obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: req.connection.user, account: user, action: 'accountcreate', msg: 'Domain account created, user ' + req.connection.user, domain: domain.id }) + if (usercount == 0) { user2.siteadmin = 0xFFFFFFFF; } // If this is the first user, give the account site admin. + obj.users[req.session.userid] = user2; + obj.db.SetUser(user2); + obj.parent.DispatchEvent(['*', 'server-users'], obj, { etype: 'user', username: req.connection.user, account: user2, action: 'accountcreate', msg: 'Domain account created, user ' + req.connection.user, domain: domain.id }); } } } @@ -706,15 +712,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } else if (req.query.node) { currentNode = 'node/' + domain.id + '/' + req.query.node; } - var user; - var logoutcontrol; - if (obj.args.nousers != true) { - user = obj.users[req.session.userid] - logoutcontrol = 'Welcome ' + user.name + '.'; - } + var logoutcontrol = ''; + if (obj.args.nousers != true) { logoutcontrol = 'Welcome ' + obj.users[req.session.userid].name + '.'; } // Give the web page a list of supported server features - var features = 0; + features = 0; if (obj.args.wanonly == true) { features += 1; } // WAN-only mode if (obj.args.lanonly == true) { features += 2; } // LAN-only mode if (obj.args.nousers == true) { features += 4; } // Single user mode @@ -745,7 +747,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } } else { // Send back the login application - var loginmode = req.session.loginmode, features = 0; + var loginmode = req.session.loginmode; + features = 0; delete req.session.loginmode; // Clear this state, if the user hits refresh, we want to go back to the login page. if ((parent.config != null) && (parent.config.settings != null) && (parent.config.settings.allowframing == true)) { features += 32; } // Allow site within iframe var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified @@ -900,7 +903,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { for (var i = 3; i < spliturl.length; i++) { if (obj.common.IsFilenameValid(spliturl[i]) == true) { path += '/' + spliturl[i]; filename = spliturl[i]; } else { res.sendStatus(404); return; } } var stat = null; - try { stat = obj.fs.statSync(path) } catch (e) { } + try { stat = obj.fs.statSync(path); } catch (e) { } if ((stat != null) && ((stat.mode & 0x004000) == 0)) { if (req.query.download == 1) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=\"' + filename + '\"' }); @@ -923,9 +926,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (splitpath[1] != '') { serverpath += '-' + splitpath[1]; } // Add the domain if needed serverpath += ('/' + splitpath[0] + '-' + splitpath[2]); for (var i = 3; i < splitpath.length; i++) { if (obj.common.IsFilenameValid(splitpath[i]) == true) { serverpath += '/' + splitpath[i]; filename = splitpath[i]; } else { return null; } } // Check that each folder is correct - var fullpath = obj.path.resolve(obj.filespath, serverpath), quota = 0; - return { fullpath: fullpath, path: serverpath, name: filename, quota: obj.getQuota(objid, domain) }; - } + return { fullpath: obj.path.resolve(obj.filespath, serverpath), path: serverpath, name: filename, quota: obj.getQuota(objid, domain) }; + }; // Return the maximum number of bytes allowed in the user account "My Files". obj.getQuota = function (objid, domain) { @@ -944,7 +946,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { return 1048576; // By default, the server will have a 1 meg limit on mesh accounts } return 0; - } + }; // Download a file from the server function handleDownloadFile(req, res) { @@ -1014,7 +1016,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.fs.mkdir(xfile.fullpath, function () { // Write the file obj.fs.writeFile(obj.path.join(xfile.fullpath, filename), filedata, function () { - obj.parent.DispatchEvent([user._id], obj, 'updatefiles') // Fire an event causing this user to update this files + obj.parent.DispatchEvent([user._id], obj, 'updatefiles'); // Fire an event causing this user to update this files }); }); })(xfile.fullpath, names[i], filedata); @@ -1027,7 +1029,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var file = files.files[i], fpath = obj.path.join(xfile.fullpath, file.originalFilename); if (obj.common.IsFilenameValid(file.originalFilename) && ((totalsize + file.size) < xfile.quota)) { // Check if quota would not be broken if we add this file obj.fs.rename(file.path, fpath, function () { - obj.parent.DispatchEvent([user._id], obj, 'updatefiles') // Fire an event causing this user to update this files + obj.parent.DispatchEvent([user._id], obj, 'updatefiles'); // Fire an event causing this user to update this files }); } else { try { obj.fs.unlink(file.path); } catch (e) { } @@ -1051,7 +1053,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.parent.RemoveAllEventDispatch(target); obj.parent.AddEventDispatch(subscriptions, target); return subscriptions; - } + }; // Handle a web socket relay request function handleRelayWebSocket(ws, req) { @@ -1143,13 +1145,13 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // CIRA ---> TLS Debug(3, 'Relay TLS CIRA data', data.length); if (data.length > 0) { try { ser.updateBuffer(Buffer.from(data, 'binary')); } catch (e) { } } - } + }; // Handke CIRA tunnel state change chnl.onStateChange = function (ciraconn, state) { Debug(2, 'Relay TLS CIRA state change', state); if (state == 0) { try { ws.close(); } catch (e) { } } - } + }; // TLSSocket to encapsulate TLS communication, which then tunneled via SerialTunnel an then wrapped through CIRA APF var TLSSocket = require('tls').TLSSocket; @@ -1199,18 +1201,18 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { ws.forwardclient.onStateChange = function (ciraconn, state) { Debug(2, 'Relay CIRA state change', state); if (state == 0) { try { ws.close(); } catch (e) { } } - } + }; ws.forwardclient.onData = function (ciraconn, data) { Debug(4, 'Relay CIRA data', data.length); if (ws.interceptor) { data = ws.interceptor.processAmtData(data); } // Run data thru interceptor if (data.length > 0) { try { ws.send(data); } catch (e) { } } // TODO: Add TLS support - } + }; ws.forwardclient.onSendOk = function (ciraconn) { // TODO: Flow control? (Dont' really need it with AMT, but would be nice) //console.log('onSendOk'); - } + }; // Fetch Intel AMT credentials & Setup interceptor if (req.query.p == 1) { @@ -1345,7 +1347,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { var r = 0, dir; try { dir = obj.fs.readdirSync(path); } catch (e) { return 0; } for (var i in dir) { - var stat = obj.fs.statSync(path + '/' + dir[i]) + var stat = obj.fs.statSync(path + '/' + dir[i]); if ((stat.mode & 0x004000) == 0) { r += stat.size; } else { r += readTotalFileSize(path + '/' + dir[i]); } } return r; @@ -1359,15 +1361,15 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { if (obj.fs.lstatSync(pathx).isDirectory()) { deleteFolderRec(pathx); } else { obj.fs.unlinkSync(pathx); } }); obj.fs.rmdirSync(path); - }; + } // Handle Intel AMT events // To subscribe, add "http://server:port/amtevents.ashx" to Intel AMT subscriptions. obj.handleAmtEventRequest = function (req, res) { var domain = getDomain(req); try { - if (req.headers['authorization']) { - var authstr = req.headers['authorization']; + if (req.headers.authorization) { + var authstr = req.headers.authorization; if (authstr.substring(0, 7) == "Digest ") { var auth = obj.common.parseNameValueList(obj.common.quoteSplit(authstr.substring(7))); if ((req.url === auth.uri) && (obj.httpAuthRealm === auth.realm) && (auth.opaque === obj.crypto.createHmac('SHA384', obj.httpAuthRandom).update(auth.nonce).digest('hex'))) { @@ -1436,7 +1438,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { res.set({ 'WWW-Authenticate': 'Digest realm="' + obj.httpAuthRealm + '", qop="auth,auth-int", nonce="' + nonce + '", opaque="' + opaque + '"' }); res.sendStatus(401); }); - } + }; // Handle a server backup request function handleBackupRequest(req, res) { @@ -1541,11 +1543,11 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // If the agentid is 3 or 4, check if we have a signed MeshCmd.exe if ((agentid == 3)) { // Signed Windows MeshCmd.exe x86 var stats = null, meshCmdPath = obj.path.join(__dirname, 'agents', 'MeshCmd-signed.exe'); - try { stats = obj.fs.statSync(meshCmdPath) } catch (e) { } + try { stats = obj.fs.statSync(meshCmdPath); } catch (e) { } if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 3) ? '.exe' : '') }); res.sendFile(meshCmdPath); return; } } else if ((agentid == 4)) { // Signed Windows MeshCmd64.exe x64 var stats = null, meshCmd64Path = obj.path.join(__dirname, 'agents', 'MeshCmd64-signed.exe'); - try { stats = obj.fs.statSync(meshCmd64Path) } catch (e) { } + try { stats = obj.fs.statSync(meshCmd64Path); } catch (e) { } if ((stats != null)) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshcmd' + ((req.query.meshcmd <= 4) ? '.exe' : '') }); res.sendFile(meshCmd64Path); return; } } // No signed agents, we are going to merge a new MeshCmd. @@ -1581,7 +1583,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate debugLevel: 0 - } + }; if (user != null) { meshaction.username = user.name; } var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; } @@ -1596,7 +1598,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { serverId: obj.agentCertificateHashHex.toUpperCase(), // SHA384 of server HTTPS public key serverHttpsHash: new Buffer(obj.webCertificateHash, 'binary').toString('hex').toUpperCase(), // SHA384 of server HTTPS certificate debugLevel: 0 - } + }; if (user != null) { meshaction.username = user.name; } var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified if (obj.args.lanonly != true) { meshaction.serverUrl = ((obj.args.notls == true) ? 'ws://' : 'wss://') + getWebServerName(domain) + ':' + httpsPort + '/' + ((domain.id == '') ? '' : ('/' + domain.id)) + 'meshrelay.ashx'; } @@ -1619,7 +1621,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { response += ''; res.send(response); } - } + }; // Get the web server hostname. This may change if using a domain with a DNS name. function getWebServerName(domain) { @@ -1660,7 +1662,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0', 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=meshagent.msh' }); res.send(meshsettings); - } + }; // Add HTTP security headers to all responses obj.app.use(function (req, res, next) { @@ -1785,7 +1787,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Check we have agent rights var rights = user.links[agent.dbMeshKey].rights; if ((rights != null) && ((rights & MESHRIGHT_AGENTCONSOLE) != 0) && (user.siteadmin == 0xFFFFFFFF)) { agent.close(disconnectMode); } - } + }; // Send the core module to the mesh agent obj.sendMeshAgentCore = function (user, domain, nodeid, core) { @@ -1815,7 +1817,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { agent.send(obj.common.ShortToStr(10) + obj.common.ShortToStr(0) + hash + core); } } - } + }; // Get the server path of a user or mesh object function getServerRootFilePath(obj) { @@ -1878,36 +1880,37 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { // Count sessions and event any changes obj.recountSessions = function (changedSessionId) { + var userid, oldcount, newcount, x, serverid; if (changedSessionId == null) { // Recount all sessions // Calculate the session count for all userid's var newSessionsCount = {}; - for (var userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; } - for (var serverid in obj.wsPeerSessions3) { - for (var userid in obj.wsPeerSessions3[serverid]) { - var c = obj.wsPeerSessions3[serverid][userid].length; - if (newSessionsCount[userid] == null) { newSessionsCount[userid] = c; } else { newSessionsCount[userid] += c; } + for (userid in obj.wssessions) { newSessionsCount[userid] = obj.wssessions[userid].length; } + for (serverid in obj.wsPeerSessions3) { + for (userid in obj.wsPeerSessions3[serverid]) { + x = obj.wsPeerSessions3[serverid][userid].length; + if (newSessionsCount[userid] == null) { newSessionsCount[userid] = x; } else { newSessionsCount[userid] += x; } } } // See what session counts have changed, event any changes - for (var userid in newSessionsCount) { - var newcount = newSessionsCount[userid]; - var oldcount = obj.sessionsCount[userid]; + for (userid in newSessionsCount) { + newcount = newSessionsCount[userid]; + oldcount = obj.sessionsCount[userid]; if (oldcount == null) { oldcount = 0; } else { delete obj.sessionsCount[userid]; } if (newcount != oldcount) { - var x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }) + x = userid.split('/'); + obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); } } // If there are any counts left in the old counts, event to zero - for (var userid in obj.sessionsCount) { - var oldcount = obj.sessionsCount[userid]; + for (userid in obj.sessionsCount) { + oldcount = obj.sessionsCount[userid]; if ((oldcount != null) && (oldcount != 0)) { - var x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 }) + x = userid.split('/'); + obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: 0, domain: x[1], nolog: 1, nopeers: 1 }); } } @@ -1915,23 +1918,23 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { obj.sessionsCount = newSessionsCount; } else { // Figure out the userid - var userid = changedSessionId.split('/').slice(0, 3).join('/'); + userid = changedSessionId.split('/').slice(0, 3).join('/'); // Recount only changedSessionId - var newcount = 0; + newcount = 0; if (obj.wssessions[userid] != null) { newcount = obj.wssessions[userid].length; } - for (var serverid in obj.wsPeerSessions3) { if (obj.wsPeerSessions3[serverid][userid] != null) { newcount += obj.wsPeerSessions3[serverid][userid].length; } } - var oldcount = obj.sessionsCount[userid]; + for (serverid in obj.wsPeerSessions3) { if (obj.wsPeerSessions3[serverid][userid] != null) { newcount += obj.wsPeerSessions3[serverid][userid].length; } } + oldcount = obj.sessionsCount[userid]; if (oldcount == null) { oldcount = 0; } // If the count changed, update and event if (newcount != oldcount) { - var x = userid.split('/'); - obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }) + x = userid.split('/'); + obj.parent.DispatchEvent(['*'], obj, { action: 'wssessioncount', username: x[2], count: newcount, domain: x[1], nolog: 1, nopeers: 1 }); obj.sessionsCount[userid] = newcount; } } - } + }; // Return true if a mobile browser is detected. // This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/ @@ -1943,5 +1946,4 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) { } return obj; -} - +}; \ No newline at end of file diff --git a/winservice.js b/winservice.js index a2bb2b30..01096d17 100644 --- a/winservice.js +++ b/winservice.js @@ -6,7 +6,12 @@ * @version v0.0.1 */ -'use strict'; +/*jslint node: true */ +/*jshint node: true */ +/*jshint strict:false */ +/*jshint -W097 */ +/*jshint esversion: 6 */ +"use strict"; // This module is only called when MeshCentral is running as a Windows service. // In this case, we don't want to start a child process, so we launch directly without arguments.