1. Added user-consent 2. Added Diagnostic Agent test methods for 'console'

1. Added user-consent 2. Added Diagnostic Agent test methods for 'console'

Bryan Roe committed Jun 14, 2019 at 13:57 UTC 6611b56cc6f9ad35083a4dd92933d9a902ec7f3a
1 file changed +332 -59
agents/meshcore.js
+332 -59
@@ -22,6 +22,7 @@ process.on('uncaughtException', function (ex) {
22 //attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
23
24 // Mesh Rights
25 +var MNG_ERROR = 65;
26 var MESHRIGHT_EDITMESH = 1;
27 var MESHRIGHT_MANAGEUSERS = 2;
28 var MESHRIGHT_MANAGECOMPUTERS = 4;
@@ -36,10 +37,12 @@ var MESHRIGHT_NOFILES = 1024;
37 var MESHRIGHT_NOAMT = 2048;
38 var MESHRIGHT_LIMITEDINPUT = 4096;
39
39 -function createMeshCore(agent) {
40 +function createMeshCore(agent)
41 +{
42 var obj = {};
43
42 - if (process.platform == 'darwin' && !process.versions) {
44 + if (process.platform == 'darwin' && !process.versions)
45 + {
46 // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
47 var child = require('child_process').execFile('/bin/sh', ['sh']);
48 child.stdout.str = '';
@@ -48,18 +51,21 @@ function createMeshCore(agent) {
51 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]; } }");
52 child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
53 child.waitExit();
51 - if (child.stdout.str.trim() == 'Crashed') {
54 + if (child.stdout.str.trim() == 'Crashed')
55 + {
56 child = require('child_process').execFile('/bin/sh', ['sh']);
57 child.stdout.str = '';
58 child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
59 child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
60 child.waitExit();
61
58 - if (parseInt(child.stdout.str.trim()) == process.pid) {
62 + if (parseInt(child.stdout.str.trim()) == process.pid)
63 + {
64 // The currently running MeshAgent is us, so we can continue with the update
65 var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
66 var tokens = plist.split('<key>KeepAlive</key>');
62 - if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
67 + if (tokens[1].split('>')[0].split('<')[1] == 'dict')
68 + {
69 var tmp = tokens[1].split('</dict>');
70 tmp.shift();
71 tokens[1] = '\n <true/>' + tmp.join('</dict>');
@@ -114,6 +120,133 @@ function createMeshCore(agent) {
120 }
121 }
122
123 + // Create Secure IPC for Diagnostic Agent Communications
124 + obj.DAIPC = require('net').createServer();
125 + if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
126 + obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
127 + try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
128 + obj.DAIPC.on('connection', function (c)
129 + {
130 + c._send = function (j)
131 + {
132 + var data = JSON.stringify(j);
133 + var packet = Buffer.alloc(data.length + 4);
134 + packet.writeUInt32LE(data.length + 4, 0);
135 + Buffer.from(data).copy(packet, 4);
136 + this.end(packet);
137 + };
138 + this._daipc = c;
139 + c.parent = this;
140 + c.on('end', function () { console.log('Connection Closed'); this.parent._daipc = null; });
141 + c.on('data', function (chunk)
142 + {
143 + if (chunk.length < 4) { this.unshift(chunk); return; }
144 + var len = chunk.readUInt32LE(0);
145 + if (len > 8192) { this.parent._daipc = null; this.end(); return; }
146 + if (chunk.length < len) { this.unshift(chunk); return; }
147 +
148 + var data = chunk.slice(4, len);
149 + try
150 + {
151 + data = JSON.parse(data.toString());
152 + }
153 + catch(de)
154 + {
155 + this.parent._daipc = null; this.end(); return;
156 + }
157 +
158 + if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
159 +
160 + try
161 + {
162 + switch(data.cmd)
163 + {
164 + case 'query':
165 + switch(data.value)
166 + {
167 + case 'connection':
168 + data.result = require('MeshAgent').ConnectedServer;
169 + this._send(data);
170 + break;
171 + }
172 + break;
173 + default:
174 + this.parent._daipc = null; this.end(); return;
175 + break;
176 + }
177 + }
178 + catch(xe)
179 + {
180 + this.parent._daipc = null; this.end(); return;
181 + }
182 + });
183 + });
184 + function diagnosticAgent_uninstall()
185 + {
186 + require('service-manager').manager.uninstallService('meshagentDiagnostic');
187 + require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
188 + };
189 + function diagnosticAgent_installCheck(install)
190 + {
191 + try
192 + {
193 + var diag = require('service-manager').manager.getService('meshagentDiagnostic');
194 + return (diag);
195 + }
196 + catch (e)
197 + {
198 + }
199 + if (!install) { return (null); }
200 +
201 + var svc = null;
202 + try
203 + {
204 + require('service-manager').manager.installService(
205 + {
206 + name: 'meshagentDiagnostic',
207 + displayName: 'Mesh Agent Diagnostic Service',
208 + description: 'Mesh Agent Diagnostic Service',
209 + servicePath: process.execPath,
210 + parameters: ['-recovery']
211 + //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
212 + });
213 + svc = require('service-manager').manager.getService('meshagentDiagnostic');
214 + }
215 + catch (e)
216 + {
217 + return (null);
218 + }
219 + var proxyConfig = require('global-tunnel').proxyConfig;
220 + var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
221 + var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
222 + ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
223 + ddb.Put('disableUpdate', '1');
224 + ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
225 + ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
226 + ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
227 + if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
228 + if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
229 + if (proxyConfig)
230 + {
231 + ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
232 + }
233 + else
234 + {
235 + ddb.Put('ignoreProxyFile', '1');
236 + }
237 +
238 + require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
239 + require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'Diagnostic Agent Registered [' + nodeid.length + '/' + nodeid + ']' });
240 +
241 + delete ddb;
242 +
243 + // Set a recurrent task, to run the Diagnostic Agent every 2 days
244 + 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'});
245 + //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
246 +
247 + return (svc);
248 + }
249 +
250 /*
251 function borderController() {
252 this.container = null;
@@ -191,6 +324,8 @@ function createMeshCore(agent) {
324 mesh = agent.getMeshApi();
325 }
326
327 + mesh.DAIPC = obj.DAIPC;
328 +
329 /*
330 var AMTScanner = require("AMTScanner");
331 var scan = new AMTScanner();
@@ -815,39 +950,7 @@ function createMeshCore(agent) {
950 return;
951 }
952
818 - // Test the console messaging system
819 - //this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote terminal...' })); // Send a console message back using the console channel, "\n" is supported.
820 -
821 - // Perform notification if needed. Toast messages may not be supported on all platforms.
822 - if (this.httprequest.consent && (this.httprequest.consent & 2)) {
823 - try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote terminal session.'); } catch (ex) { }
824 - }
825 -
826 - // Remote terminal using native pipes
827 - if (process.platform == "win32")
828 - {
829 - this.httprequest._term = require('win-terminal').Start(80, 25);
830 - this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
831 - this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
832 - this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
833 - //this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
834 - } else {
835 - if (fs.existsSync("/bin/bash")) {
836 - this.httprequest.process = childProcess.execFile("/bin/bash", ["bash", "-i"], { type: childProcess.SpawnTypes.TERM });
837 - if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
838 - } else {
839 - this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
840 - if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); }
841 - }
842 - //if (this.httprequest.process == null) { }
843 - this.httprequest.process.tunnel = this;
844 - this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
845 - this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
846 - this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
847 - this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
848 - this.prependListener('end', function () { this.httprequest.process.kill(); });
849 - }
850 -
953 +
954 this.end = function () {
955 if (process.platform == "win32") {
956 // Unpipe the web socket
@@ -868,11 +971,71 @@ function createMeshCore(agent) {
971 }
972 };
973
974 + // Remote terminal using native pipes
975 + if (process.platform == "win32") {
976 + this.httprequest._term = require('win-terminal').Start(80, 25);
977 + this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
978 + this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
979 + this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed'); }); });
980 + } else {
981 + this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
982 + this.httprequest.process.tunnel = this;
983 + this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
984 + this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
985 + this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
986 + this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
987 + this.prependListener('end', function () { this.httprequest.process.kill(); });
988 + this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nclear\n");
989 + }
990 +
991 +
992 +
993 + // Perform notification if needed. Toast messages may not be supported on all platforms.
994 + if (this.httprequest.consent && (this.httprequest.consent & 16)) {
995 + // User Consent Prompt is required
996 +
997 + // Send a console message back using the console channel, "\n" is supported.
998 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
999 +
1000 + var pr = require('message-box').create('Mesh Central', this.httprequest.username + ' requesting Terminal Access. Grant access?', 10);
1001 + pr.ws = this;
1002 + this.pause();
1003 +
1004 + pr.then(
1005 + function () {
1006 + // Success!
1007 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
1008 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2)) {
1009 + // User Notifications is required
1010 + try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote terminal session.'); } catch (ex) { }
1011 + }
1012 +
1013 + this.ws.resume();
1014 + },
1015 + function (e) {
1016 + // User Consent Denied/Failed!
1017 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
1018 + this.ws.end();
1019 + });
1020 + }
1021 + else {
1022 + // User Consent Prompt is not required
1023 + if (this.httprequest.consent && (this.httprequest.consent & 2)) {
1024 + // User Notifications is required
1025 + try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote terminal session.'); } catch (ex) { }
1026 + }
1027 + this.resume();
1028 + }
1029 +
1030 +
1031 +
1032 +
1033 +
1034 this.removeAllListeners('data');
1035 this.on('data', onTunnelControlData);
1036 //this.write('MeshCore Terminal Hello');
874 - } else if (this.httprequest.protocol == 2) {
875 -
1037 + } else if (this.httprequest.protocol == 2)
1038 + {
1039 // Check user access rights for desktop
1040 if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
1041 // Disengage this tunnel, user does not have the rights to do this!!
@@ -882,13 +1045,6 @@ function createMeshCore(agent) {
1045 return;
1046 }
1047
885 - // Test the console messaging system
886 - //this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote desktop...' })); // Send a console message back using the console channel, "\n" is supported.
887 -
888 - // Perform notification if needed. Toast messages may not be supported on all platforms.
889 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
890 - try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote desktop session.'); } catch (ex) { }
891 - }
1048
1049 // Remote desktop using native pipes
1050 this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
@@ -926,7 +1082,56 @@ function createMeshCore(agent) {
1082 // TODO!!!
1083 }
1084
929 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. Pipe the KVM --> Browser images.
1085 + // Perform notification if needed. Toast messages may not be supported on all platforms.
1086 + if (this.httprequest.consent && (this.httprequest.consent & 8))
1087 + {
1088 + // User Consent Prompt is required
1089 +
1090 + // Send a console message back using the console channel, "\n" is supported.
1091 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
1092 +
1093 + var pr = require('message-box').create('Mesh Central', this.httprequest.username + ' requesting KVM Access. Grant access?', 10);
1094 + pr.ws = this;
1095 + this.pause();
1096 +
1097 + pr.then(
1098 + function ()
1099 + {
1100 + // Success!
1101 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
1102 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1))
1103 + {
1104 + // User Notifications is required
1105 + try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote desktop session.'); } catch (ex) { }
1106 + }
1107 +
1108 + this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1109 + this.ws.resume();
1110 + },
1111 + function (e)
1112 + {
1113 + // User Consent Denied/Failed!
1114 + this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
1115 +
1116 + //var err = 'User consent: ' + e.toString();
1117 + //var b = Buffer.alloc(5 + err.length);
1118 + //b.writeUInt16BE(MNG_ERROR, 0);
1119 + //b.writeUInt16BE(err.length + 4, 2);
1120 + //Buffer.from(err).copy(b, 4);
1121 + //this.ws.end(b);
1122 + });
1123 + }
1124 + else
1125 + {
1126 + // User Consent Prompt is not required
1127 + if (this.httprequest.consent && (this.httprequest.consent & 1))
1128 + {
1129 + // User Notifications is required
1130 + try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote desktop session.'); } catch (ex) { }
1131 + }
1132 + this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1133 + }
1134 +
1135 this.removeAllListeners('data');
1136 this.on('data', onTunnelControlData);
1137 //this.write('MeshCore KVM Hello!1');
@@ -942,12 +1147,41 @@ function createMeshCore(agent) {
1147 return;
1148 }
1149
945 - // Test the console messaging system
946 - //this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'This is a sample test for remote files...' })); // Send a console message back using the console channel, "\n" is supported.
1150 + // Perform notification if needed. Toast messages may not be supported on all platforms.
1151 + if (this.httprequest.consent && (this.httprequest.consent & 32))
1152 + {
1153 + // User Consent Prompt is required
1154 +
1155 + // Send a console message back using the console channel, "\n" is supported.
1156 + this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'Waiting for user to grant access...' }));
1157
948 - // Perform notification if needed
949 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
950 - try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote file access.'); } catch (ex) { }
1158 + var pr = require('message-box').create('Mesh Central', this.httprequest.username + ' requesting remote File Access. Grant access?', 10);
1159 + pr.ws = this;
1160 + this.pause();
1161 +
1162 + pr.then(
1163 + function () {
1164 + // Success!
1165 + this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
1166 + if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4))
1167 + {
1168 + // User Notifications is required
1169 + try { require('toaster').Toast('MeshCentral', this.ws.httprequest.username + ' started a remote file session.'); } catch (ex) { }
1170 + }
1171 + this.ws.resume();
1172 + },
1173 + function (e) {
1174 + // User Consent Denied/Failed!
1175 + this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
1176 + });
1177 + }
1178 + else {
1179 + // User Consent Prompt is not required
1180 + if (this.httprequest.consent && (this.httprequest.consent & 4)) {
1181 + // User Notifications is required
1182 + try { require('toaster').Toast('MeshCentral', this.httprequest.username + ' started a remote file session.'); } catch (ex) { }
1183 + }
1184 + this.resume();
1185 }
1186
1187 // Setup files
@@ -1372,12 +1606,9 @@ function createMeshCore(agent) {
1606 break;
1607 }
1608 case 'toast': {
1375 - if (process.platform == 'win32') {
1376 - if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
1377 - try { require('toaster').Toast('MeshCentral', args['_'][0]); response = 'ok'; } catch (ex) { response = ex; }
1378 - }
1379 - } else {
1380 - response = 'Only supported on Windows.';
1609 + if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else
1610 + {
1611 + require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
1612 }
1613 break;
1614 }
@@ -1763,6 +1994,48 @@ function createMeshCore(agent) {
1994 }
1995 break;
1996 }
1997 + case 'diagnostic':
1998 + {
1999 + if (!mesh.DAIPC.listening)
2000 + {
2001 + response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
2002 + break;
2003 + }
2004 + var diag = diagnosticAgent_installCheck();
2005 + if (diag)
2006 + {
2007 + if (args['_'].length == 1 && args['_'][0] == 'uninstall')
2008 + {
2009 + diagnosticAgent_uninstall();
2010 + response = 'Diagnostic Agent uninstalled';
2011 + }
2012 + else
2013 + {
2014 + response = 'Diagnostic Agent installed at: ' + diag.appLocation();
2015 + }
2016 + }
2017 + else
2018 + {
2019 + if (args['_'].length == 1 && args['_'][0] == 'install')
2020 + {
2021 + diag = diagnosticAgent_installCheck(true);
2022 + if (diag)
2023 + {
2024 + response = 'Diagnostic agent was installed at: ' + diag.appLocation();
2025 + }
2026 + else
2027 + {
2028 + response = 'Diagnostic agent installation failed';
2029 + }
2030 + }
2031 + else
2032 + {
2033 + response = 'Diagnostic Agent Not installed. To install: diagnostic install';
2034 + }
2035 + }
2036 + if (diag) { diag.close(); diag = null; }
2037 + break;
2038 + }
2039 default: { // This is an unknown command, return an error message
2040 response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
2041 break;