Added server setting for agent core dump.
Ylian Saint-Hilaire committed
Jul 6, 2020 at 16:01 UTC
35e2e0cd209d541d6b95260ed124f9adbcb157d0
5 files changed
+3506
-47
agents/meshcore.js
+16
-39
@@ -958,6 +958,14 @@ function createMeshCore(agent) {
958
try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
959
break;
960
}
961
+ case 'coredump':
962
+ if (data.value === true) {
963
+ // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
964
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
965
+ } else if (data.value === false) {
966
+ process.coreDumpLocation = null;
967
+ }
968
+ break;
969
default:
970
// Unknown action, ignore it.
971
break;
@@ -2261,16 +2269,13 @@ function createMeshCore(agent) {
2269
break;
2270
}
2271
case 'coredump':
2264
- if (args['_'].length != 1)
2265
- {
2266
- response = "Proper usage: coredump on|off|status"; // Display usage
2267
- }
2268
- else
2269
- {
2272
+ if (args['_'].length != 1) {
2273
+ response = "Proper usage: coredump on|off|status"; // Display usage
2274
+ } else {
2275
switch (args['_'][0].toLowerCase())
2276
{
2277
case 'on':
2273
- process.coreDumpLocation = process.platform == 'win32' ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2278
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2279
response = 'coredump is now on';
2280
break;
2281
case 'off':
@@ -2278,10 +2283,10 @@ function createMeshCore(agent) {
2283
response = 'coredump is now off';
2284
break;
2285
case 'status':
2281
- response = 'coredump is: ' + (process.coreDumpLocation == null ? 'off' : 'on');
2286
+ response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2287
break;
2288
default:
2284
- response = "Proper usage: coredump on|off|status"; // Display usage
2289
+ response = "Proper usage: coredump on|off|status"; // Display usage
2290
break;
2291
}
2292
}
@@ -2289,7 +2294,7 @@ function createMeshCore(agent) {
2294
case 'service':
2295
if (args['_'].length != 1)
2296
{
2292
- response = "Proper usage: service status|restart"; // Display usage
2297
+ response = "Proper usage: service status|restart"; // Display usage
2298
}
2299
else
2300
{
@@ -2310,7 +2315,7 @@ function createMeshCore(agent) {
2315
}
2316
break;
2317
default:
2313
- response = "Proper usage: service status|restart"; // Display usage
2318
+ response = "Proper usage: service status|restart"; // Display usage
2319
break;
2320
}
2321
if (process.platform == 'win32') { s.close(); }
@@ -2789,34 +2794,6 @@ function createMeshCore(agent) {
2794
}
2795
break;
2796
}
2792
- case 'dump':
2793
- if (args['_'].length < 1) {
2794
- response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2795
- }
2796
- else {
2797
- switch (args['_'][0].toLowerCase()) {
2798
- case 'on':
2799
- process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
2800
- response = 'enabled';
2801
- break;
2802
- case 'off':
2803
- process.coreDumpLocation = null;
2804
- response = 'disabled';
2805
- break;
2806
- case 'status':
2807
- if (process.coreDumpLocation) {
2808
- response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
2809
- }
2810
- else {
2811
- response = 'Core Dump: [DISABLED]';
2812
- }
2813
- break;
2814
- default:
2815
- response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2816
- break;
2817
- }
2818
- }
2819
- break;
2797
case 'eval': { // Eval JavaScript
2798
if (args['_'].length < 1) {
2799
response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
agents/meshcore.txt
new
+3474
@@ -0,0 +1,3474 @@
1
+/*
2
+Copyright 2018-2020 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
+process.on('uncaughtException', function (ex) {
18
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
19
+});
20
+
21
+// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
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;
29
+var MESHRIGHT_REMOTECONTROL = 8;
30
+var MESHRIGHT_AGENTCONSOLE = 16;
31
+var MESHRIGHT_SERVERFILES = 32;
32
+var MESHRIGHT_WAKEDEVICE = 64;
33
+var MESHRIGHT_SETNOTES = 128;
34
+var MESHRIGHT_REMOTEVIEW = 256;
35
+var MESHRIGHT_NOTERMINAL = 512;
36
+var MESHRIGHT_NOFILES = 1024;
37
+var MESHRIGHT_NOAMT = 2048;
38
+var MESHRIGHT_LIMITEDINPUT = 4096;
39
+var MESHRIGHT_LIMITEVENTS = 8192;
40
+var MESHRIGHT_CHATNOTIFY = 16384;
41
+var MESHRIGHT_UNINSTALL = 32768;
42
+var MESHRIGHT_NODESKTOP = 65536;
43
+
44
+function createMeshCore(agent) {
45
+ var obj = {};
46
+ if (process.platform == 'win32' && require('user-sessions').isRoot()) {
47
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
48
+ try {
49
+ var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
50
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (x) { }
51
+ if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (x2) { } }
52
+ } catch (ex) { }
53
+
54
+ // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
55
+ try {
56
+ var meshCheck = false;
57
+ try { meshCheck = require('service-manager').manager.getService('Mesh Agent').isMe(); } catch (mce) { }
58
+ if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59
+ } catch (ex) { }
60
+ }
61
+
62
+ if (process.platform == 'darwin' && !process.versions) {
63
+ // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
64
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
65
+ child.stdout.str = '';
66
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
67
+ child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); ");
68
+ 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]; } }");
69
+ child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
70
+ child.waitExit();
71
+ if (child.stdout.str.trim() == 'Crashed') {
72
+ child = require('child_process').execFile('/bin/sh', ['sh']);
73
+ child.stdout.str = '';
74
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
75
+ child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
76
+ child.waitExit();
77
+
78
+ if (parseInt(child.stdout.str.trim()) == process.pid) {
79
+ // The currently running MeshAgent is us, so we can continue with the update
80
+ var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
81
+ var tokens = plist.split('<key>KeepAlive</key>');
82
+ if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
83
+ var tmp = tokens[1].split('</dict>');
84
+ tmp.shift();
85
+ tokens[1] = '\n <true/>' + tmp.join('</dict>');
86
+ tokens = tokens.join('<key>KeepAlive</key>');
87
+
88
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
89
+
90
+ var fix = '';
91
+ fix += ("function macosRepair()\n");
92
+ fix += ("{\n");
93
+ fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
94
+ fix += (" child.stdout.str = '';\n");
95
+ fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
96
+ fix += (" child.stderr.on('data', function (chunk) { });\n");
97
+ fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
98
+ fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
99
+ fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
100
+ fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
101
+ fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
102
+ fix += (" child.waitExit();\n");
103
+ fix += ("}\n");
104
+ fix += ("macosRepair();\n");
105
+ fix += ("process.exit();\n");
106
+ require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
107
+
108
+ var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
109
+ plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
110
+ plist += '<plist version="1.0">\n';
111
+ plist += ' <dict>\n';
112
+ plist += ' <key>Label</key>\n';
113
+ plist += (' <string>meshagentRepair</string>\n');
114
+ plist += ' <key>ProgramArguments</key>\n';
115
+ plist += ' <array>\n';
116
+ plist += (' <string>' + process.execPath + '</string>\n');
117
+ plist += ' <string>macosRepair.js</string>\n';
118
+ plist += ' </array>\n';
119
+ plist += ' <key>WorkingDirectory</key>\n';
120
+ plist += (' <string>' + process.cwd() + '</string>\n');
121
+ plist += ' <key>RunAtLoad</key>\n';
122
+ plist += ' <true/>\n';
123
+ plist += ' </dict>\n';
124
+ plist += '</plist>';
125
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
126
+
127
+ child = require('child_process').execFile('/bin/sh', ['sh']);
128
+ child.stdout.str = '';
129
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
130
+ child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
131
+ child.waitExit();
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // Create Secure IPC for Diagnostic Agent Communications
138
+ obj.DAIPC = require('net').createServer();
139
+ if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (ee) { } }
140
+ obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141
+ try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH }); } catch (e) { }
142
+ obj.DAIPC.on('connection', function (c) {
143
+ c._send = function (j) {
144
+ var data = JSON.stringify(j);
145
+ var packet = Buffer.alloc(data.length + 4);
146
+ packet.writeUInt32LE(data.length + 4, 0);
147
+ Buffer.from(data).copy(packet, 4);
148
+ this.end(packet);
149
+ };
150
+ this._daipc = c;
151
+ c.parent = this;
152
+ c.on('end', function () { console.log("Connection Closed"); this.parent._daipc = null; });
153
+ c.on('data', function (chunk) {
154
+ if (chunk.length < 4) { this.unshift(chunk); return; }
155
+ var len = chunk.readUInt32LE(0);
156
+ if (len > 8192) { this.parent._daipc = null; this.end(); return; }
157
+ if (chunk.length < len) { this.unshift(chunk); return; }
158
+
159
+ var data = chunk.slice(4, len);
160
+ try {
161
+ data = JSON.parse(data.toString());
162
+ }
163
+ catch (de) {
164
+ this.parent._daipc = null; this.end(); return;
165
+ }
166
+
167
+ if (!data.cmd) { this.parent._daipc = null; this.end(); return; }
168
+
169
+ try {
170
+ switch (data.cmd) {
171
+ case 'query':
172
+ switch (data.value) {
173
+ case 'connection':
174
+ data.result = require('MeshAgent').ConnectedServer;
175
+ this._send(data);
176
+ break;
177
+ case 'descriptors':
178
+ require('ChainViewer').getSnapshot().then(function (f)
179
+ {
180
+ this.tag.payload.result = f;
181
+ this.tag.ipc._send(this.tag.payload);
182
+ }).parentPromise.tag = { ipc: this, payload: data };
183
+ break;
184
+ }
185
+ break;
186
+ default:
187
+ this.parent._daipc = null;
188
+ this.end();
189
+ return;
190
+ }
191
+ }
192
+ catch (xe) {
193
+ this.parent._daipc = null; this.end(); return;
194
+ }
195
+ });
196
+ });
197
+ function diagnosticAgent_uninstall() {
198
+ require('service-manager').manager.uninstallService('meshagentDiagnostic');
199
+ require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
200
+ };
201
+ function diagnosticAgent_installCheck(install) {
202
+ try {
203
+ var diag = require('service-manager').manager.getService('meshagentDiagnostic');
204
+ return (diag);
205
+ }
206
+ catch (e) {
207
+ }
208
+ if (!install) { return (null); }
209
+
210
+ var svc = null;
211
+ try {
212
+ require('service-manager').manager.installService(
213
+ {
214
+ name: 'meshagentDiagnostic',
215
+ displayName: "Mesh Agent Diagnostic Service",
216
+ description: "Mesh Agent Diagnostic Service",
217
+ servicePath: process.execPath,
218
+ parameters: ['-recovery']
219
+ //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
220
+ });
221
+ svc = require('service-manager').manager.getService('meshagentDiagnostic');
222
+ }
223
+ catch (e) {
224
+ return (null);
225
+ }
226
+ var proxyConfig = require('global-tunnel').proxyConfig;
227
+ var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
228
+ var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
229
+ ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
230
+ ddb.Put('disableUpdate', '1');
231
+ ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
232
+ ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
233
+ ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
234
+ if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
235
+ if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
236
+ if (proxyConfig) {
237
+ ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
238
+ } else {
239
+ ddb.Put('ignoreProxyFile', '1');
240
+ }
241
+
242
+ require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
243
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
244
+
245
+ delete ddb;
246
+
247
+ // Set a recurrent task, to run the Diagnostic Agent every 2 days
248
+ 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' });
249
+ //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
250
+
251
+ return (svc);
252
+ }
253
+
254
+ if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered())
255
+ {
256
+ require('MeshAgent')._battLevelChanged = function _battLevelChanged(val)
257
+ {
258
+ _battLevelChanged.self._currentBatteryLevel = val;
259
+ _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
260
+ };
261
+ require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
262
+ require('MeshAgent')._powerChanged = function _powerChanged(val)
263
+ {
264
+ _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
265
+ _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
266
+ };
267
+ require('MeshAgent')._powerChanged.self = require('MeshAgent');
268
+ require('MeshAgent').on('Connected', function (status)
269
+ {
270
+ if (status == 0)
271
+ {
272
+ require('power-monitor').removeListener('acdc', this._powerChanged);
273
+ require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
274
+ }
275
+ else
276
+ {
277
+ require('power-monitor').on('acdc', this._powerChanged);
278
+ require('power-monitor').on('batteryLevel', this._battLevelChanged);
279
+ }
280
+ });
281
+ }
282
+
283
+
284
+ /*
285
+ function borderController() {
286
+ this.container = null;
287
+ this.Start = function Start(user) {
288
+ if (this.container == null) {
289
+ if (process.platform == 'win32') {
290
+ try {
291
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
292
+ } catch (ex) {
293
+ this.container = require('ScriptContainer').Create({ processIsolation: 1 });
294
+ }
295
+ } else {
296
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
297
+ }
298
+ this.container.parent = this;
299
+ this.container.addModule('monitor-info', getJSModule('monitor-info'));
300
+ this.container.addModule('monitor-border', getJSModule('monitor-border'));
301
+ this.container.addModule('promise', getJSModule('promise'));
302
+ this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
303
+ this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
304
+ }
305
+ }
306
+ this.Stop = function Stop() {
307
+ if (this.container != null) {
308
+ this._container = this.container;
309
+ this._container.parent = this;
310
+ this.container = null;
311
+ this._container.exit();
312
+ }
313
+ }
314
+ }
315
+ obj.borderManager = new borderController();
316
+ */
317
+
318
+ // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
319
+ var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
320
+
321
+
322
+ // Get the operating system description string
323
+ try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (ex) { }
324
+
325
+ var meshServerConnectionState = 0;
326
+ var tunnels = {};
327
+ var lastMeInfo = null;
328
+ var lastNetworkInfo = null;
329
+ var lastPublicLocationInfo = null;
330
+ var selfInfoUpdateTimer = null;
331
+ var http = require('http');
332
+ var net = require('net');
333
+ var fs = require('fs');
334
+ var rtc = require('ILibWebRTC');
335
+ var amt = null;
336
+ var processManager = require('process-manager');
337
+ var wifiScannerLib = null;
338
+ var wifiScanner = null;
339
+ var networkMonitor = null;
340
+ var amtscanner = null;
341
+ var nextTunnelIndex = 1;
342
+ var amtPolicy = null;
343
+ var apftunnel = null;
344
+ var tunnelUserCount = { terminal: {}, files: {} }; // List of userid->count sessions for terminal and files.
345
+
346
+ // Add to the server event log
347
+ function MeshServerLog(msg, state) {
348
+ if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
349
+ if (state) {
350
+ if (state.userid) { msg.userid = state.userid; }
351
+ if (state.username) { msg.username = state.username; }
352
+ if (state.sessionid) { msg.sessionid = state.sessionid; }
353
+ }
354
+ mesh.SendCommand(msg);
355
+ }
356
+
357
+ // If we are running in Duktape, agent will be null
358
+ if (agent == null) {
359
+ // Running in native agent, Import libraries
360
+ db = require('SimpleDataStore').Shared();
361
+ sha = require('SHA256Stream');
362
+ mesh = require('MeshAgent');
363
+ childProcess = require('child_process');
364
+ if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
365
+ // Check if this computer supports a desktop
366
+ try
367
+ {
368
+ if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
369
+ {
370
+ meshCoreObj.caps |= 1;
371
+ }
372
+ else if(process.platform == 'linux' || process.platform == 'freebsd')
373
+ {
374
+ require('monitor-info').on('kvmSupportDetected', function (value)
375
+ {
376
+ meshCoreObj.caps |= 1;
377
+ mesh.SendCommand(meshCoreObj);
378
+ });
379
+ }
380
+ } catch (ex) { }
381
+ }
382
+ } else {
383
+ // Running in nodejs
384
+ meshCoreObj.value += '-NodeJS';
385
+ meshCoreObj.caps = 8;
386
+ mesh = agent.getMeshApi();
387
+ }
388
+
389
+ mesh.DAIPC = obj.DAIPC;
390
+
391
+ /*
392
+ var AMTScanner = require("AMTScanner");
393
+ var scan = new AMTScanner();
394
+
395
+ scan.on("found", function (data) {
396
+ if (typeof data === 'string') {
397
+ console.log(data);
398
+ } else {
399
+ console.log(JSON.stringify(data, null, " "));
400
+ }
401
+ });
402
+ scan.scan("10.2.55.140", 1000);
403
+ scan.scan("10.2.55.139-10.2.55.145", 1000);
404
+ scan.scan("10.2.55.128/25", 2000);
405
+ */
406
+
407
+ /*
408
+ // Try to load up the network monitor
409
+ try {
410
+ networkMonitor = require('NetworkMonitor');
411
+ networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
412
+ networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
413
+ networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
414
+ } catch (e) { networkMonitor = null; }
415
+ */
416
+
417
+ // Try to load up the Intel AMT scanner
418
+ try {
419
+ var AMTScannerModule = require('amt-scanner');
420
+ amtscanner = new AMTScannerModule();
421
+ //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
422
+ } catch (ex) { amtscanner = null; }
423
+
424
+ // Fetch the SMBios Tables
425
+ var SMBiosTables = null;
426
+ var SMBiosTablesRaw = null;
427
+ try {
428
+ var SMBiosModule = null;
429
+ try { SMBiosModule = require('smbios'); } catch (ex) { }
430
+ if (SMBiosModule != null) {
431
+ SMBiosModule.get(function (data) {
432
+ if (data != null) {
433
+ SMBiosTablesRaw = data;
434
+ SMBiosTables = require('smbios').parse(data)
435
+ if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
436
+
437
+ // If SMBios tables say that Intel AMT is present, try to connect MEI
438
+ if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
439
+ var amtmodule = require('amt-manage');
440
+ amt = new amtmodule(mesh, db, false);
441
+ amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
442
+ if (amtPolicy != null) { amt.setPolicy(amtPolicy); }
443
+ amt.start();
444
+ }
445
+ }
446
+ });
447
+ }
448
+ } catch (ex) { sendConsoleText("ex1: " + ex); }
449
+
450
+ // Try to load up the WIFI scanner
451
+ try {
452
+ var wifiScannerLib = require('wifi-scanner');
453
+ wifiScanner = new wifiScannerLib();
454
+ wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
455
+ } catch (ex) { wifiScannerLib = null; wifiScanner = null; }
456
+
457
+ // Get our location (lat/long) using our public IP address
458
+ var getIpLocationDataExInProgress = false;
459
+ var getIpLocationDataExCounts = [0, 0];
460
+ function getIpLocationDataEx(func) {
461
+ if (getIpLocationDataExInProgress == true) { return false; }
462
+ try {
463
+ getIpLocationDataExInProgress = true;
464
+ getIpLocationDataExCounts[0]++;
465
+ var options = http.parseUri("http://ipinfo.io/json");
466
+ options.method = 'GET';
467
+ http.request(options, function (resp) {
468
+ if (resp.statusCode == 200) {
469
+ var geoData = '';
470
+ resp.data = function (geoipdata) { geoData += geoipdata; };
471
+ resp.end = function () {
472
+ var location = null;
473
+ try {
474
+ if (typeof geoData == 'string') {
475
+ var result = JSON.parse(geoData);
476
+ if (result.ip && result.loc) { location = result; }
477
+ }
478
+ } catch (e) { }
479
+ if (func) { getIpLocationDataExCounts[1]++; func(location); }
480
+ }
481
+ } else { func(null); }
482
+ getIpLocationDataExInProgress = false;
483
+ }).end();
484
+ return true;
485
+ }
486
+ catch (e) { return false; }
487
+ }
488
+
489
+ // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
490
+ function clearGatewayMac(str) {
491
+ if (str == null) return null;
492
+ var x = JSON.parse(str);
493
+ for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
494
+ return JSON.stringify(x);
495
+ }
496
+
497
+ function getIpLocationData(func) {
498
+ // Get the location information for the cache if possible
499
+ var publicLocationInfo = db.Get('publicLocationInfo');
500
+ if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
501
+ if (publicLocationInfo == null) {
502
+ // Nothing in the cache, fetch the data
503
+ getIpLocationDataEx(function (locationData) {
504
+ if (locationData != null) {
505
+ publicLocationInfo = {};
506
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
507
+ publicLocationInfo.locationData = locationData;
508
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
509
+ if (func) func(locationData); // Report the new location
510
+ } else {
511
+ if (func) func(null); // Report no location
512
+ }
513
+ });
514
+ } else {
515
+ // Check the cache
516
+ if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
517
+ // Cache match
518
+ if (func) func(publicLocationInfo.locationData);
519
+ } else {
520
+ // Cache mismatch
521
+ getIpLocationDataEx(function (locationData) {
522
+ if (locationData != null) {
523
+ publicLocationInfo = {};
524
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
525
+ publicLocationInfo.locationData = locationData;
526
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
527
+ if (func) func(locationData); // Report the new location
528
+ } else {
529
+ if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
530
+ }
531
+ });
532
+ }
533
+ }
534
+ }
535
+
536
+ // Polyfill String.endsWith
537
+ if (!String.prototype.endsWith) {
538
+ String.prototype.endsWith = function (searchString, position) {
539
+ var subjectString = this.toString();
540
+ if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
541
+ position -= searchString.length;
542
+ var lastIndex = subjectString.lastIndexOf(searchString, position);
543
+ return lastIndex !== -1 && lastIndex === position;
544
+ };
545
+ }
546
+
547
+ // Polyfill path.join
548
+ obj.path = {
549
+ join: function () {
550
+ var x = [];
551
+ for (var i in arguments) {
552
+ var w = arguments[i];
553
+ if (w != null) {
554
+ while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
555
+ if (i != 0) {
556
+ while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
557
+ }
558
+ x.push(w);
559
+ }
560
+ }
561
+ if (x.length == 0) return '/';
562
+ return x.join('/');
563
+ }
564
+ };
565
+
566
+ // Replace a string with a number if the string is an exact number
567
+ function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
568
+
569
+ // Convert decimal to hex
570
+ function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
571
+
572
+ // Convert a raw string to a hex string
573
+ function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
574
+
575
+ // Convert a buffer into a string
576
+ function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
577
+
578
+ // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
579
+ function hex2rstr(d) {
580
+ if (typeof d != "string" || d.length == 0) return '';
581
+ var r = '', m = ('' + d).match(/../g), t;
582
+ while (t = m.shift()) r += String.fromCharCode('0x' + t);
583
+ return r
584
+ }
585
+
586
+ // Convert an object to string with all functions
587
+ function objToString(x, p, pad, ret) {
588
+ if (ret == undefined) ret = '';
589
+ if (p == undefined) p = 0;
590
+ if (x == null) { return '[null]'; }
591
+ if (p > 8) { return '[...]'; }
592
+ if (x == undefined) { return '[undefined]'; }
593
+ if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
594
+ if (typeof x == 'buffer') { return '[buffer]'; }
595
+ if (typeof x != 'object') { return x; }
596
+ var r = '{' + (ret ? '\r\n' : ' ');
597
+ for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
598
+ return r + addPad(p, pad) + '}';
599
+ }
600
+
601
+ // Return p number of spaces
602
+ function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
603
+
604
+ // Split a string taking into account the quoats. Used for command line parsing
605
+ function splitArgs(str) {
606
+ var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
607
+ do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
608
+ return myArray;
609
+ }
610
+
611
+ // Parse arguments string array into an object
612
+ function parseArgs(argv) {
613
+ var results = { '_': [] }, current = null;
614
+ for (var i = 1, len = argv.length; i < len; i++) {
615
+ var x = argv[i];
616
+ if (x.length > 2 && x[0] == '-' && x[1] == '-') {
617
+ if (current != null) { results[current] = true; }
618
+ current = x.substring(2);
619
+ } else {
620
+ if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
621
+ }
622
+ }
623
+ if (current != null) { results[current] = true; }
624
+ return results;
625
+ }
626
+
627
+ // Get server target url with a custom path
628
+ function getServerTargetUrl(path) {
629
+ var x = mesh.ServerUrl;
630
+ //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
631
+ if (x == null) { return null; }
632
+ if (path == null) { path = ''; }
633
+ x = http.parseUri(x);
634
+ if (x == null) return null;
635
+ return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
636
+ }
637
+
638
+ // Get server url. If the url starts with "*/..." change it, it not use the url as is.
639
+ function getServerTargetUrlEx(url) {
640
+ if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
641
+ return url;
642
+ }
643
+
644
+ // Send a wake-on-lan packet
645
+ function sendWakeOnLan(hexMac) {
646
+ var count = 0;
647
+ try {
648
+ var interfaces = require('os').networkInterfaces();
649
+ var magic = 'FFFFFFFFFFFF';
650
+ for (var x = 1; x <= 16; ++x) { magic += hexMac; }
651
+ var magicbin = Buffer.from(magic, 'hex');
652
+
653
+ for (var adapter in interfaces)
654
+ {
655
+ if (interfaces.hasOwnProperty(adapter))
656
+ {
657
+ for (var i = 0; i < interfaces[adapter].length; ++i)
658
+ {
659
+ var addr = interfaces[adapter][i];
660
+ if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00'))
661
+ {
662
+ try
663
+ {
664
+ var socket = require('dgram').createSocket({ type: 'udp4' });
665
+ socket.bind({ address: addr.address });
666
+ socket.setBroadcast(true);
667
+ socket.send(magicbin, 7, '255.255.255.255');
668
+ socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
669
+ count++;
670
+ }
671
+ catch(ee)
672
+ {
673
+ }
674
+ }
675
+ }
676
+ }
677
+ }
678
+ } catch (e) { }
679
+ return count;
680
+ }
681
+
682
+ // Handle a mesh agent command
683
+ function handleServerCommand(data) {
684
+ if (typeof data == 'object') {
685
+ // If this is a console command, parse it and call the console handler
686
+ switch (data.action) {
687
+ case 'msg': {
688
+ switch (data.type) {
689
+ case 'console': { // Process a console command
690
+ if (data.value && data.sessionid) {
691
+ MeshServerLog("Processing console command: " + data.value, data);
692
+ var args = splitArgs(data.value);
693
+ processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
694
+ }
695
+ break;
696
+ }
697
+ case 'tunnel': {
698
+ if (data.value != null) { // Process a new tunnel connection request
699
+ // Create a new tunnel object
700
+ var xurl = getServerTargetUrlEx(data.value);
701
+ if (xurl != null) {
702
+ xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
703
+ var woptions = http.parseUri(xurl);
704
+ woptions.rejectUnauthorized = 0;
705
+ //sendConsoleText(JSON.stringify(woptions));
706
+ //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
707
+ var tunnel = http.request(woptions);
708
+ tunnel.upgrade = onTunnelUpgrade;
709
+ tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
710
+ tunnel.sessionid = data.sessionid;
711
+ tunnel.rights = data.rights;
712
+ tunnel.consent = data.consent;
713
+ tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
714
+ tunnel.username = data.username;
715
+ tunnel.userid = data.userid;
716
+ tunnel.remoteaddr = data.remoteaddr;
717
+ tunnel.state = 0;
718
+ tunnel.url = xurl;
719
+ tunnel.protocol = 0;
720
+ tunnel.soptions = data.soptions;
721
+ tunnel.tcpaddr = data.tcpaddr;
722
+ tunnel.tcpport = data.tcpport;
723
+ tunnel.udpaddr = data.udpaddr;
724
+ tunnel.udpport = data.udpport;
725
+ tunnel.end();
726
+ // Put the tunnel in the tunnels list
727
+ var index = nextTunnelIndex++;
728
+ tunnel.index = index;
729
+ tunnels[index] = tunnel;
730
+
731
+ //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
732
+ }
733
+ }
734
+ break;
735
+ }
736
+ case 'messagebox': {
737
+ // Display a message box
738
+ if (data.title && data.msg) {
739
+ MeshServerLog("Displaying message box, title=" + data.title + ", message=" + data.msg, data);
740
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
741
+ try { require('message-box').create(data.title, data.msg, 120); } catch (ex) { }
742
+ }
743
+ break;
744
+ }
745
+ case 'ps': {
746
+ // Return the list of running processes
747
+ if (data.sessionid) {
748
+ processManager.getProcesses(function (plist) {
749
+ mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
750
+ });
751
+ }
752
+ break;
753
+ }
754
+ case 'pskill': {
755
+ // Kill a process
756
+ if (data.value) {
757
+ MeshServerLog("Killing process " + data.value, data);
758
+ try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
759
+ }
760
+ break;
761
+ }
762
+ case 'services': {
763
+ // Return the list of installed services
764
+ var services = null;
765
+ try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
766
+ if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
767
+ break;
768
+ }
769
+ case 'serviceStop': {
770
+ // Stop a service
771
+ try {
772
+ var service = require('service-manager').manager.getService(data.serviceName);
773
+ if (service != null) { service.stop(); }
774
+ } catch (e) { }
775
+ break;
776
+ }
777
+ case 'serviceStart': {
778
+ // Start a service
779
+ try {
780
+ var service = require('service-manager').manager.getService(data.serviceName);
781
+ if (service != null) { service.start(); }
782
+ } catch (e) { }
783
+ break;
784
+ }
785
+ case 'serviceRestart': {
786
+ // Restart a service
787
+ try {
788
+ var service = require('service-manager').manager.getService(data.serviceName);
789
+ if (service != null) { service.restart(); }
790
+ } catch (e) { }
791
+ break;
792
+ }
793
+ case 'deskBackground':
794
+ {
795
+ // Toggle desktop background
796
+ try {
797
+ if (process.platform == 'win32') {
798
+ var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
799
+ var sid = undefined;
800
+ if (stype == 1) {
801
+ if (require('MeshAgent')._tsid != null) {
802
+ stype = 5;
803
+ sid = require('MeshAgent')._tsid;
804
+ }
805
+ }
806
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
807
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
808
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
809
+ child.stderr.on('data', function () { });
810
+ child.waitExit();
811
+ var current = child.stdout.str.trim();
812
+ if (current != '') { require('MeshAgent')._wallpaper = current; }
813
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
814
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
815
+ child.stderr.on('data', function () { });
816
+ child.waitExit();
817
+ } else {
818
+ var id = require('user-sessions').consoleUid();
819
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
820
+ if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
821
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
822
+ }
823
+ } catch (e) {
824
+ sendConsoleText(e);
825
+ }
826
+ break;
827
+ }
828
+ case 'openUrl': {
829
+ // Open a local web browser and return success/fail
830
+ MeshServerLog("Opening: " + data.url, data);
831
+ sendConsoleText("OpenURL: " + data.url);
832
+ if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
833
+ break;
834
+ }
835
+ case 'getclip': {
836
+ // Send the load clipboard back to the user
837
+ //sendConsoleText('getClip: ' + JSON.stringify(data));
838
+ if (require('MeshAgent').isService) {
839
+ require('clipboard').dispatchRead().then(function (str) {
840
+ if (str) {
841
+ MeshServerLog("Getting clipboard content, " + str.length + " byte(s)", data);
842
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str });
843
+ }
844
+ });
845
+ } else {
846
+ require("clipboard").read().then(function (str) {
847
+ if (str) {
848
+ MeshServerLog("Getting clipboard content, " + str.length + " byte(s)", data);
849
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str });
850
+ }
851
+ });
852
+ }
853
+ break;
854
+ }
855
+ case 'setclip': {
856
+ // Set the load clipboard to a user value
857
+ //sendConsoleText('setClip: ' + JSON.stringify(data));
858
+ if (typeof data.data == 'string') {
859
+ MeshServerLog("Setting clipboard content, " + data.data.length + " byte(s)", data);
860
+ if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
861
+ mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
862
+ }
863
+ break;
864
+ }
865
+ case 'userSessions': {
866
+ // Send back current user sessions list, this is Windows only.
867
+ //sendConsoleText('userSessions: ' + JSON.stringify(data));
868
+ if (process.platform != 'win32') break;
869
+ var p = require('user-sessions').enumerateUsers();
870
+ p.sessionid = data.sessionid;
871
+ p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
872
+ break;
873
+ }
874
+ default:
875
+ // Unknown action, ignore it.
876
+ break;
877
+ }
878
+ break;
879
+ }
880
+ case 'acmactivate': {
881
+ if (amt != null) {
882
+ MeshServerLog("Attempting Intel AMT ACM mode activation", data);
883
+ amt.setAcmResponse(data);
884
+ }
885
+ break;
886
+ }
887
+ case 'wakeonlan': {
888
+ // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
889
+ sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
890
+ for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
891
+ break;
892
+ }
893
+ case 'uninstallagent':
894
+ // Uninstall this agent
895
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
896
+ if (require('service-manager').manager.getService(agentName).isMe()) {
897
+ try { diagnosticAgent_uninstall(); } catch (x) { }
898
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
899
+ 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 });
900
+ }
901
+ break;
902
+ case 'poweraction': {
903
+ // Server telling us to execute a power action
904
+ if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
905
+ var forced = 0;
906
+ if (data.forced == 1) { forced = 1; }
907
+ data.actiontype = parseInt(data.actiontype);
908
+ MeshServerLog("Performing power action=" + data.actiontype + ", forced=" + forced, data);
909
+ sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
910
+ var r = mesh.ExecPowerState(data.actiontype, forced);
911
+ sendConsoleText("ExecPowerState returned code: " + r);
912
+ }
913
+ break;
914
+ }
915
+ case 'iplocation': {
916
+ // Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
917
+ getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
918
+ break;
919
+ }
920
+ case 'toast': {
921
+ // Display a toast message
922
+ if (data.title && data.msg) {
923
+ MeshServerLog("Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
924
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
925
+ try { require('toaster').Toast(data.title, data.msg); } catch (ex) { }
926
+ }
927
+ break;
928
+ }
929
+ case 'openUrl': {
930
+ // Open a local web browser and return success/fail
931
+ //sendConsoleText('OpenURL: ' + data.url);
932
+ MeshServerLog("Opening: " + data.url, data);
933
+ if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
934
+ break;
935
+ }
936
+ case 'amtPolicy': {
937
+ // Store the latest Intel AMT policy
938
+ amtPolicy = data.amtPolicy;
939
+ if (data.amtPolicy != null) { db.Put('amtPolicy', JSON.stringify(data.amtPolicy)); } else { db.Put('amtPolicy', null); }
940
+ if (amt != null) { amt.setPolicy(amtPolicy, true); }
941
+ break;
942
+ }
943
+ case 'getScript': {
944
+ // Received a configuration script from the server
945
+ sendConsoleText('getScript: ' + JSON.stringify(data));
946
+ break;
947
+ }
948
+ case 'sysinfo': {
949
+ // Fetch system information
950
+ getSystemInformation(function (results) {
951
+ if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
952
+ });
953
+ break;
954
+ }
955
+ case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
956
+ case 'pong': { break; }
957
+ case 'plugin': {
958
+ try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
959
+ break;
960
+ }
961
+ case 'coredump':
962
+ if (data.value === true) {
963
+ // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
964
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
965
+ } else if (data.value === false) {
966
+ process.coreDumpLocation = null;
967
+ }
968
+ break;
969
+ default:
970
+ // Unknown action, ignore it.
971
+ break;
972
+ }
973
+ }
974
+ }
975
+
976
+ // Called when a file changed in the file system
977
+ /*
978
+ function onFileWatcher(a, b) {
979
+ console.log('onFileWatcher', a, b, this.path);
980
+ var response = getDirectoryInfo(this.path);
981
+ if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
982
+ }
983
+ */
984
+
985
+ function getSystemInformation(func) {
986
+ try {
987
+ var results = { hardware: require('identifiers').get() }; // Hardware info
988
+ if (results.hardware && results.hardware.windows) {
989
+ // Remove extra entries and things that change quickly
990
+ var x = results.hardware.windows.osinfo;
991
+ try { delete x.FreePhysicalMemory; } catch (ex) { }
992
+ try { delete x.FreeSpaceInPagingFiles; } catch (ex) { }
993
+ try { delete x.FreeVirtualMemory; } catch (ex) { }
994
+ try { delete x.LocalDateTime; } catch (ex) { }
995
+ try { delete x.MaxProcessMemorySize; } catch (ex) { }
996
+ try { delete x.TotalVirtualMemorySize; } catch (ex) { }
997
+ try { delete x.TotalVisibleMemorySize; } catch (ex) { }
998
+ try {
999
+ if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1000
+ if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1001
+ if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1002
+ } catch (ex) { }
1003
+ }
1004
+ if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1005
+ /*
1006
+ if (process.platform == 'win32') {
1007
+ var defragResult = function (r) {
1008
+ if (typeof r == 'object') { results[this.callname] = r; }
1009
+ if (this.callname == 'defrag') {
1010
+ var pr = require('win-info').installedApps(); // Installed apps
1011
+ pr.callname = 'installedApps';
1012
+ pr.sessionid = data.sessionid;
1013
+ pr.then(defragResult, defragResult);
1014
+ }
1015
+ else {
1016
+ results.winpatches = require('win-info').qfe(); // Windows patches
1017
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1018
+ func(results);
1019
+ }
1020
+ }
1021
+ var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1022
+ pr.callname = 'defrag';
1023
+ pr.sessionid = data.sessionid;
1024
+ pr.then(defragResult, defragResult);
1025
+ } else {
1026
+ */
1027
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1028
+ func(results);
1029
+ //}
1030
+ } catch (ex) { func(null, ex); }
1031
+ }
1032
+
1033
+ // Get a formated response for a given directory path
1034
+ function getDirectoryInfo(reqpath) {
1035
+ var response = { path: reqpath, dir: [] };
1036
+ if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1037
+ // List all the drives in the root, or the root itself
1038
+ var results = null;
1039
+ try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1040
+ if (results != null) {
1041
+ for (var i = 0; i < results.length; ++i) {
1042
+ var drive = { n: results[i].name, t: 1 };
1043
+ if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1044
+ response.dir.push(drive);
1045
+ }
1046
+ }
1047
+ } else {
1048
+ // List all the files and folders in this path
1049
+ if (reqpath == '') { reqpath = '/'; }
1050
+ var results = null, xpath = obj.path.join(reqpath, '*');
1051
+ //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1052
+ try { results = fs.readdirSync(xpath); } catch (e) { }
1053
+ if (results != null) {
1054
+ for (var i = 0; i < results.length; ++i) {
1055
+ if ((results[i] != '.') && (results[i] != '..')) {
1056
+ var stat = null, p = obj.path.join(reqpath, results[i]);
1057
+ //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1058
+ try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1059
+ if ((stat != null) && (stat != undefined)) {
1060
+ if (stat.isDirectory() == true) {
1061
+ response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1062
+ } else {
1063
+ response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1064
+ }
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+ return response;
1071
+ }
1072
+
1073
+ // Tunnel callback operations
1074
+ function onTunnelUpgrade(response, s, head) {
1075
+ this.s = s;
1076
+ s.httprequest = this;
1077
+ s.end = onTunnelClosed;
1078
+ s.tunnel = this;
1079
+ s.descriptorMetadata = "MeshAgent_relayTunnel";
1080
+
1081
+ if (require('MeshAgent').idleTimeout != null)
1082
+ {
1083
+ s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1084
+ s.on('timeout', function ()
1085
+ {
1086
+ this.ping();
1087
+ this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1088
+ });
1089
+ }
1090
+
1091
+
1092
+ //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1093
+
1094
+ if (this.tcpport != null) {
1095
+ // This is a TCP relay connection, pause now and try to connect to the target.
1096
+ s.pause();
1097
+ s.data = onTcpRelayServerTunnelData;
1098
+ var connectionOptions = { port: parseInt(this.tcpport) };
1099
+ if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1100
+ s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1101
+ s.tcprelay.peerindex = this.index;
1102
+ } if (this.udpport != null) {
1103
+ // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1104
+ s.data = onUdpRelayServerTunnelData;
1105
+ s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1106
+ s.udprelay.bind({ port: 0 });
1107
+ s.udprelay.peerindex = this.index;
1108
+ s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1109
+ s.udprelay.udpport = this.udpport;
1110
+ s.udprelay.udpaddr = this.udpaddr;
1111
+ s.udprelay.first = true;
1112
+ } else {
1113
+ // This is a normal connect for KVM/Terminal/Files
1114
+ s.data = onTunnelData;
1115
+ }
1116
+ }
1117
+
1118
+ // Called when UDP relay data is received // TODO****
1119
+ function onUdpRelayTargetTunnelConnect(data) {
1120
+ var peerTunnel = tunnels[this.peerindex];
1121
+ peerTunnel.s.write(data);
1122
+ }
1123
+
1124
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1125
+ function onUdpRelayServerTunnelData(data) {
1126
+ if (this.udprelay.first === true) {
1127
+ delete this.udprelay.first; // Skip the first 'c' that is received.
1128
+ } else {
1129
+ this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1130
+ }
1131
+ }
1132
+
1133
+ // Called when the TCP relay target is connected
1134
+ function onTcpRelayTargetTunnelConnect() {
1135
+ var peerTunnel = tunnels[this.peerindex];
1136
+ this.pipe(peerTunnel.s); // Pipe Target --> Server
1137
+ peerTunnel.s.first = true;
1138
+ peerTunnel.s.resume();
1139
+ }
1140
+
1141
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1142
+ function onTcpRelayServerTunnelData(data) {
1143
+ if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
1144
+ }
1145
+
1146
+ function onTunnelClosed() {
1147
+ if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
1148
+ //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
1149
+ delete tunnels[this.httprequest.index];
1150
+
1151
+ /*
1152
+ // Close the watcher if required
1153
+ if (this.httprequest.watcher != undefined) {
1154
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1155
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1156
+ delete this.httprequest.watcher;
1157
+ }
1158
+ */
1159
+
1160
+ // If there is a upload or download active on this connection, close the file
1161
+ if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
1162
+ if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
1163
+
1164
+ // Clean up WebRTC
1165
+ if (this.webrtc != null) {
1166
+ if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
1167
+ if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1168
+ try { this.webrtc.close(); } catch (e) { }
1169
+ this.webrtc.removeAllListeners('connected');
1170
+ this.webrtc.removeAllListeners('disconnected');
1171
+ this.webrtc.removeAllListeners('dataChannel');
1172
+ delete this.webrtc;
1173
+ }
1174
+
1175
+ // Clean up WebSocket
1176
+ this.removeAllListeners('data');
1177
+ }
1178
+ function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1179
+ function onTunnelData(data) {
1180
+ //console.log("OnTunnelData");
1181
+ //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1182
+
1183
+ // If this is upload data, save it to file
1184
+ if (this.httprequest.uploadFile) {
1185
+ if (typeof data == 'object') {
1186
+ 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.
1187
+ this.write(new Buffer(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data
1188
+ }
1189
+ return;
1190
+ }
1191
+ /*
1192
+ // If this is a download, send more of the file
1193
+ if (this.httprequest.downloadFile) {
1194
+ var buf = new Buffer(4096);
1195
+ var len = fs.readSync(this.httprequest.downloadFile, buf, 0, 4096, null);
1196
+ this.httprequest.downloadFilePtr += len;
1197
+ if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); }
1198
+ return;
1199
+ }
1200
+ */
1201
+
1202
+ if (this.httprequest.state == 0) {
1203
+ // Check if this is a relay connection
1204
+ if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1205
+ }
1206
+ else
1207
+ {
1208
+ // Handle tunnel data
1209
+ if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user)
1210
+ // Take a look at the protocol
1211
+ if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1212
+ this.httprequest.protocol = parseInt(data);
1213
+ if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1214
+ if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1215
+ {
1216
+ //
1217
+ // Remote Terminal
1218
+ //
1219
+
1220
+ // Check user access rights for terminal
1221
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1222
+ // Disengage this tunnel, user does not have the rights to do this!!
1223
+ this.httprequest.protocol = 999999;
1224
+ this.httprequest.s.end();
1225
+ sendConsoleText("Error: No Terminal Control Rights.");
1226
+ return;
1227
+ }
1228
+
1229
+ this.descriptorMetadata = "Remote Terminal";
1230
+
1231
+ if (process.platform == 'win32')
1232
+ {
1233
+ if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1234
+ {
1235
+ this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1236
+ this.httprequest.s.end();
1237
+ return;
1238
+ }
1239
+ }
1240
+
1241
+ var prom = require('promise');
1242
+ this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1243
+ this.httprequest.tpromise.that = this;
1244
+ this.httprequest.tpromise.httprequest = this.httprequest;
1245
+
1246
+ this.end = function ()
1247
+ {
1248
+ if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1249
+ if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1250
+
1251
+ // Remove the terminal session to the count to update the server
1252
+ if (this.httprequest.userid != null)
1253
+ {
1254
+ if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } }
1255
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
1256
+ }
1257
+
1258
+ if (process.platform == 'win32')
1259
+ {
1260
+ // Unpipe the web socket
1261
+ this.unpipe(this.httprequest._term);
1262
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1263
+
1264
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1265
+ if (this.rtcchannel)
1266
+ {
1267
+ this.rtcchannel.unpipe(this.httprequest._term);
1268
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1269
+ }
1270
+
1271
+ // Clean up
1272
+ if (this.httprequest._term) { this.httprequest._term.end(); }
1273
+ this.httprequest._term = null;
1274
+ }
1275
+ };
1276
+
1277
+ // Perform User-Consent if needed.
1278
+ if (this.httprequest.consent && (this.httprequest.consent & 16))
1279
+ {
1280
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1281
+ var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1282
+ if (this.httprequest.soptions != null) {
1283
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1284
+ if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.username); }
1285
+ }
1286
+ this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1287
+ this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1288
+ this.httprequest.tpromise._consent.then(
1289
+ function ()
1290
+ {
1291
+ // Success
1292
+ MeshServerLog("Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1293
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1294
+ this.retPromise._consent = null;
1295
+ this.retPromise._res();
1296
+ },
1297
+ function (e)
1298
+ {
1299
+ // Denied
1300
+ MeshServerLog("Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1301
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1302
+ this.retPromise._rej(e.toString());
1303
+ });
1304
+ }
1305
+ else
1306
+ {
1307
+ // User-Consent is not required, so just resolve this promise
1308
+ this.httprequest.tpromise._res();
1309
+ }
1310
+
1311
+
1312
+ this.httprequest.tpromise.then(
1313
+ function ()
1314
+ {
1315
+ this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1316
+ this.httprequest.connectionPromise.ws = this.that;
1317
+
1318
+ // Start Terminal
1319
+ if(process.platform == 'win32')
1320
+ {
1321
+ try
1322
+ {
1323
+ var cols = 80, rows = 25;
1324
+ if (this.httprequest.xoptions)
1325
+ {
1326
+ if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1327
+ if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1328
+ }
1329
+
1330
+ if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1331
+ {
1332
+ // Admin Terminal
1333
+ if (require('win-virtual-terminal').supported)
1334
+ {
1335
+ // ConPTY PseudoTerminal
1336
+ // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1337
+
1338
+ // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1339
+ this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1340
+ this.httprequest._dispatcher.httprequest = this.httprequest;
1341
+ this.httprequest._dispatcher.on('connection', function (c)
1342
+ {
1343
+ if (this.httprequest.connectionPromise.completed)
1344
+ {
1345
+ c.end();
1346
+ }
1347
+ else
1348
+ {
1349
+ this.httprequest.connectionPromise._res(c);
1350
+ }
1351
+ });
1352
+ }
1353
+ else
1354
+ {
1355
+ // Legacy Terminal
1356
+ this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1357
+ }
1358
+ }
1359
+ else
1360
+ {
1361
+ // Logged in user
1362
+ var userPromise = require('user-sessions').enumerateUsers();
1363
+ userPromise.that = this;
1364
+ userPromise.then(function (u)
1365
+ {
1366
+ var that = this.that;
1367
+ if (u.Active.length > 0)
1368
+ {
1369
+ var username = u.Active[0].Username;
1370
+ if (require('win-virtual-terminal').supported)
1371
+ {
1372
+ // ConPTY PseudoTerminal
1373
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1374
+ }
1375
+ else
1376
+ {
1377
+ // Legacy Terminal
1378
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1379
+ }
1380
+ that.httprequest._dispatcher.ws = that;
1381
+ that.httprequest._dispatcher.on('connection', function (c)
1382
+ {
1383
+ if (this.ws.httprequest.connectionPromise.completed)
1384
+ {
1385
+ c.end();
1386
+ }
1387
+ else
1388
+ {
1389
+ this.ws.httprequest.connectionPromise._res(c);
1390
+ }
1391
+ });
1392
+ }
1393
+ });
1394
+ }
1395
+ }
1396
+ catch (e)
1397
+ {
1398
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1399
+ }
1400
+ }
1401
+ else
1402
+ {
1403
+ try
1404
+ {
1405
+ var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1406
+ var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1407
+ var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1408
+
1409
+ var env = { HISTCONTROL: 'ignoreboth' };
1410
+ if (this.httprequest.xoptions)
1411
+ {
1412
+ if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1413
+ if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1414
+ }
1415
+ var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1416
+ if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1417
+ {
1418
+ if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1419
+ this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1420
+ }
1421
+ else if (bash)
1422
+ {
1423
+ var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1424
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1425
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1426
+ this.httprequest.connectionPromise._res(p);
1427
+ }
1428
+ else if (sh)
1429
+ {
1430
+ var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1431
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1432
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1433
+ this.httprequest.connectionPromise._res(p);
1434
+ }
1435
+ else
1436
+ {
1437
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1438
+ }
1439
+ }
1440
+ catch (e)
1441
+ {
1442
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1443
+ }
1444
+ }
1445
+
1446
+ this.httprequest.connectionPromise.then(
1447
+ function (term)
1448
+ {
1449
+ // SUCCESS
1450
+ var stdoutstream;
1451
+ var stdinstream;
1452
+ if (process.platform == 'win32')
1453
+ {
1454
+ this.ws.httprequest._term = term;
1455
+ this.ws.httprequest._term.tunnel = this.ws;
1456
+ stdoutstream = stdinstream = term;
1457
+ }
1458
+ else
1459
+ {
1460
+ term.descriptorMetadata = 'Remote Terminal';
1461
+ this.ws.httprequest.process = term;
1462
+ this.ws.httprequest.process.tunnel = this.ws;
1463
+ term.stderr.stdout = term.stdout;
1464
+ term.stderr.on('data', function (c) { this.stdout.write(c); });
1465
+ stdoutstream = term.stdout;
1466
+ stdinstream = term.stdin;
1467
+ this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1468
+ term.prependListener('exit', function () { this.tunnel.end(); });
1469
+ }
1470
+
1471
+ this.ws.removeAllListeners('data');
1472
+ this.ws.on('data', onTunnelControlData);
1473
+
1474
+ stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1475
+ this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1476
+
1477
+ // Add the terminal session to the count to update the server
1478
+ if (this.ws.httprequest.userid != null)
1479
+ {
1480
+ if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1481
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (ex) { }
1482
+ }
1483
+
1484
+ // Toast Notification, if required
1485
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1486
+ {
1487
+ // User Notifications is required
1488
+ var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1489
+ if (this.ws.httprequest.soptions != null) {
1490
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1491
+ if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.username); }
1492
+ }
1493
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1494
+ }
1495
+ },
1496
+ function (e)
1497
+ {
1498
+ // FAILED to connect terminal
1499
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1500
+ this.ws.end();
1501
+ });
1502
+ },
1503
+ function (e)
1504
+ {
1505
+ // DO NOT start terminal
1506
+ this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1507
+ this.that.end();
1508
+ });
1509
+ }
1510
+ else if (this.httprequest.protocol == 2)
1511
+ {
1512
+ //
1513
+ // Remote KVM
1514
+ //
1515
+
1516
+ // Check user access rights for desktop
1517
+ if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1518
+ // Disengage this tunnel, user does not have the rights to do this!!
1519
+ this.httprequest.protocol = 999999;
1520
+ this.httprequest.s.end();
1521
+ sendConsoleText("Error: No Desktop Control Rights.");
1522
+ return;
1523
+ }
1524
+
1525
+ this.descriptorMetadata = "Remote KVM";
1526
+
1527
+ // Look for a TSID
1528
+ var tsid = null;
1529
+ if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1530
+ require('MeshAgent')._tsid = tsid;
1531
+
1532
+ // Remote desktop using native pipes
1533
+ this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1534
+ this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1535
+ this.desktop = this.httprequest.desktop;
1536
+
1537
+ // Add ourself to the list of remote desktop sessions
1538
+ if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1539
+ this.httprequest.desktop.kvm.tunnels.push(this);
1540
+
1541
+ // Send a metadata update to all desktop sessions
1542
+ var users = {};
1543
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1544
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (ex) { } }
1545
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (ex) { } }
1546
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
1547
+ }
1548
+
1549
+ this.end = function () {
1550
+ --this.desktop.kvm.connectionCount;
1551
+
1552
+ // Remove ourself from the list of remote desktop session
1553
+ var i = this.desktop.kvm.tunnels.indexOf(this);
1554
+ if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1555
+
1556
+ // Send a metadata update to all desktop sessions
1557
+ var users = {};
1558
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1559
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (ex) { } }
1560
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (ex) { } }
1561
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (ex) { }
1562
+ }
1563
+
1564
+ // Unpipe the web socket
1565
+ try
1566
+ {
1567
+ this.unpipe(this.httprequest.desktop.kvm);
1568
+ this.httprequest.desktop.kvm.unpipe(this);
1569
+ }
1570
+ catch(ex) { }
1571
+
1572
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1573
+ if (this.rtcchannel)
1574
+ {
1575
+ try
1576
+ {
1577
+ this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1578
+ this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1579
+ }
1580
+ catch(ex) { }
1581
+ }
1582
+
1583
+ // Place wallpaper back if needed
1584
+ // TODO
1585
+
1586
+ if (this.desktop.kvm.connectionCount == 0) {
1587
+ // Display a toast message. This may not be supported on all platforms.
1588
+ // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (ex) { }
1589
+
1590
+ this.httprequest.desktop.kvm.end();
1591
+ if (this.httprequest.desktop.kvm.connectionBar) {
1592
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1593
+ this.httprequest.desktop.kvm.connectionBar.close();
1594
+ this.httprequest.desktop.kvm.connectionBar = null;
1595
+ }
1596
+ } else {
1597
+ for (var i in this.httprequest.desktop.kvm.users) {
1598
+ if (this.httprequest.desktop.kvm.users[i] == this.httprequest.username && this.httprequest.desktop.kvm.connectionBar) {
1599
+ this.httprequest.desktop.kvm.users.splice(i, 1);
1600
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1601
+ this.httprequest.desktop.kvm.connectionBar.close();
1602
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1603
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1604
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1605
+ MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1606
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1607
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1608
+ }
1609
+ this.httprequest.desktop.kvm.end();
1610
+ });
1611
+ break;
1612
+ }
1613
+ }
1614
+ }
1615
+ };
1616
+ if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1617
+ this.httprequest.desktop.kvm.connectionCount++;
1618
+ this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1619
+ } else {
1620
+ this.httprequest.desktop.kvm.connectionCount = 1;
1621
+ this.httprequest.desktop.kvm.users = [this.httprequest.username];
1622
+ }
1623
+
1624
+ if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1625
+ // If we have remote control rights, pipe the KVM input
1626
+ this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1627
+ } else {
1628
+ // We need to only pipe non-mouse & non-keyboard inputs.
1629
+ //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1630
+ // TODO!!!
1631
+ }
1632
+
1633
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
1634
+ if (this.httprequest.consent && (this.httprequest.consent & 8))
1635
+ {
1636
+ // User Consent Prompt is required
1637
+ // Send a console message back using the console channel, "\n" is supported.
1638
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1639
+ var consentMessage = this.httprequest.username + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1640
+ if (this.httprequest.soptions != null) {
1641
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1642
+ if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.username); }
1643
+ }
1644
+ var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1645
+ pr.ws = this;
1646
+ this.pause();
1647
+ this._consentpromise = pr;
1648
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1649
+ pr.then(
1650
+ function ()
1651
+ {
1652
+ // Success
1653
+ this.ws._consentpromise = null;
1654
+ MeshServerLog("Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1655
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1656
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1657
+ // User Notifications is required
1658
+ var notifyMessage = this.ws.httprequest.username + " started a remote desktop session.", notifyTitle = "MeshCentral";
1659
+ if (this.ws.httprequest.soptions != null) {
1660
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1661
+ if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.username); }
1662
+ }
1663
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
1664
+ }
1665
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1666
+ // Connection Bar is required
1667
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1668
+ this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1669
+ this.ws.httprequest.desktop.kvm.connectionBar.close();
1670
+ }
1671
+ try {
1672
+ this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1673
+ MeshServerLog("Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1674
+ }
1675
+ catch (xx) {
1676
+ if (process.platform != 'darwin') {
1677
+ MeshServerLog("Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1678
+ }
1679
+ }
1680
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1681
+ this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1682
+ this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1683
+ MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1684
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1685
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1686
+ }
1687
+ this.httprequest.desktop.kvm.end();
1688
+ });
1689
+ }
1690
+ }
1691
+ this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1692
+ this.ws.resume();
1693
+ },
1694
+ function (e)
1695
+ {
1696
+ // User Consent Denied/Failed
1697
+ this.ws._consentpromise = null;
1698
+ MeshServerLog("Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1699
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1700
+ });
1701
+ }
1702
+ else {
1703
+ // User Consent Prompt is not required
1704
+ if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1705
+ // User Notifications is required
1706
+ MeshServerLog("Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1707
+ var notifyMessage = this.httprequest.username + " started a remote desktop session.", notifyTitle = "MeshCentral";
1708
+ if (this.httprequest.soptions != null) {
1709
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1710
+ if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.username); }
1711
+ }
1712
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (ex) { }
1713
+ } else {
1714
+ MeshServerLog("Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1715
+ }
1716
+ if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1717
+ // Connection Bar is required
1718
+ if (this.httprequest.desktop.kvm.connectionBar) {
1719
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1720
+ this.httprequest.desktop.kvm.connectionBar.close();
1721
+ }
1722
+ try {
1723
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.sort().join(', ')), require('MeshAgent')._tsid);
1724
+ MeshServerLog("Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1725
+ }
1726
+ catch (xx) {
1727
+ MeshServerLog("Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1728
+ }
1729
+ if (this.httprequest.desktop.kvm.connectionBar) {
1730
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1731
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1732
+ MeshServerLog("Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1733
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1734
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1735
+ }
1736
+ this.httprequest.desktop.kvm.end();
1737
+ });
1738
+ }
1739
+ }
1740
+ this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1741
+ }
1742
+
1743
+ this.removeAllListeners('data');
1744
+ this.on('data', onTunnelControlData);
1745
+ //this.write('MeshCore KVM Hello!1');
1746
+
1747
+ } else if (this.httprequest.protocol == 5)
1748
+ {
1749
+ //
1750
+ // Remote Files
1751
+ //
1752
+
1753
+ // Check user access rights for files
1754
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
1755
+ // Disengage this tunnel, user does not have the rights to do this!!
1756
+ this.httprequest.protocol = 999999;
1757
+ this.httprequest.s.end();
1758
+ sendConsoleText("Error: No files control rights.");
1759
+ return;
1760
+ }
1761
+
1762
+ this.descriptorMetadata = "Remote Files";
1763
+
1764
+ // Add the files session to the count to update the server
1765
+ if (this.httprequest.userid != null) {
1766
+ if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
1767
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
1768
+ }
1769
+
1770
+ this.end = function () {
1771
+ // Remove the files session from the count to update the server
1772
+ if (this.httprequest.userid != null) {
1773
+ if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } }
1774
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (ex) { }
1775
+ }
1776
+ };
1777
+
1778
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
1779
+ if (this.httprequest.consent && (this.httprequest.consent & 32)) {
1780
+ // User Consent Prompt is required
1781
+ // Send a console message back using the console channel, "\n" is supported.
1782
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1783
+ var consentMessage = this.httprequest.username + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
1784
+ if (this.httprequest.soptions != null) {
1785
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1786
+ if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.username); }
1787
+ }
1788
+ var pr = require('message-box').create(consentTitle, consentMessage, 30);
1789
+ pr.ws = this;
1790
+ this.pause();
1791
+ this._consentpromise = pr;
1792
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
1793
+ pr.then(
1794
+ function ()
1795
+ {
1796
+ // Success
1797
+ this.ws._consentpromise = null;
1798
+ MeshServerLog("Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1799
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
1800
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
1801
+ // User Notifications is required
1802
+ var notifyMessage = this.ws.httprequest.username + " started a remote file session.", notifyTitle = "MeshCentral";
1803
+ if (this.ws.httprequest.soptions != null) {
1804
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1805
+ if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.username); }
1806
+ }
1807
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1808
+ }
1809
+ this.ws.resume();
1810
+ },
1811
+ function (e)
1812
+ {
1813
+ // User Consent Denied/Failed
1814
+ this.ws._consentpromise = null;
1815
+ MeshServerLog("Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1816
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1817
+ });
1818
+ } else {
1819
+ // User Consent Prompt is not required
1820
+ if (this.httprequest.consent && (this.httprequest.consent & 4)) {
1821
+ // User Notifications is required
1822
+ MeshServerLog("Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1823
+ var notifyMessage = this.httprequest.username + " started a remote file session.", notifyTitle = "MeshCentral";
1824
+ if (this.httprequest.soptions != null) {
1825
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1826
+ if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.username); }
1827
+ }
1828
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (ex) { }
1829
+ } else {
1830
+ MeshServerLog("Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1831
+ }
1832
+ this.resume();
1833
+ }
1834
+
1835
+ // Setup files
1836
+ // NOP
1837
+ }
1838
+ } else if (this.httprequest.protocol == 1) {
1839
+ // Send data into terminal stdin
1840
+ //this.write(data); // Echo back the keys (Does not seem to be a good idea)
1841
+ } else if (this.httprequest.protocol == 2) {
1842
+ // Send data into remote desktop
1843
+ if (this.httprequest.desktop.state == 0) {
1844
+ this.write(new Buffer(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
1845
+ this.httprequest.desktop.state = 1;
1846
+ } else {
1847
+ this.httprequest.desktop.write(data);
1848
+ }
1849
+ } else if (this.httprequest.protocol == 5) {
1850
+ // Process files commands
1851
+ var cmd = null;
1852
+ try { cmd = JSON.parse(data); } catch (e) { };
1853
+ if (cmd == null) { return; }
1854
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
1855
+ if (cmd.action == undefined) { return; }
1856
+ //sendConsoleText('CMD: ' + JSON.stringify(cmd));
1857
+
1858
+ if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
1859
+ //console.log(objToString(cmd, 0, ' '));
1860
+ switch (cmd.action) {
1861
+ case 'ls': {
1862
+ /*
1863
+ // Close the watcher if required
1864
+ var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
1865
+ if ((this.httprequest.watcher != undefined) && (samepath == false)) {
1866
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1867
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1868
+ delete this.httprequest.watcher;
1869
+ }
1870
+ */
1871
+
1872
+ // Send the folder content to the browser
1873
+ var response = getDirectoryInfo(cmd.path);
1874
+ if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
1875
+ this.write(new Buffer(JSON.stringify(response)));
1876
+
1877
+ /*
1878
+ // Start the directory watcher
1879
+ if ((cmd.path != '') && (samepath == false)) {
1880
+ var watcher = fs.watch(cmd.path, onFileWatcher);
1881
+ watcher.tunnel = this.httprequest;
1882
+ watcher.path = cmd.path;
1883
+ this.httprequest.watcher = watcher;
1884
+ //console.log('Starting watcher: ' + this.httprequest.watcher.path);
1885
+ }
1886
+ */
1887
+ break;
1888
+ }
1889
+ case 'mkdir': {
1890
+ // Create a new empty folder
1891
+ fs.mkdirSync(cmd.path);
1892
+ MeshServerLog("Create folder: \"" + cmd.path + "\"", this.httprequest);
1893
+ break;
1894
+ }
1895
+ case 'rm': {
1896
+ // Delete, possibly recursive delete
1897
+ for (var i in cmd.delfiles) {
1898
+ var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
1899
+ try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
1900
+ if ((delcount == 1) && !cmd.rec) {
1901
+ MeshServerLog("Delete: \"" + p + "\"", this.httprequest);
1902
+ } else {
1903
+ MeshServerLog((cmd.rec ? "Delete recursive: \"" : "Delete: \"") + p + "\", " + delcount + " element(s) removed", this.httprequest);
1904
+ }
1905
+ }
1906
+ break;
1907
+ }
1908
+ case 'rename': {
1909
+ // Rename a file or folder
1910
+ var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
1911
+ var newfullpath = obj.path.join(cmd.path, cmd.newname);
1912
+ MeshServerLog('Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
1913
+ try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
1914
+ break;
1915
+ }
1916
+ case 'download': {
1917
+ // Download a file
1918
+ var sendNextBlock = 0;
1919
+ if (cmd.sub == 'start') { // Setup the download
1920
+ MeshServerLog('Download: \"' + cmd.path + '\"', this.httprequest);
1921
+ if (this.filedownload != null) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
1922
+ this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
1923
+ try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
1924
+ if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
1925
+ } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
1926
+ if (cmd.sub == 'startack') { sendNextBlock = 8; } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
1927
+ }
1928
+ // Send the next download block(s)
1929
+ while (sendNextBlock > 0) {
1930
+ sendNextBlock--;
1931
+ var buf = new Buffer(4096);
1932
+ var len = fs.readSync(this.filedownload.f, buf, 4, 4092, null);
1933
+ this.filedownload.ptr += len;
1934
+ if (len < 4092) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
1935
+ this.write(buf.slice(0, len + 4)); // Write as binary
1936
+ }
1937
+ break;
1938
+ }
1939
+ /*
1940
+ case 'download': {
1941
+ // Packet download of a file, agent to browser
1942
+ if (cmd.path == undefined) break;
1943
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1944
+ //console.log('Download: ' + filepath);
1945
+ try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
1946
+ this.httprequest.downloadFileId = cmd.reqid;
1947
+ this.httprequest.downloadFilePtr = 0;
1948
+ if (this.httprequest.downloadFile) { this.write(new Buffer(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
1949
+ break;
1950
+ }
1951
+ case 'download2': {
1952
+ // Stream download of a file, agent to browser
1953
+ if (cmd.path == undefined) break;
1954
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1955
+ try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
1956
+ this.httprequest.downloadFile.pipe(this);
1957
+ this.httprequest.downloadFile.end = function () { }
1958
+ break;
1959
+ }
1960
+ */
1961
+ case 'upload': {
1962
+ // Upload a file, browser to agent
1963
+ if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
1964
+ if (cmd.path == undefined) break;
1965
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
1966
+ MeshServerLog('Upload: \"' + filepath + '\"', this.httprequest);
1967
+ try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
1968
+ this.httprequest.uploadFileid = cmd.reqid;
1969
+ if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
1970
+ break;
1971
+ }
1972
+ case 'copy': {
1973
+ // Copy a bunch of files from scpath to dspath
1974
+ for (var i in cmd.names) {
1975
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
1976
+ MeshServerLog('Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
1977
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
1978
+ }
1979
+ break;
1980
+ }
1981
+ case 'move': {
1982
+ // Move a bunch of files from scpath to dspath
1983
+ for (var i in cmd.names) {
1984
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
1985
+ MeshServerLog('Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
1986
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
1987
+ }
1988
+ break;
1989
+ }
1990
+ default:
1991
+ // Unknown action, ignore it.
1992
+ break;
1993
+ }
1994
+ } else if (this.httprequest.protocol == 7) { // Plugin data exchange
1995
+ var cmd = null;
1996
+ try { cmd = JSON.parse(data); } catch (e) { };
1997
+ if (cmd == null) { return; }
1998
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
1999
+ if (cmd.action == undefined) return;
2000
+
2001
+ switch (cmd.action) {
2002
+ case 'plugin': {
2003
+ try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2004
+ break;
2005
+ }
2006
+ default: {
2007
+ // probably shouldn't happen, but just in case this feature is expanded
2008
+ }
2009
+ }
2010
+
2011
+ }
2012
+ //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2013
+ }
2014
+ }
2015
+
2016
+ // Delete a directory with a files and directories within it
2017
+ function deleteFolderRecursive(path, rec) {
2018
+ var count = 0;
2019
+ if (fs.existsSync(path)) {
2020
+ if (rec == true) {
2021
+ fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2022
+ var curPath = obj.path.join(path, file);
2023
+ if (fs.statSync(curPath).isDirectory()) { // recurse
2024
+ count += deleteFolderRecursive(curPath, true);
2025
+ } else { // delete file
2026
+ fs.unlinkSync(curPath);
2027
+ count++;
2028
+ }
2029
+ });
2030
+ }
2031
+ fs.unlinkSync(path);
2032
+ count++;
2033
+ }
2034
+ return count;
2035
+ };
2036
+
2037
+ // Called when receiving control data on WebRTC
2038
+ function onTunnelWebRTCControlData(data) {
2039
+ if (typeof data != 'string') return;
2040
+ var obj;
2041
+ try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2042
+ if (obj.type == 'close') {
2043
+ //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2044
+ try { this.close(); } catch (e) { }
2045
+ try { this.xrtc.close(); } catch (e) { }
2046
+ }
2047
+ }
2048
+
2049
+ // Called when receiving control data on websocket
2050
+ function onTunnelControlData(data, ws) {
2051
+ var obj;
2052
+ if (ws == null) { ws = this; }
2053
+ if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2054
+ else if (typeof data == 'object') { obj = data; } else { return; }
2055
+ //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2056
+ //console.log('onTunnelControlData: ' + JSON.stringify(data));
2057
+
2058
+ if (obj.action) {
2059
+ switch (obj.action) {
2060
+ case 'lock': {
2061
+ // Lock the current user out of the desktop
2062
+ try {
2063
+ if (process.platform == 'win32') {
2064
+ MeshServerLog("Locking remote user out of desktop", ws.httprequest);
2065
+ var child = require('child_process');
2066
+ child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2067
+ }
2068
+ } catch (e) { }
2069
+ break;
2070
+ }
2071
+ default:
2072
+ // Unknown action, ignore it.
2073
+ break;
2074
+ }
2075
+ return;
2076
+ }
2077
+
2078
+ switch (obj.type) {
2079
+ case 'options': {
2080
+ // These are additional connection options passed in the control channel.
2081
+ //sendConsoleText('options: ' + JSON.stringify(obj));
2082
+ delete obj.type;
2083
+ ws.httprequest.xoptions = obj;
2084
+
2085
+ // Set additional user consent options if present
2086
+ if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2087
+
2088
+ break;
2089
+ }
2090
+ case 'close': {
2091
+ // We received the close on the websocket
2092
+ //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2093
+ try { ws.close(); } catch (e) { }
2094
+ break;
2095
+ }
2096
+ case 'termsize': {
2097
+ // Indicates a change in terminal size
2098
+ if (process.platform == 'win32')
2099
+ {
2100
+ if (ws.httprequest._dispatcher == null) return;
2101
+ //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2102
+ if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2103
+ } else
2104
+ {
2105
+ if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2106
+ //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2107
+
2108
+ if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2109
+ }
2110
+ break;
2111
+ }
2112
+ case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2113
+ if (ws.httprequest.protocol == 1) { // Terminal
2114
+ // 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
2115
+ if (process.platform == 'win32') {
2116
+ ws.httprequest._term.unpipe(ws);
2117
+ } else {
2118
+ ws.httprequest.process.stdout.unpipe(ws);
2119
+ ws.httprequest.process.stderr.unpipe(ws);
2120
+ }
2121
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2122
+ // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
2123
+ ws.httprequest.desktop.kvm.unpipe(ws);
2124
+ } else {
2125
+ // Switch things around so all WebRTC data goes to onTunnelData().
2126
+ ws.rtcchannel.httprequest = ws.httprequest;
2127
+ ws.rtcchannel.removeAllListeners('data');
2128
+ ws.rtcchannel.on('data', onTunnelData);
2129
+ }
2130
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2131
+ break;
2132
+ }
2133
+ case 'webrtc1': {
2134
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2135
+ // Switch the user input from websocket to webrtc at this point.
2136
+ if (process.platform == 'win32') {
2137
+ ws.unpipe(ws.httprequest._term);
2138
+ ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2139
+ } else {
2140
+ ws.unpipe(ws.httprequest.process.stdin);
2141
+ ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2142
+ }
2143
+ ws.resume(); // Resume the websocket to keep receiving control data
2144
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2145
+ // Switch the user input from websocket to webrtc at this point.
2146
+ ws.unpipe(ws.httprequest.desktop.kvm);
2147
+ try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2148
+ ws.resume(); // Resume the websocket to keep receiving control data
2149
+ }
2150
+ ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2151
+ break;
2152
+ }
2153
+ case 'webrtc2': {
2154
+ // Other side received websocket end of data marker, start sending data on WebRTC channel
2155
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2156
+ if (process.platform == 'win32') {
2157
+ ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2158
+ } else {
2159
+ ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2160
+ ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2161
+ }
2162
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2163
+ ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2164
+ }
2165
+ break;
2166
+ }
2167
+ case 'offer': {
2168
+ // This is a WebRTC offer.
2169
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2170
+ ws.webrtc = rtc.createConnection();
2171
+ ws.webrtc.websocket = ws;
2172
+ ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2173
+ ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2174
+ ws.webrtc.on('dataChannel', function (rtcchannel) {
2175
+ //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2176
+ rtcchannel.maxFragmentSize = 32768;
2177
+ rtcchannel.xrtc = this;
2178
+ rtcchannel.websocket = this.websocket;
2179
+ this.rtcchannel = rtcchannel;
2180
+ this.websocket.rtcchannel = rtcchannel;
2181
+ this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2182
+ this.websocket.rtcchannel.on('end', function () {
2183
+ // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2184
+ //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2185
+ if (this.websocket.desktop && this.websocket.desktop.kvm)
2186
+ {
2187
+ try
2188
+ {
2189
+ this.unpipe(this.websocket.desktop.kvm);
2190
+ this.websocket.httprequest.desktop.kvm.unpipe(this);
2191
+ }
2192
+ catch (xx)
2193
+ { }
2194
+ }
2195
+ });
2196
+ this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2197
+ });
2198
+ var sdp = null;
2199
+ try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
2200
+ if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2201
+ break;
2202
+ }
2203
+ case 'rtt': {
2204
+ ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2205
+ break;
2206
+ }
2207
+ }
2208
+ }
2209
+
2210
+ // Console state
2211
+ var consoleWebSockets = {};
2212
+ var consoleHttpRequest = null;
2213
+
2214
+ // Console HTTP response
2215
+ function consoleHttpResponse(response) {
2216
+ response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2217
+ response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2218
+ };
2219
+
2220
+ // Open a web browser to a specified URL on current user's desktop
2221
+ function openUserDesktopUrl(url) {
2222
+ var child = null;
2223
+ try {
2224
+ switch (process.platform) {
2225
+ case 'win32':
2226
+ var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2227
+ child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2228
+ child.stderr.on('data', function () { });
2229
+ child.stdout.on('data', function () { });
2230
+ child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process.env['windir'] + '\\system32\\cmd.exe /C START ' + url + '"\r\n');
2231
+ child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2232
+ child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2233
+ child.stdin.write('exit\r\n');
2234
+ child.waitExit();
2235
+ break;
2236
+ case 'linux':
2237
+ child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2238
+ break;
2239
+ case 'darwin':
2240
+ child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2241
+ break;
2242
+ default:
2243
+ // Unknown platform, ignore this command.
2244
+ break;
2245
+ }
2246
+ } catch (ex) { }
2247
+ return child;
2248
+ }
2249
+
2250
+ // Process a mesh agent console command
2251
+ function processConsoleCommand(cmd, args, rights, sessionid) {
2252
+ try {
2253
+ var response = null;
2254
+ switch (cmd) {
2255
+ case 'help': { // Displays available commands
2256
+ var fin = '', f = '', availcommands = 'coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,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,wallpaper';
2257
+ if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2258
+ if (process.platform != 'freebsd') { availcommands += ',vm';}
2259
+ if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2260
+ try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (xx) { }
2261
+
2262
+ availcommands = availcommands.split(',').sort();
2263
+ while (availcommands.length > 0) {
2264
+ if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2265
+ f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2266
+ }
2267
+ if (f != '') { fin += f; }
2268
+ response = "Available commands: \r\n" + fin + ".";
2269
+ break;
2270
+ }
2271
+ case 'coredump':
2272
+ if (args['_'].length != 1) {
2273
+ response = "Proper usage: coredump on|off|status"; // Display usage
2274
+ } else {
2275
+ switch (args['_'][0].toLowerCase())
2276
+ {
2277
+ case 'on':
2278
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2279
+ response = 'coredump is now on';
2280
+ break;
2281
+ case 'off':
2282
+ process.coreDumpLocation = null;
2283
+ response = 'coredump is now off';
2284
+ break;
2285
+ case 'status':
2286
+ response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2287
+ break;
2288
+ default:
2289
+ response = "Proper usage: coredump on|off|status"; // Display usage
2290
+ break;
2291
+ }
2292
+ }
2293
+ break;
2294
+ case 'service':
2295
+ if (args['_'].length != 1)
2296
+ {
2297
+ response = "Proper usage: service status|restart"; // Display usage
2298
+ }
2299
+ else
2300
+ {
2301
+ var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2302
+ switch(args['_'][0].toLowerCase())
2303
+ {
2304
+ case 'status':
2305
+ response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2306
+ break;
2307
+ case 'restart':
2308
+ if (s.isMe())
2309
+ {
2310
+ s.restart();
2311
+ }
2312
+ else
2313
+ {
2314
+ response = 'Restarting another agent instance is not allowed';
2315
+ }
2316
+ break;
2317
+ default:
2318
+ response = "Proper usage: service status|restart"; // Display usage
2319
+ break;
2320
+ }
2321
+ if (process.platform == 'win32') { s.close(); }
2322
+ }
2323
+ break;
2324
+ case 'zip':
2325
+ if (args['_'].length == 0)
2326
+ {
2327
+ response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2328
+ }
2329
+ else
2330
+ {
2331
+ var p = args['_'].join(' ').split(',');
2332
+ var ofile = p.shift();
2333
+ sendConsoleText('Writing ' + ofile + '...');
2334
+ var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2335
+ out.fname = ofile;
2336
+ out.sessionid = sessionid;
2337
+ out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2338
+ var zip = require('zip-writer').write({ files: p });
2339
+ zip.pipe(out);
2340
+ }
2341
+ break;
2342
+ case 'unzip':
2343
+ if (args['_'].length == 0)
2344
+ {
2345
+ response = "Proper usage: unzip input, destination"; // Display usage
2346
+ }
2347
+ else
2348
+ {
2349
+ var p = args['_'].join(' ').split(',');
2350
+ if (p.length != 2)
2351
+ {
2352
+ response = "Proper usage: unzip input, destination"; // Display usage
2353
+ break;
2354
+ }
2355
+ var prom = require('zip-reader').read(p[0]);
2356
+ prom._dest = p[1];
2357
+ prom.self = this;
2358
+ prom.sessionid = sessionid;
2359
+ prom.then(function (zipped)
2360
+ {
2361
+ sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2362
+ zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2363
+ }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2364
+ }
2365
+ break;
2366
+ case 'setbattery':
2367
+ // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2368
+ if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2369
+ var b = { action: 'battery', state: args['_'][0] };
2370
+ if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2371
+ require('MeshAgent').SendCommand(b);
2372
+ } else {
2373
+ require('MeshAgent').SendCommand({ action: 'battery' });
2374
+ }
2375
+ break;
2376
+ case 'fdsnapshot':
2377
+ require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2378
+ break;
2379
+ case 'fdcount':
2380
+ require('DescriptorEvents').getDescriptorCount().then(
2381
+ function (c)
2382
+ {
2383
+ sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2384
+ }, function (e)
2385
+ {
2386
+ sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2387
+ }).parentPromise.sessionid = sessionid;
2388
+ break;
2389
+ case 'uac':
2390
+ if (process.platform != 'win32')
2391
+ {
2392
+ response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2393
+ break;
2394
+ }
2395
+ if (args['_'].length != 1)
2396
+ {
2397
+ response = 'Proper usage: uac [get|interactive|secure]';
2398
+ }
2399
+ else
2400
+ {
2401
+ switch(args['_'][0].toUpperCase())
2402
+ {
2403
+ case 'GET':
2404
+ var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2405
+ response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2406
+ break;
2407
+ case 'INTERACTIVE':
2408
+ try
2409
+ {
2410
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2411
+ response = 'UAC mode changed to: Interactive Desktop';
2412
+ }
2413
+ catch (e)
2414
+ {
2415
+ response = "Unable to change UAC Mode";
2416
+ }
2417
+ break;
2418
+ case 'SECURE':
2419
+ try
2420
+ {
2421
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2422
+ response = 'UAC mode changed to: Secure Desktop';
2423
+ }
2424
+ catch(e)
2425
+ {
2426
+ response = "Unable to change UAC Mode";
2427
+ }
2428
+ break;
2429
+ default:
2430
+ response = 'Proper usage: uac [get|interactive|secure]';
2431
+ break;
2432
+ }
2433
+ }
2434
+ break;
2435
+ case 'vm':
2436
+ response = 'Virtual Machine = ' + require('identifiers').isVM();
2437
+ break;
2438
+ case 'startupoptions':
2439
+ response = JSON.stringify(require('MeshAgent').getStartupOptions());
2440
+ break;
2441
+ case 'kvmmode':
2442
+ if (require('MeshAgent').maxKvmTileSize == null)
2443
+ {
2444
+ response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2445
+ }
2446
+ else
2447
+ {
2448
+ if(require('MeshAgent').maxKvmTileSize == 0)
2449
+ {
2450
+ response = 'KVM Mode: Full JUMBO';
2451
+ }
2452
+ else
2453
+ {
2454
+ response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2455
+ response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2456
+ }
2457
+ }
2458
+ break;
2459
+ case 'alert':
2460
+ if (args['_'].length == 0)
2461
+ {
2462
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2463
+ }
2464
+ else
2465
+ {
2466
+ var p = args['_'].join(' ').split(',');
2467
+ if(p.length<2)
2468
+ {
2469
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2470
+ }
2471
+ else
2472
+ {
2473
+ this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2474
+ }
2475
+ }
2476
+ break;
2477
+ case 'agentsize':
2478
+ var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2479
+ if (process.platform == 'win32') {
2480
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2481
+ var writtenSize = 0;
2482
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (x) { response = x; }
2483
+ if (writtenSize != actualSize) {
2484
+ response = "Size updated from: " + writtenSize + " to: " + actualSize;
2485
+ try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (x2) { response = x2; }
2486
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2487
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2488
+ break;
2489
+ case 'versions':
2490
+ response = JSON.stringify(process.versions, null, ' ');
2491
+ break;
2492
+ case 'wpfhwacceleration':
2493
+ if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2494
+ if (args['_'].length != 1) {
2495
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2496
+ }
2497
+ else {
2498
+ var reg = require('win-registry');
2499
+ var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2500
+ var key = reg.usernameToUserKey(uname);
2501
+
2502
+ switch (args['_'][0].toUpperCase()) {
2503
+ default:
2504
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2505
+ break;
2506
+ case 'ON':
2507
+ try {
2508
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2509
+ response = "OK";
2510
+ } catch (ex) { response = "FAILED"; }
2511
+ break;
2512
+ case 'OFF':
2513
+ try {
2514
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2515
+ response = 'OK';
2516
+ } catch (ex) { response = 'FAILED'; }
2517
+ break;
2518
+ case 'STATUS':
2519
+ var s;
2520
+ try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (ex) { s = 'DEFAULT'; }
2521
+ response = "WPF Hardware Acceleration: " + s;
2522
+ break;
2523
+ case 'DEFAULT':
2524
+ try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (ex) { }
2525
+ response = 'OK';
2526
+ break;
2527
+ }
2528
+ }
2529
+ break;
2530
+ case 'tsid':
2531
+ if (process.platform == 'win32') {
2532
+ if (args['_'].length != 1) {
2533
+ response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2534
+ } else {
2535
+ var i = parseInt(args['_'][0]);
2536
+ require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2537
+ response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2538
+ }
2539
+ } else { response = "TSID command only supported on Windows"; }
2540
+ break;
2541
+ case 'activeusers':
2542
+ if (process.platform == 'win32') {
2543
+ var p = require('user-sessions').enumerateUsers();
2544
+ p.sessionid = sessionid;
2545
+ p.then(function (u) {
2546
+ var v = [];
2547
+ for (var i in u) {
2548
+ if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2549
+ }
2550
+ sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2551
+ });
2552
+ } else { response = "activeusers command only supported on Windows"; }
2553
+ break;
2554
+ case 'wallpaper':
2555
+ if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2556
+ response = "wallpaper command not supported on this platform";
2557
+ }
2558
+ else {
2559
+ if (args['_'].length != 1) {
2560
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2561
+ }
2562
+ else {
2563
+ switch (args['_'][0].toUpperCase()) {
2564
+ default:
2565
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2566
+ break;
2567
+ case 'GET':
2568
+ case 'TOGGLE':
2569
+ if (process.platform == 'win32') {
2570
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2571
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2572
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2573
+ child.stderr.on('data', function () { });
2574
+ child.waitExit();
2575
+ var current = child.stdout.str.trim();
2576
+ if (args['_'][0].toUpperCase() == 'GET') {
2577
+ response = current;
2578
+ break;
2579
+ }
2580
+ if (current != '') {
2581
+ require('MeshAgent')._wallpaper = current;
2582
+ response = 'Wallpaper cleared';
2583
+ } else {
2584
+ response = 'Wallpaper restored';
2585
+ }
2586
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2587
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2588
+ child.stderr.on('data', function () { });
2589
+ child.waitExit();
2590
+ }
2591
+ else {
2592
+ var id = require('user-sessions').consoleUid();
2593
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2594
+ if (args['_'][0].toUpperCase() == 'GET') {
2595
+ response = current;
2596
+ break;
2597
+ }
2598
+ if (current != '/dev/null') {
2599
+ require('MeshAgent')._wallpaper = current;
2600
+ response = 'Wallpaper cleared';
2601
+ } else {
2602
+ response = 'Wallpaper restored';
2603
+ }
2604
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2605
+ }
2606
+ break;
2607
+ }
2608
+ }
2609
+ }
2610
+ break;
2611
+ case 'safemode':
2612
+ if (process.platform != 'win32') {
2613
+ response = 'safemode only supported on Windows Platforms'
2614
+ }
2615
+ else {
2616
+ if (args['_'].length != 1) {
2617
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2618
+ }
2619
+ else {
2620
+ switch (args['_'][0].toUpperCase()) {
2621
+ default:
2622
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2623
+ break;
2624
+ case 'ON':
2625
+ require('win-bcd').setKey('safeboot', 'Network');
2626
+ require('win-bcd').enableSafeModeService('Mesh Agent');
2627
+ break;
2628
+ case 'OFF':
2629
+ require('win-bcd').deleteKey('safeboot');
2630
+ break;
2631
+ case 'STATUS':
2632
+ var nextboot = require('win-bcd').getKey('safeboot');
2633
+ if (nextboot) {
2634
+ switch (nextboot) {
2635
+ case 'Network':
2636
+ case 'network':
2637
+ nextboot = 'SAFE_MODE_NETWORK';
2638
+ break;
2639
+ default:
2640
+ nextboot = 'SAFE_MODE';
2641
+ break;
2642
+ }
2643
+ }
2644
+ response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
2645
+ break;
2646
+ }
2647
+ }
2648
+ }
2649
+ break;
2650
+ /*
2651
+ case 'border':
2652
+ {
2653
+ if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
2654
+ if (meshCoreObj.users.length > 0) {
2655
+ obj.borderManager.Start(meshCoreObj.users[0]);
2656
+ response = 'Border blinking is on.';
2657
+ } else {
2658
+ response = 'Cannot turn on border blinking, no logged in users.';
2659
+ }
2660
+ } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
2661
+ obj.borderManager.Stop();
2662
+ response = 'Border blinking is off.';
2663
+ } else {
2664
+ response = 'Proper usage: border "on|off"'; // Display correct command usage
2665
+ }
2666
+ }
2667
+ break;
2668
+ */
2669
+ case 'av':
2670
+ if (process.platform == 'win32') {
2671
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
2672
+ response = JSON.stringify(require('win-info').av(), null, 1);
2673
+ } else {
2674
+ response = 'Not supported on the platform';
2675
+ }
2676
+ break;
2677
+ case 'log':
2678
+ if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
2679
+ break;
2680
+ case 'getclip':
2681
+ if (require('MeshAgent').isService) {
2682
+ require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
2683
+ } else {
2684
+ require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
2685
+ }
2686
+ break;
2687
+ case 'setclip': {
2688
+ if (args['_'].length != 1) {
2689
+ response = 'Proper usage: setclip "sample text"';
2690
+ } else {
2691
+ if (require('MeshAgent').isService) {
2692
+ require('clipboard').dispatchWrite(args['_'][0]);
2693
+ response = 'Setting clipboard to: "' + args['_'][0] + '"';
2694
+ }
2695
+ else {
2696
+ require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
2697
+ }
2698
+ }
2699
+ break;
2700
+ }
2701
+ case 'amtreset': {
2702
+ if (amt != null) { amt.reset(); response = 'Done.'; }
2703
+ break;
2704
+ }
2705
+ case 'amtlmsreset': {
2706
+ if (amt != null) { amt.lmsreset(); response = 'Done.'; }
2707
+ break;
2708
+ }
2709
+ case 'amtccm': {
2710
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
2711
+ if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
2712
+ else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
2713
+ }
2714
+ break;
2715
+ }
2716
+ case 'amtacm': {
2717
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
2718
+ amt.setPolicy({ type: 0 });
2719
+ amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
2720
+ }
2721
+ break;
2722
+ }
2723
+ case 'amtdeactivate': {
2724
+ if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
2725
+ break;
2726
+ }
2727
+ case 'amtpolicy': {
2728
+ if (amtPolicy == null) {
2729
+ response = 'No Intel(R) AMT policy.';
2730
+ } else {
2731
+ response = JSON.stringify(amtPolicy);
2732
+ }
2733
+ break;
2734
+ }
2735
+ case 'openurl': {
2736
+ if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
2737
+ else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
2738
+ break;
2739
+ }
2740
+ case 'users': {
2741
+ if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
2742
+ require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
2743
+ break;
2744
+ }
2745
+ case 'toast': {
2746
+ if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
2747
+ if (require('MeshAgent')._tsid == null) {
2748
+ require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
2749
+ }
2750
+ else {
2751
+ require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
2752
+ }
2753
+ }
2754
+ break;
2755
+ }
2756
+ case 'setdebug': {
2757
+ if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
2758
+ else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
2759
+ break;
2760
+ }
2761
+ case 'ps': {
2762
+ processManager.getProcesses(function (plist) {
2763
+ var x = '';
2764
+ for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
2765
+ sendConsoleText(x, sessionid);
2766
+ });
2767
+ break;
2768
+ }
2769
+ case 'kill': {
2770
+ if ((args['_'].length < 1)) {
2771
+ response = 'Proper usage: kill [pid]'; // Display correct command usage
2772
+ } else {
2773
+ process.kill(parseInt(args['_'][0]));
2774
+ response = 'Killed process ' + args['_'][0] + '.';
2775
+ }
2776
+ break;
2777
+ }
2778
+ case 'smbios': {
2779
+ if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
2780
+ break;
2781
+ }
2782
+ case 'rawsmbios': {
2783
+ if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
2784
+ response = '';
2785
+ for (var i in SMBiosTablesRaw) {
2786
+ var header = false;
2787
+ for (var j in SMBiosTablesRaw[i]) {
2788
+ if (SMBiosTablesRaw[i][j].length > 0) {
2789
+ if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
2790
+ response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
2791
+ }
2792
+ }
2793
+ }
2794
+ }
2795
+ break;
2796
+ }
2797
+ case 'dump':
2798
+ if (args['_'].length < 1) {
2799
+ response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2800
+ }
2801
+ else {
2802
+ switch (args['_'][0].toLowerCase()) {
2803
+ case 'on':
2804
+ process.coreDumpLocation = process.platform == 'win32' ? process.execPath.replace('.exe', '.dmp') : (process.execPath + '.dmp');
2805
+ response = 'enabled';
2806
+ break;
2807
+ case 'off':
2808
+ process.coreDumpLocation = null;
2809
+ response = 'disabled';
2810
+ break;
2811
+ case 'status':
2812
+ if (process.coreDumpLocation) {
2813
+ response = 'Core Dump: [ENABLED' + (require('fs').existsSync(process.coreDumpLocation) ? (', (DMP file exists)]') : (']'));
2814
+ }
2815
+ else {
2816
+ response = 'Core Dump: [DISABLED]';
2817
+ }
2818
+ break;
2819
+ default:
2820
+ response = 'Proper usage: dump [on/off/status]'; // Display correct command usage
2821
+ break;
2822
+ }
2823
+ }
2824
+ break;
2825
+ case 'eval': { // Eval JavaScript
2826
+ if (args['_'].length < 1) {
2827
+ response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
2828
+ } else {
2829
+ response = JSON.stringify(mesh.eval(args['_'][0]));
2830
+ }
2831
+ break;
2832
+ }
2833
+ case 'uninstallagent': // Uninstall this agent
2834
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
2835
+ if (!require('service-manager').manager.getService(agentName).isMe()) {
2836
+ response = 'Uininstall failed, this instance is not the service instance';
2837
+ } else {
2838
+ try { diagnosticAgent_uninstall(); } catch (x) { }
2839
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
2840
+ 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 });
2841
+ }
2842
+ break;
2843
+ case 'notify': { // Send a notification message to the mesh
2844
+ if (args['_'].length != 1) {
2845
+ response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
2846
+ } else {
2847
+ var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
2848
+ if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
2849
+ mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
2850
+ response = "ok";
2851
+ }
2852
+ break;
2853
+ }
2854
+ case 'cpuinfo': { // Return system information
2855
+ // CPU & memory utilization
2856
+ pr = require('sysinfo').cpuUtilization();
2857
+ pr.sessionid = sessionid;
2858
+ pr.then(function (data) {
2859
+ sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
2860
+ }, function (e) {
2861
+ sendConsoleText(e);
2862
+ });
2863
+ break;
2864
+ }
2865
+ case 'sysinfo': { // Return system information
2866
+ getSystemInformation(function (results, err) {
2867
+ if (results == null) { sendConsoleText(err, this.sessionid); } else {
2868
+ sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
2869
+ }
2870
+ });
2871
+ break;
2872
+ }
2873
+ case 'info': { // Return information about the agent and agent core module
2874
+ response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
2875
+ if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
2876
+ if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
2877
+ response += '\r\nModules: ' + addedModules.join(', ') + '.';
2878
+ response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
2879
+ response += '\r\lastMeInfo: ' + lastMeInfo + '.';
2880
+ var oldNodeId = db.Get('OldNodeId');
2881
+ if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
2882
+ if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
2883
+ break;
2884
+ }
2885
+ case 'osinfo': { // Return the operating system information
2886
+ var i = 1;
2887
+ if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
2888
+ for (var j = 0; j < i; j++) {
2889
+ var pr = require('os').name();
2890
+ pr.sessionid = sessionid;
2891
+ pr.then(function (v) {
2892
+ sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
2893
+ });
2894
+ }
2895
+ break;
2896
+ }
2897
+ case 'sendcaps': { // Send capability flags to the server
2898
+ if (args['_'].length == 0) {
2899
+ response = 'Proper usage: sendcaps (number)'; // Display correct command usage
2900
+ } else {
2901
+ meshCoreObj.caps = parseInt(args['_'][0]);
2902
+ mesh.SendCommand(meshCoreObj);
2903
+ response = JSON.stringify(meshCoreObj);
2904
+ }
2905
+ break;
2906
+ }
2907
+ case 'sendosdesc': { // Send OS description
2908
+ if (args['_'].length > 0) {
2909
+ meshCoreObj.osdesc = args['_'][0];
2910
+ mesh.SendCommand(meshCoreObj);
2911
+ response = JSON.stringify(meshCoreObj);
2912
+ } else {
2913
+ response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
2914
+ }
2915
+ break;
2916
+ }
2917
+ case 'args': { // Displays parsed command arguments
2918
+ response = 'args ' + objToString(args, 0, ' ', true);
2919
+ break;
2920
+ }
2921
+ case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
2922
+ var r = [];
2923
+ for (var i in args['_']) { r.push(args['_'][i]); }
2924
+ console.log(r.join(' '));
2925
+ response = 'Message printed on agent console.';
2926
+ break;
2927
+ }
2928
+ case 'type': { // Returns the content of a file
2929
+ if (args['_'].length == 0) {
2930
+ response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
2931
+ } else {
2932
+ var max = 4096;
2933
+ if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
2934
+ if (max > 4096) max = 4096;
2935
+ var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
2936
+ response = buf.toString();
2937
+ var i = response.indexOf('\n');
2938
+ if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
2939
+ if (r == max) response += '...';
2940
+ fs.closeSync(fd);
2941
+ }
2942
+ break;
2943
+ }
2944
+ case 'dbkeys': { // Return all data store keys
2945
+ response = JSON.stringify(db.Keys);
2946
+ break;
2947
+ }
2948
+ case 'dbget': { // Return the data store value for a given key
2949
+ if (db == null) { response = 'Database not accessible.'; break; }
2950
+ if (args['_'].length != 1) {
2951
+ response = 'Proper usage: dbget (key)'; // Display the value for a given database key
2952
+ } else {
2953
+ response = db.Get(args['_'][0]);
2954
+ }
2955
+ break;
2956
+ }
2957
+ case 'dbset': { // Set a data store key and value pair
2958
+ if (db == null) { response = 'Database not accessible.'; break; }
2959
+ if (args['_'].length != 2) {
2960
+ response = 'Proper usage: dbset (key) (value)'; // Set a database key
2961
+ } else {
2962
+ var r = db.Put(args['_'][0], args['_'][1]);
2963
+ response = 'Key set: ' + r;
2964
+ }
2965
+ break;
2966
+ }
2967
+ case 'dbcompact': { // Compact the data store
2968
+ if (db == null) { response = 'Database not accessible.'; break; }
2969
+ var r = db.Compact();
2970
+ response = 'Database compacted: ' + r;
2971
+ break;
2972
+ }
2973
+ case 'httpget': {
2974
+ if (consoleHttpRequest != null) {
2975
+ response = 'HTTP operation already in progress.';
2976
+ } else {
2977
+ if (args['_'].length != 1) {
2978
+ response = 'Proper usage: httpget (url)';
2979
+ } else {
2980
+ var options = http.parseUri(args['_'][0]);
2981
+ options.method = 'GET';
2982
+ if (options == null) {
2983
+ response = 'Invalid url.';
2984
+ } else {
2985
+ try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
2986
+ consoleHttpRequest.sessionid = sessionid;
2987
+ if (consoleHttpRequest != null) {
2988
+ consoleHttpRequest.end();
2989
+ response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
2990
+ }
2991
+ }
2992
+ }
2993
+ }
2994
+ break;
2995
+ }
2996
+ case 'wslist': { // List all web sockets
2997
+ response = '';
2998
+ for (var i in consoleWebSockets) {
2999
+ var httprequest = consoleWebSockets[i];
3000
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3001
+ }
3002
+ if (response == '') { response = 'no websocket sessions.'; }
3003
+ break;
3004
+ }
3005
+ case 'wsconnect': { // Setup a web socket
3006
+ if (args['_'].length == 0) {
3007
+ response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3008
+ } else {
3009
+ var httprequest = null;
3010
+ try {
3011
+ var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3012
+ options.rejectUnauthorized = 0;
3013
+ httprequest = http.request(options);
3014
+ } catch (e) { response = 'Invalid HTTP websocket request'; }
3015
+ if (httprequest != null) {
3016
+ httprequest.upgrade = onWebSocketUpgrade;
3017
+ httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3018
+
3019
+ var index = 1;
3020
+ while (consoleWebSockets[index]) { index++; }
3021
+ httprequest.sessionid = sessionid;
3022
+ httprequest.index = index;
3023
+ httprequest.url = args['_'][0];
3024
+ consoleWebSockets[index] = httprequest;
3025
+ response = 'New websocket session #' + index;
3026
+ }
3027
+ }
3028
+ break;
3029
+ }
3030
+ case 'wssend': { // Send data on a web socket
3031
+ if (args['_'].length == 0) {
3032
+ response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3033
+ for (var i in consoleWebSockets) {
3034
+ var httprequest = consoleWebSockets[i];
3035
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3036
+ }
3037
+ } else {
3038
+ var i = parseInt(args['_'][0]);
3039
+ var httprequest = consoleWebSockets[i];
3040
+ if (httprequest != undefined) {
3041
+ httprequest.s.write(args['_'][1]);
3042
+ response = 'ok';
3043
+ } else {
3044
+ response = 'Invalid web socket number';
3045
+ }
3046
+ }
3047
+ break;
3048
+ }
3049
+ case 'wsclose': { // Close a websocket
3050
+ if (args['_'].length == 0) {
3051
+ response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3052
+ } else {
3053
+ var i = parseInt(args['_'][0]);
3054
+ var httprequest = consoleWebSockets[i];
3055
+ if (httprequest != undefined) {
3056
+ if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3057
+ response = 'ok';
3058
+ } else {
3059
+ response = 'Invalid web socket number';
3060
+ }
3061
+ }
3062
+ break;
3063
+ }
3064
+ case 'tunnels': { // Show the list of current tunnels
3065
+ response = '';
3066
+ for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3067
+ if (response == '') { response = 'No websocket sessions.'; }
3068
+ break;
3069
+ }
3070
+ case 'ls': { // Show list of files and folders
3071
+ response = '';
3072
+ var xpath = '*';
3073
+ if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3074
+ response = 'List of ' + xpath + '\r\n';
3075
+ var results = fs.readdirSync(xpath);
3076
+ for (var i = 0; i < results.length; ++i) {
3077
+ var stat = null, p = obj.path.join(args['_'][0], results[i]);
3078
+ try { stat = fs.statSync(p); } catch (e) { }
3079
+ if ((stat == null) || (stat == undefined)) {
3080
+ response += (results[i] + "\r\n");
3081
+ } else {
3082
+ response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3083
+ }
3084
+ }
3085
+ break;
3086
+ }
3087
+ case 'lsx': { // Show list of files and folders
3088
+ response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3089
+ break;
3090
+ }
3091
+ case 'lock': { // Lock the current user out of the desktop
3092
+ if (process.platform == 'win32') { var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); response = 'Ok'; }
3093
+ else { response = 'Not supported on the platform'; }
3094
+ break;
3095
+ }
3096
+ case 'amt': { // Show Intel AMT status
3097
+ if (amt != null) {
3098
+ amt.getAmtInfo(function (state) {
3099
+ var resp = 'Intel AMT not detected.';
3100
+ if (state != null) { resp = objToString(state, 0, ' ', true); }
3101
+ sendConsoleText(resp, sessionid);
3102
+ });
3103
+ } else {
3104
+ response = 'Intel AMT not detected.';
3105
+ }
3106
+ break;
3107
+ }
3108
+ case 'netinfo': { // Show network interface information
3109
+ var interfaces = require('os').networkInterfaces();
3110
+ response = objToString(interfaces, 0, ' ', true);
3111
+ break;
3112
+ }
3113
+ case 'wakeonlan': { // Send wake-on-lan
3114
+ if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3115
+ response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3116
+ } else {
3117
+ var count = sendWakeOnLan(args['_'][0]);
3118
+ response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3119
+ }
3120
+ break;
3121
+ }
3122
+ case 'sendall': { // Send a message to all consoles on this mesh
3123
+ sendConsoleText(args['_'].join(' '));
3124
+ break;
3125
+ }
3126
+ case 'power': { // Execute a power action on this computer
3127
+ if (mesh.ExecPowerState == undefined) {
3128
+ response = 'Power command not supported on this agent.';
3129
+ } else {
3130
+ if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3131
+ response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
3132
+ } else {
3133
+ var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3134
+ response = 'Power action executed with return code: ' + r + '.';
3135
+ }
3136
+ }
3137
+ break;
3138
+ }
3139
+ case 'location': {
3140
+ getIpLocationData(function (location) {
3141
+ sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3142
+ });
3143
+ break;
3144
+ }
3145
+ case 'parseuri': {
3146
+ response = JSON.stringify(http.parseUri(args['_'][0]));
3147
+ break;
3148
+ }
3149
+ case 'scanwifi': {
3150
+ if (wifiScanner != null) {
3151
+ var wifiPresent = wifiScanner.hasWireless;
3152
+ if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3153
+ } else { response = "Wifi module not present."; }
3154
+ break;
3155
+ }
3156
+ case 'scanamt': {
3157
+ if (amtscanner != null) {
3158
+ if (args['_'].length != 1) {
3159
+ response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n'; // Display correct command usage
3160
+ } else {
3161
+ response = 'Scanning: ' + args['_'][0] + '...';
3162
+ amtscanner.scan(args['_'][0], 2000, function (data) {
3163
+ if (data.length > 0) {
3164
+ var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3165
+ for (var i in data) {
3166
+ var x = data[i];
3167
+ if (r != '') { r += '\r\n'; }
3168
+ r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3169
+ if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3170
+ if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3171
+ r += '.';
3172
+ }
3173
+ } else {
3174
+ r = 'No Intel AMT found.';
3175
+ }
3176
+ sendConsoleText(r);
3177
+ });
3178
+ }
3179
+ } else { response = "Intel AMT scanner module not present."; }
3180
+ break;
3181
+ }
3182
+ case 'modules': {
3183
+ response = JSON.stringify(addedModules);
3184
+ break;
3185
+ }
3186
+ case 'listservices': {
3187
+ var services = require('service-manager').manager.enumerateService();
3188
+ response = JSON.stringify(services, null, 1);
3189
+ break;
3190
+ }
3191
+ case 'getscript': {
3192
+ if (args['_'].length != 1) {
3193
+ response = "Proper usage: getscript [scriptNumber].";
3194
+ } else {
3195
+ mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3196
+ }
3197
+ break;
3198
+ }
3199
+ case 'diagnostic':
3200
+ {
3201
+ if (!mesh.DAIPC.listening) {
3202
+ response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3203
+ break;
3204
+ }
3205
+ var diag = diagnosticAgent_installCheck();
3206
+ if (diag) {
3207
+ if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3208
+ diagnosticAgent_uninstall();
3209
+ response = 'Diagnostic Agent uninstalled';
3210
+ }
3211
+ else {
3212
+ response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3213
+ }
3214
+ }
3215
+ else {
3216
+ if (args['_'].length == 1 && args['_'][0] == 'install') {
3217
+ diag = diagnosticAgent_installCheck(true);
3218
+ if (diag) {
3219
+ response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3220
+ }
3221
+ else {
3222
+ response = 'Diagnostic agent installation failed';
3223
+ }
3224
+ }
3225
+ else {
3226
+ response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3227
+ }
3228
+ }
3229
+ if (diag) { diag.close(); diag = null; }
3230
+ break;
3231
+ }
3232
+ case 'apf': {
3233
+ if (meshCoreObj.intelamt !== null) {
3234
+ if (args['_'].length == 1) {
3235
+ if (args['_'][0] == 'on') {
3236
+ response = "Starting APF tunnel";
3237
+ var apfarg = {
3238
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3239
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3240
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3241
+ mpskeepalive: 60000,
3242
+ clientname: require('os').hostname(),
3243
+ clientaddress: '127.0.0.1',
3244
+ clientuuid: meshCoreObj.intelamt.uuid
3245
+ };
3246
+ var tobj = { debug: false }; //
3247
+ apftunnel = require('apfclient')(tobj, apfarg);
3248
+ try {
3249
+ apftunnel.connect();
3250
+ response += "..success";
3251
+ } catch (e) {
3252
+ response += JSON.stringify(e);
3253
+ }
3254
+ } else if (args['_'][0] == 'off') {
3255
+ response = "Stopping APF tunnel";
3256
+ try {
3257
+ apftunnel.disconnect();
3258
+ response += "..success";
3259
+ } catch (e) {
3260
+ response += JSON.stringify(e);
3261
+ }
3262
+ apftunnel = null;
3263
+ } else {
3264
+ response = "Invalid command.\r\nCmd syntax: apf on|off";
3265
+ }
3266
+ } else {
3267
+ response = "APF tunnel is " + (apftunnel == null ? "off" : "on");
3268
+ }
3269
+ } else {
3270
+ response = "APF tunnel requires Intel AMT";
3271
+ }
3272
+ break;
3273
+ }
3274
+ case 'plugin': {
3275
+ if (typeof args['_'][0] == 'string') {
3276
+ try {
3277
+ // Pass off the action to the plugin
3278
+ // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3279
+ // to control the output / actions here.
3280
+ response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3281
+ } catch (e) {
3282
+ response = "There was an error in the plugin (" + e + ")";
3283
+ }
3284
+ } else {
3285
+ response = "Proper usage: plugin [pluginName] [args].";
3286
+ }
3287
+ break;
3288
+ }
3289
+ default: { // This is an unknown command, return an error message
3290
+ response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3291
+ break;
3292
+ }
3293
+ }
3294
+ } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3295
+ if (response != null) { sendConsoleText(response, sessionid); }
3296
+ }
3297
+
3298
+ // Send a mesh agent console command
3299
+ function sendConsoleText(text, sessionid) {
3300
+ if (typeof text == 'object') { text = JSON.stringify(text); }
3301
+ mesh.SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3302
+ }
3303
+
3304
+ // Called before the process exits
3305
+ //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3306
+
3307
+ // Called when the server connection state changes
3308
+ function handleServerConnection(state) {
3309
+ meshServerConnectionState = state;
3310
+ if (meshServerConnectionState == 0) {
3311
+ // Server disconnected
3312
+ if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3313
+ lastSelfInfo = null;
3314
+ } else {
3315
+ // Server connected, send mesh core information
3316
+ var oldNodeId = db.Get('OldNodeId');
3317
+ if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3318
+
3319
+ // Update the server with basic info, logged in users and more.
3320
+ mesh.SendCommand(meshCoreObj);
3321
+
3322
+ // Send SMBios tables if present
3323
+ if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3324
+
3325
+ // Update the server on more advanced stuff, like Intel ME and Network Settings
3326
+ meInfoStr = null;
3327
+ sendPeriodicServerUpdate();
3328
+ if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3329
+ }
3330
+ }
3331
+
3332
+ // Update the server with the latest network interface information
3333
+ var sendNetworkUpdateNagleTimer = null;
3334
+ function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3335
+ function sendNetworkUpdate(force) {
3336
+ sendNetworkUpdateNagleTimer = null;
3337
+
3338
+ // Update the network interfaces information data
3339
+ var netInfo = { netif2: require('os').networkInterfaces() };
3340
+ if (netInfo.netif2) {
3341
+ netInfo.action = 'netinfo';
3342
+ var netInfoStr = JSON.stringify(netInfo);
3343
+ if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3344
+ }
3345
+ }
3346
+
3347
+ // Called periodically to check if we need to send updates to the server
3348
+ function sendPeriodicServerUpdate(flags) {
3349
+ if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3350
+ if (!flags) { flags = 0xFFFFFFFF; }
3351
+
3352
+ if ((flags & 1) && (amt != null)) {
3353
+ // If we have a connected MEI, get Intel ME information
3354
+ amt.getAmtInfo(function (meinfo) {
3355
+ try {
3356
+ if (meinfo == null) return;
3357
+ var intelamt = {}, p = false;
3358
+ if ((meinfo.Versions != null) && (meinfo.Versions.AMT != null)) { intelamt.ver = meinfo.Versions.AMT; p = true; if (meinfo.Versions.Sku != null) { intelamt.sku = parseInt(meinfo.Versions.Sku); } }
3359
+ if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3360
+ if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3361
+ if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3362
+ if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3363
+ if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3364
+ // Not activated and we have wired ethernet, look for the trusted DNS
3365
+ var dns = meinfo.DNS;
3366
+ if (dns == null) {
3367
+ // Trusted DNS not set, let's look for the OS network DNS suffix
3368
+ var interfaces = require('os').networkInterfaces();
3369
+ for (var i in interfaces) {
3370
+ for (var j in interfaces[i]) {
3371
+ if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3372
+ }
3373
+ }
3374
+ }
3375
+ if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3376
+ } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3377
+ if (p == true) {
3378
+ var meInfoStr = JSON.stringify(intelamt);
3379
+ if (meInfoStr != lastMeInfo) {
3380
+ meshCoreObj.intelamt = intelamt;
3381
+ mesh.SendCommand(meshCoreObj);
3382
+ lastMeInfo = meInfoStr;
3383
+ }
3384
+ }
3385
+ } catch (ex) { }
3386
+ });
3387
+ }
3388
+
3389
+ if (flags & 2) {
3390
+ // Update network information
3391
+ sendNetworkUpdateNagle(false);
3392
+ }
3393
+
3394
+ if ((flags & 4) && (process.platform == 'win32')) {
3395
+ // Update anti-virus information
3396
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3397
+ var av, pr;
3398
+ try { av = require('win-info').av(); } catch (ex) { av = null; } // Antivirus
3399
+ //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (ex) { pr = null; } } // Pending reboot
3400
+ if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3401
+ }
3402
+ }
3403
+
3404
+ // Starting function
3405
+ obj.start = function () {
3406
+ // Setup the mesh agent event handlers
3407
+ mesh.AddCommandHandler(handleServerCommand);
3408
+ mesh.AddConnectHandler(handleServerConnection);
3409
+
3410
+ // Parse input arguments
3411
+ //var args = parseArgs(process.argv);
3412
+ //console.log(args);
3413
+
3414
+ //resetMicroLms();
3415
+
3416
+ // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3417
+ try {
3418
+ var userSession = require('user-sessions');
3419
+ userSession.on('changed', function onUserSessionChanged() {
3420
+ userSession.enumerateUsers().then(function (users) {
3421
+ var u = [], a = users.Active;
3422
+ for (var i = 0; i < a.length; i++) {
3423
+ var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3424
+ if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3425
+ }
3426
+ meshCoreObj.users = u;
3427
+ mesh.SendCommand(meshCoreObj);
3428
+ });
3429
+ });
3430
+ userSession.emit('changed');
3431
+ //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3432
+ //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3433
+ } catch (ex) { }
3434
+ }
3435
+
3436
+ obj.stop = function () {
3437
+ mesh.AddCommandHandler(null);
3438
+ mesh.AddConnectHandler(null);
3439
+ }
3440
+
3441
+ function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3442
+ function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3443
+ function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3444
+
3445
+ function onWebSocketUpgrade(response, s, head) {
3446
+ sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3447
+ this.s = s;
3448
+ s.httprequest = this;
3449
+ s.end = onWebSocketClosed;
3450
+ s.data = onWebSocketData;
3451
+ }
3452
+
3453
+ return obj;
3454
+}
3455
+
3456
+//
3457
+// Module startup
3458
+//
3459
+
3460
+try {
3461
+ var xexports = null, mainMeshCore = null;
3462
+ try { xexports = module.exports; } catch (e) { }
3463
+
3464
+ if (xexports != null) {
3465
+ // If we are running within NodeJS, export the core
3466
+ module.exports.createMeshCore = createMeshCore;
3467
+ } else {
3468
+ // If we are not running in NodeJS, launch the core
3469
+ mainMeshCore = createMeshCore();
3470
+ mainMeshCore.start(null);
3471
+ }
3472
+} catch (ex) {
3473
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3474
+}
meshagent.js
+6
-1
@@ -975,6 +975,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
975
if ((results != null) && (results.length == 1)) { obj.send(JSON.stringify({ action: 'sysinfo', hash: results[0].hash })); } else { obj.send(JSON.stringify({ action: 'sysinfo' })); }
976
});
977
978
+ // Set agent core dump
979
+ if ((parent.parent.config.settings.agentcoredump === true) || (parent.parent.config.settings.agentcoredump === false)) {
980
+ obj.send(JSON.stringify({ action: 'coredump', value: parent.parent.config.settings.agentcoredump }));
981
+ }
982
+
983
// Do this if IP location is enabled on this domain TODO: Set IP location per device group?
984
if (domain.iplocation == true) {
985
// Check if we already have IP location information for this node
@@ -1270,7 +1275,7 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
1275
parent.parent.DispatchEvent(parent.CreateMeshDispatchTargets(obj.dbMeshKey, [obj.dbNodeKey]), obj, event);
1276
1277
// Update the device Intel AMT information
1273
- ChangeAgentCoreInfo({ "intelamt": { user: 'admin', pass: amtpassword, uuid: command.uuid, realm: command.realm } });
1278
+ ChangeAgentCoreInfo({ 'intelamt': { user: 'admin', pass: amtpassword, uuid: command.uuid, realm: command.realm } });
1279
1280
// Send the activation response
1281
obj.send(JSON.stringify(signResponse));
translate/translate.json
+8
-5
@@ -12480,11 +12480,7 @@
12480
"nl": "even wachten, reset e-mail verzonden.",
12481
"pt": "Aguarde, redefina o email enviado.",
12482
"ru": "Подождите, письмо для сброса отправлено.",
12483
- "zh-chs": "稍等,重置已發送的郵件。",
12484
- "xloc": [
12485
- "login-mobile.handlebars->5->1",
12486
- "login.handlebars->5->1"
12487
- ]
12483
+ "zh-chs": "稍等,重置已發送的郵件。"
12484
},
12485
{
12486
"cs": "Drží se jeden záznam pro kopii",
@@ -12871,6 +12867,13 @@
12867
"default.handlebars->27->883"
12868
]
12869
},
12870
+ {
12871
+ "en": "If valid, reset mail sent.",
12872
+ "xloc": [
12873
+ "login-mobile.handlebars->5->1",
12874
+ "login.handlebars->5->1"
12875
+ ]
12876
+ },
12877
{
12878
"cs": "Pokud jste tento požadavek nezačali, ignorujte tento e-mail.",
12879
"de": "Wenn Sie diese Anfrage nicht initiiert haben, ignorieren Sie diese Mail bitte.",
views/default.handlebars
+2
-2
@@ -6959,7 +6959,7 @@
6959
for (var i in p) {
6960
if (p[i].p != 0) {
6961
var c = p[i].c;
6962
- if (c.length > 30) { c = '<span title="' + EscapeHtml(c) + '">' + EscapeHTML(c.substring(0,30)) + '...</span>' } else { c = EscapeHtml(c); }
6962
+ if (c.length > 30) { c = '<span title="' + EscapeHtml(c) + '">' + EscapeHtml(c.substring(0,30)) + '...</span>' } else { c = EscapeHtml(c); }
6963
x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + EscapeHtml(p[i].p) + '</div><a href=# style=float:right;padding-right:5px;cursor:pointer title="' + "Stop process" + '" onclick=\'return stopProcess(' + EscapeHtml(p[i].p) + ',"' + EscapeHtml(p[i].c) + '")\'><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u ? EscapeHtml(p[i].u) : '') + '</div><div>' + c + '</div></div>';
6964
}
6965
}
@@ -7555,7 +7555,7 @@
7555
} else {
7556
var link = shortname;
7557
if (f.s > 0) { link = '<a href=# style=cursor:pointer onclick="return p13downloadfile(\'' + encodeURIComponentEx(newlinkpath + '/' + name) + '\',\'' + encodeURIComponentEx(name) + '\',' + f.s + ')">' + shortname + '</a>'; }
7558
- h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span class=fsize>' + fdatestr + '</span><span style=float:right>' + EscapeHTML(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
7558
+ h = '<div id=fileEntry cmenu=filesContextMenu fileIndex=' + i + ' class=filelist file=3><input file=3 style=float:left name=fd class=fcb type=checkbox onchange=p13setActions() value=\'' + f.nx + '\'> <span class=fsize>' + fdatestr + '</span><span style=float:right>' + EscapeHtml(fsize) + '</span><span><div class=fileIcon' + f.t + '></div>' + link + '</span></div>';
7559
}
7560
7561
if (f.t < 3) { html1 += h; } else { html2 += h; }