diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj
index 8639af10..e1c50f24 100644
--- a/MeshCentralServer.njsproj
+++ b/MeshCentralServer.njsproj
@@ -117,6 +117,7 @@
+
diff --git a/agents/meshcore.js b/agents/meshcore.js
index 6de857a6..8f177891 100644
--- a/agents/meshcore.js
+++ b/agents/meshcore.js
@@ -37,12 +37,10 @@ var MESHRIGHT_NOFILES = 1024;
var MESHRIGHT_NOAMT = 2048;
var MESHRIGHT_LIMITEDINPUT = 4096;
-function createMeshCore(agent)
-{
+function createMeshCore(agent) {
var obj = {};
- if (process.platform == 'darwin' && !process.versions)
- {
+ if (process.platform == 'darwin' && !process.versions) {
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
@@ -51,21 +49,18 @@ function createMeshCore(agent)
child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"\"); if(split(d[1], truval, \"\")>1) { split(truval[1], kn1, \"\"); split(kn1[2], kn2, \"\"); print kn2[1]; } }");
child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
child.waitExit();
- if (child.stdout.str.trim() == 'Crashed')
- {
+ if (child.stdout.str.trim() == 'Crashed') {
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
child.waitExit();
- if (parseInt(child.stdout.str.trim()) == process.pid)
- {
+ if (parseInt(child.stdout.str.trim()) == process.pid) {
// The currently running MeshAgent is us, so we can continue with the update
var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
var tokens = plist.split('KeepAlive');
- if (tokens[1].split('>')[0].split('<')[1] == 'dict')
- {
+ if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
var tmp = tokens[1].split('');
tmp.shift();
tokens[1] = '\n ' + tmp.join('');
@@ -125,10 +120,8 @@ function createMeshCore(agent)
if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
- obj.DAIPC.on('connection', function (c)
- {
- c._send = function (j)
- {
+ obj.DAIPC.on('connection', function (c) {
+ c._send = function (j) {
var data = JSON.stringify(j);
var packet = Buffer.alloc(data.length + 4);
packet.writeUInt32LE(data.length + 4, 0);
@@ -138,32 +131,26 @@ function createMeshCore(agent)
this._daipc = c;
c.parent = this;
c.on('end', function () { console.log('Connection Closed'); this.parent._daipc = null; });
- c.on('data', function (chunk)
- {
+ c.on('data', function (chunk) {
if (chunk.length < 4) { this.unshift(chunk); return; }
var len = chunk.readUInt32LE(0);
if (len > 8192) { this.parent._daipc = null; this.end(); return; }
if (chunk.length < len) { this.unshift(chunk); return; }
-
+
var data = chunk.slice(4, len);
- try
- {
+ try {
data = JSON.parse(data.toString());
}
- catch(de)
- {
+ catch (de) {
this.parent._daipc = null; this.end(); return;
}
-
+
if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
- try
- {
- switch(data.cmd)
- {
+ try {
+ switch (data.cmd) {
case 'query':
- switch(data.value)
- {
+ switch (data.value) {
case 'connection':
data.result = require('MeshAgent').ConnectedServer;
this._send(data);
@@ -176,32 +163,26 @@ function createMeshCore(agent)
return;
}
}
- catch(xe)
- {
+ catch (xe) {
this.parent._daipc = null; this.end(); return;
}
});
});
- function diagnosticAgent_uninstall()
- {
+ function diagnosticAgent_uninstall() {
require('service-manager').manager.uninstallService('meshagentDiagnostic');
require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
};
- function diagnosticAgent_installCheck(install)
- {
- try
- {
+ function diagnosticAgent_installCheck(install) {
+ try {
var diag = require('service-manager').manager.getService('meshagentDiagnostic');
return (diag);
}
- catch (e)
- {
+ catch (e) {
}
if (!install) { return (null); }
var svc = null;
- try
- {
+ try {
require('service-manager').manager.installService(
{
name: 'meshagentDiagnostic',
@@ -213,8 +194,7 @@ function createMeshCore(agent)
});
svc = require('service-manager').manager.getService('meshagentDiagnostic');
}
- catch (e)
- {
+ catch (e) {
return (null);
}
var proxyConfig = require('global-tunnel').proxyConfig;
@@ -227,12 +207,10 @@ function createMeshCore(agent)
ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
- if (proxyConfig)
- {
+ if (proxyConfig) {
ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
}
- else
- {
+ else {
ddb.Put('ignoreProxyFile', '1');
}
@@ -242,7 +220,7 @@ function createMeshCore(agent)
delete ddb;
// Set a recurrent task, to run the Diagnostic Agent every 2 days
- require('task-scheduler').create({name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic'});
+ require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
return (svc);
@@ -281,7 +259,7 @@ function createMeshCore(agent)
}
obj.borderManager = new borderController();
*/
-
+
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
var meshCoreObj = { "action": "coreinfo", "value": "MeshCore v6", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
@@ -397,7 +375,7 @@ function createMeshCore(agent)
});
}
} catch (ex) { sendConsoleText("ex1: " + ex); }
-
+
// Try to load up the WIFI scanner
try {
var wifiScannerLib = require('wifi-scanner');
@@ -436,7 +414,7 @@ function createMeshCore(agent)
}
catch (e) { return false; }
}
-
+
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
function clearGatewayMac(str) {
if (str == null) return null;
@@ -444,7 +422,7 @@ function createMeshCore(agent)
for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
return JSON.stringify(x);
}
-
+
function getIpLocationData(func) {
// Get the location information for the cache if possible
var publicLocationInfo = db.Get('publicLocationInfo');
@@ -483,7 +461,7 @@ function createMeshCore(agent)
}
}
}
-
+
// Polyfill String.endsWith
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
@@ -494,7 +472,7 @@ function createMeshCore(agent)
return lastIndex !== -1 && lastIndex === position;
};
}
-
+
// Polyfill path.join
obj.path = {
join: function () {
@@ -513,19 +491,19 @@ function createMeshCore(agent)
return x.join('/');
}
};
-
+
// Replace a string with a number if the string is an exact number
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
-
+
// Convert decimal to hex
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
-
+
// Convert a raw string to a hex string
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
-
+
// Convert a buffer into a string
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
-
+
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
function hex2rstr(d) {
if (typeof d != "string" || d.length == 0) return '';
@@ -533,7 +511,7 @@ function createMeshCore(agent)
while (t = m.shift()) r += String.fromCharCode('0x' + t);
return r
}
-
+
// Convert an object to string with all functions
function objToString(x, p, pad, ret) {
if (ret == undefined) ret = '';
@@ -548,17 +526,17 @@ function createMeshCore(agent)
for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
return r + addPad(p, pad) + '}';
}
-
+
// Return p number of spaces
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
-
+
// Split a string taking into account the quoats. Used for command line parsing
function splitArgs(str) {
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
return myArray;
}
-
+
// Parse arguments string array into an object
function parseArgs(argv) {
var results = { '_': [] }, current = null;
@@ -574,7 +552,7 @@ function createMeshCore(agent)
if (current != null) { results[current] = true; }
return results;
}
-
+
// Get server target url with a custom path
function getServerTargetUrl(path) {
var x = mesh.ServerUrl;
@@ -585,13 +563,13 @@ function createMeshCore(agent)
if (x == null) return null;
return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
}
-
+
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
function getServerTargetUrlEx(url) {
if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
return url;
}
-
+
// Send a wake-on-lan packet
function sendWakeOnLan(hexMac) {
var count = 0;
@@ -600,7 +578,7 @@ function createMeshCore(agent)
var magic = 'FFFFFFFFFFFF';
for (var x = 1; x <= 16; ++x) { magic += hexMac; }
var magicbin = Buffer.from(magic, 'hex');
-
+
for (var adapter in interfaces) {
if (interfaces.hasOwnProperty(adapter)) {
for (var i = 0; i < interfaces[adapter].length; ++i) {
@@ -618,7 +596,7 @@ function createMeshCore(agent)
} catch (e) { }
return count;
}
-
+
// Handle a mesh agent command
function handleServerCommand(data) {
if (typeof data == 'object') {
@@ -722,7 +700,7 @@ function createMeshCore(agent)
// Open a local web browser and return success/fail
MeshServerLog('Opening: ' + data.url, data);
sendConsoleText('OpenURL: ' + data.url);
- if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
+ if (data.url) { mesh.SendCommand({ "action": "msg", "type": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
break;
}
case 'getclip': {
@@ -831,13 +809,25 @@ function createMeshCore(agent)
}
case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
case 'pong': { break; }
+ case 'plugin': {
+ if (typeof data.pluginaction == 'string') {
+ try {
+ MeshServerLog('Plugin called', data);
+ // Not yet implemented
+ // require(data.plugin.name).serveraction(data);
+ } catch (e) {
+ MeshServerLog('Error calling plugin', data);
+ }
+ }
+ break;
+ }
default:
// Unknown action, ignore it.
break;
}
}
}
-
+
// Called when a file changed in the file system
/*
function onFileWatcher(a, b) {
@@ -889,8 +879,8 @@ function createMeshCore(agent)
pr.then(defragResult, defragResult);
} else {
*/
- results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
- func(results);
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
+ func(results);
//}
} catch (ex) { func(null, ex); }
}
@@ -934,7 +924,7 @@ function createMeshCore(agent)
}
return response;
}
-
+
// Tunnel callback operations
function onTunnelUpgrade(response, s, head) {
this.s = s;
@@ -990,7 +980,7 @@ function createMeshCore(agent)
peerTunnel.s.first = true;
peerTunnel.s.resume();
}
-
+
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onTcpRelayServerTunnelData(data) {
if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
@@ -1000,7 +990,7 @@ function createMeshCore(agent)
if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
delete tunnels[this.httprequest.index];
-
+
/*
// Close the watcher if required
if (this.httprequest.watcher != undefined) {
@@ -1032,7 +1022,7 @@ function createMeshCore(agent)
function onTunnelData(data) {
//console.log("OnTunnelData");
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
-
+
// If this is upload data, save it to file
if (this.httprequest.uploadFile) {
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
@@ -1069,17 +1059,14 @@ function createMeshCore(agent)
return;
}
- this.end = function ()
- {
- if (process.platform == "win32")
- {
+ this.end = function () {
+ if (process.platform == "win32") {
// Unpipe the web socket
this.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
- if (this.rtcchannel)
- {
+ if (this.rtcchannel) {
this.rtcchannel.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
}
@@ -1087,27 +1074,21 @@ function createMeshCore(agent)
// Clean up
if (this.httprequest._term) { this.httprequest._term.end(); }
this.httprequest._term = null;
- } else
- {
+ } else {
// TODO!!
}
};
// Remote terminal using native pipes
- if (process.platform == "win32")
- {
- try
- {
- if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true))
- {
+ if (process.platform == "win32") {
+ try {
+ if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
- } else
- {
+ } else {
this.httprequest._term = require('win-terminal').Start(80, 25);
}
}
- catch(e)
- {
+ catch (e) {
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
this.end();
@@ -1116,14 +1097,11 @@ function createMeshCore(agent)
this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
- } else
- {
- if (fs.existsSync("/bin/bash"))
- {
+ } else {
+ if (fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
- } else
- {
+ } else {
this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); }
}
@@ -1176,8 +1154,7 @@ function createMeshCore(agent)
this.removeAllListeners('data');
this.on('data', onTunnelControlData);
//this.write('MeshCore Terminal Hello');
- } else if (this.httprequest.protocol == 2)
- {
+ } else if (this.httprequest.protocol == 2) {
// Check user access rights for desktop
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
// Disengage this tunnel, user does not have the rights to do this!!
@@ -1209,7 +1186,7 @@ function createMeshCore(agent)
if (this.desktop.kvm.connectionCount == 0) {
// Display a toast message. This may not be supported on all platforms.
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
-
+
this.httprequest.desktop.kvm.end();
}
};
@@ -1225,8 +1202,7 @@ function createMeshCore(agent)
}
// Perform notification if needed. Toast messages may not be supported on all platforms.
- if (this.httprequest.consent && (this.httprequest.consent & 8))
- {
+ if (this.httprequest.consent && (this.httprequest.consent & 8)) {
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
@@ -1235,8 +1211,7 @@ function createMeshCore(agent)
this.pause();
pr.then(
- function ()
- {
+ function () {
// Success
MeshServerLog('Starting remote desktop after local user accepted (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
@@ -1247,15 +1222,13 @@ function createMeshCore(agent)
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
this.ws.resume();
},
- function (e)
- {
+ function (e) {
// User Consent Denied/Failed
MeshServerLog('Failed to start remote desktop after local user rejected (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
});
}
- else
- {
+ else {
// User Consent Prompt is not required
if (this.httprequest.consent && (this.httprequest.consent & 1)) {
// User Notifications is required
@@ -1283,8 +1256,7 @@ function createMeshCore(agent)
}
// Perform notification if needed. Toast messages may not be supported on all platforms.
- if (this.httprequest.consent && (this.httprequest.consent & 32))
- {
+ if (this.httprequest.consent && (this.httprequest.consent & 32)) {
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
@@ -1361,7 +1333,7 @@ function createMeshCore(agent)
var response = getDirectoryInfo(cmd.path);
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
this.write(new Buffer(JSON.stringify(response)));
-
+
/*
// Start the directory watcher
if ((cmd.path != '') && (samepath == false)) {
@@ -1479,6 +1451,23 @@ function createMeshCore(agent)
// Unknown action, ignore it.
break;
}
+ } else if (this.httprequest.protocol == 7) { // plugin data exchange
+ var cmd = null;
+ try { cmd = JSON.parse(data); } catch (e) { };
+ if (cmd == null) { return; }
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
+ if (cmd.action == undefined) return;
+
+ switch (cmd.action) {
+ case 'plugin': {
+ try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
+ break;
+ }
+ default: {
+ // probably shouldn't happen, but just in case this feature is expanded
+ }
+ }
+
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
@@ -1553,12 +1542,10 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
if (ws.httprequest.protocol == 1) { // Terminal
// 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
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.httprequest._term.unpipe(ws);
}
- else
- {
+ else {
ws.httprequest.process.stdout.unpipe(ws);
ws.httprequest.process.stderr.unpipe(ws);
}
@@ -1575,13 +1562,11 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc1') {
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
// Switch the user input from websocket to webrtc at this point.
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.unpipe(ws.httprequest._term);
ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
- else
- {
+ else {
ws.unpipe(ws.httprequest.process.stdin);
ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
@@ -1596,12 +1581,10 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc2') {
// Other side received websocket end of data marker, start sending data on WebRTC channel
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
}
- else
- {
+ else {
ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
}
@@ -1641,7 +1624,7 @@ function createMeshCore(agent)
// Console state
var consoleWebSockets = {};
var consoleHttpRequest = null;
-
+
// Console HTTP response
function consoleHttpResponse(response) {
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
@@ -1684,7 +1667,7 @@ function createMeshCore(agent)
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
- var fin = '', f = '', availcommands = 'help,info,osinfo,args,print,type,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt';
+ var fin = '', f = '', availcommands = 'help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt';
availcommands = availcommands.split(',').sort();
while (availcommands.length > 0) {
if (f.length > 100) { fin += (f + ',\r\n'); f = ''; }
@@ -1802,7 +1785,7 @@ function createMeshCore(agent)
case 'ps': {
processManager.getProcesses(function (plist) {
var x = '';
- for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
+ for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user) : '') + '\r\n'; }
sendConsoleText(x, sessionid);
});
break;
@@ -1836,14 +1819,11 @@ function createMeshCore(agent)
break;
}
case 'dump':
- if (args['_'].length < 1)
- {
+ if (args['_'].length < 1) {
response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
}
- else
- {
- switch(args['_'][0].toLowerCase())
- {
+ else {
+ switch (args['_'][0].toLowerCase()) {
case 'on':
process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
response = 'enabled';
@@ -1853,12 +1833,10 @@ function createMeshCore(agent)
response = 'disabled';
break;
case 'status':
- if (process.coreDumpLocation)
- {
+ if (process.coreDumpLocation) {
response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
}
- else
- {
+ else {
response = 'Core Dump: [DISABLED]';
}
break;
@@ -1878,18 +1856,14 @@ function createMeshCore(agent)
}
case 'uninstallagent':
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
- if (!require('service-manager').manager.getService(agentName).isMe())
- {
+ if (!require('service-manager').manager.getService(agentName).isMe()) {
response = 'Uininstall failed, this instance is not the service instance';
}
- else
- {
- try
- {
+ else {
+ try {
diagnosticAgent_uninstall();
}
- catch(x)
- {
+ catch (x) {
}
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
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 });
@@ -2068,7 +2042,7 @@ function createMeshCore(agent)
if (httprequest != null) {
httprequest.upgrade = onWebSocketUpgrade;
httprequest.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
-
+
var index = 1;
while (consoleWebSockets[index]) { index++; }
httprequest.sessionid = sessionid;
@@ -2256,40 +2230,31 @@ function createMeshCore(agent)
}
case 'diagnostic':
{
- if (!mesh.DAIPC.listening)
- {
+ if (!mesh.DAIPC.listening) {
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
break;
}
var diag = diagnosticAgent_installCheck();
- if (diag)
- {
- if (args['_'].length == 1 && args['_'][0] == 'uninstall')
- {
+ if (diag) {
+ if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
diagnosticAgent_uninstall();
response = 'Diagnostic Agent uninstalled';
}
- else
- {
+ else {
response = 'Diagnostic Agent installed at: ' + diag.appLocation();
}
}
- else
- {
- if (args['_'].length == 1 && args['_'][0] == 'install')
- {
+ else {
+ if (args['_'].length == 1 && args['_'][0] == 'install') {
diag = diagnosticAgent_installCheck(true);
- if (diag)
- {
+ if (diag) {
response = 'Diagnostic agent was installed at: ' + diag.appLocation();
}
- else
- {
+ else {
response = 'Diagnostic agent installation failed';
}
}
- else
- {
+ else {
response = 'Diagnostic Agent Not installed. To install: diagnostic install';
}
}
@@ -2297,27 +2262,27 @@ function createMeshCore(agent)
break;
}
case 'apf': {
- if (meshCoreObj.intelamt!==null) {
+ if (meshCoreObj.intelamt !== null) {
if (args['_'].length == 1) {
if (args['_'][0] == 'on') {
response = 'Starting APF tunnel'
var apfarg = {
- mpsurl: mesh.ServerUrl.replace('agent.ashx','apf.ashx'),
- mpsuser: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
- mpspass: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpskeepalive: 60000,
clientname: require('os').hostname(),
clientaddress: '127.0.0.1',
clientuuid: meshCoreObj.intelamt.uuid
};
var tobj = { debug: false }; //
- apftunnel= require('apfclient')(tobj,apfarg);
+ apftunnel = require('apfclient')(tobj, apfarg);
try {
apftunnel.connect();
response += "..success";
} catch (e) {
response += JSON.stringify(e);
- }
+ }
} else if (args['_'][0] == 'off') {
response = 'Stopping APF tunnel';
try {
@@ -2326,19 +2291,34 @@ function createMeshCore(agent)
} catch (e) {
response += JSON.stringify(e);
}
- apftunnel=null;
+ apftunnel = null;
} else {
response = 'Invalid command.\r\nCmd syntax: apf on|off';
}
- } else {
- response = 'APF tunnel is '+ (apftunnel == null ? 'off': 'on' );
+ } else {
+ response = 'APF tunnel is ' + (apftunnel == null ? 'off' : 'on');
}
-
+
} else {
response = 'APF tunnel requires Intel AMT';
}
break;
}
+ case 'plugin': {
+ if (typeof args['_'][0] == 'string') {
+ try {
+ // Pass off the action to the plugin
+ // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
+ // to control the output / actions here.
+ response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
+ } catch (e) {
+ response = 'There was an error in the plugin (' + e + ')';
+ }
+ } else {
+ response = 'Proper usage: plugin [pluginName] [args].';
+ }
+ break;
+ }
default: { // This is an unknown command, return an error message
response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
break;
@@ -2347,7 +2327,7 @@ function createMeshCore(agent)
} catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
if (response != null) { sendConsoleText(response, sessionid); }
}
-
+
// Send a mesh agent console command
function sendConsoleText(text, sessionid) {
if (typeof text == 'object') { text = JSON.stringify(text); }
@@ -2356,7 +2336,7 @@ function createMeshCore(agent)
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
-
+
// Called when the server connection state changes
function handleServerConnection(state) {
meshServerConnectionState = state;
@@ -2381,13 +2361,13 @@ function createMeshCore(agent)
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
}
}
-
+
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null;
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
function sendNetworkUpdate(force) {
sendNetworkUpdateNagleTimer = null;
-
+
// Update the network interfaces information data
var netInfo = mesh.NetInfo;
if (netInfo) {
@@ -2396,7 +2376,7 @@ function createMeshCore(agent)
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
}
}
-
+
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate(flags) {
if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
@@ -2451,8 +2431,10 @@ function createMeshCore(agent)
//if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
}
+
+ // TODO: add plugin hook here
}
-
+
// Starting function
obj.start = function () {
@@ -2485,16 +2467,16 @@ function createMeshCore(agent)
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
} catch (ex) { }
}
-
+
obj.stop = function () {
mesh.AddCommandHandler(null);
mesh.AddConnectHandler(null);
}
-
+
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
-
+
function onWebSocketUpgrade(response, s, head) {
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
this.s = s;
diff --git a/agents/meshcore.min.js b/agents/meshcore.min.js
index 21b632ca..8f177891 100644
--- a/agents/meshcore.min.js
+++ b/agents/meshcore.min.js
@@ -37,12 +37,10 @@ var MESHRIGHT_NOFILES = 1024;
var MESHRIGHT_NOAMT = 2048;
var MESHRIGHT_LIMITEDINPUT = 4096;
-function createMeshCore(agent)
-{
+function createMeshCore(agent) {
var obj = {};
- if (process.platform == 'darwin' && !process.versions)
- {
+ if (process.platform == 'darwin' && !process.versions) {
// This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
var child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
@@ -51,21 +49,18 @@ function createMeshCore(agent)
child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"\"); if(split(d[1], truval, \"\")>1) { split(truval[1], kn1, \"\"); split(kn1[2], kn2, \"\"); print kn2[1]; } }");
child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
child.waitExit();
- if (child.stdout.str.trim() == 'Crashed')
- {
+ if (child.stdout.str.trim() == 'Crashed') {
child = require('child_process').execFile('/bin/sh', ['sh']);
child.stdout.str = '';
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
child.waitExit();
- if (parseInt(child.stdout.str.trim()) == process.pid)
- {
+ if (parseInt(child.stdout.str.trim()) == process.pid) {
// The currently running MeshAgent is us, so we can continue with the update
var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
var tokens = plist.split('KeepAlive');
- if (tokens[1].split('>')[0].split('<')[1] == 'dict')
- {
+ if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
var tmp = tokens[1].split('');
tmp.shift();
tokens[1] = '\n ' + tmp.join('');
@@ -125,10 +120,8 @@ function createMeshCore(agent)
if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
- obj.DAIPC.on('connection', function (c)
- {
- c._send = function (j)
- {
+ obj.DAIPC.on('connection', function (c) {
+ c._send = function (j) {
var data = JSON.stringify(j);
var packet = Buffer.alloc(data.length + 4);
packet.writeUInt32LE(data.length + 4, 0);
@@ -138,32 +131,26 @@ function createMeshCore(agent)
this._daipc = c;
c.parent = this;
c.on('end', function () { console.log('Connection Closed'); this.parent._daipc = null; });
- c.on('data', function (chunk)
- {
+ c.on('data', function (chunk) {
if (chunk.length < 4) { this.unshift(chunk); return; }
var len = chunk.readUInt32LE(0);
if (len > 8192) { this.parent._daipc = null; this.end(); return; }
if (chunk.length < len) { this.unshift(chunk); return; }
-
+
var data = chunk.slice(4, len);
- try
- {
+ try {
data = JSON.parse(data.toString());
}
- catch(de)
- {
+ catch (de) {
this.parent._daipc = null; this.end(); return;
}
-
+
if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
- try
- {
- switch(data.cmd)
- {
+ try {
+ switch (data.cmd) {
case 'query':
- switch(data.value)
- {
+ switch (data.value) {
case 'connection':
data.result = require('MeshAgent').ConnectedServer;
this._send(data);
@@ -176,32 +163,26 @@ function createMeshCore(agent)
return;
}
}
- catch(xe)
- {
+ catch (xe) {
this.parent._daipc = null; this.end(); return;
}
});
});
- function diagnosticAgent_uninstall()
- {
+ function diagnosticAgent_uninstall() {
require('service-manager').manager.uninstallService('meshagentDiagnostic');
require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
};
- function diagnosticAgent_installCheck(install)
- {
- try
- {
+ function diagnosticAgent_installCheck(install) {
+ try {
var diag = require('service-manager').manager.getService('meshagentDiagnostic');
return (diag);
}
- catch (e)
- {
+ catch (e) {
}
if (!install) { return (null); }
var svc = null;
- try
- {
+ try {
require('service-manager').manager.installService(
{
name: 'meshagentDiagnostic',
@@ -213,8 +194,7 @@ function createMeshCore(agent)
});
svc = require('service-manager').manager.getService('meshagentDiagnostic');
}
- catch (e)
- {
+ catch (e) {
return (null);
}
var proxyConfig = require('global-tunnel').proxyConfig;
@@ -227,12 +207,10 @@ function createMeshCore(agent)
ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
- if (proxyConfig)
- {
+ if (proxyConfig) {
ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
}
- else
- {
+ else {
ddb.Put('ignoreProxyFile', '1');
}
@@ -242,7 +220,7 @@ function createMeshCore(agent)
delete ddb;
// Set a recurrent task, to run the Diagnostic Agent every 2 days
- require('task-scheduler').create({name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic'});
+ require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
//require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
return (svc);
@@ -281,7 +259,7 @@ function createMeshCore(agent)
}
obj.borderManager = new borderController();
*/
-
+
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
var meshCoreObj = { "action": "coreinfo", "value": "MeshCore v6", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
@@ -397,7 +375,7 @@ function createMeshCore(agent)
});
}
} catch (ex) { sendConsoleText("ex1: " + ex); }
-
+
// Try to load up the WIFI scanner
try {
var wifiScannerLib = require('wifi-scanner');
@@ -436,7 +414,7 @@ function createMeshCore(agent)
}
catch (e) { return false; }
}
-
+
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
function clearGatewayMac(str) {
if (str == null) return null;
@@ -444,7 +422,7 @@ function createMeshCore(agent)
for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
return JSON.stringify(x);
}
-
+
function getIpLocationData(func) {
// Get the location information for the cache if possible
var publicLocationInfo = db.Get('publicLocationInfo');
@@ -483,7 +461,7 @@ function createMeshCore(agent)
}
}
}
-
+
// Polyfill String.endsWith
if (!String.prototype.endsWith) {
String.prototype.endsWith = function (searchString, position) {
@@ -494,7 +472,7 @@ function createMeshCore(agent)
return lastIndex !== -1 && lastIndex === position;
};
}
-
+
// Polyfill path.join
obj.path = {
join: function () {
@@ -513,19 +491,19 @@ function createMeshCore(agent)
return x.join('/');
}
};
-
+
// Replace a string with a number if the string is an exact number
function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
-
+
// Convert decimal to hex
function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
-
+
// Convert a raw string to a hex string
function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
-
+
// Convert a buffer into a string
function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
-
+
// Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
function hex2rstr(d) {
if (typeof d != "string" || d.length == 0) return '';
@@ -533,7 +511,7 @@ function createMeshCore(agent)
while (t = m.shift()) r += String.fromCharCode('0x' + t);
return r
}
-
+
// Convert an object to string with all functions
function objToString(x, p, pad, ret) {
if (ret == undefined) ret = '';
@@ -548,17 +526,17 @@ function createMeshCore(agent)
for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
return r + addPad(p, pad) + '}';
}
-
+
// Return p number of spaces
function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
-
+
// Split a string taking into account the quoats. Used for command line parsing
function splitArgs(str) {
var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
return myArray;
}
-
+
// Parse arguments string array into an object
function parseArgs(argv) {
var results = { '_': [] }, current = null;
@@ -574,7 +552,7 @@ function createMeshCore(agent)
if (current != null) { results[current] = true; }
return results;
}
-
+
// Get server target url with a custom path
function getServerTargetUrl(path) {
var x = mesh.ServerUrl;
@@ -585,13 +563,13 @@ function createMeshCore(agent)
if (x == null) return null;
return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
}
-
+
// Get server url. If the url starts with "*/..." change it, it not use the url as is.
function getServerTargetUrlEx(url) {
if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
return url;
}
-
+
// Send a wake-on-lan packet
function sendWakeOnLan(hexMac) {
var count = 0;
@@ -600,7 +578,7 @@ function createMeshCore(agent)
var magic = 'FFFFFFFFFFFF';
for (var x = 1; x <= 16; ++x) { magic += hexMac; }
var magicbin = Buffer.from(magic, 'hex');
-
+
for (var adapter in interfaces) {
if (interfaces.hasOwnProperty(adapter)) {
for (var i = 0; i < interfaces[adapter].length; ++i) {
@@ -618,7 +596,7 @@ function createMeshCore(agent)
} catch (e) { }
return count;
}
-
+
// Handle a mesh agent command
function handleServerCommand(data) {
if (typeof data == 'object') {
@@ -722,7 +700,7 @@ function createMeshCore(agent)
// Open a local web browser and return success/fail
MeshServerLog('Opening: ' + data.url, data);
sendConsoleText('OpenURL: ' + data.url);
- if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
+ if (data.url) { mesh.SendCommand({ "action": "msg", "type": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
break;
}
case 'getclip': {
@@ -834,11 +812,10 @@ function createMeshCore(agent)
case 'plugin': {
if (typeof data.pluginaction == 'string') {
try {
-
MeshServerLog('Plugin called', data);
- /* Not yet implmented
- require(data.plugin.name).serveraction(data);*/
- } catch(e) {
+ // Not yet implemented
+ // require(data.plugin.name).serveraction(data);
+ } catch (e) {
MeshServerLog('Error calling plugin', data);
}
}
@@ -850,7 +827,7 @@ function createMeshCore(agent)
}
}
}
-
+
// Called when a file changed in the file system
/*
function onFileWatcher(a, b) {
@@ -902,8 +879,8 @@ function createMeshCore(agent)
pr.then(defragResult, defragResult);
} else {
*/
- results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
- func(results);
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
+ func(results);
//}
} catch (ex) { func(null, ex); }
}
@@ -947,7 +924,7 @@ function createMeshCore(agent)
}
return response;
}
-
+
// Tunnel callback operations
function onTunnelUpgrade(response, s, head) {
this.s = s;
@@ -1003,7 +980,7 @@ function createMeshCore(agent)
peerTunnel.s.first = true;
peerTunnel.s.resume();
}
-
+
// Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
function onTcpRelayServerTunnelData(data) {
if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
@@ -1013,7 +990,7 @@ function createMeshCore(agent)
if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
//sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
delete tunnels[this.httprequest.index];
-
+
/*
// Close the watcher if required
if (this.httprequest.watcher != undefined) {
@@ -1045,7 +1022,7 @@ function createMeshCore(agent)
function onTunnelData(data) {
//console.log("OnTunnelData");
//sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
-
+
// If this is upload data, save it to file
if (this.httprequest.uploadFile) {
try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
@@ -1082,17 +1059,14 @@ function createMeshCore(agent)
return;
}
- this.end = function ()
- {
- if (process.platform == "win32")
- {
+ this.end = function () {
+ if (process.platform == "win32") {
// Unpipe the web socket
this.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
// Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
- if (this.rtcchannel)
- {
+ if (this.rtcchannel) {
this.rtcchannel.unpipe(this.httprequest._term);
if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
}
@@ -1100,27 +1074,21 @@ function createMeshCore(agent)
// Clean up
if (this.httprequest._term) { this.httprequest._term.end(); }
this.httprequest._term = null;
- } else
- {
+ } else {
// TODO!!
}
};
// Remote terminal using native pipes
- if (process.platform == "win32")
- {
- try
- {
- if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true))
- {
+ if (process.platform == "win32") {
+ try {
+ if ((this.httprequest.protocol == 6) && (require('win-terminal').PowerShellCapable() == true)) {
this.httprequest._term = require('win-terminal').StartPowerShell(80, 25);
- } else
- {
+ } else {
this.httprequest._term = require('win-terminal').Start(80, 25);
}
}
- catch(e)
- {
+ catch (e) {
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
this.end();
@@ -1129,14 +1097,11 @@ function createMeshCore(agent)
this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
- } else
- {
- if (fs.existsSync("/bin/bash"))
- {
+ } else {
+ if (fs.existsSync("/bin/bash")) {
this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
- } else
- {
+ } else {
this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); }
}
@@ -1189,8 +1154,7 @@ function createMeshCore(agent)
this.removeAllListeners('data');
this.on('data', onTunnelControlData);
//this.write('MeshCore Terminal Hello');
- } else if (this.httprequest.protocol == 2)
- {
+ } else if (this.httprequest.protocol == 2) {
// Check user access rights for desktop
if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
// Disengage this tunnel, user does not have the rights to do this!!
@@ -1222,7 +1186,7 @@ function createMeshCore(agent)
if (this.desktop.kvm.connectionCount == 0) {
// Display a toast message. This may not be supported on all platforms.
// try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
-
+
this.httprequest.desktop.kvm.end();
}
};
@@ -1238,8 +1202,7 @@ function createMeshCore(agent)
}
// Perform notification if needed. Toast messages may not be supported on all platforms.
- if (this.httprequest.consent && (this.httprequest.consent & 8))
- {
+ if (this.httprequest.consent && (this.httprequest.consent & 8)) {
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
@@ -1248,8 +1211,7 @@ function createMeshCore(agent)
this.pause();
pr.then(
- function ()
- {
+ function () {
// Success
MeshServerLog('Starting remote desktop after local user accepted (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
@@ -1260,15 +1222,13 @@ function createMeshCore(agent)
this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
this.ws.resume();
},
- function (e)
- {
+ function (e) {
// User Consent Denied/Failed
MeshServerLog('Failed to start remote desktop after local user rejected (' + this.ws.httprequest.remoteaddr + ')', this.ws.httprequest);
this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
});
}
- else
- {
+ else {
// User Consent Prompt is not required
if (this.httprequest.consent && (this.httprequest.consent & 1)) {
// User Notifications is required
@@ -1296,8 +1256,7 @@ function createMeshCore(agent)
}
// Perform notification if needed. Toast messages may not be supported on all platforms.
- if (this.httprequest.consent && (this.httprequest.consent & 32))
- {
+ if (this.httprequest.consent && (this.httprequest.consent & 32)) {
// User Consent Prompt is required
// Send a console message back using the console channel, "\n" is supported.
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
@@ -1374,7 +1333,7 @@ function createMeshCore(agent)
var response = getDirectoryInfo(cmd.path);
if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
this.write(new Buffer(JSON.stringify(response)));
-
+
/*
// Start the directory watcher
if ((cmd.path != '') && (samepath == false)) {
@@ -1493,25 +1452,22 @@ function createMeshCore(agent)
break;
}
} else if (this.httprequest.protocol == 7) { // plugin data exchange
- var cmd = null;
- try { cmd = JSON.parse(data); } catch (e) { };
- if (cmd == null) { return; }
- if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
- if (cmd.action == undefined) { return; }
+ var cmd = null;
+ try { cmd = JSON.parse(data); } catch (e) { };
+ if (cmd == null) { return; }
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
+ if (cmd.action == undefined) return;
+
+ switch (cmd.action) {
+ case 'plugin': {
+ try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
+ break;
+ }
+ default: {
+ // probably shouldn't happen, but just in case this feature is expanded
+ }
+ }
- switch (cmd.action) {
- case 'plugin': {
- try {
- require(cmd.plugin).consoleaction(cmd, null, null, this);
- } catch (e) { throw e; }
-
- break;
- }
- default: {
- // probably shouldn't happen, but just in case this feature is expanded
- }
- }
-
}
//sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
}
@@ -1586,12 +1542,10 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
if (ws.httprequest.protocol == 1) { // Terminal
// 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
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.httprequest._term.unpipe(ws);
}
- else
- {
+ else {
ws.httprequest.process.stdout.unpipe(ws);
ws.httprequest.process.stderr.unpipe(ws);
}
@@ -1608,13 +1562,11 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc1') {
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
// Switch the user input from websocket to webrtc at this point.
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.unpipe(ws.httprequest._term);
ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
- else
- {
+ else {
ws.unpipe(ws.httprequest.process.stdin);
ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
}
@@ -1629,12 +1581,10 @@ function createMeshCore(agent)
} else if (obj.type == 'webrtc2') {
// Other side received websocket end of data marker, start sending data on WebRTC channel
if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
- if (process.platform == 'win32')
- {
+ if (process.platform == 'win32') {
ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
}
- else
- {
+ else {
ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
}
@@ -1674,7 +1624,7 @@ function createMeshCore(agent)
// Console state
var consoleWebSockets = {};
var consoleHttpRequest = null;
-
+
// Console HTTP response
function consoleHttpResponse(response) {
response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
@@ -1835,7 +1785,7 @@ function createMeshCore(agent)
case 'ps': {
processManager.getProcesses(function (plist) {
var x = '';
- for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
+ for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user) : '') + '\r\n'; }
sendConsoleText(x, sessionid);
});
break;
@@ -1869,14 +1819,11 @@ function createMeshCore(agent)
break;
}
case 'dump':
- if (args['_'].length < 1)
- {
+ if (args['_'].length < 1) {
response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
}
- else
- {
- switch(args['_'][0].toLowerCase())
- {
+ else {
+ switch (args['_'][0].toLowerCase()) {
case 'on':
process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
response = 'enabled';
@@ -1886,12 +1833,10 @@ function createMeshCore(agent)
response = 'disabled';
break;
case 'status':
- if (process.coreDumpLocation)
- {
+ if (process.coreDumpLocation) {
response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
}
- else
- {
+ else {
response = 'Core Dump: [DISABLED]';
}
break;
@@ -1911,18 +1856,14 @@ function createMeshCore(agent)
}
case 'uninstallagent':
var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
- if (!require('service-manager').manager.getService(agentName).isMe())
- {
+ if (!require('service-manager').manager.getService(agentName).isMe()) {
response = 'Uininstall failed, this instance is not the service instance';
}
- else
- {
- try
- {
+ else {
+ try {
diagnosticAgent_uninstall();
}
- catch(x)
- {
+ catch (x) {
}
var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
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 });
@@ -2101,7 +2042,7 @@ function createMeshCore(agent)
if (httprequest != null) {
httprequest.upgrade = onWebSocketUpgrade;
httprequest.on('error', function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); });
-
+
var index = 1;
while (consoleWebSockets[index]) { index++; }
httprequest.sessionid = sessionid;
@@ -2289,40 +2230,31 @@ function createMeshCore(agent)
}
case 'diagnostic':
{
- if (!mesh.DAIPC.listening)
- {
+ if (!mesh.DAIPC.listening) {
response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
break;
}
var diag = diagnosticAgent_installCheck();
- if (diag)
- {
- if (args['_'].length == 1 && args['_'][0] == 'uninstall')
- {
+ if (diag) {
+ if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
diagnosticAgent_uninstall();
response = 'Diagnostic Agent uninstalled';
}
- else
- {
+ else {
response = 'Diagnostic Agent installed at: ' + diag.appLocation();
}
}
- else
- {
- if (args['_'].length == 1 && args['_'][0] == 'install')
- {
+ else {
+ if (args['_'].length == 1 && args['_'][0] == 'install') {
diag = diagnosticAgent_installCheck(true);
- if (diag)
- {
+ if (diag) {
response = 'Diagnostic agent was installed at: ' + diag.appLocation();
}
- else
- {
+ else {
response = 'Diagnostic agent installation failed';
}
}
- else
- {
+ else {
response = 'Diagnostic Agent Not installed. To install: diagnostic install';
}
}
@@ -2330,27 +2262,27 @@ function createMeshCore(agent)
break;
}
case 'apf': {
- if (meshCoreObj.intelamt!==null) {
+ if (meshCoreObj.intelamt !== null) {
if (args['_'].length == 1) {
if (args['_'][0] == 'on') {
response = 'Starting APF tunnel'
var apfarg = {
- mpsurl: mesh.ServerUrl.replace('agent.ashx','apf.ashx'),
- mpsuser: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
- mpspass: Buffer.from(mesh.ServerInfo.MeshID,'hex').toString('base64').substring(0,16),
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
mpskeepalive: 60000,
clientname: require('os').hostname(),
clientaddress: '127.0.0.1',
clientuuid: meshCoreObj.intelamt.uuid
};
var tobj = { debug: false }; //
- apftunnel= require('apfclient')(tobj,apfarg);
+ apftunnel = require('apfclient')(tobj, apfarg);
try {
apftunnel.connect();
response += "..success";
} catch (e) {
response += JSON.stringify(e);
- }
+ }
} else if (args['_'][0] == 'off') {
response = 'Stopping APF tunnel';
try {
@@ -2359,14 +2291,14 @@ function createMeshCore(agent)
} catch (e) {
response += JSON.stringify(e);
}
- apftunnel=null;
+ apftunnel = null;
} else {
response = 'Invalid command.\r\nCmd syntax: apf on|off';
}
- } else {
- response = 'APF tunnel is '+ (apftunnel == null ? 'off': 'on' );
+ } else {
+ response = 'APF tunnel is ' + (apftunnel == null ? 'off' : 'on');
}
-
+
} else {
response = 'APF tunnel requires Intel AMT';
}
@@ -2375,17 +2307,16 @@ function createMeshCore(agent)
case 'plugin': {
if (typeof args['_'][0] == 'string') {
try {
- // pass off the action to the plugin
+ // Pass off the action to the plugin
// for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
// to control the output / actions here.
response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
- } catch(e) {
+ } catch (e) {
response = 'There was an error in the plugin (' + e + ')';
}
} else {
- response = 'Proper usage: plugin [pluginName] [args].';
+ response = 'Proper usage: plugin [pluginName] [args].';
}
-
break;
}
default: { // This is an unknown command, return an error message
@@ -2396,7 +2327,7 @@ function createMeshCore(agent)
} catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
if (response != null) { sendConsoleText(response, sessionid); }
}
-
+
// Send a mesh agent console command
function sendConsoleText(text, sessionid) {
if (typeof text == 'object') { text = JSON.stringify(text); }
@@ -2405,7 +2336,7 @@ function createMeshCore(agent)
// Called before the process exits
//process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
-
+
// Called when the server connection state changes
function handleServerConnection(state) {
meshServerConnectionState = state;
@@ -2430,13 +2361,13 @@ function createMeshCore(agent)
if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
}
}
-
+
// Update the server with the latest network interface information
var sendNetworkUpdateNagleTimer = null;
function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
function sendNetworkUpdate(force) {
sendNetworkUpdateNagleTimer = null;
-
+
// Update the network interfaces information data
var netInfo = mesh.NetInfo;
if (netInfo) {
@@ -2445,7 +2376,7 @@ function createMeshCore(agent)
if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
}
}
-
+
// Called periodically to check if we need to send updates to the server
function sendPeriodicServerUpdate(flags) {
if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
@@ -2500,10 +2431,10 @@ function createMeshCore(agent)
//if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
}
-
+
// TODO: add plugin hook here
}
-
+
// Starting function
obj.start = function () {
@@ -2536,16 +2467,16 @@ function createMeshCore(agent)
//userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
} catch (ex) { }
}
-
+
obj.stop = function () {
mesh.AddCommandHandler(null);
mesh.AddConnectHandler(null);
}
-
+
function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
-
+
function onWebSocketUpgrade(response, s, head) {
sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
this.s = s;
diff --git a/meshagent.js b/meshagent.js
index 9224db2d..4593df2d 100644
--- a/meshagent.js
+++ b/meshagent.js
@@ -1278,14 +1278,12 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
break;
}
case 'plugin': {
- if (typeof command.plugin == 'string') {
- try {
+ if (typeof command.plugin != 'string') break;
+ try {
var pluginHandler = require('./pluginHandler.js').pluginHandler(parent.parent);
pluginHandler.plugins[command.plugin].serveraction(command, obj, parent);
- } catch (e) {
-
- console.log('Error loading plugin handler ('+ e + ')');
- }
+ } catch (e) {
+ console.log('Error loading plugin handler (' + e + ')');
}
break;
}
diff --git a/meshcentral.js b/meshcentral.js
index a1583df1..b93939fe 100644
--- a/meshcentral.js
+++ b/meshcentral.js
@@ -31,6 +31,7 @@ function CreateMeshCentralServer(config, args) {
obj.swarmserver = null;
obj.mailserver = null;
obj.amtEventHandler = null;
+ obj.pluginHandler = null;
obj.amtScanner = null;
obj.meshScanner = null;
obj.letsencrypt = null;
@@ -702,6 +703,11 @@ function CreateMeshCentralServer(config, args) {
return;
}
+ // Start plugin manager if configuration allows this.
+ if ((obj.config) && (obj.config.settings) && (obj.config.settings.plugins != null)) {
+ obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj);
+ }
+
// Load the default meshcore and meshcmd
obj.updateMeshCore();
obj.updateMeshCmd();
@@ -865,10 +871,8 @@ function CreateMeshCentralServer(config, args) {
// Dispatch an event that the server is now running
obj.DispatchEvent(['*'], obj, { etype: 'server', action: 'started', msg: 'Server started' });
- obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj);
-
// Plugin hook. Need to run something at server startup? This is the place.
- obj.pluginHandler.callHook("server_startup");
+ if (obj.pluginHandler) { obj.pluginHandler.callHook("server_startup"); }
// Load the login cookie encryption key from the database if allowed
if ((obj.config) && (obj.config.settings) && (obj.config.settings.allowlogintoken == true)) {
@@ -1352,8 +1356,10 @@ function CreateMeshCentralServer(config, args) {
}
}
}
- obj.pluginHandler = require("./pluginHandler.js").pluginHandler(obj);
- obj.pluginHandler.addMeshCoreModules(modulesAdd);
+
+ // Add plugins to cores
+ if (obj.pluginHandler) { obj.pluginHandler.addMeshCoreModules(modulesAdd); }
+
// Merge the cores and compute the hashes
for (var i in modulesAdd) {
if ((i == 'windows-recovery') || (i == 'linux-recovery')) {
diff --git a/meshrelay.js b/meshrelay.js
index 9d5676cd..7798807b 100644
--- a/meshrelay.js
+++ b/meshrelay.js
@@ -198,7 +198,11 @@ module.exports.CreateMeshRelay = function (parent, ws, req, domain, user, cookie
if (xdevicename2 != null) { metadata.devicename = xdevicename2; }
var firstBlock = JSON.stringify(metadata);
recordingEntry(fd, 1, ((req.query.browser) ? 2 : 0), firstBlock, function () {
- relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false };
+ try { relayinfo.peer1.ws.logfile = ws.logfile = { fd: fd, lock: false }; } catch (ex) {
+ try { ws.send('c'); } catch (ex) { } // Send connect to both peers, 'cr' indicates the session is being recorded.
+ try { relayinfo.peer1.ws.send('c'); } catch (ex) { }
+ return;
+ }
try { ws.send('cr'); } catch (ex) { } // Send connect to both peers, 'cr' indicates the session is being recorded.
try { relayinfo.peer1.ws.send('cr'); } catch (ex) { }
});
diff --git a/package.json b/package.json
index 05dcb330..fe9aed58 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "meshcentral",
- "version": "0.4.1-y",
+ "version": "0.4.1-z",
"keywords": [
"Remote Management",
"Intel AMT",
diff --git a/pluginHandler.js b/pluginHandler.js
index 2eb93294..cb96ec42 100644
--- a/pluginHandler.js
+++ b/pluginHandler.js
@@ -23,144 +23,136 @@ module.exports.pluginHandler = function (parent) {
obj.pluginPath = obj.parent.path.join(obj.parent.datapath, 'plugins');
obj.plugins = {};
obj.exports = {};
-
- try {
- obj.enabled = obj.parent.config.settings.plugins.enabled;
- obj.loadList = obj.parent.config.settings.plugins.list;
- } catch (e) { // Config file options not present, disable self
- obj.enabled = false;
- obj.loadList = {};
- console.log('Plugin options not added to the config file. Plugins disabled. Please see the documentation.');
- }
-
- if (obj.enabled) {
- obj.loadList.forEach(function(plugin, index) {
- if (obj.fs.existsSync(obj.pluginPath + '/' + plugin)) {
- try {
- obj.plugins[plugin] = require(obj.pluginPath + '/' + plugin + '/' + plugin + '.js')[plugin](obj);
- obj.exports[plugin] = obj.plugins[plugin].exports;
- } catch (e) {
- console.log("Error loading plugin: " + plugin + " (" + e + "). It has been disabled.", e.stack);
- }
- }
- });
- }
-
- obj.prepExports = function() {
- var str = 'function() {\r\n';
- str += ' var obj = {};\r\n';
-
- for (const p of Object.keys(obj.plugins)) {
- str += ' obj.'+ p +' = {};\r\n';
- for (const l of Object.values(obj.exports[p])) {
- str += ' obj.'+ p +'.'+ l + ' = '+ obj.plugins[p][l].toString()+'\r\n';
- }
- }
-
- str += 'obj.enabled = '+ obj.enabled +';\r\n';
- str += `obj.onDeviceRefeshEnd = function(nodeid, panel, refresh, event) {
- for (const p of Object.keys(obj)) {
- if (typeof obj[p].onDeviceRefreshEnd == 'function') {
- obj[p].onDeviceRefreshEnd(nodeid, panel, refresh, event);
- }
- }
- };
- obj.registerPluginTab = function(pluginRegInfo) {
- var d = pluginRegInfo();
- if (!Q(d.tabId)) {
- QA('p19headers', ''+d.tabTitle+'');
- }
- };
- obj.callPluginPage = function(id) {
- var pages = Q('p19pages').querySelectorAll("#p19pages>div");
- for (const i of pages) {
- i.style.display = 'none';
- }
- QV(id, true);
- };
- return obj; };`;
- return str;
- }
-
- obj.callHook = function(hookName, ...args) {
- for (var p in obj.plugins) {
- if (typeof obj.plugins[p][hookName] == 'function') {
- try {
- obj.plugins[p][hookName](args);
- } catch (e) {
- console.log('Error ocurred while running plugin hook' + p + ':' + hookName + ' (' + e + ')');
- }
- }
- }
- };
-
- obj.addMeshCoreModules = function(modulesAdd) {
- if (obj.enabled !== true) return;
- for (var plugin in obj.plugins) {
- var moduleDirPath = null;
- var modulesDir = null;
- //if (obj.args.minifycore !== false) { try { moduleDirPath = obj.path.join(obj.pluginPath, 'modules_meshcore_min'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Favor minified modules if present.
- if (modulesDir == null) { try { moduleDirPath = obj.path.join(obj.pluginPath, plugin + '/modules_meshcore'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Use non-minified mofules.
- if (modulesDir != null) {
- for (var i in modulesDir) {
- if (modulesDir[i].toLowerCase().endsWith('.js')) {
- var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
- if (moduleName.endsWith('.min')) { moduleName = moduleName.substring(0, moduleName.length - 4); } // Remove the ".min" for ".min.js" files.
- var moduleData = [ 'try { addModule("', moduleName, '", "', obj.parent.escapeCodeString(obj.fs.readFileSync(obj.path.join(moduleDirPath, modulesDir[i])).toString('binary')), '"); addedModules.push("', moduleName, '"); } catch (e) { }\r\n' ];
+ obj.loadList = obj.parent.config.settings.plugins.list;
- // Merge this module
- // NOTE: "smbios" module makes some non-AI Linux segfault, only include for IA platforms.
- if (moduleName.startsWith('amt-') || (moduleName == 'smbios')) {
- // Add to IA / Intel AMT cores only
- modulesAdd['windows-amt'].push(...moduleData);
- modulesAdd['linux-amt'].push(...moduleData);
- } else if (moduleName.startsWith('win-')) {
- // Add to Windows cores only
- modulesAdd['windows-amt'].push(...moduleData);
- } else if (moduleName.startsWith('linux-')) {
- // Add to Linux cores only
- modulesAdd['linux-amt'].push(...moduleData);
- modulesAdd['linux-noamt'].push(...moduleData);
- } else {
- // Add to all cores
- modulesAdd['windows-amt'].push(...moduleData);
- modulesAdd['linux-amt'].push(...moduleData);
- modulesAdd['linux-noamt'].push(...moduleData);
- }
-
- // Merge this module to recovery modules if needed
- if (modulesAdd['windows-recovery'] != null) {
- if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
- modulesAdd['windows-recovery'].push(...moduleData);
- }
- }
+ if (typeof obj.loadList != 'object') {
+ obj.loadList = {};
+ console.log('Plugin list not specified, please fix configuration file.');
+ return null;
+ }
- // Merge this module to agent recovery modules if needed
- if (modulesAdd['windows-agentrecovery'] != null) {
- if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
- modulesAdd['windows-agentrecovery'].push(...moduleData);
- }
- }
- }
- }
+ obj.loadList.forEach(function (plugin, index) {
+ if (obj.fs.existsSync(obj.pluginPath + '/' + plugin)) {
+ try {
+ obj.plugins[plugin] = require(obj.pluginPath + '/' + plugin + '/' + plugin + '.js')[plugin](obj);
+ obj.exports[plugin] = obj.plugins[plugin].exports;
+ } catch (e) {
+ console.log("Error loading plugin: " + plugin + " (" + e + "). It has been disabled.", e.stack);
}
- }
- };
-
- obj.deviceViewPanel = function() {
- var panel = {};
- for (var p in obj.plugins) {
- if (typeof obj.plugins[p][hookName] == 'function') {
- try {
- panel[p].header = obj.plugins[p].on_device_header();
- panel[p].content = obj.plugins[p].on_device_page();
- } catch (e) {
- console.log('Error ocurred while getting plugin views ' + p + ':' + ' (' + e + ')');
- }
- }
- }
- return panel;
+ }
+ });
+
+ obj.prepExports = function () {
+ var str = 'function() {\r\n';
+ str += ' var obj = {};\r\n';
+
+ for (const p of Object.keys(obj.plugins)) {
+ str += ' obj.' + p + ' = {};\r\n';
+ for (const l of Object.values(obj.exports[p])) {
+ str += ' obj.' + p + '.' + l + ' = ' + obj.plugins[p][l].toString() + '\r\n';
+ }
+ }
+
+ str += `obj.onDeviceRefeshEnd = function(nodeid, panel, refresh, event) {
+ for (const p of Object.keys(obj)) {
+ if (typeof obj[p].onDeviceRefreshEnd == 'function') {
+ obj[p].onDeviceRefreshEnd(nodeid, panel, refresh, event);
+ }
+ }
+ };
+ obj.registerPluginTab = function(pluginRegInfo) {
+ var d = pluginRegInfo();
+ if (!Q(d.tabId)) {
+ QA('p19headers', ''+d.tabTitle+'');
+ }
+ };
+ obj.callPluginPage = function(id) {
+ var pages = Q('p19pages').querySelectorAll("#p19pages>div");
+ for (const i of pages) { i.style.display = 'none'; }
+ QV(id, true);
+ };
+ return obj; };`;
+ return str;
}
-
+
+ obj.callHook = function (hookName, ...args) {
+ for (var p in obj.plugins) {
+ if (typeof obj.plugins[p][hookName] == 'function') {
+ try {
+ obj.plugins[p][hookName](args);
+ } catch (e) {
+ console.log('Error ocurred while running plugin hook' + p + ':' + hookName + ' (' + e + ')');
+ }
+ }
+ }
+ };
+
+ obj.addMeshCoreModules = function (modulesAdd) {
+ for (var plugin in obj.plugins) {
+ var moduleDirPath = null;
+ var modulesDir = null;
+ //if (obj.args.minifycore !== false) { try { moduleDirPath = obj.path.join(obj.pluginPath, 'modules_meshcore_min'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Favor minified modules if present.
+ if (modulesDir == null) { try { moduleDirPath = obj.path.join(obj.pluginPath, plugin + '/modules_meshcore'); modulesDir = obj.fs.readdirSync(moduleDirPath); } catch (e) { } } // Use non-minified mofules.
+ if (modulesDir != null) {
+ for (var i in modulesDir) {
+ if (modulesDir[i].toLowerCase().endsWith('.js')) {
+ var moduleName = modulesDir[i].substring(0, modulesDir[i].length - 3);
+ if (moduleName.endsWith('.min')) { moduleName = moduleName.substring(0, moduleName.length - 4); } // Remove the ".min" for ".min.js" files.
+ var moduleData = ['try { addModule("', moduleName, '", "', obj.parent.escapeCodeString(obj.fs.readFileSync(obj.path.join(moduleDirPath, modulesDir[i])).toString('binary')), '"); addedModules.push("', moduleName, '"); } catch (e) { }\r\n'];
+
+ // Merge this module
+ // NOTE: "smbios" module makes some non-AI Linux segfault, only include for IA platforms.
+ if (moduleName.startsWith('amt-') || (moduleName == 'smbios')) {
+ // Add to IA / Intel AMT cores only
+ modulesAdd['windows-amt'].push(...moduleData);
+ modulesAdd['linux-amt'].push(...moduleData);
+ } else if (moduleName.startsWith('win-')) {
+ // Add to Windows cores only
+ modulesAdd['windows-amt'].push(...moduleData);
+ } else if (moduleName.startsWith('linux-')) {
+ // Add to Linux cores only
+ modulesAdd['linux-amt'].push(...moduleData);
+ modulesAdd['linux-noamt'].push(...moduleData);
+ } else {
+ // Add to all cores
+ modulesAdd['windows-amt'].push(...moduleData);
+ modulesAdd['linux-amt'].push(...moduleData);
+ modulesAdd['linux-noamt'].push(...moduleData);
+ }
+
+ // Merge this module to recovery modules if needed
+ if (modulesAdd['windows-recovery'] != null) {
+ if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
+ modulesAdd['windows-recovery'].push(...moduleData);
+ }
+ }
+
+ // Merge this module to agent recovery modules if needed
+ if (modulesAdd['windows-agentrecovery'] != null) {
+ if ((moduleName == 'win-console') || (moduleName == 'win-message-pump') || (moduleName == 'win-terminal')) {
+ modulesAdd['windows-agentrecovery'].push(...moduleData);
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
+ obj.deviceViewPanel = function () {
+ var panel = {};
+ for (var p in obj.plugins) {
+ if (typeof obj.plugins[p][hookName] == 'function') {
+ try {
+ panel[p].header = obj.plugins[p].on_device_header();
+ panel[p].content = obj.plugins[p].on_device_page();
+ } catch (e) {
+ console.log('Error ocurred while getting plugin views ' + p + ':' + ' (' + e + ')');
+ }
+ }
+ }
+ return panel;
+ }
+
return obj;
};
\ No newline at end of file
diff --git a/views/default-min.handlebars b/views/default-min.handlebars
index 28619be2..66e162b5 100644
--- a/views/default-min.handlebars
+++ b/views/default-min.handlebars
@@ -1 +1,15280 @@
-
{{{title}}} My Devices | My Account | My Events | My Files | My Users | My Server | |
General | Desktop | Terminal | Files | Events | Details | Intel® AMT | Console | |
Server disconnected, click to reconnect.
My Devices
| No device groups. |
My Account
Device Groups ( New ) My Events
| Show | |
My Files
These files are shared publicly, click "link" to get public url.
✓
✗
My Server
Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
| Show | |
General -
Events -
| Show | |
File Selection
Agent Remote Desktop
Scaling
Frame rate
Intel® AMT Hardware KVM
Image Encoding
\ No newline at end of file
+ {{{title}}} My Devices | My Account | My Events | My Files | My Users | My Server | |
General | Desktop | Terminal | Files | Events | Details | Intel® AMT | Console | Plugins | |
Server disconnected, click to reconnect.
My Devices
| No device groups. |
My Account
Device Groups ( New ) My Events
| Show | |
My Files
These files are shared publicly, click "link" to get public url.
✓
✗
My Server
Server Statistics
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
Intel® AMT Redirection port or KVM feature is disabled, click here to enable it.
Remote computer is not powered on, click here to issue a power command.
| Show | |
General -
Events -
| Show | |
File Selection
Agent Remote Desktop
Scaling
Frame rate
Intel® AMT Hardware KVM
Image Encoding
\ No newline at end of file
diff --git a/views/default.handlebars b/views/default.handlebars
index 23878299..b30d7fd7 100644
--- a/views/default.handlebars
+++ b/views/default.handlebars
@@ -1036,7 +1036,8 @@
var sessionActivity = Date.now();
var updateSessionTimer = null;
var pluginHandlerBuilder = {{{pluginHandler}}};
- var pluginHandler = new pluginHandlerBuilder();
+ var pluginHandler = null;
+ if (pluginHandlerBuilder != null) { pluginHandler = new pluginHandlerBuilder(); }
// Console Message Display Timers
var p11DeskConsoleMsgTimer = null;
@@ -2329,14 +2330,9 @@
break;
}
case 'plugin': {
- if (typeof message.plugin == 'string') {
- try {
- pluginHandler[message.plugin][message.method](server, message);
- } catch (e) {
- console.log('Error loading plugin handler ('+ e + ')');
- }
- }
- break;
+ if ((pluginHandler == null) || (typeof message.plugin != 'string')) break;
+ try { pluginHandler[message.plugin][message.method](server, message); } catch (e) { console.log('Error loading plugin handler ('+ e + ')'); }
+ break;
}
default:
//console.log('Unknown message.action', message.action);
@@ -4390,6 +4386,7 @@
QV('MainDevFiles', ((mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 4) != 0))) && (meshrights & 8) && fileAccess);
QV('MainDevAmt', (node.intelamt != null) && ((node.intelamt.state == 2) || (node.conn & 2)) && (meshrights & 8) && amtAccess);
QV('MainDevConsole', (consoleRights && (mesh.mtype == 2) && ((node.agent == null) || (node.agent.caps == null) || ((node.agent.caps & 8) != 0))) && (meshrights & 8));
+ QV('MainDevPlugins', pluginHandler != null);
QV('p15uploadCore', (node.agent != null) && (node.agent.caps != null) && ((node.agent.caps & 16) != 0));
QH('p15coreName', ((node.agent != null) && (node.agent.core != null))?node.agent.core:'');
@@ -4435,7 +4432,8 @@
p12clearConsoleMsg();
p13clearConsoleMsg();
- pluginHandler.onDeviceRefeshEnd(nodeid, panel, refresh, event);
+ // Device refresh plugin handler
+ if (pluginHandler != null) { pluginHandler.onDeviceRefeshEnd(nodeid, panel, refresh, event); }
}
setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
if (!panel) panel = 10;
@@ -9168,16 +9166,14 @@
// If we are going to panel 0 in "full screen mode", hide the left bar.
QV('topbar', x != 0);
- if ((x == 0) && (webPageFullScreen)) {
- QC('body').add("arg_hide");
- }
+ if ((x == 0) && (webPageFullScreen)) { QC('body').add("arg_hide"); }
QV('MainSubMenuSpan', x >= 10 && x < 20);
QV('UserDummyMenuSpan', (x < 10) && (x != 6) && webPageFullScreen);
QV('MeshSubMenuSpan', x >= 20 && x < 30);
QV('UserSubMenuSpan', x >= 30 && x < 40);
QV('ServerSubMenuSpan', x == 6 || x == 115 || x == 40 || x == 41);
- var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 19: 'Plugins', 115: 'ServerConsole' };
+ var panels = { 10: 'MainDev', 11: 'MainDevDesktop', 12: 'MainDevTerminal', 13: 'MainDevFiles', 14: 'MainDevAmt', 15: 'MainDevConsole', 16: 'MainDevEvents', 17: 'MainDevInfo', 19: 'MainDevPlugins', 20: 'MeshGeneral', 30: 'UserGeneral', 31: 'UserEvents', 6: 'ServerGeneral', 40: 'ServerStats', 41: 'ServerTrace', 115: 'ServerConsole' };
for (var i in panels) {
QC(panels[i]).remove('style3x');
QC(panels[i]).remove('style3sel');
diff --git a/webserver.js b/webserver.js
index ea227799..a281f2b9 100644
--- a/webserver.js
+++ b/webserver.js
@@ -1517,14 +1517,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// Clean up the U2F challenge if needed
if (req.session.u2fchallenge) { delete req.session.u2fchallenge; };
-
- var pluginHandler = require('./pluginHandler.js').pluginHandler(parent);
-
+
// Fetch the web state
parent.debug('web', 'handleRootRequestEx: success.');
obj.db.Get('ws' + user._id, function (err, states) {
var webstate = (states.length == 1) ? states[0].state : '';
- res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: pluginHandler.prepExports() });
+ res.render(getRenderPage('default', req), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate), pluginHandler: (parent.pluginHandler == null)?'null':parent.pluginHandler.prepExports() });
});
} else {
// Send back the login application