Updated agentupdate

Bryan Roe committed Jan 14, 2021 at 23:57 UTC 562f8a6cebb41f4ee09884c29a8f052703541ae1
2 files changed +1227 -684
agents/meshcore.js
+1199 -636
@@ -40,6 +40,7 @@ var MESHRIGHT_LIMITEVENTS = 8192;
40 var MESHRIGHT_CHATNOTIFY = 16384;
41 var MESHRIGHT_UNINSTALL = 32768;
42 var MESHRIGHT_NODESKTOP = 65536;
43 +
44 if (require('MeshAgent').ARCHID == null)
45 {
46 var id = null;
@@ -58,7 +59,8 @@ if (require('MeshAgent').ARCHID == null)
59 if (id != null) { Object.defineProperty(require('MeshAgent'), 'ARCHID', { value: id }); }
60 }
61
61 -function createMeshCore(agent) {
62 +function createMeshCore(agent)
63 +{
64 var obj = {};
65 var agentFileHttpRequests = {}; // Currently active agent HTTPS GET requests from the server.
66 var agentFileHttpPendingRequests = []; // Pending HTTPS GET requests from the server.
@@ -67,7 +69,8 @@ function createMeshCore(agent) {
69 if (process.platform == 'win32' && require('user-sessions').isRoot())
70 {
71 // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
70 - try {
72 + try
73 + {
74 var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
75 try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
76 if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
@@ -91,7 +94,8 @@ function createMeshCore(agent) {
94 } catch (e) { }
95 }
96
94 - if (process.platform == 'darwin' && !process.versions) {
97 + if (process.platform == 'darwin' && !process.versions)
98 + {
99 // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
100 var child = require('child_process').execFile('/bin/sh', ['sh']);
101 child.stdout.str = '';
@@ -100,18 +104,21 @@ function createMeshCore(agent) {
104 child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
105 child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
106 child.waitExit();
103 - if (child.stdout.str.trim() == 'Crashed') {
107 + if (child.stdout.str.trim() == 'Crashed')
108 + {
109 child = require('child_process').execFile('/bin/sh', ['sh']);
110 child.stdout.str = '';
111 child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
112 child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
113 child.waitExit();
114
110 - if (parseInt(child.stdout.str.trim()) == process.pid) {
115 + if (parseInt(child.stdout.str.trim()) == process.pid)
116 + {
117 // The currently running MeshAgent is us, so we can continue with the update
118 var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
119 var tokens = plist.split('<key>KeepAlive</key>');
114 - if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
120 + if (tokens[1].split('>')[0].split('<')[1] == 'dict')
121 + {
122 var tmp = tokens[1].split('</dict>');
123 tmp.shift();
124 tokens[1] = '\n <true/>' + tmp.join('</dict>');
@@ -167,7 +174,8 @@ function createMeshCore(agent) {
174 }
175
176 // Add an Intel AMT event to the log
170 - function addAmtEvent(msg) {
177 + function addAmtEvent(msg)
178 + {
179 if (obj.amtevents == null) { obj.amtevents = []; }
180 var d = new Date();
181 obj.amtevents.push(zeroPad(d.getHours(), 2) + ':' + zeroPad(d.getMinutes(), 2) + ':' + zeroPad(d.getSeconds(), 2) + ', ' + msg);
@@ -182,8 +190,10 @@ function createMeshCore(agent) {
190 obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
191 try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
192 obj.DAIPC._daipc = [];
185 - obj.DAIPC.on('connection', function (c) {
186 - c._send = function (j) {
193 + obj.DAIPC.on('connection', function (c)
194 + {
195 + c._send = function (j)
196 + {
197 var data = JSON.stringify(j);
198 var packet = Buffer.alloc(data.length + 4);
199 packet.writeUInt32LE(data.length + 4, 0);
@@ -193,7 +203,8 @@ function createMeshCore(agent) {
203 this._daipc.push(c);
204 c.parent = this;
205 c.on('end', function () { removeRegisteredApp(this); });
196 - c.on('data', function (chunk) {
206 + c.on('data', function (chunk)
207 + {
208 if (chunk.length < 4) { this.unshift(chunk); return; }
209 var len = chunk.readUInt32LE(0);
210 if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
@@ -203,8 +214,10 @@ function createMeshCore(agent) {
214 try { data = JSON.parse(data.toString()); } catch (e) { }
215 if ((data == null) || (typeof data.cmd != 'string')) return;
216
206 - try {
207 - switch (data.cmd) {
217 + try
218 + {
219 + switch (data.cmd)
220 + {
221 case 'requesthelp':
222 if (this._registered == null) return;
223 sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
@@ -219,7 +232,8 @@ function createMeshCore(agent) {
232 try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
233 break;
234 case 'register':
222 - if (typeof data.value == 'string') {
235 + if (typeof data.value == 'string')
236 + {
237 this._registered = data.value;
238 var apps = {};
239 apps[data.value] = 1;
@@ -235,12 +249,13 @@ function createMeshCore(agent) {
249 this._send(data);
250 break;
251 case 'descriptors':
238 - require('ChainViewer').getSnapshot().then(function (f) {
252 + require('ChainViewer').getSnapshot().then(function (f)
253 + {
254 this.tag.payload.result = f;
255 this.tag.ipc._send(this.tag.payload);
256 }).parentPromise.tag = { ipc: this, payload: data };
257 break;
243 - case 'timerinfo':
258 + case 'timerinfo':
259 data.result = require('ChainViewer').getTimerInfo();
260 this._send(data);
261 break;
@@ -256,10 +271,11 @@ function createMeshCore(agent) {
271 this._send({ cmd: 'sessions', sessions: tunnelUserCount });
272 break;
273 case 'meshToolInfo':
259 - try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie?true:false, pipe: true }); } catch (e) { }
274 + try { mesh.SendCommand({ action: 'meshToolInfo', name: data.name, hash: data.hash, cookie: data.cookie ? true : false, pipe: true }); } catch (e) { }
275 break;
276 case 'console':
262 - if (debugConsole) {
277 + if (debugConsole)
278 + {
279 var args = splitArgs(data.value);
280 processConsoleCommand(args[0].toLowerCase(), parseArgs(args), 0, 'pipe');
281 }
@@ -271,24 +287,28 @@ function createMeshCore(agent) {
287 });
288
289 // Send current sessions to registered apps
274 - function broadcastSessionsToRegisteredApps(x) {
290 + function broadcastSessionsToRegisteredApps(x)
291 + {
292 broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
293 }
294
295 // Send this object to all registered local applications
279 - function broadcastToRegisteredApps(x) {
296 + function broadcastToRegisteredApps(x)
297 + {
298 if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
299 for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
300 }
301
302 // Send this object to a specific registered local applications
285 - function sendToRegisteredApp(appid, x) {
303 + function sendToRegisteredApp(appid, x)
304 + {
305 if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
306 for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered == appid) { obj.DAIPC._daipc[i]._send(x); } }
307 }
308
309 // Send list of registered apps to the server
291 - function updateRegisteredAppsToServer() {
310 + function updateRegisteredAppsToServer()
311 + {
312 if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
313 var apps = {};
314 for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
@@ -296,26 +316,32 @@ function createMeshCore(agent) {
316 }
317
318 // Remove a registered app
299 - function removeRegisteredApp(pipe) {
319 + function removeRegisteredApp(pipe)
320 + {
321 for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
322 if (pipe._registered != null) updateRegisteredAppsToServer();
323 }
324
304 - function diagnosticAgent_uninstall() {
325 + function diagnosticAgent_uninstall()
326 + {
327 require('service-manager').manager.uninstallService('meshagentDiagnostic');
328 require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
329 };
308 - function diagnosticAgent_installCheck(install) {
309 - try {
330 + function diagnosticAgent_installCheck(install)
331 + {
332 + try
333 + {
334 var diag = require('service-manager').manager.getService('meshagentDiagnostic');
335 return (diag);
336 }
313 - catch (e) {
337 + catch (e)
338 + {
339 }
340 if (!install) { return (null); }
341
342 var svc = null;
318 - try {
343 + try
344 + {
345 require('service-manager').manager.installService(
346 {
347 name: 'meshagentDiagnostic',
@@ -327,7 +353,8 @@ function createMeshCore(agent) {
353 });
354 svc = require('service-manager').manager.getService('meshagentDiagnostic');
355 }
330 - catch (e) {
356 + catch (e)
357 + {
358 return (null);
359 }
360 var proxyConfig = require('global-tunnel').proxyConfig;
@@ -340,9 +367,11 @@ function createMeshCore(agent) {
367 ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
368 if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
369 if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
343 - if (proxyConfig) {
370 + if (proxyConfig)
371 + {
372 ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
345 - } else {
373 + } else
374 + {
375 ddb.Put('ignoreProxyFile', '1');
376 }
377
@@ -359,18 +388,24 @@ function createMeshCore(agent) {
388 }
389
390 // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
362 - if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
391 + if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null))
392 + {
393 // Setup manual battery monitoring
364 - require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
394 + require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function ()
395 + {
396 if (require('MeshAgent')._batteryFileTimer != null) return;
366 - require('MeshAgent')._batteryFileTimer = setTimeout(function () {
367 - try {
397 + require('MeshAgent')._batteryFileTimer = setTimeout(function ()
398 + {
399 + try
400 + {
401 require('MeshAgent')._batteryFileTimer = null;
402 var data = null;
403 try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
371 - if ((data != null) && (data.length < 10)) {
404 + if ((data != null) && (data.length < 10))
405 + {
406 data = data.split(',');
373 - if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
407 + if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc')))
408 + {
409 var level = parseInt(data[1]);
410 if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
411 }
@@ -378,24 +413,31 @@ function createMeshCore(agent) {
413 } catch (e) { }
414 }, 1000);
415 });
381 - } else {
416 + } else
417 + {
418 // Setup normal battery monitoring
383 - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
384 - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
419 + if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered())
420 + {
421 + require('MeshAgent')._battLevelChanged = function _battLevelChanged(val)
422 + {
423 _battLevelChanged.self._currentBatteryLevel = val;
424 _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
425 };
426 require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
389 - require('MeshAgent')._powerChanged = function _powerChanged(val) {
427 + require('MeshAgent')._powerChanged = function _powerChanged(val)
428 + {
429 _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
430 _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
431 };
432 require('MeshAgent')._powerChanged.self = require('MeshAgent');
394 - require('MeshAgent').on('Connected', function (status) {
395 - if (status == 0) {
433 + require('MeshAgent').on('Connected', function (status)
434 + {
435 + if (status == 0)
436 + {
437 require('power-monitor').removeListener('acdc', this._powerChanged);
438 require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
398 - } else {
439 + } else
440 + {
441 require('power-monitor').on('acdc', this._powerChanged);
442 require('power-monitor').on('batteryLevel', this._battLevelChanged);
443 }
@@ -445,12 +487,16 @@ function createMeshCore(agent) {
487 try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; meshCoreObjChanged(); }); } catch (e) { }
488
489 // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
448 - try {
490 + try
491 + {
492 var userSession = require('user-sessions');
450 - userSession.on('changed', function onUserSessionChanged() {
451 - userSession.enumerateUsers().then(function (users) {
493 + userSession.on('changed', function onUserSessionChanged()
494 + {
495 + userSession.enumerateUsers().then(function (users)
496 + {
497 var u = [], a = users.Active;
453 - for (var i = 0; i < a.length; i++) {
498 + for (var i = 0; i < a.length; i++)
499 + {
500 var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
501 if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
502 }
@@ -482,9 +528,11 @@ function createMeshCore(agent) {
528 var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
529
530 // Add to the server event log
485 - function MeshServerLog(msg, state) {
531 + function MeshServerLog(msg, state)
532 + {
533 if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
487 - if (state) {
534 + if (state)
535 + {
536 if (state.userid) { msg.userid = state.userid; }
537 if (state.username) { msg.username = state.username; }
538 if (state.sessionid) { msg.sessionid = state.sessionid; }
@@ -494,9 +542,11 @@ function createMeshCore(agent) {
542 }
543
544 // Add to the server event log, use internationalized events
497 - function MeshServerLogEx(id, args, msg, state) {
545 + function MeshServerLogEx(id, args, msg, state)
546 + {
547 var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
499 - if (state) {
548 + if (state)
549 + {
550 if (state.userid) { msg.userid = state.userid; }
551 if (state.username) { msg.username = state.username; }
552 if (state.sessionid) { msg.sessionid = state.sessionid; }
@@ -510,13 +560,16 @@ function createMeshCore(agent) {
560 sha = require('SHA256Stream');
561 mesh = require('MeshAgent');
562 childProcess = require('child_process');
513 - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
563 + if (mesh.hasKVM == 1)
564 + { // if the agent is compiled with KVM support
565 // Check if this computer supports a desktop
566 try
567 {
517 - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) {
568 + if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
569 + {
570 meshCoreObj.caps |= 1; meshCoreObjChanged();
519 - } else if (process.platform == 'linux' || process.platform == 'freebsd') {
571 + } else if (process.platform == 'linux' || process.platform == 'freebsd')
572 + {
573 require('monitor-info').on('kvmSupportDetected', function (value) { meshCoreObj.caps |= 1; meshCoreObjChanged(); });
574 }
575 } catch (e) { }
@@ -536,12 +589,16 @@ function createMeshCore(agent) {
589 // Fetch the SMBios Tables
590 var SMBiosTables = null;
591 var SMBiosTablesRaw = null;
539 - try {
592 + try
593 + {
594 var SMBiosModule = null;
595 try { SMBiosModule = require('smbios'); } catch (e) { }
542 - if (SMBiosModule != null) {
543 - SMBiosModule.get(function (data) {
544 - if (data != null) {
596 + if (SMBiosModule != null)
597 + {
598 + SMBiosModule.get(function (data)
599 + {
600 + if (data != null)
601 + {
602 SMBiosTablesRaw = data;
603 SMBiosTables = require('smbios').parse(data)
604 if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
@@ -562,7 +619,8 @@ function createMeshCore(agent) {
619 } catch (e) { sendConsoleText("ex1: " + e); }
620
621 // Try to load up the WIFI scanner
565 - try {
622 + try
623 + {
624 var wifiScannerLib = require('wifi-scanner');
625 wifiScanner = new wifiScannerLib();
626 wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
@@ -571,28 +629,36 @@ function createMeshCore(agent) {
629 // Get our location (lat/long) using our public IP address
630 var getIpLocationDataExInProgress = false;
631 var getIpLocationDataExCounts = [0, 0];
574 - function getIpLocationDataEx(func) {
632 + function getIpLocationDataEx(func)
633 + {
634 if (getIpLocationDataExInProgress == true) { return false; }
576 - try {
635 + try
636 + {
637 getIpLocationDataExInProgress = true;
638 getIpLocationDataExCounts[0]++;
639 var options = http.parseUri("http://ipinfo.io/json");
640 options.method = 'GET';
581 - http.request(options, function (resp) {
582 - if (resp.statusCode == 200) {
641 + http.request(options, function (resp)
642 + {
643 + if (resp.statusCode == 200)
644 + {
645 var geoData = '';
646 resp.data = function (geoipdata) { geoData += geoipdata; };
585 - resp.end = function () {
647 + resp.end = function ()
648 + {
649 var location = null;
587 - try {
588 - if (typeof geoData == 'string') {
650 + try
651 + {
652 + if (typeof geoData == 'string')
653 + {
654 var result = JSON.parse(geoData);
655 if (result.ip && result.loc) { location = result; }
656 }
657 } catch (e) { }
658 if (func) { getIpLocationDataExCounts[1]++; func(location); }
659 }
595 - } else { func(null); }
660 + } else
661 + { func(null); }
662 getIpLocationDataExInProgress = false;
663 }).end();
664 return true;
@@ -601,45 +667,57 @@ function createMeshCore(agent) {
667 }
668
669 // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
604 - function clearGatewayMac(str) {
670 + function clearGatewayMac(str)
671 + {
672 if (str == null) return null;
673 var x = JSON.parse(str);
674 for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
675 return JSON.stringify(x);
676 }
677
611 - function getIpLocationData(func) {
678 + function getIpLocationData(func)
679 + {
680 // Get the location information for the cache if possible
681 var publicLocationInfo = db.Get('publicLocationInfo');
682 if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
615 - if (publicLocationInfo == null) {
683 + if (publicLocationInfo == null)
684 + {
685 // Nothing in the cache, fetch the data
617 - getIpLocationDataEx(function (locationData) {
618 - if (locationData != null) {
686 + getIpLocationDataEx(function (locationData)
687 + {
688 + if (locationData != null)
689 + {
690 publicLocationInfo = {};
691 publicLocationInfo.netInfoStr = lastNetworkInfo;
692 publicLocationInfo.locationData = locationData;
693 var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
694 if (func) func(locationData); // Report the new location
624 - } else {
695 + } else
696 + {
697 if (func) func(null); // Report no location
698 }
699 });
628 - } else {
700 + } else
701 + {
702 // Check the cache
630 - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
703 + if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo))
704 + {
705 // Cache match
706 if (func) func(publicLocationInfo.locationData);
633 - } else {
707 + } else
708 + {
709 // Cache mismatch
635 - getIpLocationDataEx(function (locationData) {
636 - if (locationData != null) {
710 + getIpLocationDataEx(function (locationData)
711 + {
712 + if (locationData != null)
713 + {
714 publicLocationInfo = {};
715 publicLocationInfo.netInfoStr = lastNetworkInfo;
716 publicLocationInfo.locationData = locationData;
717 var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
718 if (func) func(locationData); // Report the new location
642 - } else {
719 + } else
720 + {
721 if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
722 }
723 });
@@ -648,8 +726,10 @@ function createMeshCore(agent) {
726 }
727
728 // Polyfill String.endsWith
651 - if (!String.prototype.endsWith) {
652 - String.prototype.endsWith = function (searchString, position) {
729 + if (!String.prototype.endsWith)
730 + {
731 + String.prototype.endsWith = function (searchString, position)
732 + {
733 var subjectString = this.toString();
734 if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
735 position -= searchString.length;
@@ -660,13 +740,17 @@ function createMeshCore(agent) {
740
741 // Polyfill path.join
742 obj.path = {
663 - join: function () {
743 + join: function ()
744 + {
745 var x = [];
665 - for (var i in arguments) {
746 + for (var i in arguments)
747 + {
748 var w = arguments[i];
667 - if (w != null) {
749 + if (w != null)
750 + {
751 while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
669 - if (i != 0) {
752 + if (i != 0)
753 + {
754 while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
755 }
756 x.push(w);
@@ -690,7 +774,8 @@ function createMeshCore(agent) {
774 function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
775
776 // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
693 - function hex2rstr(d) {
777 + function hex2rstr(d)
778 + {
779 if (typeof d != "string" || d.length == 0) return '';
780 var r = '', m = ('' + d).match(/../g), t;
781 while (t = m.shift()) r += String.fromCharCode('0x' + t);
@@ -698,7 +783,8 @@ function createMeshCore(agent) {
783 }
784
785 // Convert an object to string with all functions
701 - function objToString(x, p, pad, ret) {
786 + function objToString(x, p, pad, ret)
787 + {
788 if (ret == undefined) ret = '';
789 if (p == undefined) p = 0;
790 if (x == null) { return '[null]'; }
@@ -716,21 +802,26 @@ function createMeshCore(agent) {
802 function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
803
804 // Split a string taking into account the quoats. Used for command line parsing
719 - function splitArgs(str) {
805 + function splitArgs(str)
806 + {
807 var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
808 do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
809 return myArray;
810 }
811
812 // Parse arguments string array into an object
726 - function parseArgs(argv) {
813 + function parseArgs(argv)
814 + {
815 var results = { '_': [] }, current = null;
728 - for (var i = 1, len = argv.length; i < len; i++) {
816 + for (var i = 1, len = argv.length; i < len; i++)
817 + {
818 var x = argv[i];
730 - if (x.length > 2 && x[0] == '-' && x[1] == '-') {
819 + if (x.length > 2 && x[0] == '-' && x[1] == '-')
820 + {
821 if (current != null) { results[current] = true; }
822 current = x.substring(2);
733 - } else {
823 + } else
824 + {
825 if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
826 }
827 }
@@ -739,7 +830,8 @@ function createMeshCore(agent) {
830 }
831
832 // Get server target url with a custom path
742 - function getServerTargetUrl(path) {
833 + function getServerTargetUrl(path)
834 + {
835 var x = mesh.ServerUrl;
836 //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
837 if (x == null) { return null; }
@@ -750,27 +842,35 @@ function createMeshCore(agent) {
842 }
843
844 // Get server url. If the url starts with "*/..." change it, it not use the url as is.
753 - function getServerTargetUrlEx(url) {
845 + function getServerTargetUrlEx(url)
846 + {
847 if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
848 return url;
849 }
850
851 // Send a wake-on-lan packet
759 - function sendWakeOnLan(hexMac) {
852 + function sendWakeOnLan(hexMac)
853 + {
854 hexMac = hexMac.split(':').join('');
855 var count = 0;
762 - try {
856 + try
857 + {
858 var interfaces = require('os').networkInterfaces();
859 var magic = 'FFFFFFFFFFFF';
860 for (var x = 1; x <= 16; ++x) { magic += hexMac; }
861 var magicbin = Buffer.from(magic, 'hex');
862
768 - for (var adapter in interfaces) {
769 - if (interfaces.hasOwnProperty(adapter)) {
770 - for (var i = 0; i < interfaces[adapter].length; ++i) {
863 + for (var adapter in interfaces)
864 + {
865 + if (interfaces.hasOwnProperty(adapter))
866 + {
867 + for (var i = 0; i < interfaces[adapter].length; ++i)
868 + {
869 var addr = interfaces[adapter][i];
772 - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
773 - try {
870 + if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00'))
871 + {
872 + try
873 + {
874 var socket = require('dgram').createSocket({ type: 'udp4' });
875 socket.bind({ address: addr.address });
876 socket.setBroadcast(true);
@@ -790,14 +890,22 @@ function createMeshCore(agent) {
890 }
891
892 // Handle a mesh agent command
793 - function handleServerCommand(data) {
794 - if (typeof data == 'object') {
893 + function handleServerCommand(data)
894 + {
895 + if (typeof data == 'object')
896 + {
897 // If this is a console command, parse it and call the console handler
796 - switch (data.action) {
898 + switch (data.action)
899 + {
900 + case 'agentupdate':
901 + agentUpdate_Start(data.url, { hash: data.hash, tlshash: data.servertlshash });
902 + break;
903 case 'msg': {
798 - switch (data.type) {
904 + switch (data.type)
905 + {
906 case 'console': { // Process a console command
800 - if (data.value && data.sessionid) {
907 + if (data.value && data.sessionid)
908 + {
909 MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
910 var args = splitArgs(data.value);
911 processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
@@ -805,10 +913,12 @@ function createMeshCore(agent) {
913 break;
914 }
915 case 'tunnel': {
808 - if (data.value != null) { // Process a new tunnel connection request
916 + if (data.value != null)
917 + { // Process a new tunnel connection request
918 // Create a new tunnel object
919 var xurl = getServerTargetUrlEx(data.value);
811 - if (xurl != null) {
920 + if (xurl != null)
921 + {
922 xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
923 var woptions = http.parseUri(xurl);
924 woptions.perMessageDeflate = false;
@@ -816,7 +926,8 @@ function createMeshCore(agent) {
926
927 // Perform manual server TLS certificate checking based on the certificate hash given by the server.
928 woptions.rejectUnauthorized = 0;
819 - woptions.checkServerIdentity = function checkServerIdentity(certs) {
929 + woptions.checkServerIdentity = function checkServerIdentity(certs)
930 + {
931 // If the tunnel certificate matches the control channel certificate, accept the connection
932 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
933 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
@@ -859,7 +970,8 @@ function createMeshCore(agent) {
970 }
971 case 'messagebox': {
972 // Display a message box
862 - if (data.title && data.msg) {
973 + if (data.title && data.msg)
974 + {
975 MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
976 data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
977 try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
@@ -868,8 +980,10 @@ function createMeshCore(agent) {
980 }
981 case 'ps': {
982 // Return the list of running processes
871 - if (data.sessionid) {
872 - processManager.getProcesses(function (plist) {
983 + if (data.sessionid)
984 + {
985 + processManager.getProcesses(function (plist)
986 + {
987 mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
988 });
989 }
@@ -877,7 +991,8 @@ function createMeshCore(agent) {
991 }
992 case 'pskill': {
993 // Kill a process
880 - if (data.value) {
994 + if (data.value)
995 + {
996 MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
997 try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
998 }
@@ -892,7 +1007,8 @@ function createMeshCore(agent) {
1007 }
1008 case 'serviceStop': {
1009 // Stop a service
895 - try {
1010 + try
1011 + {
1012 var service = require('service-manager').manager.getService(data.serviceName);
1013 if (service != null) { service.stop(); }
1014 } catch (e) { }
@@ -900,7 +1016,8 @@ function createMeshCore(agent) {
1016 }
1017 case 'serviceStart': {
1018 // Start a service
903 - try {
1019 + try
1020 + {
1021 var service = require('service-manager').manager.getService(data.serviceName);
1022 if (service != null) { service.start(); }
1023 } catch (e) { }
@@ -908,7 +1025,8 @@ function createMeshCore(agent) {
1025 }
1026 case 'serviceRestart': {
1027 // Restart a service
911 - try {
1028 + try
1029 + {
1030 var service = require('service-manager').manager.getService(data.serviceName);
1031 if (service != null) { service.restart(); }
1032 } catch (e) { }
@@ -917,12 +1035,16 @@ function createMeshCore(agent) {
1035 case 'deskBackground':
1036 {
1037 // Toggle desktop background
920 - try {
921 - if (process.platform == 'win32') {
1038 + try
1039 + {
1040 + if (process.platform == 'win32')
1041 + {
1042 var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
1043 var sid = undefined;
924 - if (stype == 1) {
925 - if (require('MeshAgent')._tsid != null) {
1044 + if (stype == 1)
1045 + {
1046 + if (require('MeshAgent')._tsid != null)
1047 + {
1048 stype = 5;
1049 sid = require('MeshAgent')._tsid;
1050 }
@@ -938,13 +1060,15 @@ function createMeshCore(agent) {
1060 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
1061 child.stderr.on('data', function () { });
1062 child.waitExit();
941 - } else {
1063 + } else
1064 + {
1065 var id = require('user-sessions').consoleUid();
1066 var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
1067 if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
1068 require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
1069 }
947 - } catch (e) {
1070 + } catch (e)
1071 + {
1072 sendConsoleText(e);
1073 }
1074 break;
@@ -959,16 +1083,22 @@ function createMeshCore(agent) {
1083 case 'getclip': {
1084 // Send the load clipboard back to the user
1085 //sendConsoleText('getClip: ' + JSON.stringify(data));
962 - if (require('MeshAgent').isService) {
963 - require('clipboard').dispatchRead().then(function (str) {
964 - if (str) {
1086 + if (require('MeshAgent').isService)
1087 + {
1088 + require('clipboard').dispatchRead().then(function (str)
1089 + {
1090 + if (str)
1091 + {
1092 MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1093 mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1094 }
1095 });
969 - } else {
970 - require("clipboard").read().then(function (str) {
971 - if (str) {
1096 + } else
1097 + {
1098 + require("clipboard").read().then(function (str)
1099 + {
1100 + if (str)
1101 + {
1102 MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
1103 mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
1104 }
@@ -979,7 +1109,8 @@ function createMeshCore(agent) {
1109 case 'setclip': {
1110 // Set the load clipboard to a user value
1111 //sendConsoleText('setClip: ' + JSON.stringify(data));
982 - if (typeof data.data == 'string') {
1112 + if (typeof data.data == 'string')
1113 + {
1114 MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
1115 if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
1116 mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
@@ -1014,7 +1145,8 @@ function createMeshCore(agent) {
1145 break;
1146 }
1147 case 'acmactivate': {
1017 - if (amt != null) {
1148 + if (amt != null)
1149 + {
1150 MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
1151 amt.setAcmResponse(data);
1152 }
@@ -1032,17 +1164,21 @@ function createMeshCore(agent) {
1164
1165 // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
1166 var options = {};
1035 - if (data.runAsUser > 0) {
1167 + if (data.runAsUser > 0)
1168 + {
1169 try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
1170 options.type = require('child_process').SpawnTypes.TERM;
1171 }
1039 - if (data.runAsUser == 2) {
1172 + if (data.runAsUser == 2)
1173 + {
1174 if (options.uid == null) break;
1175 if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1176 }
1177
1044 - if (process.platform == 'win32') {
1045 - if (data.type == 1) {
1178 + if (process.platform == 'win32')
1179 + {
1180 + if (data.type == 1)
1181 + {
1182 // Windows command shell
1183 mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1184 mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
@@ -1050,7 +1186,8 @@ function createMeshCore(agent) {
1186 mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1187 mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1188 mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1053 - } else if (data.type == 2) {
1189 + } else if (data.type == 2)
1190 + {
1191 // Windows Powershell
1192 mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1193 mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
@@ -1059,7 +1196,8 @@ function createMeshCore(agent) {
1196 mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1197 mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1198 }
1062 - } else if (data.type == 3) {
1199 + } else if (data.type == 3)
1200 + {
1201 // Linux shell
1202 mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1203 mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
@@ -1081,7 +1219,8 @@ function createMeshCore(agent) {
1219 {
1220 }
1221
1084 - if (require('service-manager').manager.getService(agentName).isMe()) {
1222 + if (require('service-manager').manager.getService(agentName).isMe())
1223 + {
1224 try { diagnosticAgent_uninstall(); } catch (e) { }
1225 var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1226 this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
@@ -1089,7 +1228,8 @@ function createMeshCore(agent) {
1228 break;
1229 case 'poweraction': {
1230 // Server telling us to execute a power action
1092 - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1231 + if ((mesh.ExecPowerState != undefined) && (data.actiontype))
1232 + {
1233 var forced = 0;
1234 if (data.forced == 1) { forced = 1; }
1235 data.actiontype = parseInt(data.actiontype);
@@ -1107,7 +1247,8 @@ function createMeshCore(agent) {
1247 }
1248 case 'toast': {
1249 // Display a toast message
1110 - if (data.title && data.msg) {
1250 + if (data.title && data.msg)
1251 + {
1252 MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1253 data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1254 try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
@@ -1124,7 +1265,8 @@ function createMeshCore(agent) {
1265 case 'amtconfig': {
1266 // Perform Intel AMT activation and/or configuration
1267 if ((apftunnel != null) || (amt == null) || (typeof data.user != 'string') || (typeof data.pass != 'string')) break;
1127 - amt.getMeiState(15, function (state) {
1268 + amt.getMeiState(15, function (state)
1269 + {
1270 if ((apftunnel != null) || (amt == null)) return;
1271 if ((state == null) || (state.ProvisioningState == null)) return;
1272 if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
@@ -1141,10 +1283,12 @@ function createMeshCore(agent) {
1283 };
1284 addAmtEvent('LMS tunnel start.');
1285 apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
1144 - apftunnel.onJsonControl = function (data) {
1286 + apftunnel.onJsonControl = function (data)
1287 + {
1288 if (data.action == 'console') { addAmtEvent(data.msg); } // Add console message to AMT event log
1289 if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1147 - if (data.action == 'deactivate') { // Request CCM deactivation
1290 + if (data.action == 'deactivate')
1291 + { // Request CCM deactivation
1292 var amtMeiModule, amtMei;
1293 try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
1294 amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); });
@@ -1164,7 +1308,8 @@ function createMeshCore(agent) {
1308 }
1309 case 'sysinfo': {
1310 // Fetch system information
1167 - getSystemInformation(function (results) {
1311 + getSystemInformation(function (results)
1312 + {
1313 if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1314 });
1315 break;
@@ -1177,14 +1322,18 @@ function createMeshCore(agent) {
1322 }
1323 case 'coredump':
1324 // Set the current agent coredump situation.
1180 - if (data.value === true) {
1181 - if (process.platform == 'win32') {
1325 + if (data.value === true)
1326 + {
1327 + if (process.platform == 'win32')
1328 + {
1329 // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1330 process.coreDumpLocation = process.execPath.replace('.exe', '.dmp');
1184 - } else {
1331 + } else
1332 + {
1333 process.coreDumpLocation = (process.cwd() != '//') ? (process.cwd() + 'core') : null;
1334 }
1187 - } else if (data.value === false) {
1335 + } else if (data.value === false)
1336 + {
1337 process.coreDumpLocation = null;
1338 }
1339 break;
@@ -1193,15 +1342,18 @@ function createMeshCore(agent) {
1342 var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1343 var coreDumpPath = null;
1344 if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1196 - if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) {
1197 - try {
1345 + if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath)))
1346 + {
1347 + try
1348 + {
1349 var coredate = fs.statSync(coreDumpPath).mtime;
1350 var coretime = new Date(coredate).getTime();
1351 var agenttime = new Date(fs.statSync(process.execPath).mtime).getTime();
1352 if (coretime > agenttime) { r.exists = (db.Get('CoreDumpTime') != coredate); }
1353 } catch (ex) { }
1354 }
1204 - if (r.exists == true) {
1355 + if (r.exists == true)
1356 + {
1357 r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); // Hash of current agent
1358 r.corehashhex = getSHA384FileHash(coreDumpPath).toString('hex'); // Hash of core dump file
1359 }
@@ -1222,7 +1374,8 @@ function createMeshCore(agent) {
1374 }
1375
1376 // Agent just get a file from the server and save it locally.
1225 - function serverFetchFile() {
1377 + function serverFetchFile()
1378 + {
1379 if ((Object.keys(agentFileHttpRequests).length > 4) || (agentFileHttpPendingRequests.length == 0)) return; // No more than 4 active HTTPS requests to the server.
1380 var data = agentFileHttpPendingRequests.shift();
1381 if ((data.overwrite !== true) && fs.existsSync(data.path)) return; // Don't overwrite an existing file.
@@ -1233,7 +1386,8 @@ function createMeshCore(agent) {
1386
1387 // Perform manual server TLS certificate checking based on the certificate hash given by the server.
1388 agentFileHttpOptions.rejectUnauthorized = 0;
1236 - agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs) {
1389 + agentFileHttpOptions.checkServerIdentity = function checkServerIdentity(certs)
1390 + {
1391 // If the tunnel certificate matches the control channel certificate, accept the connection
1392 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
1393 try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
@@ -1244,9 +1398,11 @@ function createMeshCore(agent) {
1398
1399 if (agentFileHttpOptions == null) return;
1400 var agentFileHttpRequest = http.request(agentFileHttpOptions,
1247 - function (response) {
1401 + function (response)
1402 + {
1403 response.xparent = this;
1249 - try {
1404 + try
1405 + {
1406 response.xfile = fs.createWriteStream(this.xpath, { flags: 'wbN' })
1407 response.pipe(response.xfile);
1408 response.end = function () { delete agentFileHttpRequests[this.xparent.xurlpath]; delete this.xparent; serverFetchFile(); }
@@ -1269,10 +1425,13 @@ function createMeshCore(agent) {
1425 }
1426 */
1427
1272 - function getSystemInformation(func) {
1273 - try {
1428 + function getSystemInformation(func)
1429 + {
1430 + try
1431 + {
1432 var results = { hardware: require('identifiers').get() }; // Hardware info
1275 - if (results.hardware && results.hardware.windows) {
1433 + if (results.hardware && results.hardware.windows)
1434 + {
1435 // Remove extra entries and things that change quickly
1436 var x = results.hardware.windows.osinfo;
1437 try { delete x.FreePhysicalMemory; } catch (e) { }
@@ -1282,7 +1441,8 @@ function createMeshCore(agent) {
1441 try { delete x.MaxProcessMemorySize; } catch (e) { }
1442 try { delete x.TotalVirtualMemorySize; } catch (e) { }
1443 try { delete x.TotalVisibleMemorySize; } catch (e) { }
1285 - try {
1444 + try
1445 + {
1446 if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1447 if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1448 if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
@@ -1318,35 +1478,46 @@ function createMeshCore(agent) {
1478 }
1479
1480 // Get a formated response for a given directory path
1321 - function getDirectoryInfo(reqpath) {
1481 + function getDirectoryInfo(reqpath)
1482 + {
1483 var response = { path: reqpath, dir: [] };
1323 - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1484 + if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32'))
1485 + {
1486 // List all the drives in the root, or the root itself
1487 var results = null;
1488 try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1327 - if (results != null) {
1328 - for (var i = 0; i < results.length; ++i) {
1489 + if (results != null)
1490 + {
1491 + for (var i = 0; i < results.length; ++i)
1492 + {
1493 var drive = { n: results[i].name, t: 1 };
1494 if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1495 response.dir.push(drive);
1496 }
1497 }
1334 - } else {
1498 + } else
1499 + {
1500 // List all the files and folders in this path
1501 if (reqpath == '') { reqpath = '/'; }
1502 var results = null, xpath = obj.path.join(reqpath, '*');
1503 //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1504 try { results = fs.readdirSync(xpath); } catch (e) { }
1340 - if (results != null) {
1341 - for (var i = 0; i < results.length; ++i) {
1342 - if ((results[i] != '.') && (results[i] != '..')) {
1505 + if (results != null)
1506 + {
1507 + for (var i = 0; i < results.length; ++i)
1508 + {
1509 + if ((results[i] != '.') && (results[i] != '..'))
1510 + {
1511 var stat = null, p = obj.path.join(reqpath, results[i]);
1512 //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1513 try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1346 - if ((stat != null) && (stat != undefined)) {
1347 - if (stat.isDirectory() == true) {
1514 + if ((stat != null) && (stat != undefined))
1515 + {
1516 + if (stat.isDirectory() == true)
1517 + {
1518 response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1349 - } else {
1519 + } else
1520 + {
1521 response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1522 }
1523 }
@@ -1358,7 +1529,8 @@ function createMeshCore(agent) {
1529 }
1530
1531 // Tunnel callback operations
1361 - function onTunnelUpgrade(response, s, head) {
1532 + function onTunnelUpgrade(response, s, head)
1533 + {
1534 this.s = s;
1535 s.httprequest = this;
1536 s.end = onTunnelClosed;
@@ -1377,7 +1549,8 @@ function createMeshCore(agent) {
1549
1550 //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1551
1380 - if (this.tcpport != null) {
1552 + if (this.tcpport != null)
1553 + {
1554 // This is a TCP relay connection, pause now and try to connect to the target.
1555 s.pause();
1556 s.data = onTcpRelayServerTunnelData;
@@ -1387,12 +1560,14 @@ function createMeshCore(agent) {
1560 s.tcprelay.peerindex = this.index;
1561
1562 // Add the TCP session to the count and update the server
1390 - if (s.httprequest.userid != null) {
1563 + if (s.httprequest.userid != null)
1564 + {
1565 if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1566 try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1567 broadcastSessionsToRegisteredApps();
1568 }
1395 - } if (this.udpport != null) {
1569 + } if (this.udpport != null)
1570 + {
1571 // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1572 s.data = onUdpRelayServerTunnelData;
1573 s.udprelay = require('dgram').createSocket({ type: 'udp4' });
@@ -1404,34 +1579,41 @@ function createMeshCore(agent) {
1579 s.udprelay.first = true;
1580
1581 // Add the UDP session to the count and update the server
1407 - if (s.httprequest.userid != null) {
1582 + if (s.httprequest.userid != null)
1583 + {
1584 if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1585 try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1586 broadcastSessionsToRegisteredApps();
1587 }
1412 - } else {
1588 + } else
1589 + {
1590 // This is a normal connect for KVM/Terminal/Files
1591 s.data = onTunnelData;
1592 }
1593 }
1594
1595 // Called when UDP relay data is received // TODO****
1419 - function onUdpRelayTargetTunnelConnect(data) {
1596 + function onUdpRelayTargetTunnelConnect(data)
1597 + {
1598 var peerTunnel = tunnels[this.peerindex];
1599 peerTunnel.s.write(data);
1600 }
1601
1602 // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1425 - function onUdpRelayServerTunnelData(data) {
1426 - if (this.udprelay.first === true) {
1603 + function onUdpRelayServerTunnelData(data)
1604 + {
1605 + if (this.udprelay.first === true)
1606 + {
1607 delete this.udprelay.first; // Skip the first 'c' that is received.
1428 - } else {
1608 + } else
1609 + {
1610 this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1611 }
1612 }
1613
1614 // Called when the TCP relay target is connected
1434 - function onTcpRelayTargetTunnelConnect() {
1615 + function onTcpRelayTargetTunnelConnect()
1616 + {
1617 var peerTunnel = tunnels[this.peerindex];
1618 this.pipe(peerTunnel.s); // Pipe Target --> Server
1619 peerTunnel.s.first = true;
@@ -1439,24 +1621,30 @@ function createMeshCore(agent) {
1621 }
1622
1623 // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1442 - function onTcpRelayServerTunnelData(data) {
1443 - if (this.first == true) {
1444 - this.first = false;
1624 + function onTcpRelayServerTunnelData(data)
1625 + {
1626 + if (this.first == true)
1627 + {
1628 + this.first = false;
1629 this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1630 }
1631 }
1632
1449 - function onTunnelClosed() {
1633 + function onTunnelClosed()
1634 + {
1635 var tunnel = tunnels[this.httprequest.index];
1636 if (tunnel == null) return; // Stop duplicate calls.
1637
1638 // If this is a routing session, clean up and send the new session counts.
1454 - if (this.httprequest.userid != null) {
1455 - if (this.httprequest.tcpport != null) {
1639 + if (this.httprequest.userid != null)
1640 + {
1641 + if (this.httprequest.tcpport != null)
1642 + {
1643 if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
1644 try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1645 broadcastSessionsToRegisteredApps();
1459 - } else if (this.httprequest.udpport != null) {
1646 + } else if (this.httprequest.udpport != null)
1647 + {
1648 if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
1649 try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1650 broadcastSessionsToRegisteredApps();
@@ -1464,7 +1652,8 @@ function createMeshCore(agent) {
1652 }
1653
1654 // Sent tunnel statistics to the server, only send this if compression was used.
1467 - if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1655 + if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString()))
1656 + {
1657 mesh.SendCommand({
1658 action: 'tunnelCloseStats',
1659 url: tunnel.url,
@@ -1497,7 +1686,8 @@ function createMeshCore(agent) {
1686 if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1687
1688 // Clean up WebRTC
1500 - if (this.webrtc != null) {
1689 + if (this.webrtc != null)
1690 + {
1691 if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
1692 if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1693 try { this.webrtc.close(); } catch (e) { }
@@ -1511,17 +1701,21 @@ function createMeshCore(agent) {
1701 this.removeAllListeners('data');
1702 }
1703 function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1514 - function onTunnelData(data) {
1704 + function onTunnelData(data)
1705 + {
1706 //console.log("OnTunnelData");
1707 //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1708
1709 // If this is upload data, save it to file
1519 - if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1710 + if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123))
1711 + {
1712 // Save the data to file being uploaded.
1521 - if (data[0] == 0) {
1713 + if (data[0] == 0)
1714 + {
1715 // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1716 try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1524 - } else {
1717 + } else
1718 + {
1719 // If data does not start with zero, save as-is.
1720 try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1721 }
@@ -1529,19 +1723,22 @@ function createMeshCore(agent) {
1723 return;
1724 }
1725
1532 - if (this.httprequest.state == 0) {
1726 + if (this.httprequest.state == 0)
1727 + {
1728 // Check if this is a relay connection
1729 if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1730 }
1731 else
1732 {
1733 // Handle tunnel data
1539 - if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1734 + if (this.httprequest.protocol == 0)
1735 + { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1736 // Take a look at the protocol
1737 if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1738 this.httprequest.protocol = parseInt(data);
1739 if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1544 - if (this.httprequest.protocol == 10) {
1740 + if (this.httprequest.protocol == 10)
1741 + {
1742 //
1743 // Basic file transfer
1744 //
@@ -1549,12 +1746,14 @@ function createMeshCore(agent) {
1746 if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1747 try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1748 try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1552 - if (this.httprequest.downloadFile) {
1749 + if (this.httprequest.downloadFile)
1750 + {
1751 //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1752 this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1753 this.httprequest.downloadFile.pipe(this);
1754 this.httprequest.downloadFile.end = function () { }
1557 - } else {
1755 + } else
1756 + {
1757 //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1758 this.write(JSON.stringify({ op: 'cancel' }));
1759 }
@@ -1566,7 +1765,8 @@ function createMeshCore(agent) {
1765 //
1766
1767 // Check user access rights for terminal
1569 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1768 + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0)))
1769 + {
1770 // Disengage this tunnel, user does not have the rights to do this!!
1771 this.httprequest.protocol = 999999;
1772 this.httprequest.s.end();
@@ -1628,7 +1828,8 @@ function createMeshCore(agent) {
1828 {
1829 this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1830 var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1631 - if (this.httprequest.soptions != null) {
1831 + if (this.httprequest.soptions != null)
1832 + {
1833 if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1834 if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1835 }
@@ -1665,7 +1866,7 @@ function createMeshCore(agent) {
1866 this.httprequest.connectionPromise.ws = this.that;
1867
1868 // Start Terminal
1668 - if(process.platform == 'win32')
1869 + if (process.platform == 'win32')
1870 {
1871 try
1872 {
@@ -1836,7 +2037,8 @@ function createMeshCore(agent) {
2037 {
2038 // User Notifications is required
2039 var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1839 - if (this.ws.httprequest.soptions != null) {
2040 + if (this.ws.httprequest.soptions != null)
2041 + {
2042 if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2043 if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2044 }
@@ -1855,7 +2057,7 @@ function createMeshCore(agent) {
2057 // DO NOT start terminal
2058 this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2059 this.that.end();
1858 - });
2060 + });
2061 }
2062 else if (this.httprequest.protocol == 2)
2063 {
@@ -1864,7 +2066,8 @@ function createMeshCore(agent) {
2066 //
2067
2068 // Check user access rights for desktop
1867 - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
2069 + if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0)))
2070 + {
2071 // Disengage this tunnel, user does not have the rights to do this!!
2072 this.httprequest.protocol = 999999;
2073 this.httprequest.s.end();
@@ -1890,7 +2093,8 @@ function createMeshCore(agent) {
2093
2094 // Send a metadata update to all desktop sessions
2095 var users = {};
1893 - if (this.httprequest.desktop.kvm.tunnels != null) {
2096 + if (this.httprequest.desktop.kvm.tunnels != null)
2097 + {
2098 for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
2099 for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
2100 tunnelUserCount.desktop = users;
@@ -1898,7 +2102,8 @@ function createMeshCore(agent) {
2102 broadcastSessionsToRegisteredApps();
2103 }
2104
1901 - this.end = function () {
2105 + this.end = function ()
2106 + {
2107 --this.desktop.kvm.connectionCount;
2108
2109 // Remove ourself from the list of remote desktop session
@@ -1907,7 +2112,8 @@ function createMeshCore(agent) {
2112
2113 // Send a metadata update to all desktop sessions
2114 var users = {};
1910 - if (this.httprequest.desktop.kvm.tunnels != null) {
2115 + if (this.httprequest.desktop.kvm.tunnels != null)
2116 + {
2117 for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
2118 for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
2119 tunnelUserCount.desktop = users;
@@ -1921,7 +2127,7 @@ function createMeshCore(agent) {
2127 this.unpipe(this.httprequest.desktop.kvm);
2128 this.httprequest.desktop.kvm.unpipe(this);
2129 }
1924 - catch(e) { }
2130 + catch (e) { }
2131
2132 // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
2133 if (this.rtcchannel)
@@ -1931,34 +2137,41 @@ function createMeshCore(agent) {
2137 this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
2138 this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
2139 }
1934 - catch(e) { }
2140 + catch (e) { }
2141 }
2142
2143 // Place wallpaper back if needed
2144 // TODO
2145
1940 - if (this.desktop.kvm.connectionCount == 0) {
2146 + if (this.desktop.kvm.connectionCount == 0)
2147 + {
2148 // Display a toast message. This may not be supported on all platforms.
2149 // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
2150
2151 this.httprequest.desktop.kvm.end();
1945 - if (this.httprequest.desktop.kvm.connectionBar) {
2152 + if (this.httprequest.desktop.kvm.connectionBar)
2153 + {
2154 this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2155 this.httprequest.desktop.kvm.connectionBar.close();
2156 this.httprequest.desktop.kvm.connectionBar = null;
2157 }
1950 - } else {
1951 - for (var i in this.httprequest.desktop.kvm.users) {
1952 - if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
2158 + } else
2159 + {
2160 + for (var i in this.httprequest.desktop.kvm.users)
2161 + {
2162 + if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar)
2163 + {
2164 for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
2165 this.httprequest.desktop.kvm.users.splice(i, 1);
2166 this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2167 this.httprequest.desktop.kvm.connectionBar.close();
2168 this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
2169 this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1959 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
2170 + this.httprequest.desktop.kvm.connectionBar.on('close', function ()
2171 + {
2172 MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1961 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
2173 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2174 + {
2175 this.httprequest.desktop.kvm._pipedStreams[i].end();
2176 }
2177 this.httprequest.desktop.kvm.end();
@@ -1968,22 +2181,26 @@ function createMeshCore(agent) {
2181 }
2182 }
2183 };
1971 - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
2184 + if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount'))
2185 + {
2186 this.httprequest.desktop.kvm.connectionCount++;
2187 this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
2188 this.httprequest.desktop.kvm.users.push(this.httprequest.username);
2189 this.httprequest.desktop.kvm.rusers.sort();
2190 this.httprequest.desktop.kvm.users.sort();
1977 - } else {
2191 + } else
2192 + {
2193 this.httprequest.desktop.kvm.connectionCount = 1;
2194 this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
2195 this.httprequest.desktop.kvm.users = [this.httprequest.username];
2196 }
2197
1983 - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
2198 + if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)))
2199 + {
2200 // If we have remote control rights, pipe the KVM input
2201 this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1986 - } else {
2202 + } else
2203 + {
2204 // We need to only pipe non-mouse & non-keyboard inputs.
2205 //sendConsoleText('Warning: No Remote Desktop Input Rights.');
2206 // TODO!!!
@@ -1996,7 +2213,8 @@ function createMeshCore(agent) {
2213 // Send a console message back using the console channel, "\n" is supported.
2214 this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2215 var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1999 - if (this.httprequest.soptions != null) {
2216 + if (this.httprequest.soptions != null)
2217 + {
2218 if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2219 if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2220 }
@@ -2004,7 +2222,7 @@ function createMeshCore(agent) {
2222 pr.ws = this;
2223 this.pause();
2224 this._consentpromise = pr;
2007 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
2225 + this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2226 pr.then(
2227 function ()
2228 {
@@ -2012,35 +2230,45 @@ function createMeshCore(agent) {
2230 this.ws._consentpromise = null;
2231 MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2232 this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
2015 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
2233 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
2234 + {
2235 // User Notifications is required
2236 var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2018 - if (this.ws.httprequest.soptions != null) {
2237 + if (this.ws.httprequest.soptions != null)
2238 + {
2239 if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2240 if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2241 }
2242 try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2243 }
2024 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
2244 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40))
2245 + {
2246 // Connection Bar is required
2026 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
2247 + if (this.ws.httprequest.desktop.kvm.connectionBar)
2248 + {
2249 this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2250 this.ws.httprequest.desktop.kvm.connectionBar.close();
2251 }
2030 - try {
2252 + try
2253 + {
2254 this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
2255 MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2256 }
2034 - catch (e) {
2035 - if (process.platform != 'darwin') {
2257 + catch (e)
2258 + {
2259 + if (process.platform != 'darwin')
2260 + {
2261 MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2262 }
2263 }
2039 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
2264 + if (this.ws.httprequest.desktop.kvm.connectionBar)
2265 + {
2266 this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
2041 - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
2267 + this.ws.httprequest.desktop.kvm.connectionBar.on('close', function ()
2268 + {
2269 MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2043 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
2270 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2271 + {
2272 this.httprequest.desktop.kvm._pipedStreams[i].end();
2273 }
2274 this.httprequest.desktop.kvm.end();
@@ -2057,38 +2285,49 @@ function createMeshCore(agent) {
2285 MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2286 this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2287 });
2060 - } else {
2288 + } else
2289 + {
2290 // User Consent Prompt is not required
2062 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
2291 + if (this.httprequest.consent && (this.httprequest.consent & 1))
2292 + {
2293 // User Notifications is required
2294 MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2295 var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
2066 - if (this.httprequest.soptions != null) {
2296 + if (this.httprequest.soptions != null)
2297 + {
2298 if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2299 if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2300 }
2301 try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
2071 - } else {
2302 + } else
2303 + {
2304 MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2305 }
2074 - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
2306 + if (this.httprequest.consent && (this.httprequest.consent & 0x40))
2307 + {
2308 // Connection Bar is required
2076 - if (this.httprequest.desktop.kvm.connectionBar) {
2309 + if (this.httprequest.desktop.kvm.connectionBar)
2310 + {
2311 this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
2312 this.httprequest.desktop.kvm.connectionBar.close();
2313 }
2080 - try {
2314 + try
2315 + {
2316 this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
2317 MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
2318 }
2084 - catch (e) {
2319 + catch (e)
2320 + {
2321 MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
2322 }
2087 - if (this.httprequest.desktop.kvm.connectionBar) {
2323 + if (this.httprequest.desktop.kvm.connectionBar)
2324 + {
2325 this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
2089 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
2326 + this.httprequest.desktop.kvm.connectionBar.on('close', function ()
2327 + {
2328 MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
2091 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
2329 + for (var i in this.httprequest.desktop.kvm._pipedStreams)
2330 + {
2331 this.httprequest.desktop.kvm._pipedStreams[i].end();
2332 }
2333 this.httprequest.desktop.kvm.end();
@@ -2102,13 +2341,15 @@ function createMeshCore(agent) {
2341 this.on('data', onTunnelControlData);
2342 //this.write('MeshCore KVM Hello!1');
2343
2105 - } else if (this.httprequest.protocol == 5) {
2344 + } else if (this.httprequest.protocol == 5)
2345 + {
2346 //
2347 // Remote Files
2348 //
2349
2350 // Check user access rights for files
2111 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
2351 + if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0)))
2352 + {
2353 // Disengage this tunnel, user does not have the rights to do this!!
2354 this.httprequest.protocol = 999999;
2355 this.httprequest.s.end();
@@ -2119,15 +2360,18 @@ function createMeshCore(agent) {
2360 this.descriptorMetadata = "Remote Files";
2361
2362 // Add the files session to the count to update the server
2122 - if (this.httprequest.userid != null) {
2363 + if (this.httprequest.userid != null)
2364 + {
2365 if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
2366 try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2367 broadcastSessionsToRegisteredApps();
2368 }
2369
2128 - this.end = function () {
2370 + this.end = function ()
2371 + {
2372 // Remove the files session from the count to update the server
2130 - if (this.httprequest.userid != null) {
2373 + if (this.httprequest.userid != null)
2374 + {
2375 if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } }
2376 try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2377 broadcastSessionsToRegisteredApps();
@@ -2135,12 +2379,14 @@ function createMeshCore(agent) {
2379 };
2380
2381 // Perform notification if needed. Toast messages may not be supported on all platforms.
2138 - if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2382 + if (this.httprequest.consent && (this.httprequest.consent & 32))
2383 + {
2384 // User Consent Prompt is required
2385 // Send a console message back using the console channel, "\n" is supported.
2386 this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2387 var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2143 - if (this.httprequest.soptions != null) {
2388 + if (this.httprequest.soptions != null)
2389 + {
2390 if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2391 if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2392 }
@@ -2156,10 +2402,12 @@ function createMeshCore(agent) {
2402 this.ws._consentpromise = null;
2403 MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2404 this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2159 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2405 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
2406 + {
2407 // User Notifications is required
2408 var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2162 - if (this.ws.httprequest.soptions != null) {
2409 + if (this.ws.httprequest.soptions != null)
2410 + {
2411 if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2412 if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2413 }
@@ -2174,18 +2422,22 @@ function createMeshCore(agent) {
2422 MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2423 this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2424 });
2177 - } else {
2425 + } else
2426 + {
2427 // User Consent Prompt is not required
2179 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2428 + if (this.httprequest.consent && (this.httprequest.consent & 4))
2429 + {
2430 // User Notifications is required
2431 MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2432 var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2183 - if (this.httprequest.soptions != null) {
2433 + if (this.httprequest.soptions != null)
2434 + {
2435 if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2436 if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2437 }
2438 try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2188 - } else {
2439 + } else
2440 + {
2441 MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2442 }
2443 this.resume();
@@ -2194,18 +2446,23 @@ function createMeshCore(agent) {
2446 // Setup files
2447 // NOP
2448 }
2197 - } else if (this.httprequest.protocol == 1) {
2449 + } else if (this.httprequest.protocol == 1)
2450 + {
2451 // Send data into terminal stdin
2452 //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2200 - } else if (this.httprequest.protocol == 2) {
2453 + } else if (this.httprequest.protocol == 2)
2454 + {
2455 // Send data into remote desktop
2202 - if (this.httprequest.desktop.state == 0) {
2456 + if (this.httprequest.desktop.state == 0)
2457 + {
2458 this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2459 this.httprequest.desktop.state = 1;
2205 - } else {
2460 + } else
2461 + {
2462 this.httprequest.desktop.write(data);
2463 }
2208 - } else if (this.httprequest.protocol == 5) {
2464 + } else if (this.httprequest.protocol == 5)
2465 + {
2466 // Process files commands
2467 var cmd = null;
2468 try { cmd = JSON.parse(data); } catch (e) { };
@@ -2216,7 +2473,8 @@ function createMeshCore(agent) {
2473
2474 if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2475 //console.log(objToString(cmd, 0, ' '));
2219 - switch (cmd.action) {
2476 + switch (cmd.action)
2477 + {
2478 case 'ls': {
2479 /*
2480 // Close the watcher if required
@@ -2253,15 +2511,20 @@ function createMeshCore(agent) {
2511 }
2512 case 'rm': {
2513 // Delete, possibly recursive delete
2256 - for (var i in cmd.delfiles) {
2514 + for (var i in cmd.delfiles)
2515 + {
2516 var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2517 try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2259 - if ((delcount == 1) && !cmd.rec) {
2518 + if ((delcount == 1) && !cmd.rec)
2519 + {
2520 MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2261 - } else {
2262 - if (cmd.rec) {
2521 + } else
2522 + {
2523 + if (cmd.rec)
2524 + {
2525 MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2264 - } else {
2526 + } else
2527 + {
2528 MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2529 }
2530 }
@@ -2271,9 +2534,11 @@ function createMeshCore(agent) {
2534 case 'markcoredump': {
2535 // If we are asking for the coredump file, set the right path.
2536 var coreDumpPath = null;
2274 - if (process.platform == 'win32') {
2537 + if (process.platform == 'win32')
2538 + {
2539 if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2276 - } else {
2540 + } else
2541 + {
2542 if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2543 }
2544 if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
@@ -2305,24 +2570,30 @@ function createMeshCore(agent) {
2570 case 'download': {
2571 // Download a file
2572 var sendNextBlock = 0;
2308 - if (cmd.sub == 'start') { // Setup the download
2309 - if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2310 - if (process.platform == 'win32') {
2573 + if (cmd.sub == 'start')
2574 + { // Setup the download
2575 + if ((cmd.path == null) && (cmd.ask == 'coredump'))
2576 + { // If we are asking for the coredump file, set the right path.
2577 + if (process.platform == 'win32')
2578 + {
2579 if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2312 - } else {
2580 + } else
2581 + {
2582 if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2583 }
2584 }
2316 - MeshServerLogEx((cmd.ask == 'coredump')?104:49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2585 + MeshServerLogEx((cmd.ask == 'coredump') ? 104 : 49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2586 if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2587 this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2588 try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2589 if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2321 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2590 + } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id))
2591 + { // Download commands
2592 if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2593 }
2594 // Send the next download block(s)
2325 - while (sendNextBlock > 0) {
2595 + while (sendNextBlock > 0)
2596 + {
2597 sendNextBlock--;
2598 var buf = Buffer.alloc(16384);
2599 var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
@@ -2332,28 +2603,28 @@ function createMeshCore(agent) {
2603 }
2604 break;
2605 }
2335 - /*
2336 - case 'download': {
2337 - // Packet download of a file, agent to browser
2338 - if (cmd.path == undefined) break;
2339 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2340 - //console.log('Download: ' + filepath);
2341 - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2342 - this.httprequest.downloadFileId = cmd.reqid;
2343 - this.httprequest.downloadFilePtr = 0;
2344 - if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2345 - break;
2346 - }
2347 - case 'download2': {
2348 - // Stream download of a file, agent to browser
2349 - if (cmd.path == undefined) break;
2350 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2351 - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2352 - this.httprequest.downloadFile.pipe(this);
2353 - this.httprequest.downloadFile.end = function () { }
2354 - break;
2355 - }
2356 - */
2606 + /*
2607 + case 'download': {
2608 + // Packet download of a file, agent to browser
2609 + if (cmd.path == undefined) break;
2610 + var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2611 + //console.log('Download: ' + filepath);
2612 + try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2613 + this.httprequest.downloadFileId = cmd.reqid;
2614 + this.httprequest.downloadFilePtr = 0;
2615 + if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2616 + break;
2617 + }
2618 + case 'download2': {
2619 + // Stream download of a file, agent to browser
2620 + if (cmd.path == undefined) break;
2621 + var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2622 + try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2623 + this.httprequest.downloadFile.pipe(this);
2624 + this.httprequest.downloadFile.end = function () { }
2625 + break;
2626 + }
2627 + */
2628 case 'upload': {
2629 // Upload a file, browser to agent
2630 if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
@@ -2368,7 +2639,8 @@ function createMeshCore(agent) {
2639 }
2640 case 'uploaddone': {
2641 // Indicates that an upload is done
2371 - if (this.httprequest.uploadFile) {
2642 + if (this.httprequest.uploadFile)
2643 + {
2644 fs.closeSync(this.httprequest.uploadFile);
2645 this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2646 delete this.httprequest.uploadFile;
@@ -2379,7 +2651,8 @@ function createMeshCore(agent) {
2651 }
2652 case 'uploadcancel': {
2653 // Indicates that an upload is canceled
2382 - if (this.httprequest.uploadFile) {
2654 + if (this.httprequest.uploadFile)
2655 + {
2656 fs.closeSync(this.httprequest.uploadFile);
2657 fs.unlinkSync(this.httprequest.uploadFilePath);
2658 this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
@@ -2391,7 +2664,8 @@ function createMeshCore(agent) {
2664 }
2665 case 'copy': {
2666 // Copy a bunch of files from scpath to dspath
2394 - for (var i in cmd.names) {
2667 + for (var i in cmd.names)
2668 + {
2669 var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2670 MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2671 if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
@@ -2400,7 +2674,8 @@ function createMeshCore(agent) {
2674 }
2675 case 'move': {
2676 // Move a bunch of files from scpath to dspath
2403 - for (var i in cmd.names) {
2677 + for (var i in cmd.names)
2678 + {
2679 var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2680 MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2681 if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
@@ -2423,7 +2698,8 @@ function createMeshCore(agent) {
2698 delete this.zipcancel;
2699 var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2700 out.xws = this;
2426 - out.on('close', function () {
2701 + out.on('close', function ()
2702 + {
2703 this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2704 this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2705 if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
@@ -2445,14 +2721,16 @@ function createMeshCore(agent) {
2721 // Unknown action, ignore it.
2722 break;
2723 }
2448 - } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2724 + } else if (this.httprequest.protocol == 7)
2725 + { // Plugin data exchange
2726 var cmd = null;
2727 try { cmd = JSON.parse(data); } catch (e) { };
2728 if (cmd == null) { return; }
2729 if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2730 if (cmd.action == undefined) return;
2731
2455 - switch (cmd.action) {
2732 + switch (cmd.action)
2733 + {
2734 case 'plugin': {
2735 try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2736 break;
@@ -2468,15 +2746,21 @@ function createMeshCore(agent) {
2746 }
2747
2748 // Delete a directory with a files and directories within it
2471 - function deleteFolderRecursive(path, rec) {
2749 + function deleteFolderRecursive(path, rec)
2750 + {
2751 var count = 0;
2473 - if (fs.existsSync(path)) {
2474 - if (rec == true) {
2475 - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2752 + if (fs.existsSync(path))
2753 + {
2754 + if (rec == true)
2755 + {
2756 + fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index)
2757 + {
2758 var curPath = obj.path.join(path, file);
2477 - if (fs.statSync(curPath).isDirectory()) { // recurse
2759 + if (fs.statSync(curPath).isDirectory())
2760 + { // recurse
2761 count += deleteFolderRecursive(curPath, true);
2479 - } else { // delete file
2762 + } else
2763 + { // delete file
2764 fs.unlinkSync(curPath);
2765 count++;
2766 }
@@ -2489,11 +2773,13 @@ function createMeshCore(agent) {
2773 };
2774
2775 // Called when receiving control data on WebRTC
2492 - function onTunnelWebRTCControlData(data) {
2776 + function onTunnelWebRTCControlData(data)
2777 + {
2778 if (typeof data != 'string') return;
2779 var obj;
2780 try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2496 - if (obj.type == 'close') {
2781 + if (obj.type == 'close')
2782 + {
2783 //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2784 try { this.close(); } catch (e) { }
2785 try { this.xrtc.close(); } catch (e) { }
@@ -2501,7 +2787,8 @@ function createMeshCore(agent) {
2787 }
2788
2789 // Called when receiving control data on websocket
2504 - function onTunnelControlData(data, ws) {
2790 + function onTunnelControlData(data, ws)
2791 + {
2792 var obj;
2793 if (ws == null) { ws = this; }
2794 if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
@@ -2509,12 +2796,16 @@ function createMeshCore(agent) {
2796 //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2797 //console.log('onTunnelControlData: ' + JSON.stringify(data));
2798
2512 - if (obj.action) {
2513 - switch (obj.action) {
2799 + if (obj.action)
2800 + {
2801 + switch (obj.action)
2802 + {
2803 case 'lock': {
2804 // Lock the current user out of the desktop
2516 - try {
2517 - if (process.platform == 'win32') {
2805 + try
2806 + {
2807 + if (process.platform == 'win32')
2808 + {
2809 MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2810 var child = require('child_process');
2811 child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
@@ -2529,7 +2820,8 @@ function createMeshCore(agent) {
2820 return;
2821 }
2822
2532 - switch (obj.type) {
2823 + switch (obj.type)
2824 + {
2825 case 'options': {
2826 // These are additional connection options passed in the control channel.
2827 //sendConsoleText('options: ' + JSON.stringify(obj));
@@ -2564,18 +2856,23 @@ function createMeshCore(agent) {
2856 break;
2857 }
2858 case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2567 - if (ws.httprequest.protocol == 1) { // Terminal
2859 + if (ws.httprequest.protocol == 1)
2860 + { // Terminal
2861 // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
2569 - if (process.platform == 'win32') {
2862 + if (process.platform == 'win32')
2863 + {
2864 ws.httprequest._term.unpipe(ws);
2571 - } else {
2865 + } else
2866 + {
2867 ws.httprequest.process.stdout.unpipe(ws);
2868 ws.httprequest.process.stderr.unpipe(ws);
2869 }
2575 - } else if (ws.httprequest.protocol == 2) { // Desktop
2870 + } else if (ws.httprequest.protocol == 2)
2871 + { // Desktop
2872 // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
2873 ws.httprequest.desktop.kvm.unpipe(ws);
2578 - } else {
2874 + } else
2875 + {
2876 // Switch things around so all WebRTC data goes to onTunnelData().
2877 ws.rtcchannel.httprequest = ws.httprequest;
2878 ws.rtcchannel.removeAllListeners('data');
@@ -2585,17 +2882,21 @@ function createMeshCore(agent) {
2882 break;
2883 }
2884 case 'webrtc1': {
2588 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2885 + if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6))
2886 + { // Terminal
2887 // Switch the user input from websocket to webrtc at this point.
2590 - if (process.platform == 'win32') {
2888 + if (process.platform == 'win32')
2889 + {
2890 ws.unpipe(ws.httprequest._term);
2891 ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2593 - } else {
2892 + } else
2893 + {
2894 ws.unpipe(ws.httprequest.process.stdin);
2895 ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2896 }
2897 ws.resume(); // Resume the websocket to keep receiving control data
2598 - } else if (ws.httprequest.protocol == 2) { // Desktop
2898 + } else if (ws.httprequest.protocol == 2)
2899 + { // Desktop
2900 // Switch the user input from websocket to webrtc at this point.
2901 ws.unpipe(ws.httprequest.desktop.kvm);
2902 try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
@@ -2606,14 +2907,18 @@ function createMeshCore(agent) {
2907 }
2908 case 'webrtc2': {
2909 // Other side received websocket end of data marker, start sending data on WebRTC channel
2609 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2610 - if (process.platform == 'win32') {
2910 + if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6))
2911 + { // Terminal
2912 + if (process.platform == 'win32')
2913 + {
2914 ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2612 - } else {
2915 + } else
2916 + {
2917 ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2918 ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2919 }
2616 - } else if (ws.httprequest.protocol == 2) { // Desktop
2920 + } else if (ws.httprequest.protocol == 2)
2921 + { // Desktop
2922 ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2923 }
2924 break;
@@ -2625,7 +2930,8 @@ function createMeshCore(agent) {
2930 ws.webrtc.websocket = ws;
2931 ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2932 ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2628 - ws.webrtc.on('dataChannel', function (rtcchannel) {
2933 + ws.webrtc.on('dataChannel', function (rtcchannel)
2934 + {
2935 //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2936 rtcchannel.maxFragmentSize = 32768;
2937 rtcchannel.xrtc = this;
@@ -2633,7 +2939,8 @@ function createMeshCore(agent) {
2939 this.rtcchannel = rtcchannel;
2940 this.websocket.rtcchannel = rtcchannel;
2941 this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2636 - this.websocket.rtcchannel.on('end', function () {
2942 + this.websocket.rtcchannel.on('end', function ()
2943 + {
2944 // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2945 //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2946 if (this.websocket.desktop && this.websocket.desktop.kvm)
@@ -2672,17 +2979,21 @@ function createMeshCore(agent) {
2979 var consoleHttpRequest = null;
2980
2981 // Console HTTP response
2675 - function consoleHttpResponse(response) {
2982 + function consoleHttpResponse(response)
2983 + {
2984 response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2985 response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2986 };
2987
2988 // Open a web browser to a specified URL on current user's desktop
2681 - function openUserDesktopUrl(url) {
2989 + function openUserDesktopUrl(url)
2990 + {
2991 if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
2992 var child = null;
2684 - try {
2685 - switch (process.platform) {
2993 + try
2994 + {
2995 + switch (process.platform)
2996 + {
2997 case 'win32':
2998 var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2999 child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
@@ -2709,20 +3020,24 @@ function createMeshCore(agent) {
3020 }
3021
3022 // Process a mesh agent console command
2712 - function processConsoleCommand(cmd, args, rights, sessionid) {
2713 - try {
3023 + function processConsoleCommand(cmd, args, rights, sessionid)
3024 + {
3025 + try
3026 + {
3027 var response = null;
2715 - switch (cmd) {
3028 + switch (cmd)
3029 + {
3030 case 'help': { // Displays available commands
2717 - var fin = '', f = '', availcommands = 'msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg';
3031 + var fin = '', f = '', availcommands = 'agentupdate,msh,timerinfo,coreinfo,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,wallpaper,agentmsg';
3032 if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
3033 if (amt != null) { availcommands += ',amt,amtconfig,amtevents'; }
2720 - if (process.platform != 'freebsd') { availcommands += ',vm';}
3034 + if (process.platform != 'freebsd') { availcommands += ',vm'; }
3035 if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
3036 try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
3037
3038 availcommands = availcommands.split(',').sort();
2725 - while (availcommands.length > 0) {
3039 + while (availcommands.length > 0)
3040 + {
3041 if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
3042 f += (((f != '') ? ', ' : ' ') + availcommands.shift());
3043 }
@@ -2731,93 +3046,11 @@ function createMeshCore(agent) {
3046 break;
3047 }
3048 case 'agentupdate':
2734 - if (require('MeshAgent').ARCHID == null)
2735 - {
2736 - response = 'Unable to initiate update, agent ARCHID is not defined';
2737 - break;
2738 - }
2739 - if (this._selfupdate != null)
2740 - {
2741 - response = "Self update already in progress...";
2742 - }
2743 - else
2744 - {
2745 - var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop();
2746 - var name = require('MeshAgent').serviceName;
2747 - if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; }
2748 - try
2749 - {
2750 - var s = require('service-manager').manager.getService(name);
2751 - if (s.isMe())
2752 - {
2753 - sendConsoleText('Service check SUCCESS');
2754 - }
2755 - else
2756 - {
2757 - s.close();
2758 - throw ('not a service');
2759 - break;
2760 - }
2761 - if(process.platform=='win32') {s.close();}
2762 - }
2763 - catch (zz)
2764 - {
2765 - response = 'This is not a service instance';
2766 - break;
2767 - }
2768 - sendConsoleText('Downloading update...');
2769 - var options = require('http').parseUri(require('MeshAgent').ServerUrl);
2770 - options.protocol = 'https:';
2771 - options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID);
2772 - options.rejectUnauthorized = false;
2773 - this._selfupdate = require('https').get(options);
2774 - this._selfupdate.on('response', function (img)
2775 - {
2776 - this._file = require('fs').createWriteStream(agentfilename + '.update', {flags: 'wb'});
2777 - this._filehash = require('SHA384Stream').create();
2778 - this._filehash.on('hash', function (h)
2779 - {
2780 - sendConsoleText('Download complete. HASH=' + h.toString('hex'));
2781 - if(process.platform == 'win32')
2782 - {
2783 - this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe',
2784 - ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 });
2785 - }
2786 - else
2787 - {
2788 - // remove binary
2789 - require('fs').unlinkSync(process.execPath);
2790 -
2791 - // copy update
2792 - require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath);
2793 -
2794 - // erase update
2795 - require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
2796 -
2797 - // add execute permissions
2798 - var m = require('fs').statSync(process.execPath).mode;
2799 - m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
2800 - require('fs').chmodSync(process.execPath, m);
2801 -
2802 - sendConsoleText('Restarting service...');
2803 - try
2804 - {
2805 - // restart service
2806 - var s = require('service-manager').manager.getService(name);
2807 - s.restart();
2808 - }
2809 - catch(zz)
2810 - {
2811 - sendConsoleText('Error restarting service');
2812 - }
2813 - }
2814 - });
2815 - img.pipe(this._file);
2816 - img.pipe(this._filehash);
2817 - });
2818 - this._selfupdate.on('error', function (e) { sendConsoleText('Error fetching update'); });
2819 - }
2820 -
3049 + require('MeshAgent').SendCommand({ action: 'agentupdate' });
3050 + break;
3051 + case 'agentupdateex':
3052 + // Perform an direct agent update without requesting any information from the server, this should not typically be used.
3053 + agentUpdate_Start(null, { session: sessionid });
3054 break;
3055 case 'msh':
3056 response = JSON.stringify(_MSH(), null, 2);
@@ -2850,21 +3083,26 @@ function createMeshCore(agent) {
3083 break;
3084 }
3085 case 'agentmsg': {
2853 - if (args['_'].length == 0) {
3086 + if (args['_'].length == 0)
3087 + {
3088 response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2855 - } else {
2856 - if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
3089 + } else
3090 + {
3091 + if ((args['_'][0] == 'add') && (args['_'].length > 1))
3092 + {
3093 var msgIndex = 1, iconIndex = 0;
3094 while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
3095 if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
3096 if (typeof iconIndex != 'number') { iconIndex = 0; }
3097 tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
3098 response = 'Agent message ' + msgIndex + ' added.';
2863 - } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
3099 + } else if ((args['_'][0] == 'remove') && (args['_'].length > 1))
3100 + {
3101 var msgIndex = 0;
3102 try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
3103 if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2867 - } else if (args['_'][0] == 'list') {
3104 + } else if (args['_'][0] == 'list')
3105 + {
3106 response = JSON.stringify(tunnelUserCount.msg, null, 2);
3107 }
3108 try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
@@ -2879,9 +3117,11 @@ function createMeshCore(agent) {
3117 break;
3118 }
3119 case 'coredump':
2882 - if (args['_'].length != 1) {
3120 + if (args['_'].length != 1)
3121 + {
3122 response = "Proper usage: coredump on|off|status|clear"; // Display usage
2884 - } else {
3123 + } else
3124 + {
3125 switch (args['_'][0].toLowerCase())
3126 {
3127 case 'on':
@@ -2894,15 +3134,20 @@ function createMeshCore(agent) {
3134 break;
3135 case 'status':
3136 response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2897 - if (process.coreDumpLocation != null) {
2898 - if (process.platform == 'win32') {
2899 - if (fs.existsSync(process.coreDumpLocation)) {
3137 + if (process.coreDumpLocation != null)
3138 + {
3139 + if (process.platform == 'win32')
3140 + {
3141 + if (fs.existsSync(process.coreDumpLocation))
3142 + {
3143 response += '\r\n CoreDump present at: ' + process.coreDumpLocation;
3144 response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.coreDumpLocation).mtime).getTime();
3145 response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
3146 }
2904 - } else {
2905 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) {
3147 + } else
3148 + {
3149 + if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core'))
3150 + {
3151 response += '\r\n CoreDump present at: ' + process.cwd() + 'core';
3152 response += '\r\n CoreDump Time: ' + new Date(fs.statSync(process.cwd() + 'core').mtime).getTime();
3153 response += '\r\n Agent Time : ' + new Date(fs.statSync(process.execPath).mtime).getTime();
@@ -2932,11 +3177,11 @@ function createMeshCore(agent) {
3177 {
3178 svcname = require('MeshAgent').serviceName;
3179 }
2935 - catch(x)
3180 + catch (x)
3181 {
3182 }
3183 var s = require('service-manager').manager.getService(svcname);
2939 - switch(args['_'][0].toLowerCase())
3184 + switch (args['_'][0].toLowerCase())
3185 {
3186 case 'status':
3187 response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
@@ -2959,9 +3204,11 @@ function createMeshCore(agent) {
3204 }
3205 break;
3206 case 'zip':
2962 - if (args['_'].length == 0) {
3207 + if (args['_'].length == 0)
3208 + {
3209 response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2964 - } else {
3210 + } else
3211 + {
3212 var p = args['_'].join(' ').split(',');
3213 var ofile = p.shift();
3214 sendConsoleText('Writing ' + ofile + '...');
@@ -2974,16 +3221,19 @@ function createMeshCore(agent) {
3221 }
3222 break;
3223 case 'unzip':
2977 - if (args['_'].length == 0) {
3224 + if (args['_'].length == 0)
3225 + {
3226 response = "Proper usage: unzip input, destination"; // Display usage
2979 - } else {
3227 + } else
3228 + {
3229 var p = args['_'].join(' ').split(',');
3230 if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
3231 var prom = require('zip-reader').read(p[0]);
3232 prom._dest = p[1];
3233 prom.self = this;
3234 prom.sessionid = sessionid;
2986 - prom.then(function (zipped) {
3235 + prom.then(function (zipped)
3236 + {
3237 sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
3238 zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
3239 }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
@@ -2991,11 +3241,13 @@ function createMeshCore(agent) {
3241 break;
3242 case 'setbattery':
3243 // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2994 - if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
3244 + if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc')))
3245 + {
3246 var b = { action: 'battery', state: args['_'][0] };
3247 if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
3248 require('MeshAgent').SendCommand(b);
2998 - } else {
3249 + } else
3250 + {
3251 require('MeshAgent').SendCommand({ action: 'battery' });
3252 }
3253 break;
@@ -3024,7 +3276,7 @@ function createMeshCore(agent) {
3276 }
3277 else
3278 {
3027 - switch(args['_'][0].toUpperCase())
3279 + switch (args['_'][0].toUpperCase())
3280 {
3281 case 'GET':
3282 var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
@@ -3047,7 +3299,7 @@ function createMeshCore(agent) {
3299 require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
3300 response = 'UAC mode changed to: Secure Desktop';
3301 }
3050 - catch(e)
3302 + catch (e)
3303 {
3304 response = "Unable to change UAC Mode";
3305 }
@@ -3058,9 +3310,9 @@ function createMeshCore(agent) {
3310 }
3311 }
3312 break;
3061 - case 'vm':
3062 - response = 'Virtual Machine = ' + require('identifiers').isVM();
3063 - break;
3313 + case 'vm':
3314 + response = 'Virtual Machine = ' + require('identifiers').isVM();
3315 + break;
3316 case 'startupoptions':
3317 response = JSON.stringify(require('MeshAgent').getStartupOptions());
3318 break;
@@ -3071,72 +3323,81 @@ function createMeshCore(agent) {
3323 }
3324 else
3325 {
3074 - if(require('MeshAgent').maxKvmTileSize == 0)
3326 + if (require('MeshAgent').maxKvmTileSize == 0)
3327 {
3328 response = 'KVM Mode: Full JUMBO';
3329 }
3078 - else
3330 + else
3331 {
3332 response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
3081 - response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
3333 + response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize / 1024) + ' Kbytes')));
3334 }
3335 }
3336 break;
3337 case 'alert':
3086 - if (args['_'].length == 0)
3338 + if (args['_'].length == 0)
3339 {
3340 response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
3341 }
3342 else
3343 {
3344 var p = args['_'].join(' ').split(',');
3093 - if(p.length<2)
3345 + if (p.length < 2)
3346 {
3347 response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
3348 }
3349 else
3350 {
3099 - this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
3351 + this._alert = require('message-box').create(p[0], p[1], p.length == 3 ? parseInt(p[2]) : 9999, 1);
3352 }
3353 }
3354 break;
3355 case 'agentsize':
3356 var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
3105 - if (process.platform == 'win32') {
3357 + if (process.platform == 'win32')
3358 + {
3359 // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
3360 var writtenSize = 0;
3361 try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
3109 - if (writtenSize != actualSize) {
3362 + if (writtenSize != actualSize)
3363 + {
3364 response = "Size updated from: " + writtenSize + " to: " + actualSize;
3365 try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
3112 - } else { response = "Agent Size: " + actualSize + " kb"; }
3113 - } else { response = "Agent Size: " + actualSize + " kb"; }
3366 + } else
3367 + { response = "Agent Size: " + actualSize + " kb"; }
3368 + } else
3369 + { response = "Agent Size: " + actualSize + " kb"; }
3370 break;
3371 case 'versions':
3372 response = JSON.stringify(process.versions, null, ' ');
3373 break;
3374 case 'wpfhwacceleration':
3375 if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
3120 - if (args['_'].length != 1) {
3376 + if (args['_'].length != 1)
3377 + {
3378 response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
3379 }
3123 - else {
3380 + else
3381 + {
3382 var reg = require('win-registry');
3383 var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
3384 var key = reg.usernameToUserKey(uname);
3385
3128 - switch (args['_'][0].toUpperCase()) {
3386 + switch (args['_'][0].toUpperCase())
3387 + {
3388 default:
3389 response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
3390 break;
3391 case 'ON':
3133 - try {
3392 + try
3393 + {
3394 reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
3395 response = "OK";
3396 } catch (e) { response = "FAILED"; }
3397 break;
3398 case 'OFF':
3139 - try {
3399 + try
3400 + {
3401 reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
3402 response = 'OK';
3403 } catch (e) { response = 'FAILED'; }
@@ -3154,59 +3415,76 @@ function createMeshCore(agent) {
3415 }
3416 break;
3417 case 'tsid':
3157 - if (process.platform == 'win32') {
3158 - if (args['_'].length != 1) {
3418 + if (process.platform == 'win32')
3419 + {
3420 + if (args['_'].length != 1)
3421 + {
3422 response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
3160 - } else {
3423 + } else
3424 + {
3425 var i = parseInt(args['_'][0]);
3426 require('MeshAgent')._tsid = (isNaN(i) ? null : i);
3427 response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
3428 }
3165 - } else { response = "TSID command only supported on Windows"; }
3429 + } else
3430 + { response = "TSID command only supported on Windows"; }
3431 break;
3432 case 'activeusers':
3168 - if (process.platform == 'win32') {
3433 + if (process.platform == 'win32')
3434 + {
3435 var p = require('user-sessions').enumerateUsers();
3436 p.sessionid = sessionid;
3171 - p.then(function (u) {
3437 + p.then(function (u)
3438 + {
3439 var v = [];
3173 - for (var i in u) {
3440 + for (var i in u)
3441 + {
3442 if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
3443 }
3444 sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
3445 });
3178 - } else { response = "activeusers command only supported on Windows"; }
3446 + } else
3447 + { response = "activeusers command only supported on Windows"; }
3448 break;
3449 case 'wallpaper':
3181 - if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
3450 + if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available))
3451 + {
3452 response = "wallpaper command not supported on this platform";
3453 }
3184 - else {
3185 - if (args['_'].length != 1) {
3454 + else
3455 + {
3456 + if (args['_'].length != 1)
3457 + {
3458 response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
3459 }
3188 - else {
3189 - switch (args['_'][0].toUpperCase()) {
3460 + else
3461 + {
3462 + switch (args['_'][0].toUpperCase())
3463 + {
3464 default:
3465 response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
3466 break;
3467 case 'GET':
3468 case 'TOGGLE':
3195 - if (process.platform == 'win32') {
3469 + if (process.platform == 'win32')
3470 + {
3471 var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
3472 var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
3473 child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
3474 child.stderr.on('data', function () { });
3475 child.waitExit();
3476 var current = child.stdout.str.trim();
3202 - if (args['_'][0].toUpperCase() == 'GET') {
3477 + if (args['_'][0].toUpperCase() == 'GET')
3478 + {
3479 response = current;
3480 break;
3481 }
3206 - if (current != '') {
3482 + if (current != '')
3483 + {
3484 require('MeshAgent')._wallpaper = current;
3485 response = 'Wallpaper cleared';
3209 - } else {
3486 + } else
3487 + {
3488 response = 'Wallpaper restored';
3489 }
3490 child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
@@ -3214,17 +3492,21 @@ function createMeshCore(agent) {
3492 child.stderr.on('data', function () { });
3493 child.waitExit();
3494 }
3217 - else {
3495 + else
3496 + {
3497 var id = require('user-sessions').consoleUid();
3498 var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
3220 - if (args['_'][0].toUpperCase() == 'GET') {
3499 + if (args['_'][0].toUpperCase() == 'GET')
3500 + {
3501 response = current;
3502 break;
3503 }
3224 - if (current != '/dev/null') {
3504 + if (current != '/dev/null')
3505 + {
3506 require('MeshAgent')._wallpaper = current;
3507 response = 'Wallpaper cleared';
3227 - } else {
3508 + } else
3509 + {
3510 response = 'Wallpaper restored';
3511 }
3512 require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
@@ -3289,30 +3571,32 @@ function createMeshCore(agent) {
3571 }
3572 }
3573 break;
3292 - /*
3293 - case 'border':
3294 - {
3295 - if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
3296 - if (meshCoreObj.users.length > 0) {
3297 - obj.borderManager.Start(meshCoreObj.users[0]);
3298 - response = 'Border blinking is on.';
3574 + /*
3575 + case 'border':
3576 + {
3577 + if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
3578 + if (meshCoreObj.users.length > 0) {
3579 + obj.borderManager.Start(meshCoreObj.users[0]);
3580 + response = 'Border blinking is on.';
3581 + } else {
3582 + response = 'Cannot turn on border blinking, no logged in users.';
3583 + }
3584 + } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3585 + obj.borderManager.Stop();
3586 + response = 'Border blinking is off.';
3587 } else {
3300 - response = 'Cannot turn on border blinking, no logged in users.';
3588 + response = 'Proper usage: border "on|off"'; // Display correct command usage
3589 }
3302 - } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3303 - obj.borderManager.Stop();
3304 - response = 'Border blinking is off.';
3305 - } else {
3306 - response = 'Proper usage: border "on|off"'; // Display correct command usage
3590 }
3308 - }
3309 - break;
3310 - */
3591 + break;
3592 + */
3593 case 'av':
3312 - if (process.platform == 'win32') {
3594 + if (process.platform == 'win32')
3595 + {
3596 // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3597 response = JSON.stringify(require('win-info').av(), null, 1);
3315 - } else {
3598 + } else
3599 + {
3600 response = 'Not supported on the platform';
3601 }
3602 break;
@@ -3320,21 +3604,27 @@ function createMeshCore(agent) {
3604 if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
3605 break;
3606 case 'getclip':
3323 - if (require('MeshAgent').isService) {
3607 + if (require('MeshAgent').isService)
3608 + {
3609 require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
3325 - } else {
3610 + } else
3611 + {
3612 require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
3613 }
3614 break;
3615 case 'setclip': {
3330 - if (args['_'].length != 1) {
3616 + if (args['_'].length != 1)
3617 + {
3618 response = 'Proper usage: setclip "sample text"';
3332 - } else {
3333 - if (require('MeshAgent').isService) {
3619 + } else
3620 + {
3621 + if (require('MeshAgent').isService)
3622 + {
3623 require('clipboard').dispatchWrite(args['_'][0]);
3624 response = 'Setting clipboard to: "' + args['_'][0] + '"';
3625 }
3337 - else {
3626 + else
3627 + {
3628 require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3629 }
3630 }
@@ -3352,10 +3642,12 @@ function createMeshCore(agent) {
3642 }
3643 case 'toast': {
3644 if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
3355 - if (require('MeshAgent')._tsid == null) {
3645 + if (require('MeshAgent')._tsid == null)
3646 + {
3647 require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
3648 }
3358 - else {
3649 + else
3650 + {
3651 require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
3652 }
3653 }
@@ -3367,7 +3659,8 @@ function createMeshCore(agent) {
3659 break;
3660 }
3661 case 'ps': {
3370 - processManager.getProcesses(function (plist) {
3662 + processManager.getProcesses(function (plist)
3663 + {
3664 var x = '';
3665 for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
3666 sendConsoleText(x, sessionid);
@@ -3375,9 +3668,11 @@ function createMeshCore(agent) {
3668 break;
3669 }
3670 case 'kill': {
3378 - if ((args['_'].length < 1)) {
3671 + if ((args['_'].length < 1))
3672 + {
3673 response = 'Proper usage: kill [pid]'; // Display correct command usage
3380 - } else {
3674 + } else
3675 + {
3676 process.kill(parseInt(args['_'][0]));
3677 response = 'Killed process ' + args['_'][0] + '.';
3678 }
@@ -3390,10 +3685,13 @@ function createMeshCore(agent) {
3685 case 'rawsmbios': {
3686 if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
3687 response = '';
3393 - for (var i in SMBiosTablesRaw) {
3688 + for (var i in SMBiosTablesRaw)
3689 + {
3690 var header = false;
3395 - for (var j in SMBiosTablesRaw[i]) {
3396 - if (SMBiosTablesRaw[i][j].length > 0) {
3691 + for (var j in SMBiosTablesRaw[i])
3692 + {
3693 + if (SMBiosTablesRaw[i][j].length > 0)
3694 + {
3695 if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
3696 response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
3697 }
@@ -3403,9 +3701,11 @@ function createMeshCore(agent) {
3701 break;
3702 }
3703 case 'eval': { // Eval JavaScript
3406 - if (args['_'].length < 1) {
3704 + if (args['_'].length < 1)
3705 + {
3706 response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3408 - } else {
3707 + } else
3708 + {
3709 response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3710 }
3711 break;
@@ -3420,18 +3720,22 @@ function createMeshCore(agent) {
3720 {
3721 }
3722
3423 - if (!require('service-manager').manager.getService(agentName).isMe()) {
3723 + if (!require('service-manager').manager.getService(agentName).isMe())
3724 + {
3725 response = 'Uininstall failed, this instance is not the service instance';
3425 - } else {
3726 + } else
3727 + {
3728 try { diagnosticAgent_uninstall(); } catch (e) { }
3729 var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
3730 this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
3731 }
3732 break;
3733 case 'notify': { // Send a notification message to the mesh
3432 - if (args['_'].length != 1) {
3734 + if (args['_'].length != 1)
3735 + {
3736 response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
3434 - } else {
3737 + } else
3738 + {
3739 var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
3740 if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
3741 mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
@@ -3443,15 +3747,18 @@ function createMeshCore(agent) {
3747 // CPU & memory utilization
3748 pr = require('sysinfo').cpuUtilization();
3749 pr.sessionid = sessionid;
3446 - pr.then(function (data) {
3750 + pr.then(function (data)
3751 + {
3752 sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
3448 - }, function (e) {
3753 + }, function (e)
3754 + {
3755 sendConsoleText(e);
3756 });
3757 break;
3758 }
3759 case 'sysinfo': { // Return system information
3454 - getSystemInformation(function (results, err) {
3760 + getSystemInformation(function (results, err)
3761 + {
3762 if (results == null) { sendConsoleText(err, this.sessionid); } else {
3763 sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
3764 }
@@ -3459,7 +3766,7 @@ function createMeshCore(agent) {
3766 break;
3767 }
3768 case 'info': { // Return information about the agent and agent core module
3462 - response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
3769 + response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
3770 if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
3771 if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
3772 response += '\r\nModules: ' + addedModules.join(', ') + '.';
@@ -3473,10 +3780,12 @@ function createMeshCore(agent) {
3780 case 'osinfo': { // Return the operating system information
3781 var i = 1;
3782 if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
3476 - for (var j = 0; j < i; j++) {
3783 + for (var j = 0; j < i; j++)
3784 + {
3785 var pr = require('os').name();
3786 pr.sessionid = sessionid;
3479 - pr.then(function (v) {
3787 + pr.then(function (v)
3788 + {
3789 sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
3790 });
3791 }
@@ -3494,9 +3803,11 @@ function createMeshCore(agent) {
3803 break;
3804 }
3805 case 'type': { // Returns the content of a file
3497 - if (args['_'].length == 0) {
3806 + if (args['_'].length == 0)
3807 + {
3808 response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
3499 - } else {
3809 + } else
3810 + {
3811 var max = 4096;
3812 if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
3813 if (max > 4096) max = 4096;
@@ -3515,18 +3826,22 @@ function createMeshCore(agent) {
3826 }
3827 case 'dbget': { // Return the data store value for a given key
3828 if (db == null) { response = 'Database not accessible.'; break; }
3518 - if (args['_'].length != 1) {
3829 + if (args['_'].length != 1)
3830 + {
3831 response = 'Proper usage: dbget (key)'; // Display the value for a given database key
3520 - } else {
3832 + } else
3833 + {
3834 response = db.Get(args['_'][0]);
3835 }
3836 break;
3837 }
3838 case 'dbset': { // Set a data store key and value pair
3839 if (db == null) { response = 'Database not accessible.'; break; }
3527 - if (args['_'].length != 2) {
3840 + if (args['_'].length != 2)
3841 + {
3842 response = 'Proper usage: dbset (key) (value)'; // Set a database key
3529 - } else {
3843 + } else
3844 + {
3845 var r = db.Put(args['_'][0], args['_'][1]);
3846 response = 'Key set: ' + r;
3847 }
@@ -3539,20 +3854,27 @@ function createMeshCore(agent) {
3854 break;
3855 }
3856 case 'httpget': {
3542 - if (consoleHttpRequest != null) {
3857 + if (consoleHttpRequest != null)
3858 + {
3859 response = 'HTTP operation already in progress.';
3544 - } else {
3545 - if (args['_'].length != 1) {
3860 + } else
3861 + {
3862 + if (args['_'].length != 1)
3863 + {
3864 response = 'Proper usage: httpget (url)';
3547 - } else {
3865 + } else
3866 + {
3867 var options = http.parseUri(args['_'][0]);
3868 options.method = 'GET';
3550 - if (options == null) {
3869 + if (options == null)
3870 + {
3871 response = 'Invalid url.';
3552 - } else {
3872 + } else
3873 + {
3874 try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
3875 consoleHttpRequest.sessionid = sessionid;
3555 - if (consoleHttpRequest != null) {
3876 + if (consoleHttpRequest != null)
3877 + {
3878 consoleHttpRequest.end();
3879 response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
3880 }
@@ -3563,7 +3885,8 @@ function createMeshCore(agent) {
3885 }
3886 case 'wslist': { // List all web sockets
3887 response = '';
3566 - for (var i in consoleWebSockets) {
3888 + for (var i in consoleWebSockets)
3889 + {
3890 var httprequest = consoleWebSockets[i];
3891 response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3892 }
@@ -3571,16 +3894,20 @@ function createMeshCore(agent) {
3894 break;
3895 }
3896 case 'wsconnect': { // Setup a web socket
3574 - if (args['_'].length == 0) {
3897 + if (args['_'].length == 0)
3898 + {
3899 response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3576 - } else {
3900 + } else
3901 + {
3902 var httprequest = null;
3578 - try {
3903 + try
3904 + {
3905 var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3906 options.rejectUnauthorized = 0;
3907 httprequest = http.request(options);
3908 } catch (e) { response = 'Invalid HTTP websocket request'; }
3583 - if (httprequest != null) {
3909 + if (httprequest != null)
3910 + {
3911 httprequest.upgrade = onWebSocketUpgrade;
3912 httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3913
@@ -3596,34 +3923,43 @@ function createMeshCore(agent) {
3923 break;
3924 }
3925 case 'wssend': { // Send data on a web socket
3599 - if (args['_'].length == 0) {
3926 + if (args['_'].length == 0)
3927 + {
3928 response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3601 - for (var i in consoleWebSockets) {
3929 + for (var i in consoleWebSockets)
3930 + {
3931 var httprequest = consoleWebSockets[i];
3932 response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3933 }
3605 - } else {
3934 + } else
3935 + {
3936 var i = parseInt(args['_'][0]);
3937 var httprequest = consoleWebSockets[i];
3608 - if (httprequest != undefined) {
3938 + if (httprequest != undefined)
3939 + {
3940 httprequest.s.write(args['_'][1]);
3941 response = 'ok';
3611 - } else {
3942 + } else
3943 + {
3944 response = 'Invalid web socket number';
3945 }
3946 }
3947 break;
3948 }
3949 case 'wsclose': { // Close a websocket
3618 - if (args['_'].length == 0) {
3950 + if (args['_'].length == 0)
3951 + {
3952 response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3620 - } else {
3953 + } else
3954 + {
3955 var i = parseInt(args['_'][0]);
3956 var httprequest = consoleWebSockets[i];
3623 - if (httprequest != undefined) {
3957 + if (httprequest != undefined)
3958 + {
3959 if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3960 response = 'ok';
3626 - } else {
3961 + } else
3962 + {
3963 response = 'Invalid web socket number';
3964 }
3965 }
@@ -3641,12 +3977,15 @@ function createMeshCore(agent) {
3977 if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3978 response = 'List of ' + xpath + '\r\n';
3979 var results = fs.readdirSync(xpath);
3644 - for (var i = 0; i < results.length; ++i) {
3980 + for (var i = 0; i < results.length; ++i)
3981 + {
3982 var stat = null, p = obj.path.join(args['_'][0], results[i]);
3983 try { stat = fs.statSync(p); } catch (e) { }
3647 - if ((stat == null) || (stat == undefined)) {
3984 + if ((stat == null) || (stat == undefined))
3985 + {
3986 response += (results[i] + "\r\n");
3649 - } else {
3987 + } else
3988 + {
3989 response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3990 }
3991 }
@@ -3662,13 +4001,16 @@ function createMeshCore(agent) {
4001 break;
4002 }
4003 case 'amt': { // Show Intel AMT status
3665 - if (amt != null) {
3666 - amt.getMeiState(9, function (state) {
4004 + if (amt != null)
4005 + {
4006 + amt.getMeiState(9, function (state)
4007 + {
4008 var resp = "Intel AMT not detected.";
4009 if (state != null) { resp = objToString(state, 0, ' ', true); }
4010 sendConsoleText(resp, sessionid);
4011 });
3671 - } else {
4012 + } else
4013 + {
4014 response = "Intel AMT not detected.";
4015 }
4016 break;
@@ -3679,9 +4021,11 @@ function createMeshCore(agent) {
4021 break;
4022 }
4023 case 'wakeonlan': { // Send wake-on-lan
3682 - if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
4024 + if ((args['_'].length != 1) || (args['_'][0].length != 12))
4025 + {
4026 response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3684 - } else {
4027 + } else
4028 + {
4029 var count = sendWakeOnLan(args['_'][0]);
4030 response = 'Sent wake-on-lan on ' + count + ' interface(s).';
4031 }
@@ -3692,12 +4036,16 @@ function createMeshCore(agent) {
4036 break;
4037 }
4038 case 'power': { // Execute a power action on this computer
3695 - if (mesh.ExecPowerState == undefined) {
4039 + if (mesh.ExecPowerState == undefined)
4040 + {
4041 response = 'Power command not supported on this agent.';
3697 - } else {
3698 - if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
4042 + } else
4043 + {
4044 + if ((args['_'].length == 0) || isNaN(Number(args['_'][0])))
4045 + {
4046 response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
3700 - } else {
4047 + } else
4048 + {
4049 var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
4050 response = 'Power action executed with return code: ' + r + '.';
4051 }
@@ -3705,7 +4053,8 @@ function createMeshCore(agent) {
4053 break;
4054 }
4055 case 'location': {
3708 - getIpLocationData(function (location) {
4056 + getIpLocationData(function (location)
4057 + {
4058 sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
4059 });
4060 break;
@@ -3715,10 +4064,12 @@ function createMeshCore(agent) {
4064 break;
4065 }
4066 case 'scanwifi': {
3718 - if (wifiScanner != null) {
4067 + if (wifiScanner != null)
4068 + {
4069 var wifiPresent = wifiScanner.hasWireless;
4070 if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3721 - } else { response = "Wifi module not present."; }
4071 + } else
4072 + { response = "Wifi module not present."; }
4073 break;
4074 }
4075 case 'modules': {
@@ -3731,40 +4082,51 @@ function createMeshCore(agent) {
4082 break;
4083 }
4084 case 'getscript': {
3734 - if (args['_'].length != 1) {
4085 + if (args['_'].length != 1)
4086 + {
4087 response = "Proper usage: getscript [scriptNumber].";
3736 - } else {
4088 + } else
4089 + {
4090 mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
4091 }
4092 break;
4093 }
4094 case 'diagnostic':
4095 {
3743 - if (!mesh.DAIPC.listening) {
4096 + if (!mesh.DAIPC.listening)
4097 + {
4098 response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
4099 break;
4100 }
4101 var diag = diagnosticAgent_installCheck();
3748 - if (diag) {
3749 - if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
4102 + if (diag)
4103 + {
4104 + if (args['_'].length == 1 && args['_'][0] == 'uninstall')
4105 + {
4106 diagnosticAgent_uninstall();
4107 response = 'Diagnostic Agent uninstalled';
4108 }
3753 - else {
4109 + else
4110 + {
4111 response = 'Diagnostic Agent installed at: ' + diag.appLocation();
4112 }
4113 }
3757 - else {
3758 - if (args['_'].length == 1 && args['_'][0] == 'install') {
4114 + else
4115 + {
4116 + if (args['_'].length == 1 && args['_'][0] == 'install')
4117 + {
4118 diag = diagnosticAgent_installCheck(true);
3760 - if (diag) {
4119 + if (diag)
4120 + {
4121 response = 'Diagnostic agent was installed at: ' + diag.appLocation();
4122 }
3763 - else {
4123 + else
4124 + {
4125 response = 'Diagnostic agent installation failed';
4126 }
4127 }
3767 - else {
4128 + else
4129 + {
4130 response = 'Diagnostic Agent Not installed. To install: diagnostic install';
4131 }
4132 }
@@ -3778,7 +4140,8 @@ function createMeshCore(agent) {
4140 case 'amtconfig': {
4141 if (amt == null) { response = "Intel AMT not detected."; break; }
4142 if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
3781 - amt.getMeiState(15, function (state) {
4143 + amt.getMeiState(15, function (state)
4144 + {
4145 var rx = '';
4146 if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
4147 var apfarg = {
@@ -3792,15 +4155,19 @@ function createMeshCore(agent) {
4155 conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
4156 meiState: state // MEI state will be passed to MPS server
4157 };
3795 - if ((state.UUID == null) || (state.UUID.length != 36)) {
4158 + if ((state.UUID == null) || (state.UUID.length != 36))
4159 + {
4160 rx = "Unable to get Intel AMT UUID";
3797 - } else {
4161 + } else
4162 + {
4163 addAmtEvent('User LMS tunnel start.');
4164 apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
3800 - apftunnel.onJsonControl = function (data) {
4165 + apftunnel.onJsonControl = function (data)
4166 + {
4167 if (data.action == 'console') { addAmtEvent(data.msg); require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
4168 if (data.action == 'mestate') { amt.getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3803 - if (data.action == 'deactivate') { // Request CCM deactivation
4169 + if (data.action == 'deactivate')
4170 + { // Request CCM deactivation
4171 var amtMeiModule, amtMei;
4172 try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
4173 amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
@@ -3809,10 +4176,12 @@ function createMeshCore(agent) {
4176 if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
4177 }
4178 apftunnel.onChannelClosed = function () { addAmtEvent('User LMS tunnel closed.'); apftunnel = null; }
3812 - try {
4179 + try
4180 + {
4181 apftunnel.connect();
4182 rx = "Started Intel AMT configuration";
3815 - } catch (ex) {
4183 + } catch (ex)
4184 + {
4185 rx = JSON.stringify(ex);
4186 }
4187 }
@@ -3822,14 +4191,17 @@ function createMeshCore(agent) {
4191 break;
4192 }
4193 case 'apf': {
3825 - if (meshCoreObj.intelamt !== null) {
3826 - if (args['_'].length == 1) {
4194 + if (meshCoreObj.intelamt !== null)
4195 + {
4196 + if (args['_'].length == 1)
4197 + {
4198 var connType = -1, connTypeStr = args['_'][0].toLowerCase();
4199 if (connTypeStr == 'lms') { connType = 2; }
4200 if (connTypeStr == 'relay') { connType = 1; }
4201 if (connTypeStr == 'cira') { connType = 0; }
4202 if (connTypeStr == 'off') { connType = -2; }
3832 - if (connType >= 0) { // Connect
4203 + if (connType >= 0)
4204 + { // Connect
4205 var apfarg = {
4206 mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
4207 mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
@@ -3840,52 +4212,67 @@ function createMeshCore(agent) {
4212 clientuuid: meshCoreObj.intelamt.uuid,
4213 conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
4214 };
3843 - if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
4215 + if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36))
4216 + {
4217 response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
3845 - } else {
4218 + } else
4219 + {
4220 apftunnel = require('amt-apfclient')({ debug: false }, apfarg);
3847 - apftunnel.onJsonControl = function (data) {
4221 + apftunnel.onJsonControl = function (data)
4222 + {
4223 if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
4224 if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
4225 }
4226 apftunnel.onChannelClosed = function () { apftunnel = null; }
3852 - try {
4227 + try
4228 + {
4229 apftunnel.connect();
4230 response = "Started APF tunnel";
3855 - } catch (e) {
4231 + } catch (e)
4232 + {
4233 response = JSON.stringify(e);
4234 }
4235 }
3859 - } else if (connType == -2) { // Disconnect
3860 - try {
4236 + } else if (connType == -2)
4237 + { // Disconnect
4238 + try
4239 + {
4240 apftunnel.disconnect();
4241 response = "Stopped APF tunnel";
3863 - } catch (e) {
4242 + } catch (e)
4243 + {
4244 response = JSON.stringify(e);
4245 }
4246 apftunnel = null;
3867 - } else {
4247 + } else
4248 + {
4249 response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
4250 }
3870 - } else {
4251 + } else
4252 + {
4253 response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
4254 }
3873 - } else {
4255 + } else
4256 + {
4257 response = "APF tunnel requires Intel AMT";
4258 }
4259 break;
4260 }
4261 case 'plugin': {
3879 - if (typeof args['_'][0] == 'string') {
3880 - try {
4262 + if (typeof args['_'][0] == 'string')
4263 + {
4264 + try
4265 + {
4266 // Pass off the action to the plugin
4267 // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
4268 // to control the output / actions here.
4269 response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3885 - } catch (e) {
4270 + } catch (e)
4271 + {
4272 response = "There was an error in the plugin (" + e + ")";
4273 }
3888 - } else {
4274 + } else
4275 + {
4276 response = "Proper usage: plugin [pluginName] [args].";
4277 }
4278 break;
@@ -3900,23 +4287,183 @@ function createMeshCore(agent) {
4287 }
4288
4289 // Send a mesh agent console command
3903 - function sendConsoleText(text, sessionid) {
4290 + function sendConsoleText(text, sessionid)
4291 + {
4292 if (typeof text == 'object') { text = JSON.stringify(text); }
4293 if (debugConsole && ((sessionid == null) || (sessionid == 'pipe'))) { broadcastToRegisteredApps({ cmd: 'console', value: text }); }
4294 if (sessionid != 'pipe') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid }); }
4295 }
4296
4297 + // Send a mesh agent message to server, placing a bubble/badge on the agent device
4298 + function sendAgentMessage(msg, icon)
4299 + {
4300 + if (sendAgentMessage.messages == null)
4301 + {
4302 + sendAgentMessage.messages = {};
4303 + sendAgentMessage.nextid = 1;
4304 + }
4305 + sendAgentMessage.messages[sendAgentMessage.nextid++] = { msg: msg, icon: icon };
4306 + require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages });
4307 + }
4308 +
4309 + // Start a JavaScript based Agent Self-Update
4310 + function agentUpdate_Start(updateurl, updateoptions)
4311 + {
4312 + // If this value is null
4313 + var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
4314 +
4315 + if (this._selfupdate != null)
4316 + {
4317 + // We were already called, so we will ignore this duplicate request
4318 + if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
4319 + }
4320 + else
4321 + {
4322 + if (require('MeshAgent').ARCHID == null && updateurl == null)
4323 + {
4324 + // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
4325 + sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
4326 + }
4327 + else
4328 + {
4329 + var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
4330 + var name = require('MeshAgent').serviceName;
4331 + if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default
4332 + try
4333 + {
4334 + var s = require('service-manager').manager.getService(name);
4335 + if (!s.isMe())
4336 + {
4337 + if (process.platform == 'win32') { s.close(); }
4338 + sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
4339 + return;
4340 + }
4341 + if (process.platform == 'win32') { s.close(); }
4342 + }
4343 + catch (zz)
4344 + {
4345 + sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
4346 + sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
4347 + return;
4348 + }
4349 +
4350 + sendConsoleText('Downloading update...', sessionid);
4351 + var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
4352 + options.protocol = 'https:';
4353 + if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
4354 + options.rejectUnauthorized = false;
4355 + options.checkServerIdentity = function checkServerIdentity(certs)
4356 + {
4357 + // If the tunnel certificate matches the control channel certificate, accept the connection
4358 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.digest == certs[0].digest) return; } catch (ex) { }
4359 + try { if (require('MeshAgent').ServerInfo.ControlChannelCertificate.fingerprint == certs[0].fingerprint) return; } catch (ex) { }
4360 +
4361 + // Check that the certificate is the one expected by the server, fail if not.
4362 + if (checkServerIdentity.servertlshash == null)
4363 + {
4364 + sendConsoleText('Self Update failed, because the url cannot be verified', sessionid);
4365 + sendAgentMessage('Self Update failed, because the url cannot be verified', 3);
4366 + throw new Error('BadCert');
4367 + }
4368 + if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase()))
4369 + {
4370 + sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
4371 + sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
4372 + throw new Error('BadCert')
4373 + }
4374 + }
4375 + options.checkServerIdentity.servertlshash = (updateoptions != null ? updateoptions.tlshash : null);
4376 + this._selfupdate = require('https').get(options);
4377 + this._selfupdate.on('error', function (e)
4378 + {
4379 + sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid);
4380 + sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
4381 + });
4382 + this._selfupdate.on('response', function (img)
4383 + {
4384 + this._file = require('fs').createWriteStream(agentfilename + '.update', { flags: 'wb' });
4385 + this._filehash = require('SHA384Stream').create();
4386 + this._filehash.on('hash', function (h)
4387 + {
4388 + if (updateoptions != null && updateoptions.hash != null)
4389 + {
4390 + if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
4391 + {
4392 + sendConsoleText('Download complete. HASH verified.', sessionid);
4393 + }
4394 + else
4395 + {
4396 + sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid);
4397 + sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3);
4398 + return;
4399 + }
4400 + }
4401 + else
4402 + {
4403 + sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
4404 + }
4405 +
4406 + sendConsoleText('Updating and restarting agent...', sessionid);
4407 + if (process.platform == 'win32')
4408 + {
4409 + // Use _wexecve() equivalent to perform the update
4410 + this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe',
4411 + ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 });
4412 + }
4413 + else
4414 + {
4415 + // remove binary
4416 + require('fs').unlinkSync(process.execPath);
4417 +
4418 + // copy update
4419 + require('fs').copyFileSync(process.cwd() + agentfilename + '.update', process.execPath);
4420 +
4421 + // erase update
4422 + require('fs').unlinkSync(process.cwd() + agentfilename + '.update');
4423 +
4424 + // add execute permissions
4425 + var m = require('fs').statSync(process.execPath).mode;
4426 + m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
4427 + require('fs').chmodSync(process.execPath, m);
4428 +
4429 + sendConsoleText('Restarting service...', sessionid);
4430 + try
4431 + {
4432 + // restart service
4433 + var s = require('service-manager').manager.getService(name);
4434 + s.restart();
4435 + }
4436 + catch (zz)
4437 + {
4438 + sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
4439 + sendAgentMessage('Self Update encountered an error trying to restart service', 3);
4440 + }
4441 + }
4442 + });
4443 + img.pipe(this._file);
4444 + img.pipe(this._filehash);
4445 + });
4446 + }
4447 + }
4448 + }
4449 +
4450 +
4451 +
4452 +
4453 // Called before the process exits
4454 //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
4455
4456 // Called when the server connection state changes
3913 - function handleServerConnection(state) {
4457 + function handleServerConnection(state)
4458 + {
4459 meshServerConnectionState = state;
3915 - if (meshServerConnectionState == 0) {
4460 + if (meshServerConnectionState == 0)
4461 + {
4462 // Server disconnected
4463 if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
4464 lastSelfInfo = null;
3919 - } else {
4465 + } else
4466 + {
4467 // Server connected, send mesh core information
4468 var oldNodeId = db.Get('OldNodeId');
4469 if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
@@ -3931,10 +4478,11 @@ function createMeshCore(agent) {
4478 {
4479 selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); // 20 minutes
4480 selfInfoUpdateTimer.metadata = 'meshcore (InfoUpdate Timer)';
3934 - }
4481 + }
4482
4483 // Send any state messages
3937 - if (Object.keys(tunnelUserCount.msg).length > 0) {
4484 + if (Object.keys(tunnelUserCount.msg).length > 0)
4485 + {
4486 try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
4487 broadcastSessionsToRegisteredApps();
4488 }
@@ -3950,12 +4498,14 @@ function createMeshCore(agent) {
4498 // Update the server with the latest network interface information
4499 var sendNetworkUpdateNagleTimer = null;
4500 function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3953 - function sendNetworkUpdate(force) {
4501 + function sendNetworkUpdate(force)
4502 + {
4503 sendNetworkUpdateNagleTimer = null;
4504
4505 // Update the network interfaces information data
4506 var netInfo = { netif2: require('os').networkInterfaces() };
3958 - if (netInfo.netif2) {
4507 + if (netInfo.netif2)
4508 + {
4509 netInfo.action = 'netinfo';
4510 var netInfoStr = JSON.stringify(netInfo);
4511 if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
@@ -3963,14 +4513,17 @@ function createMeshCore(agent) {
4513 }
4514
4515 // Called periodically to check if we need to send updates to the server
3966 - function sendPeriodicServerUpdate(flags, force) {
4516 + function sendPeriodicServerUpdate(flags, force)
4517 + {
4518 if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
4519 if (!flags) { flags = 0xFFFFFFFF; }
4520
4521 // If we have a connected MEI, get Intel ME information
3971 - if ((flags & 1) && (amt != null) && (amt.state == 2)) {
4522 + if ((flags & 1) && (amt != null) && (amt.state == 2))
4523 + {
4524 delete meshCoreObj.intelamt;
3973 - amt.getMeiState(9, function (meinfo) {
4525 + amt.getMeiState(9, function (meinfo)
4526 + {
4527 meshCoreObj.intelamt = meinfo;
4528 meshCoreObj.intelamt.microlms = amt.lmsstate;
4529 meshCoreObjChanged();
@@ -3981,17 +4534,20 @@ function createMeshCore(agent) {
4534 if (flags & 2) { sendNetworkUpdateNagle(false); }
4535
4536 // Update anti-virus information
3984 - if ((flags & 4) && (process.platform == 'win32')) {
4537 + if ((flags & 4) && (process.platform == 'win32'))
4538 + {
4539 // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
4540 try { meshCoreObj.av = require('win-info').av(); meshCoreObjChanged(); } catch (e) { av = null; } // Antivirus
4541 //if (process.platform == 'win32') { try { meshCoreObj.pr = require('win-info').pendingReboot(); meshCoreObjChanged(); } catch (e) { meshCoreObj.pr = null; } } // Pending reboot
4542 }
4543
4544 // Send available data right now
3991 - if (force) {
4545 + if (force)
4546 + {
4547 meshCoreObj = sortObjRec(meshCoreObj);
4548 var x = JSON.stringify(meshCoreObj);
3994 - if (x != LastPeriodicServerUpdate) {
4549 + if (x != LastPeriodicServerUpdate)
4550 + {
4551 LastPeriodicServerUpdate = x;
4552 mesh.SendCommand(meshCoreObj);
4553 }
@@ -4002,11 +4558,13 @@ function createMeshCore(agent) {
4558 var LastPeriodicServerUpdate = null;
4559 var PeriodicServerUpdateNagleTimer = null;
4560 function meshCoreObjChanged() { if (PeriodicServerUpdateNagleTimer == null) { PeriodicServerUpdateNagleTimer = setTimeout(meshCoreObjChangedEx, 500); } }
4005 - function meshCoreObjChangedEx() {
4561 + function meshCoreObjChangedEx()
4562 + {
4563 PeriodicServerUpdateNagleTimer = null;
4564 meshCoreObj = sortObjRec(meshCoreObj);
4565 var x = JSON.stringify(meshCoreObj);
4009 - if (x != LastPeriodicServerUpdate) {
4566 + if (x != LastPeriodicServerUpdate)
4567 + {
4568 try { LastPeriodicServerUpdate = x; mesh.SendCommand(meshCoreObj); } catch (ex) { }
4569 }
4570 }
@@ -4015,13 +4573,15 @@ function createMeshCore(agent) {
4573 function sortObj(o) { return Object.keys(o).sort().reduce(function (result, key) { result[key] = o[key]; return result; }, {}); }
4574
4575 // Starting function
4018 - obj.start = function () {
4576 + obj.start = function ()
4577 + {
4578 // Setup the mesh agent event handlers
4579 mesh.AddCommandHandler(handleServerCommand);
4580 mesh.AddConnectHandler(handleServerConnection);
4581 }
4582
4024 - obj.stop = function () {
4583 + obj.stop = function ()
4584 + {
4585 mesh.AddCommandHandler(null);
4586 mesh.AddConnectHandler(null);
4587 }
@@ -4030,7 +4590,8 @@ function createMeshCore(agent) {
4590 function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
4591 function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
4592
4033 - function onWebSocketUpgrade(response, s, head) {
4593 + function onWebSocketUpgrade(response, s, head)
4594 + {
4595 sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
4596 this.s = s;
4597 s.httprequest = this;
@@ -4044,9 +4605,11 @@ function createMeshCore(agent) {
4605 //
4606 // Startup for Duktape only. This file is not intended to run in NodeJS.
4607 //
4047 -try {
4608 +try
4609 +{
4610 mainMeshCore = createMeshCore();
4611 mainMeshCore.start(null);
4050 -} catch (e) {
4612 +} catch (e)
4613 +{
4614 require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
4615 }
agents/recoverycore.js
+28 -48
@@ -49,47 +49,48 @@ function sendAgentMessage(msg, icon)
49 require('MeshAgent').SendCommand({ action: 'sessions', type: 'msg', value: sendAgentMessage.messages });
50 }
51
52 +// Start a JavaScript based Agent Self-Update
53 function agentUpdate_Start(updateurl, updateoptions)
54 {
54 - var sessionid = updateoptions != null ? updateoptions.session : null;
55 + // If this value is null
56 + var sessionid = updateoptions != null ? updateoptions.session : null; // If this is null, messages will be broadcast. Otherwise they will be unicasted
57
58 if (this._selfupdate != null)
59 {
60 + // We were already called, so we will ignore this duplicate request
61 if (sessionid != null) { sendConsoleText('Self update already in progress...', sessionid); }
62 }
63 else
64 {
65 if (require('MeshAgent').ARCHID == null && updateurl == null)
66 {
64 - if (sessionid != null) { sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid); }
67 + // This agent doesn't have the ability to tell us which ARCHID it is, so we don't know which agent to pull
68 + sendConsoleText('Unable to initiate update, agent ARCHID is not defined', sessionid);
69 }
70 else
71 {
68 - var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop();
72 + var agentfilename = process.execPath.split(process.platform == 'win32' ? '\\' : '/').pop(); // Local File Name, ie: MeshAgent.exe
73 var name = require('MeshAgent').serviceName;
70 - if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; }
74 + if (name == null) { name = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'; } // This is an older agent that doesn't expose the service name, so use the default
75 try
76 {
77 var s = require('service-manager').manager.getService(name);
78 if (!s.isMe())
79 {
80 if (process.platform == 'win32') { s.close(); }
77 - if (sessionid != null) { sendConsoleText('Service check FAILED', sessionid); }
81 + sendConsoleText('Self Update cannot continue, this agent is not an instance of (' + name + ')', sessionid);
82 return;
83 }
84 if (process.platform == 'win32') { s.close(); }
85 }
86 catch (zz)
87 {
84 - if (sessionid != null) { sendConsoleText('Service check FAILED', sessionid); }
85 - else
86 - {
87 - sendAgentMessage('Self Update Failed, because this agent is not running as a service', 3);
88 - }
88 + sendConsoleText('Self Update Failed because this agent is not an instance of (' + name + ')', sessionid);
89 + sendAgentMessage('Self Update Failed because this agent is not an instance of (' + name + ')', 3);
90 return;
91 }
92
92 - if (sessionid != null) { sendConsoleText('Downloading update...', sessionid); }
93 + sendConsoleText('Downloading update...', sessionid);
94 var options = require('http').parseUri(updateurl != null ? updateurl : require('MeshAgent').ServerUrl);
95 options.protocol = 'https:';
96 if (updateurl == null) { options.path = ('/meshagents?id=' + require('MeshAgent').ARCHID); }
@@ -103,26 +104,14 @@ function agentUpdate_Start(updateurl, updateoptions)
104 // Check that the certificate is the one expected by the server, fail if not.
105 if (checkServerIdentity.servertlshash == null)
106 {
106 - if(sessionid!=null)
107 - {
108 - sendConsoleText('Self Update failed, because the url cannot be verified', sessionid);
109 - }
110 - else
111 - {
112 - sendAgentMessage('Self Update failed, because the url cannot be verified', 3);
113 - }
107 + sendConsoleText('Self Update failed, because the url cannot be verified', sessionid);
108 + sendAgentMessage('Self Update failed, because the url cannot be verified', 3);
109 throw new Error('BadCert');
110 }
111 if ((checkServerIdentity.servertlshash != null) && (checkServerIdentity.servertlshash.toLowerCase() != certs[0].digest.split(':').join('').toLowerCase()))
112 {
118 - if (sessionid != null)
119 - {
120 - sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
121 - }
122 - else
123 - {
124 - sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
125 - }
113 + sendConsoleText('Self Update failed, because the supplied certificate does not match', sessionid);
114 + sendAgentMessage('Self Update failed, because the supplied certificate does not match', 3);
115 throw new Error('BadCert')
116 }
117 }
@@ -130,11 +119,8 @@ function agentUpdate_Start(updateurl, updateoptions)
119 this._selfupdate = require('https').get(options);
120 this._selfupdate.on('error', function (e)
121 {
133 - if (sessionid != null) { sendConsoleText('Error fetching update', sessionid); }
134 - else
135 - {
136 - sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
137 - }
122 + sendConsoleText('Self Update failed, because there was a problem trying to download the update', sessionid);
123 + sendAgentMessage('Self Update failed, because there was a problem trying to download the update', 3);
124 });
125 this._selfupdate.on('response', function (img)
126 {
@@ -146,26 +132,24 @@ function agentUpdate_Start(updateurl, updateoptions)
132 {
133 if (updateoptions.hash.toLowerCase() == h.toString('hex').toLowerCase())
134 {
149 - if (sessionid != null) { sendConsoleText('Download complete. HASH verified.', sessionid); }
135 + sendConsoleText('Download complete. HASH verified.', sessionid);
136 }
137 else
138 {
153 - if (sessionid != null) { sendConsoleText('Download complete. HASH FAILED.', sessionid); }
154 - else
155 - {
156 - sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3);
157 - }
139 + sendConsoleText('Self Update FAILED because the downloaded agent FAILED hash check', sessionid);
140 + sendAgentMessage('Self Update FAILED because the downloaded agent FAILED hash check', 3);
141 return;
142 }
143 }
144 else
145 {
163 - if (sessionid != null) { sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid); }
146 + sendConsoleText('Download complete. HASH=' + h.toString('hex'), sessionid);
147 }
148
166 - if (sessionid != null) { sendConsoleText('Updating and restarting agent...', sessionid); }
149 + sendConsoleText('Updating and restarting agent...', sessionid);
150 if (process.platform == 'win32')
151 {
152 + // Use _wexecve() equivalent to perform the update
153 this.child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe',
154 ['/C wmic service "' + name + '" call stopservice && copy "' + process.cwd() + agentfilename + '.update" "' + process.execPath + '" && wmic service "' + name + '" call startservice && erase "' + process.cwd() + agentfilename + '.update"'], { type: 4 | 0x8000 });
155 }
@@ -185,7 +169,7 @@ function agentUpdate_Start(updateurl, updateoptions)
169 m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP | require('fs').CHMOD_MODES.S_IXOTH);
170 require('fs').chmodSync(process.execPath, m);
171
188 - if (sessionid != null) { sendConsoleText('Restarting service...', sessionid); }
172 + sendConsoleText('Restarting service...', sessionid);
173 try
174 {
175 // restart service
@@ -194,11 +178,8 @@ function agentUpdate_Start(updateurl, updateoptions)
178 }
179 catch (zz)
180 {
197 - if (sessionid != null) { sendConsoleText('Error restarting service', sessionid); }
198 - else
199 - {
200 - sendAgentMessage('Self Update encountered an error trying to restart service', 3);
201 - }
181 + sendConsoleText('Self Update encountered an error trying to restart service', sessionid);
182 + sendAgentMessage('Self Update encountered an error trying to restart service', 3);
183 }
184 }
185 });
@@ -209,7 +190,6 @@ function agentUpdate_Start(updateurl, updateoptions)
190 }
191 }
192
212 -
193 // Return p number of spaces
194 function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
195
@@ -643,7 +623,7 @@ function processConsoleCommand(cmd, args, rights, sessionid) {
623 var response = null;
624 switch (cmd) {
625 case 'help':
646 - response = "Available commands are: osinfo, dbkeys, dbget, dbset, dbcompact, netinfo, versions, agentupdate.";
626 + response = "Available commands are: agentupdate, dbkeys, dbget, dbset, dbcompact, netinfo, osinfo, versions.";
627 break;
628 case 'versions':
629 response = JSON.stringify(process.versions, null, ' ');