Removed debug files

Ylian Saint-Hilaire committed Oct 21, 2020 at 15:36 UTC 8f965dd4fc797cd5d09040716abd75818355d1a0
2 files changed -7827
agents/meshcore-bad.js deleted
-3924
@@ -1,3924 +0,0 @@
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 (e) { }
51 - if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
52 - } catch (x) { }
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 (e) { }
58 - if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59 - } catch (e) { }
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 (e) { } }
140 - obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141 - try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
142 - obj.DAIPC._daipc = [];
143 - obj.DAIPC.on('connection', function (c) {
144 - c._send = function (j) {
145 - var data = JSON.stringify(j);
146 - var packet = Buffer.alloc(data.length + 4);
147 - packet.writeUInt32LE(data.length + 4, 0);
148 - Buffer.from(data).copy(packet, 4);
149 - this.write(packet);
150 - };
151 - this._daipc.push(c);
152 - c.parent = this;
153 - c.on('end', function () { removeRegisteredApp(this); });
154 - c.on('data', function (chunk) {
155 - if (chunk.length < 4) { this.unshift(chunk); return; }
156 - var len = chunk.readUInt32LE(0);
157 - if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
158 - if (chunk.length < len) { this.unshift(chunk); return; }
159 -
160 - var data = chunk.slice(4, len);
161 - try { data = JSON.parse(data.toString()); } catch (e) { }
162 - if ((data == null) || (typeof data.cmd != 'string')) return;
163 -
164 - try {
165 - switch (data.cmd) {
166 - case 'requesthelp':
167 - if (this._registered == null) return;
168 - sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
169 - var help = {};
170 - help[this._registered] = data.value;
171 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
172 - MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
173 - break;
174 - case 'cancelhelp':
175 - if (this._registered == null) return;
176 - sendConsoleText('Cancel Help (' + this._registered + ')');
177 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
178 - break;
179 - case 'register':
180 - if (typeof data.value == 'string') {
181 - this._registered = data.value;
182 - var apps = {};
183 - apps[data.value] = 1;
184 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
185 - this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
186 - }
187 - break;
188 - case 'query':
189 - switch (data.value) {
190 - case 'connection':
191 - data.result = require('MeshAgent').ConnectedServer;
192 - this._send(data);
193 - break;
194 - case 'descriptors':
195 - require('ChainViewer').getSnapshot().then(function (f) {
196 - this.tag.payload.result = f;
197 - this.tag.ipc._send(this.tag.payload);
198 - }).parentPromise.tag = { ipc: this, payload: data };
199 - break;
200 - }
201 - break;
202 - case 'amtstate':
203 - if (amt == null) return;
204 - var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
205 - func.pipe = this;
206 - amt.getAmtInfo(func);
207 - break;
208 - case 'sessions':
209 - this._send({ cmd: 'sessions', sessions: tunnelUserCount });
210 - break;
211 - }
212 - }
213 - catch (e) { removeRegisteredApp(this); this.end(); return; }
214 - });
215 - });
216 -
217 - // Send current sessions to registered apps
218 - function broadcastSessionsToRegisteredApps(x) {
219 - broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
220 - }
221 -
222 - // Send this object to all registered local applications
223 - function broadcastToRegisteredApps(x) {
224 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
225 - for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
226 - }
227 -
228 - // Send list of registered apps to the server
229 - function updateRegisteredAppsToServer() {
230 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
231 - var apps = {};
232 - for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
233 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
234 - }
235 -
236 - // Remove a registered app
237 - function removeRegisteredApp(pipe) {
238 - for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
239 - if (pipe._registered != null) updateRegisteredAppsToServer();
240 - }
241 -
242 - function diagnosticAgent_uninstall() {
243 - require('service-manager').manager.uninstallService('meshagentDiagnostic');
244 - require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
245 - };
246 - function diagnosticAgent_installCheck(install) {
247 - try {
248 - var diag = require('service-manager').manager.getService('meshagentDiagnostic');
249 - return (diag);
250 - }
251 - catch (e) {
252 - }
253 - if (!install) { return (null); }
254 -
255 - var svc = null;
256 - try {
257 - require('service-manager').manager.installService(
258 - {
259 - name: 'meshagentDiagnostic',
260 - displayName: "Mesh Agent Diagnostic Service",
261 - description: "Mesh Agent Diagnostic Service",
262 - servicePath: process.execPath,
263 - parameters: ['-recovery']
264 - //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
265 - });
266 - svc = require('service-manager').manager.getService('meshagentDiagnostic');
267 - }
268 - catch (e) {
269 - return (null);
270 - }
271 - var proxyConfig = require('global-tunnel').proxyConfig;
272 - var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
273 - var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
274 - ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
275 - ddb.Put('disableUpdate', '1');
276 - ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
277 - ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
278 - ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
279 - if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
280 - if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
281 - if (proxyConfig) {
282 - ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
283 - } else {
284 - ddb.Put('ignoreProxyFile', '1');
285 - }
286 -
287 - require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
288 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
289 -
290 - delete ddb;
291 -
292 - // Set a recurrent task, to run the Diagnostic Agent every 2 days
293 - 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' });
294 - //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
295 -
296 - return (svc);
297 - }
298 -
299 - // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
300 - if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
301 - // Setup manual battery monitoring
302 - require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
303 - if (require('MeshAgent')._batteryFileTimer != null) return;
304 - require('MeshAgent')._batteryFileTimer = setTimeout(function () {
305 - try {
306 - require('MeshAgent')._batteryFileTimer = null;
307 - var data = null;
308 - try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
309 - if ((data != null) && (data.length < 10)) {
310 - data = data.split(',');
311 - if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
312 - var level = parseInt(data[1]);
313 - if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
314 - }
315 - }
316 - } catch (e) { }
317 - }, 1000);
318 - });
319 - } else {
320 - // Setup normal battery monitoring
321 - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
322 - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
323 - _battLevelChanged.self._currentBatteryLevel = val;
324 - _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
325 - };
326 - require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
327 - require('MeshAgent')._powerChanged = function _powerChanged(val) {
328 - _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
329 - _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
330 - };
331 - require('MeshAgent')._powerChanged.self = require('MeshAgent');
332 - require('MeshAgent').on('Connected', function (status) {
333 - if (status == 0) {
334 - require('power-monitor').removeListener('acdc', this._powerChanged);
335 - require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
336 - } else {
337 - require('power-monitor').on('acdc', this._powerChanged);
338 - require('power-monitor').on('batteryLevel', this._battLevelChanged);
339 - }
340 - });
341 - }
342 - }
343 -
344 -
345 - /*
346 - function borderController() {
347 - this.container = null;
348 - this.Start = function Start(user) {
349 - if (this.container == null) {
350 - if (process.platform == 'win32') {
351 - try {
352 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
353 - } catch (e) {
354 - this.container = require('ScriptContainer').Create({ processIsolation: 1 });
355 - }
356 - } else {
357 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
358 - }
359 - this.container.parent = this;
360 - this.container.addModule('monitor-info', getJSModule('monitor-info'));
361 - this.container.addModule('monitor-border', getJSModule('monitor-border'));
362 - this.container.addModule('promise', getJSModule('promise'));
363 - this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
364 - this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
365 - }
366 - }
367 - this.Stop = function Stop() {
368 - if (this.container != null) {
369 - this._container = this.container;
370 - this._container.parent = this;
371 - this.container = null;
372 - this._container.exit();
373 - }
374 - }
375 - }
376 - obj.borderManager = new borderController();
377 - */
378 -
379 - // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
380 - var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
381 -
382 -
383 - // Get the operating system description string
384 - try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (e) { }
385 -
386 - var meshServerConnectionState = 0;
387 - var tunnels = {};
388 - var lastMeInfo = null;
389 - var lastNetworkInfo = null;
390 - var lastPublicLocationInfo = null;
391 - var selfInfoUpdateTimer = null;
392 - var http = require('http');
393 - var net = require('net');
394 - var fs = require('fs');
395 - var rtc = require('ILibWebRTC');
396 - var amt = null;
397 - var processManager = require('process-manager');
398 - var wifiScannerLib = null;
399 - var wifiScanner = null;
400 - var networkMonitor = null;
401 - var amtscanner = null;
402 - var nextTunnelIndex = 1;
403 - var apftunnel = null;
404 - var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
405 -
406 - // Add to the server event log
407 - function MeshServerLog(msg, state) {
408 - if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
409 - if (state) {
410 - if (state.userid) { msg.userid = state.userid; }
411 - if (state.username) { msg.username = state.username; }
412 - if (state.sessionid) { msg.sessionid = state.sessionid; }
413 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
414 - }
415 - mesh.SendCommand(msg);
416 - }
417 -
418 - // Add to the server event log, use internationalized events
419 - function MeshServerLogEx(id, args, msg, state) {
420 - var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
421 - if (state) {
422 - if (state.userid) { msg.userid = state.userid; }
423 - if (state.username) { msg.username = state.username; }
424 - if (state.sessionid) { msg.sessionid = state.sessionid; }
425 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
426 - }
427 - mesh.SendCommand(msg);
428 - }
429 -
430 - // If we are running in Duktape, agent will be null
431 - if (agent == null) {
432 - // Running in native agent, Import libraries
433 - db = require('SimpleDataStore').Shared();
434 - sha = require('SHA256Stream');
435 - mesh = require('MeshAgent');
436 - childProcess = require('child_process');
437 - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
438 - // Check if this computer supports a desktop
439 - try
440 - {
441 - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
442 - {
443 - meshCoreObj.caps |= 1;
444 - }
445 - else if(process.platform == 'linux' || process.platform == 'freebsd')
446 - {
447 - require('monitor-info').on('kvmSupportDetected', function (value)
448 - {
449 - meshCoreObj.caps |= 1;
450 - mesh.SendCommand(meshCoreObj);
451 - });
452 - }
453 - } catch (e) { }
454 - }
455 - } else {
456 - // Running in nodejs
457 - meshCoreObj.value += '-NodeJS';
458 - meshCoreObj.caps = 8;
459 - mesh = agent.getMeshApi();
460 - }
461 -
462 - mesh.DAIPC = obj.DAIPC;
463 -
464 - /*
465 - var AMTScanner = require("AMTScanner");
466 - var scan = new AMTScanner();
467 -
468 - scan.on("found", function (data) {
469 - if (typeof data === 'string') {
470 - console.log(data);
471 - } else {
472 - console.log(JSON.stringify(data, null, " "));
473 - }
474 - });
475 - scan.scan("10.2.55.140", 1000);
476 - scan.scan("10.2.55.139-10.2.55.145", 1000);
477 - scan.scan("10.2.55.128/25", 2000);
478 - */
479 -
480 - /*
481 - // Try to load up the network monitor
482 - try {
483 - networkMonitor = require('NetworkMonitor');
484 - networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
485 - networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
486 - networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
487 - } catch (e) { networkMonitor = null; }
488 - */
489 -
490 - // Try to load up the Intel AMT scanner
491 - try {
492 - var AMTScannerModule = require('amt-scanner');
493 - amtscanner = new AMTScannerModule();
494 - //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
495 - } catch (e) { amtscanner = null; }
496 -
497 - // Fetch the SMBios Tables
498 - var SMBiosTables = null;
499 - var SMBiosTablesRaw = null;
500 - try {
501 - var SMBiosModule = null;
502 - try { SMBiosModule = require('smbios'); } catch (e) { }
503 - if (SMBiosModule != null) {
504 - SMBiosModule.get(function (data) {
505 - if (data != null) {
506 - SMBiosTablesRaw = data;
507 - SMBiosTables = require('smbios').parse(data)
508 - if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
509 -
510 - // If SMBios tables say that Intel AMT is present, try to connect MEI
511 - if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
512 - {
513 - var amtmodule = require('amt-manage');
514 - amt = new amtmodule(mesh, db, false);
515 - amt.on('portBinding_LMS', function (map)
516 - {
517 - var j = { action: 'lmsinfo', value: { ports: map.keys() } };
518 - mesh.SendCommand(j);
519 - });
520 - amt.on('stateChange_LMS', function (v)
521 - {
522 - if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; }
523 - switch(v)
524 - {
525 - case 0:
526 - meshCoreObj.intelamt.microlms = 'DISABLED';
527 - break;
528 - case 1:
529 - meshCoreObj.intelamt.microlms = 'CONNECTING';
530 - break;
531 - case 2:
532 - meshCoreObj.intelamt.microlms = 'CONNECTED';
533 - break;
534 - default:
535 - break;
536 - }
537 - mesh.SendCommand(meshCoreObj);
538 - });
539 - amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
540 - amt.start();
541 - }
542 - }
543 - });
544 - }
545 - } catch (e) { sendConsoleText("ex1: " + e); }
546 -
547 - // Try to load up the WIFI scanner
548 - try {
549 - var wifiScannerLib = require('wifi-scanner');
550 - wifiScanner = new wifiScannerLib();
551 - wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
552 - } catch (e) { wifiScannerLib = null; wifiScanner = null; }
553 -
554 - // Get our location (lat/long) using our public IP address
555 - var getIpLocationDataExInProgress = false;
556 - var getIpLocationDataExCounts = [0, 0];
557 - function getIpLocationDataEx(func) {
558 - if (getIpLocationDataExInProgress == true) { return false; }
559 - try {
560 - getIpLocationDataExInProgress = true;
561 - getIpLocationDataExCounts[0]++;
562 - var options = http.parseUri("http://ipinfo.io/json");
563 - options.method = 'GET';
564 - http.request(options, function (resp) {
565 - if (resp.statusCode == 200) {
566 - var geoData = '';
567 - resp.data = function (geoipdata) { geoData += geoipdata; };
568 - resp.end = function () {
569 - var location = null;
570 - try {
571 - if (typeof geoData == 'string') {
572 - var result = JSON.parse(geoData);
573 - if (result.ip && result.loc) { location = result; }
574 - }
575 - } catch (e) { }
576 - if (func) { getIpLocationDataExCounts[1]++; func(location); }
577 - }
578 - } else { func(null); }
579 - getIpLocationDataExInProgress = false;
580 - }).end();
581 - return true;
582 - }
583 - catch (e) { return false; }
584 - }
585 -
586 - // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
587 - function clearGatewayMac(str) {
588 - if (str == null) return null;
589 - var x = JSON.parse(str);
590 - for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
591 - return JSON.stringify(x);
592 - }
593 -
594 - function getIpLocationData(func) {
595 - // Get the location information for the cache if possible
596 - var publicLocationInfo = db.Get('publicLocationInfo');
597 - if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
598 - if (publicLocationInfo == null) {
599 - // Nothing in the cache, fetch the data
600 - getIpLocationDataEx(function (locationData) {
601 - if (locationData != null) {
602 - publicLocationInfo = {};
603 - publicLocationInfo.netInfoStr = lastNetworkInfo;
604 - publicLocationInfo.locationData = locationData;
605 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
606 - if (func) func(locationData); // Report the new location
607 - } else {
608 - if (func) func(null); // Report no location
609 - }
610 - });
611 - } else {
612 - // Check the cache
613 - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
614 - // Cache match
615 - if (func) func(publicLocationInfo.locationData);
616 - } else {
617 - // Cache mismatch
618 - getIpLocationDataEx(function (locationData) {
619 - if (locationData != null) {
620 - publicLocationInfo = {};
621 - publicLocationInfo.netInfoStr = lastNetworkInfo;
622 - publicLocationInfo.locationData = locationData;
623 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
624 - if (func) func(locationData); // Report the new location
625 - } else {
626 - if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
627 - }
628 - });
629 - }
630 - }
631 - }
632 -
633 - // Polyfill String.endsWith
634 - if (!String.prototype.endsWith) {
635 - String.prototype.endsWith = function (searchString, position) {
636 - var subjectString = this.toString();
637 - if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
638 - position -= searchString.length;
639 - var lastIndex = subjectString.lastIndexOf(searchString, position);
640 - return lastIndex !== -1 && lastIndex === position;
641 - };
642 - }
643 -
644 - // Polyfill path.join
645 - obj.path = {
646 - join: function () {
647 - var x = [];
648 - for (var i in arguments) {
649 - var w = arguments[i];
650 - if (w != null) {
651 - while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
652 - if (i != 0) {
653 - while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
654 - }
655 - x.push(w);
656 - }
657 - }
658 - if (x.length == 0) return '/';
659 - return x.join('/');
660 - }
661 - };
662 -
663 - // Replace a string with a number if the string is an exact number
664 - function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
665 -
666 - // Convert decimal to hex
667 - function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
668 -
669 - // Convert a raw string to a hex string
670 - function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
671 -
672 - // Convert a buffer into a string
673 - function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
674 -
675 - // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
676 - function hex2rstr(d) {
677 - if (typeof d != "string" || d.length == 0) return '';
678 - var r = '', m = ('' + d).match(/../g), t;
679 - while (t = m.shift()) r += String.fromCharCode('0x' + t);
680 - return r
681 - }
682 -
683 - // Convert an object to string with all functions
684 - function objToString(x, p, pad, ret) {
685 - if (ret == undefined) ret = '';
686 - if (p == undefined) p = 0;
687 - if (x == null) { return '[null]'; }
688 - if (p > 8) { return '[...]'; }
689 - if (x == undefined) { return '[undefined]'; }
690 - if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
691 - if (typeof x == 'buffer') { return '[buffer]'; }
692 - if (typeof x != 'object') { return x; }
693 - var r = '{' + (ret ? '\r\n' : ' ');
694 - for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
695 - return r + addPad(p, pad) + '}';
696 - }
697 -
698 - // Return p number of spaces
699 - function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
700 -
701 - // Split a string taking into account the quoats. Used for command line parsing
702 - function splitArgs(str) {
703 - var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
704 - do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
705 - return myArray;
706 - }
707 -
708 - // Parse arguments string array into an object
709 - function parseArgs(argv) {
710 - var results = { '_': [] }, current = null;
711 - for (var i = 1, len = argv.length; i < len; i++) {
712 - var x = argv[i];
713 - if (x.length > 2 && x[0] == '-' && x[1] == '-') {
714 - if (current != null) { results[current] = true; }
715 - current = x.substring(2);
716 - } else {
717 - if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
718 - }
719 - }
720 - if (current != null) { results[current] = true; }
721 - return results;
722 - }
723 -
724 - // Get server target url with a custom path
725 - function getServerTargetUrl(path) {
726 - var x = mesh.ServerUrl;
727 - //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
728 - if (x == null) { return null; }
729 - if (path == null) { path = ''; }
730 - x = http.parseUri(x);
731 - if (x == null) return null;
732 - return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
733 - }
734 -
735 - // Get server url. If the url starts with "*/..." change it, it not use the url as is.
736 - function getServerTargetUrlEx(url) {
737 - if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
738 - return url;
739 - }
740 -
741 - // Send a wake-on-lan packet
742 - function sendWakeOnLan(hexMac) {
743 - hexMac = hexMac.split(':').join('');
744 - var count = 0;
745 - try {
746 - var interfaces = require('os').networkInterfaces();
747 - var magic = 'FFFFFFFFFFFF';
748 - for (var x = 1; x <= 16; ++x) { magic += hexMac; }
749 - var magicbin = Buffer.from(magic, 'hex');
750 -
751 - for (var adapter in interfaces) {
752 - if (interfaces.hasOwnProperty(adapter)) {
753 - for (var i = 0; i < interfaces[adapter].length; ++i) {
754 - var addr = interfaces[adapter][i];
755 - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
756 - try {
757 - var socket = require('dgram').createSocket({ type: 'udp4' });
758 - socket.bind({ address: addr.address });
759 - socket.setBroadcast(true);
760 - socket.setMulticastInterface(addr.address);
761 - socket.setMulticastTTL(1);
762 - socket.send(magicbin, 7, '255.255.255.255');
763 - socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
764 - count++;
765 - }
766 - catch (e) { }
767 - }
768 - }
769 - }
770 - }
771 - } catch (e) { }
772 - return count;
773 - }
774 -
775 - // Handle a mesh agent command
776 - function handleServerCommand(data) {
777 - if (typeof data == 'object') {
778 - // If this is a console command, parse it and call the console handler
779 - switch (data.action) {
780 - case 'msg': {
781 - switch (data.type) {
782 - case 'console': { // Process a console command
783 - if (data.value && data.sessionid) {
784 - MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
785 - var args = splitArgs(data.value);
786 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
787 - }
788 - break;
789 - }
790 - case 'tunnel': {
791 - if (data.value != null) { // Process a new tunnel connection request
792 - // Create a new tunnel object
793 - var xurl = getServerTargetUrlEx(data.value);
794 - if (xurl != null) {
795 - xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
796 - var woptions = http.parseUri(xurl);
797 - woptions.perMessageDeflate = false;
798 - if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
799 - woptions.rejectUnauthorized = 0;
800 - //sendConsoleText(JSON.stringify(woptions));
801 - //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
802 - var tunnel = http.request(woptions);
803 - tunnel.upgrade = onTunnelUpgrade;
804 - tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
805 - tunnel.sessionid = data.sessionid;
806 - tunnel.rights = data.rights;
807 - tunnel.consent = data.consent;
808 - tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
809 - tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
810 - tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
811 - tunnel.userid = data.userid;
812 - tunnel.remoteaddr = data.remoteaddr;
813 - tunnel.state = 0;
814 - tunnel.url = xurl;
815 - tunnel.protocol = 0;
816 - tunnel.soptions = data.soptions;
817 - tunnel.tcpaddr = data.tcpaddr;
818 - tunnel.tcpport = data.tcpport;
819 - tunnel.udpaddr = data.udpaddr;
820 - tunnel.udpport = data.udpport;
821 - tunnel.end();
822 - // Put the tunnel in the tunnels list
823 - var index = nextTunnelIndex++;
824 - tunnel.index = index;
825 - tunnels[index] = tunnel;
826 -
827 - //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
828 - }
829 - }
830 - break;
831 - }
832 - case 'messagebox': {
833 - // Display a message box
834 - if (data.title && data.msg) {
835 - MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
836 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
837 - try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
838 - }
839 - break;
840 - }
841 - case 'ps': {
842 - // Return the list of running processes
843 - if (data.sessionid) {
844 - processManager.getProcesses(function (plist) {
845 - mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
846 - });
847 - }
848 - break;
849 - }
850 - case 'pskill': {
851 - // Kill a process
852 - if (data.value) {
853 - MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
854 - try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
855 - }
856 - break;
857 - }
858 - case 'services': {
859 - // Return the list of installed services
860 - var services = null;
861 - try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
862 - if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
863 - break;
864 - }
865 - case 'serviceStop': {
866 - // Stop a service
867 - try {
868 - var service = require('service-manager').manager.getService(data.serviceName);
869 - if (service != null) { service.stop(); }
870 - } catch (e) { }
871 - break;
872 - }
873 - case 'serviceStart': {
874 - // Start a service
875 - try {
876 - var service = require('service-manager').manager.getService(data.serviceName);
877 - if (service != null) { service.start(); }
878 - } catch (e) { }
879 - break;
880 - }
881 - case 'serviceRestart': {
882 - // Restart a service
883 - try {
884 - var service = require('service-manager').manager.getService(data.serviceName);
885 - if (service != null) { service.restart(); }
886 - } catch (e) { }
887 - break;
888 - }
889 - case 'deskBackground':
890 - {
891 - // Toggle desktop background
892 - try {
893 - if (process.platform == 'win32') {
894 - var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
895 - var sid = undefined;
896 - if (stype == 1) {
897 - if (require('MeshAgent')._tsid != null) {
898 - stype = 5;
899 - sid = require('MeshAgent')._tsid;
900 - }
901 - }
902 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
903 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
904 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
905 - child.stderr.on('data', function () { });
906 - child.waitExit();
907 - var current = child.stdout.str.trim();
908 - if (current != '') { require('MeshAgent')._wallpaper = current; }
909 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
910 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
911 - child.stderr.on('data', function () { });
912 - child.waitExit();
913 - } else {
914 - var id = require('user-sessions').consoleUid();
915 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
916 - if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
917 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
918 - }
919 - } catch (e) {
920 - sendConsoleText(e);
921 - }
922 - break;
923 - }
924 - case 'openUrl': {
925 - // Open a local web browser and return success/fail
926 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
927 - sendConsoleText("OpenURL: " + data.url);
928 - if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
929 - break;
930 - }
931 - case 'getclip': {
932 - // Send the load clipboard back to the user
933 - //sendConsoleText('getClip: ' + JSON.stringify(data));
934 - if (require('MeshAgent').isService) {
935 - require('clipboard').dispatchRead().then(function (str) {
936 - if (str) {
937 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
938 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
939 - }
940 - });
941 - } else {
942 - require("clipboard").read().then(function (str) {
943 - if (str) {
944 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
945 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
946 - }
947 - });
948 - }
949 - break;
950 - }
951 - case 'setclip': {
952 - // Set the load clipboard to a user value
953 - //sendConsoleText('setClip: ' + JSON.stringify(data));
954 - if (typeof data.data == 'string') {
955 - MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
956 - if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
957 - mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
958 - }
959 - break;
960 - }
961 - case 'userSessions': {
962 - // Send back current user sessions list, this is Windows only.
963 - //sendConsoleText('userSessions: ' + JSON.stringify(data));
964 - if (process.platform != 'win32') break;
965 - var p = require('user-sessions').enumerateUsers();
966 - p.sessionid = data.sessionid;
967 - p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
968 - break;
969 - }
970 - default:
971 - // Unknown action, ignore it.
972 - break;
973 - }
974 - break;
975 - }
976 - case 'acmactivate': {
977 - if (amt != null) {
978 - MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
979 - amt.setAcmResponse(data);
980 - }
981 - break;
982 - }
983 - case 'wakeonlan': {
984 - // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
985 - sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
986 - for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
987 - break;
988 - }
989 - case 'runcommands': {
990 - if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
991 - sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
992 -
993 - // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
994 - var options = {};
995 - if (data.runAsUser > 0) {
996 - try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
997 - options.type = require('child_process').SpawnTypes.TERM;
998 - }
999 - if (data.runAsUser == 2) {
1000 - if (options.uid == null) break;
1001 - if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1002 - }
1003 -
1004 - if (process.platform == 'win32') {
1005 - if (data.type == 1) {
1006 - // Windows command shell
1007 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1008 - mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1009 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1010 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1011 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1012 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1013 - } else if (data.type == 2) {
1014 - // Windows Powershell
1015 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1016 - mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1017 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1018 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1019 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1020 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1021 - }
1022 - } else if (data.type == 3) {
1023 - // Linux shell
1024 - mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1025 - mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1026 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1027 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1028 - mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1029 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1030 - }
1031 - break;
1032 - }
1033 - case 'uninstallagent':
1034 - // Uninstall this agent
1035 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1036 - if (require('service-manager').manager.getService(agentName).isMe()) {
1037 - try { diagnosticAgent_uninstall(); } catch (e) { }
1038 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1039 - 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 });
1040 - }
1041 - break;
1042 - case 'poweraction': {
1043 - // Server telling us to execute a power action
1044 - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1045 - var forced = 0;
1046 - if (data.forced == 1) { forced = 1; }
1047 - data.actiontype = parseInt(data.actiontype);
1048 - MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1049 - sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1050 - var r = mesh.ExecPowerState(data.actiontype, forced);
1051 - sendConsoleText("ExecPowerState returned code: " + r);
1052 - }
1053 - break;
1054 - }
1055 - case 'iplocation': {
1056 - // 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
1057 - getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1058 - break;
1059 - }
1060 - case 'toast': {
1061 - // Display a toast message
1062 - if (data.title && data.msg) {
1063 - MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1064 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1065 - try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1066 - }
1067 - break;
1068 - }
1069 - case 'openUrl': {
1070 - // Open a local web browser and return success/fail
1071 - //sendConsoleText('OpenURL: ' + data.url);
1072 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1073 - if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1074 - break;
1075 - }
1076 - case 'amtconfig': {
1077 - // Perform Intel AMT activation and/or configuration
1078 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'amtconfig' });
1079 - if ((apftunnel != null) || (amt == null)) break;
1080 - getMeiState(15, function (state) {
1081 - if ((state == null) || (state.ProvisioningState == null)) return;
1082 - if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
1083 - var apfarg = {
1084 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
1085 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16), // TODO: User a server provided encrypted cookie for CIRA-LMS login
1086 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
1087 - mpskeepalive: 60000,
1088 - clientname: state.OsHostname,
1089 - clientaddress: '127.0.0.1',
1090 - clientuuid: state.UUID,
1091 - conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
1092 - meiState: state // MEI state will be passed to MPS server
1093 - };
1094 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'apf-on' });
1095 - apftunnel = require('apfclient')({ debug: false }, apfarg);
1096 - apftunnel.onJsonControl = function (data) {
1097 - if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message (DEBUG)
1098 - if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1099 - if (data.action == 'deactivate') { // Request CCM deactivation
1100 - var amtMeiModule, amtMei;
1101 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
1102 - amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
1103 - amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
1104 - }
1105 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
1106 - }
1107 - apftunnel.onChannelClosed = function () { apftunnel = null; require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'apf-off' }); }
1108 - try { apftunnel.connect(); } catch (ex) { }
1109 - });
1110 - break;
1111 - }
1112 - case 'getScript': {
1113 - // Received a configuration script from the server
1114 - sendConsoleText('getScript: ' + JSON.stringify(data));
1115 - break;
1116 - }
1117 - case 'sysinfo': {
1118 - // Fetch system information
1119 - getSystemInformation(function (results) {
1120 - if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1121 - });
1122 - break;
1123 - }
1124 - case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1125 - case 'pong': { break; }
1126 - case 'plugin': {
1127 - try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1128 - break;
1129 - }
1130 - case 'coredump':
1131 - // Set the current agent coredump situation.
1132 - if (data.value === true) {
1133 - // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1134 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
1135 - } else if (data.value === false) {
1136 - process.coreDumpLocation = null;
1137 - }
1138 - break;
1139 - case 'getcoredump':
1140 - // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1141 - var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1142 - var coreDumpPath = null;
1143 - if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1144 - if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) { r.exists = (db.Get('CoreDumpTime') != require('fs').statSync(coreDumpPath).mtime); }
1145 - if (r.exists == true) { r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); }
1146 - mesh.SendCommand(JSON.stringify(r));
1147 - default:
1148 - // Unknown action, ignore it.
1149 - break;
1150 - }
1151 - }
1152 - }
1153 -
1154 - // Called when a file changed in the file system
1155 - /*
1156 - function onFileWatcher(a, b) {
1157 - console.log('onFileWatcher', a, b, this.path);
1158 - var response = getDirectoryInfo(this.path);
1159 - if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1160 - }
1161 - */
1162 -
1163 - function getSystemInformation(func) {
1164 - try {
1165 - var results = { hardware: require('identifiers').get() }; // Hardware info
1166 - if (results.hardware && results.hardware.windows) {
1167 - // Remove extra entries and things that change quickly
1168 - var x = results.hardware.windows.osinfo;
1169 - try { delete x.FreePhysicalMemory; } catch (e) { }
1170 - try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1171 - try { delete x.FreeVirtualMemory; } catch (e) { }
1172 - try { delete x.LocalDateTime; } catch (e) { }
1173 - try { delete x.MaxProcessMemorySize; } catch (e) { }
1174 - try { delete x.TotalVirtualMemorySize; } catch (e) { }
1175 - try { delete x.TotalVisibleMemorySize; } catch (e) { }
1176 - try {
1177 - if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1178 - if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1179 - if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1180 - } catch (e) { }
1181 - }
1182 - if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1183 - /*
1184 - if (process.platform == 'win32') {
1185 - var defragResult = function (r) {
1186 - if (typeof r == 'object') { results[this.callname] = r; }
1187 - if (this.callname == 'defrag') {
1188 - var pr = require('win-info').installedApps(); // Installed apps
1189 - pr.callname = 'installedApps';
1190 - pr.sessionid = data.sessionid;
1191 - pr.then(defragResult, defragResult);
1192 - }
1193 - else {
1194 - results.winpatches = require('win-info').qfe(); // Windows patches
1195 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1196 - func(results);
1197 - }
1198 - }
1199 - var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1200 - pr.callname = 'defrag';
1201 - pr.sessionid = data.sessionid;
1202 - pr.then(defragResult, defragResult);
1203 - } else {
1204 - */
1205 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1206 - func(results);
1207 - //}
1208 - } catch (e) { func(null, e); }
1209 - }
1210 -
1211 - // Get a formated response for a given directory path
1212 - function getDirectoryInfo(reqpath) {
1213 - var response = { path: reqpath, dir: [] };
1214 - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1215 - // List all the drives in the root, or the root itself
1216 - var results = null;
1217 - try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1218 - if (results != null) {
1219 - for (var i = 0; i < results.length; ++i) {
1220 - var drive = { n: results[i].name, t: 1 };
1221 - if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1222 - response.dir.push(drive);
1223 - }
1224 - }
1225 - } else {
1226 - // List all the files and folders in this path
1227 - if (reqpath == '') { reqpath = '/'; }
1228 - var results = null, xpath = obj.path.join(reqpath, '*');
1229 - //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1230 - try { results = fs.readdirSync(xpath); } catch (e) { }
1231 - if (results != null) {
1232 - for (var i = 0; i < results.length; ++i) {
1233 - if ((results[i] != '.') && (results[i] != '..')) {
1234 - var stat = null, p = obj.path.join(reqpath, results[i]);
1235 - //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1236 - try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1237 - if ((stat != null) && (stat != undefined)) {
1238 - if (stat.isDirectory() == true) {
1239 - response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1240 - } else {
1241 - response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1242 - }
1243 - }
1244 - }
1245 - }
1246 - }
1247 - }
1248 - return response;
1249 - }
1250 -
1251 - // Tunnel callback operations
1252 - function onTunnelUpgrade(response, s, head) {
1253 - this.s = s;
1254 - s.httprequest = this;
1255 - s.end = onTunnelClosed;
1256 - s.tunnel = this;
1257 - s.descriptorMetadata = "MeshAgent_relayTunnel";
1258 -
1259 - if (require('MeshAgent').idleTimeout != null)
1260 - {
1261 - s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1262 - s.on('timeout', function ()
1263 - {
1264 - this.ping();
1265 - this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1266 - });
1267 - }
1268 -
1269 - //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1270 -
1271 - if (this.tcpport != null) {
1272 - // This is a TCP relay connection, pause now and try to connect to the target.
1273 - s.pause();
1274 - s.data = onTcpRelayServerTunnelData;
1275 - var connectionOptions = { port: parseInt(this.tcpport) };
1276 - if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1277 - s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1278 - s.tcprelay.peerindex = this.index;
1279 -
1280 - // Add the TCP session to the count and update the server
1281 - if (s.httprequest.userid != null) {
1282 - if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1283 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1284 - broadcastSessionsToRegisteredApps();
1285 - }
1286 - } if (this.udpport != null) {
1287 - // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1288 - s.data = onUdpRelayServerTunnelData;
1289 - s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1290 - s.udprelay.bind({ port: 0 });
1291 - s.udprelay.peerindex = this.index;
1292 - s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1293 - s.udprelay.udpport = this.udpport;
1294 - s.udprelay.udpaddr = this.udpaddr;
1295 - s.udprelay.first = true;
1296 -
1297 - // Add the UDP session to the count and update the server
1298 - if (s.httprequest.userid != null) {
1299 - if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1300 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1301 - broadcastSessionsToRegisteredApps();
1302 - }
1303 - } else {
1304 - // This is a normal connect for KVM/Terminal/Files
1305 - s.data = onTunnelData;
1306 - }
1307 - }
1308 -
1309 - // Called when UDP relay data is received // TODO****
1310 - function onUdpRelayTargetTunnelConnect(data) {
1311 - var peerTunnel = tunnels[this.peerindex];
1312 - peerTunnel.s.write(data);
1313 - }
1314 -
1315 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1316 - function onUdpRelayServerTunnelData(data) {
1317 - if (this.udprelay.first === true) {
1318 - delete this.udprelay.first; // Skip the first 'c' that is received.
1319 - } else {
1320 - this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1321 - }
1322 - }
1323 -
1324 - // Called when the TCP relay target is connected
1325 - function onTcpRelayTargetTunnelConnect() {
1326 - var peerTunnel = tunnels[this.peerindex];
1327 - this.pipe(peerTunnel.s); // Pipe Target --> Server
1328 - peerTunnel.s.first = true;
1329 - peerTunnel.s.resume();
1330 - }
1331 -
1332 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1333 - function onTcpRelayServerTunnelData(data) {
1334 - if (this.first == true) {
1335 - this.first = false;
1336 - this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1337 - }
1338 - }
1339 -
1340 - function onTunnelClosed() {
1341 - var tunnel = tunnels[this.httprequest.index];
1342 - if (tunnel == null) return; // Stop duplicate calls.
1343 -
1344 - // If this is a routing session, clean up and send the new session counts.
1345 - if (this.httprequest.userid != null) {
1346 - if (this.httprequest.tcpport != null) {
1347 - if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
1348 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1349 - broadcastSessionsToRegisteredApps();
1350 - } else if (this.httprequest.udpport != null) {
1351 - if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
1352 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1353 - broadcastSessionsToRegisteredApps();
1354 - }
1355 - }
1356 -
1357 - // Sent tunnel statistics to the server, only send this if compression was used.
1358 - if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1359 - mesh.SendCommand({
1360 - action: 'tunnelCloseStats',
1361 - url: tunnel.url,
1362 - userid: tunnel.userid,
1363 - protocol: tunnel.protocol,
1364 - sessionid: tunnel.sessionid,
1365 - sent: this.bytesSent_uncompressed.toString(),
1366 - sentActual: this.bytesSent_actual.toString(),
1367 - sentRatio: this.bytesSent_ratio,
1368 - received: this.bytesReceived_uncompressed.toString(),
1369 - receivedActual: this.bytesReceived_actual.toString(),
1370 - receivedRatio: this.bytesReceived_ratio
1371 - });
1372 - }
1373 -
1374 - //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1375 - if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1376 -
1377 - /*
1378 - // Close the watcher if required
1379 - if (this.httprequest.watcher != undefined) {
1380 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1381 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1382 - delete this.httprequest.watcher;
1383 - }
1384 - */
1385 -
1386 - // If there is a upload or download active on this connection, close the file
1387 - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1388 - if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1389 -
1390 - // Clean up WebRTC
1391 - if (this.webrtc != null) {
1392 - 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; }
1393 - if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1394 - try { this.webrtc.close(); } catch (e) { }
1395 - this.webrtc.removeAllListeners('connected');
1396 - this.webrtc.removeAllListeners('disconnected');
1397 - this.webrtc.removeAllListeners('dataChannel');
1398 - delete this.webrtc;
1399 - }
1400 -
1401 - // Clean up WebSocket
1402 - this.removeAllListeners('data');
1403 - }
1404 - function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1405 - function onTunnelData(data) {
1406 - //console.log("OnTunnelData");
1407 - //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1408 -
1409 - // If this is upload data, save it to file
1410 - if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1411 - // Save the data to file being uploaded.
1412 - if (data[0] == 0) {
1413 - // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1414 - try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1415 - } else {
1416 - // If data does not start with zero, save as-is.
1417 - try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1418 - }
1419 - this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1420 - return;
1421 - }
1422 -
1423 - if (this.httprequest.state == 0) {
1424 - // Check if this is a relay connection
1425 - if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1426 - }
1427 - else
1428 - {
1429 - // Handle tunnel data
1430 - if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1431 - // Take a look at the protocol
1432 - if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1433 - this.httprequest.protocol = parseInt(data);
1434 - if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1435 - if (this.httprequest.protocol == 10) {
1436 - //
1437 - // Basic file transfer
1438 - //
1439 - var stats = null;
1440 - if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1441 - try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1442 - try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1443 - if (this.httprequest.downloadFile) {
1444 - //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1445 - this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1446 - this.httprequest.downloadFile.pipe(this);
1447 - this.httprequest.downloadFile.end = function () { }
1448 - } else {
1449 - //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1450 - this.write(JSON.stringify({ op: 'cancel' }));
1451 - }
1452 - }
1453 - else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1454 - {
1455 - //
1456 - // Remote Terminal
1457 - //
1458 -
1459 - // Check user access rights for terminal
1460 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1461 - // Disengage this tunnel, user does not have the rights to do this!!
1462 - this.httprequest.protocol = 999999;
1463 - this.httprequest.s.end();
1464 - sendConsoleText("Error: No Terminal Control Rights.");
1465 - return;
1466 - }
1467 -
1468 - this.descriptorMetadata = "Remote Terminal";
1469 -
1470 - if (process.platform == 'win32')
1471 - {
1472 - if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1473 - {
1474 - this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1475 - this.httprequest.s.end();
1476 - return;
1477 - }
1478 - }
1479 -
1480 - var prom = require('promise');
1481 - this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1482 - this.httprequest.tpromise.that = this;
1483 - this.httprequest.tpromise.httprequest = this.httprequest;
1484 -
1485 - this.end = function ()
1486 - {
1487 - if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1488 - if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1489 -
1490 - // Remove the terminal session to the count to update the server
1491 - if (this.httprequest.userid != null)
1492 - {
1493 - 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]; } }
1494 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1495 - broadcastSessionsToRegisteredApps();
1496 - }
1497 -
1498 - if (process.platform == 'win32')
1499 - {
1500 - // Unpipe the web socket
1501 - this.unpipe(this.httprequest._term);
1502 - if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1503 -
1504 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1505 - if (this.rtcchannel)
1506 - {
1507 - this.rtcchannel.unpipe(this.httprequest._term);
1508 - if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1509 - }
1510 -
1511 - // Clean up
1512 - if (this.httprequest._term) { this.httprequest._term.end(); }
1513 - this.httprequest._term = null;
1514 - }
1515 - };
1516 -
1517 - // Perform User-Consent if needed.
1518 - if (this.httprequest.consent && (this.httprequest.consent & 16))
1519 - {
1520 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1521 - var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1522 - if (this.httprequest.soptions != null) {
1523 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1524 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1525 - }
1526 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1527 - this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1528 - this.httprequest.tpromise._consent.then(
1529 - function ()
1530 - {
1531 - // Success
1532 - MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1533 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1534 - this.retPromise._consent = null;
1535 - this.retPromise._res();
1536 - },
1537 - function (e)
1538 - {
1539 - // Denied
1540 - MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1541 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1542 - this.retPromise._rej(e.toString());
1543 - });
1544 - }
1545 - else
1546 - {
1547 - // User-Consent is not required, so just resolve this promise
1548 - this.httprequest.tpromise._res();
1549 - }
1550 -
1551 -
1552 - this.httprequest.tpromise.then(
1553 - function ()
1554 - {
1555 - this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1556 - this.httprequest.connectionPromise.ws = this.that;
1557 -
1558 - // Start Terminal
1559 - if(process.platform == 'win32')
1560 - {
1561 - try
1562 - {
1563 - var cols = 80, rows = 25;
1564 - if (this.httprequest.xoptions)
1565 - {
1566 - if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1567 - if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1568 - }
1569 -
1570 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1571 - {
1572 - // Admin Terminal
1573 - if (require('win-virtual-terminal').supported)
1574 - {
1575 - // ConPTY PseudoTerminal
1576 - // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1577 -
1578 - // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1579 - 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] } });
1580 - this.httprequest._dispatcher.httprequest = this.httprequest;
1581 - this.httprequest._dispatcher.on('connection', function (c)
1582 - {
1583 - if (this.httprequest.connectionPromise.completed)
1584 - {
1585 - c.end();
1586 - }
1587 - else
1588 - {
1589 - this.httprequest.connectionPromise._res(c);
1590 - }
1591 - });
1592 - }
1593 - else
1594 - {
1595 - // Legacy Terminal
1596 - this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1597 - }
1598 - }
1599 - else
1600 - {
1601 - // Logged in user
1602 - var userPromise = require('user-sessions').enumerateUsers();
1603 - userPromise.that = this;
1604 - userPromise.then(function (u)
1605 - {
1606 - var that = this.that;
1607 - if (u.Active.length > 0)
1608 - {
1609 - var username = u.Active[0].Username;
1610 - if (require('win-virtual-terminal').supported)
1611 - {
1612 - // ConPTY PseudoTerminal
1613 - 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] } });
1614 - }
1615 - else
1616 - {
1617 - // Legacy Terminal
1618 - 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] } });
1619 - }
1620 - that.httprequest._dispatcher.ws = that;
1621 - that.httprequest._dispatcher.on('connection', function (c)
1622 - {
1623 - if (this.ws.httprequest.connectionPromise.completed)
1624 - {
1625 - c.end();
1626 - }
1627 - else
1628 - {
1629 - this.ws.httprequest.connectionPromise._res(c);
1630 - }
1631 - });
1632 - }
1633 - });
1634 - }
1635 - }
1636 - catch (e)
1637 - {
1638 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1639 - }
1640 - }
1641 - else
1642 - {
1643 - try
1644 - {
1645 - var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1646 - var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1647 - var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1648 -
1649 - var env = { HISTCONTROL: 'ignoreboth' };
1650 - if (this.httprequest.xoptions)
1651 - {
1652 - if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1653 - if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1654 - }
1655 - var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1656 - if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1657 - {
1658 - if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1659 - this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1660 - }
1661 - else if (bash)
1662 - {
1663 - var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1664 - // Spaces at the beginning of lines are needed to hide commands from the command history
1665 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1666 - this.httprequest.connectionPromise._res(p);
1667 - }
1668 - else if (sh)
1669 - {
1670 - var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1671 - // Spaces at the beginning of lines are needed to hide commands from the command history
1672 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1673 - this.httprequest.connectionPromise._res(p);
1674 - }
1675 - else
1676 - {
1677 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1678 - }
1679 - }
1680 - catch (e)
1681 - {
1682 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1683 - }
1684 - }
1685 -
1686 - this.httprequest.connectionPromise.then(
1687 - function (term)
1688 - {
1689 - // SUCCESS
1690 - var stdoutstream;
1691 - var stdinstream;
1692 - if (process.platform == 'win32')
1693 - {
1694 - this.ws.httprequest._term = term;
1695 - this.ws.httprequest._term.tunnel = this.ws;
1696 - stdoutstream = stdinstream = term;
1697 - }
1698 - else
1699 - {
1700 - term.descriptorMetadata = 'Remote Terminal';
1701 - this.ws.httprequest.process = term;
1702 - this.ws.httprequest.process.tunnel = this.ws;
1703 - term.stderr.stdout = term.stdout;
1704 - term.stderr.on('data', function (c) { this.stdout.write(c); });
1705 - stdoutstream = term.stdout;
1706 - stdinstream = term.stdin;
1707 - this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1708 - term.prependListener('exit', function () { this.tunnel.end(); });
1709 - }
1710 -
1711 - this.ws.removeAllListeners('data');
1712 - this.ws.on('data', onTunnelControlData);
1713 -
1714 - stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1715 - this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1716 -
1717 - // Add the terminal session to the count to update the server
1718 - if (this.ws.httprequest.userid != null)
1719 - {
1720 - if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1721 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1722 - broadcastSessionsToRegisteredApps();
1723 - }
1724 -
1725 - // Toast Notification, if required
1726 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1727 - {
1728 - // User Notifications is required
1729 - var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1730 - if (this.ws.httprequest.soptions != null) {
1731 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1732 - if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1733 - }
1734 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
1735 - }
1736 - },
1737 - function (e)
1738 - {
1739 - // FAILED to connect terminal
1740 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1741 - this.ws.end();
1742 - });
1743 - },
1744 - function (e)
1745 - {
1746 - // DO NOT start terminal
1747 - this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1748 - this.that.end();
1749 - });
1750 - }
1751 - else if (this.httprequest.protocol == 2)
1752 - {
1753 - //
1754 - // Remote KVM
1755 - //
1756 -
1757 - // Check user access rights for desktop
1758 - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1759 - // Disengage this tunnel, user does not have the rights to do this!!
1760 - this.httprequest.protocol = 999999;
1761 - this.httprequest.s.end();
1762 - sendConsoleText("Error: No Desktop Control Rights.");
1763 - return;
1764 - }
1765 -
1766 - this.descriptorMetadata = "Remote KVM";
1767 -
1768 - // Look for a TSID
1769 - var tsid = null;
1770 - if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1771 - require('MeshAgent')._tsid = tsid;
1772 -
1773 - // Remote desktop using native pipes
1774 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1775 - this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1776 - this.desktop = this.httprequest.desktop;
1777 -
1778 - // Add ourself to the list of remote desktop sessions
1779 - if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1780 - this.httprequest.desktop.kvm.tunnels.push(this);
1781 -
1782 - // Send a metadata update to all desktop sessions
1783 - var users = {};
1784 - if (this.httprequest.desktop.kvm.tunnels != null) {
1785 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1786 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1787 - tunnelUserCount.desktop = users;
1788 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1789 - broadcastSessionsToRegisteredApps();
1790 - }
1791 -
1792 - this.end = function () {
1793 - --this.desktop.kvm.connectionCount;
1794 -
1795 - // Remove ourself from the list of remote desktop session
1796 - var i = this.desktop.kvm.tunnels.indexOf(this);
1797 - if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1798 -
1799 - // Send a metadata update to all desktop sessions
1800 - var users = {};
1801 - if (this.httprequest.desktop.kvm.tunnels != null) {
1802 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1803 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1804 - tunnelUserCount.desktop = users;
1805 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1806 - broadcastSessionsToRegisteredApps();
1807 - }
1808 -
1809 - // Unpipe the web socket
1810 - try
1811 - {
1812 - this.unpipe(this.httprequest.desktop.kvm);
1813 - this.httprequest.desktop.kvm.unpipe(this);
1814 - }
1815 - catch(e) { }
1816 -
1817 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1818 - if (this.rtcchannel)
1819 - {
1820 - try
1821 - {
1822 - this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1823 - this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1824 - }
1825 - catch(e) { }
1826 - }
1827 -
1828 - // Place wallpaper back if needed
1829 - // TODO
1830 -
1831 - if (this.desktop.kvm.connectionCount == 0) {
1832 - // Display a toast message. This may not be supported on all platforms.
1833 - // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
1834 -
1835 - this.httprequest.desktop.kvm.end();
1836 - if (this.httprequest.desktop.kvm.connectionBar) {
1837 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1838 - this.httprequest.desktop.kvm.connectionBar.close();
1839 - this.httprequest.desktop.kvm.connectionBar = null;
1840 - }
1841 - } else {
1842 - for (var i in this.httprequest.desktop.kvm.users) {
1843 - if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
1844 - for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
1845 - this.httprequest.desktop.kvm.users.splice(i, 1);
1846 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1847 - this.httprequest.desktop.kvm.connectionBar.close();
1848 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1849 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1850 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1851 - MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1852 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1853 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1854 - }
1855 - this.httprequest.desktop.kvm.end();
1856 - });
1857 - break;
1858 - }
1859 - }
1860 - }
1861 - };
1862 - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1863 - this.httprequest.desktop.kvm.connectionCount++;
1864 - this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
1865 - this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1866 - this.httprequest.desktop.kvm.rusers.sort();
1867 - this.httprequest.desktop.kvm.users.sort();
1868 - } else {
1869 - this.httprequest.desktop.kvm.connectionCount = 1;
1870 - this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
1871 - this.httprequest.desktop.kvm.users = [this.httprequest.username];
1872 - }
1873 -
1874 - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1875 - // If we have remote control rights, pipe the KVM input
1876 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1877 - } else {
1878 - // We need to only pipe non-mouse & non-keyboard inputs.
1879 - //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1880 - // TODO!!!
1881 - }
1882 -
1883 - // Perform notification if needed. Toast messages may not be supported on all platforms.
1884 - if (this.httprequest.consent && (this.httprequest.consent & 8))
1885 - {
1886 - // User Consent Prompt is required
1887 - // Send a console message back using the console channel, "\n" is supported.
1888 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1889 - var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1890 - if (this.httprequest.soptions != null) {
1891 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1892 - if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1893 - }
1894 - var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1895 - pr.ws = this;
1896 - this.pause();
1897 - this._consentpromise = pr;
1898 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1899 - pr.then(
1900 - function ()
1901 - {
1902 - // Success
1903 - this.ws._consentpromise = null;
1904 - MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1905 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1906 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1907 - // User Notifications is required
1908 - var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1909 - if (this.ws.httprequest.soptions != null) {
1910 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1911 - if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1912 - }
1913 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1914 - }
1915 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1916 - // Connection Bar is required
1917 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1918 - this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1919 - this.ws.httprequest.desktop.kvm.connectionBar.close();
1920 - }
1921 - try {
1922 - this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1923 - MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1924 - }
1925 - catch (e) {
1926 - if (process.platform != 'darwin') {
1927 - MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1928 - }
1929 - }
1930 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1931 - this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1932 - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1933 - MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1934 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1935 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1936 - }
1937 - this.httprequest.desktop.kvm.end();
1938 - });
1939 - }
1940 - }
1941 - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1942 - this.ws.resume();
1943 - },
1944 - function (e)
1945 - {
1946 - // User Consent Denied/Failed
1947 - this.ws._consentpromise = null;
1948 - MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1949 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1950 - });
1951 - } else {
1952 - // User Consent Prompt is not required
1953 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1954 - // User Notifications is required
1955 - MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1956 - var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1957 - if (this.httprequest.soptions != null) {
1958 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1959 - if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1960 - }
1961 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1962 - } else {
1963 - MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1964 - }
1965 - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1966 - // Connection Bar is required
1967 - if (this.httprequest.desktop.kvm.connectionBar) {
1968 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1969 - this.httprequest.desktop.kvm.connectionBar.close();
1970 - }
1971 - try {
1972 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
1973 - MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1974 - }
1975 - catch (e) {
1976 - MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1977 - }
1978 - if (this.httprequest.desktop.kvm.connectionBar) {
1979 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1980 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1981 - MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1982 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1983 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1984 - }
1985 - this.httprequest.desktop.kvm.end();
1986 - });
1987 - }
1988 - }
1989 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1990 - }
1991 -
1992 - this.removeAllListeners('data');
1993 - this.on('data', onTunnelControlData);
1994 - //this.write('MeshCore KVM Hello!1');
1995 -
1996 - } else if (this.httprequest.protocol == 5) {
1997 - //
1998 - // Remote Files
1999 - //
2000 -
2001 - // Check user access rights for files
2002 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
2003 - // Disengage this tunnel, user does not have the rights to do this!!
2004 - this.httprequest.protocol = 999999;
2005 - this.httprequest.s.end();
2006 - sendConsoleText("Error: No files control rights.");
2007 - return;
2008 - }
2009 -
2010 - this.descriptorMetadata = "Remote Files";
2011 -
2012 - // Add the files session to the count to update the server
2013 - if (this.httprequest.userid != null) {
2014 - if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
2015 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2016 - broadcastSessionsToRegisteredApps();
2017 - }
2018 -
2019 - this.end = function () {
2020 - // Remove the files session from the count to update the server
2021 - if (this.httprequest.userid != null) {
2022 - 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]; } }
2023 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2024 - broadcastSessionsToRegisteredApps();
2025 - }
2026 - };
2027 -
2028 - // Perform notification if needed. Toast messages may not be supported on all platforms.
2029 - if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2030 - // User Consent Prompt is required
2031 - // Send a console message back using the console channel, "\n" is supported.
2032 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2033 - var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2034 - if (this.httprequest.soptions != null) {
2035 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2036 - if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2037 - }
2038 - var pr = require('message-box').create(consentTitle, consentMessage, 30);
2039 - pr.ws = this;
2040 - this.pause();
2041 - this._consentpromise = pr;
2042 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2043 - pr.then(
2044 - function ()
2045 - {
2046 - // Success
2047 - this.ws._consentpromise = null;
2048 - MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2049 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2050 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2051 - // User Notifications is required
2052 - var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2053 - if (this.ws.httprequest.soptions != null) {
2054 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2055 - if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2056 - }
2057 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2058 - }
2059 - this.ws.resume();
2060 - },
2061 - function (e)
2062 - {
2063 - // User Consent Denied/Failed
2064 - this.ws._consentpromise = null;
2065 - MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2066 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2067 - });
2068 - } else {
2069 - // User Consent Prompt is not required
2070 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2071 - // User Notifications is required
2072 - MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2073 - var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2074 - if (this.httprequest.soptions != null) {
2075 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2076 - if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2077 - }
2078 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2079 - } else {
2080 - MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2081 - }
2082 - this.resume();
2083 - }
2084 -
2085 - // Setup files
2086 - // NOP
2087 - }
2088 - } else if (this.httprequest.protocol == 1) {
2089 - // Send data into terminal stdin
2090 - //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2091 - } else if (this.httprequest.protocol == 2) {
2092 - // Send data into remote desktop
2093 - if (this.httprequest.desktop.state == 0) {
2094 - this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2095 - this.httprequest.desktop.state = 1;
2096 - } else {
2097 - this.httprequest.desktop.write(data);
2098 - }
2099 - } else if (this.httprequest.protocol == 5) {
2100 - // Process files commands
2101 - var cmd = null;
2102 - try { cmd = JSON.parse(data); } catch (e) { };
2103 - if (cmd == null) { return; }
2104 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2105 - if (cmd.action == undefined) { return; }
2106 - //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2107 -
2108 - if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2109 - //console.log(objToString(cmd, 0, ' '));
2110 - switch (cmd.action) {
2111 - case 'ls': {
2112 - /*
2113 - // Close the watcher if required
2114 - var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2115 - if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2116 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2117 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2118 - delete this.httprequest.watcher;
2119 - }
2120 - */
2121 -
2122 - // Send the folder content to the browser
2123 - var response = getDirectoryInfo(cmd.path);
2124 - if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2125 - this.write(Buffer.from(JSON.stringify(response)));
2126 -
2127 - /*
2128 - // Start the directory watcher
2129 - if ((cmd.path != '') && (samepath == false)) {
2130 - var watcher = fs.watch(cmd.path, onFileWatcher);
2131 - watcher.tunnel = this.httprequest;
2132 - watcher.path = cmd.path;
2133 - this.httprequest.watcher = watcher;
2134 - //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2135 - }
2136 - */
2137 - break;
2138 - }
2139 - case 'mkdir': {
2140 - // Create a new empty folder
2141 - fs.mkdirSync(cmd.path);
2142 - MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2143 - break;
2144 - }
2145 - case 'rm': {
2146 - // Delete, possibly recursive delete
2147 - for (var i in cmd.delfiles) {
2148 - var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2149 - try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2150 - if ((delcount == 1) && !cmd.rec) {
2151 - MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2152 - } else {
2153 - if (cmd.rec) {
2154 - MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2155 - } else {
2156 - MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2157 - }
2158 - }
2159 - }
2160 - break;
2161 - }
2162 - case 'markcoredump': {
2163 - // If we are asking for the coredump file, set the right path.
2164 - var coreDumpPath = null;
2165 - if (process.platform == 'win32') {
2166 - if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2167 - } else {
2168 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2169 - }
2170 - if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2171 - break;
2172 - }
2173 - case 'rename': {
2174 - // Rename a file or folder
2175 - var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2176 - var newfullpath = obj.path.join(cmd.path, cmd.newname);
2177 - MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2178 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2179 - break;
2180 - }
2181 - case 'download': {
2182 - // Download a file
2183 - var sendNextBlock = 0;
2184 - if (cmd.sub == 'start') { // Setup the download
2185 - if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2186 - if (process.platform == 'win32') {
2187 - if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2188 - } else {
2189 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2190 - }
2191 - }
2192 - MeshServerLogEx(49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2193 - if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2194 - this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2195 - 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; }
2196 - if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2197 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2198 - if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2199 - }
2200 - // Send the next download block(s)
2201 - while (sendNextBlock > 0) {
2202 - sendNextBlock--;
2203 - var buf = Buffer.alloc(16384);
2204 - var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2205 - this.filedownload.ptr += len;
2206 - if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2207 - this.write(buf.slice(0, len + 4)); // Write as binary
2208 - }
2209 - break;
2210 - }
2211 - /*
2212 - case 'download': {
2213 - // Packet download of a file, agent to browser
2214 - if (cmd.path == undefined) break;
2215 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2216 - //console.log('Download: ' + filepath);
2217 - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2218 - this.httprequest.downloadFileId = cmd.reqid;
2219 - this.httprequest.downloadFilePtr = 0;
2220 - if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2221 - break;
2222 - }
2223 - case 'download2': {
2224 - // Stream download of a file, agent to browser
2225 - if (cmd.path == undefined) break;
2226 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2227 - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2228 - this.httprequest.downloadFile.pipe(this);
2229 - this.httprequest.downloadFile.end = function () { }
2230 - break;
2231 - }
2232 - */
2233 - case 'upload': {
2234 - // Upload a file, browser to agent
2235 - if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2236 - if (cmd.path == undefined) break;
2237 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2238 - this.httprequest.uploadFilePath = filepath;
2239 - MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2240 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2241 - this.httprequest.uploadFileid = cmd.reqid;
2242 - if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2243 - break;
2244 - }
2245 - case 'uploaddone': {
2246 - // Indicates that an upload is done
2247 - if (this.httprequest.uploadFile) {
2248 - fs.closeSync(this.httprequest.uploadFile);
2249 - this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2250 - delete this.httprequest.uploadFile;
2251 - delete this.httprequest.uploadFileid;
2252 - delete this.httprequest.uploadFilePath;
2253 - }
2254 - break;
2255 - }
2256 - case 'uploadcancel': {
2257 - // Indicates that an upload is canceled
2258 - if (this.httprequest.uploadFile) {
2259 - fs.closeSync(this.httprequest.uploadFile);
2260 - fs.unlinkSync(this.httprequest.uploadFilePath);
2261 - this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2262 - delete this.httprequest.uploadFile;
2263 - delete this.httprequest.uploadFileid;
2264 - delete this.httprequest.uploadFilePath;
2265 - }
2266 - break;
2267 - }
2268 - case 'copy': {
2269 - // Copy a bunch of files from scpath to dspath
2270 - for (var i in cmd.names) {
2271 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2272 - MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2273 - if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2274 - }
2275 - break;
2276 - }
2277 - case 'move': {
2278 - // Move a bunch of files from scpath to dspath
2279 - for (var i in cmd.names) {
2280 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2281 - MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2282 - if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2283 - }
2284 - break;
2285 - }
2286 - case 'zip':
2287 - // Zip a bunch of files
2288 - if (this.zip != null) return; // Zip operating is currently running, exit now.
2289 -
2290 - // Check that the specified files exist & build full paths
2291 - var fp, stat, p = [];
2292 - for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (e) { } if (stat != null) { p.push(fp); } }
2293 - if (p.length == 0) return; // No files, quit now.
2294 -
2295 - // Setup file compression
2296 - var ofile = cmd.path + '/' + cmd.output;
2297 - this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2298 - this.zipfile = ofile;
2299 - delete this.zipcancel;
2300 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2301 - out.xws = this;
2302 - out.on('close', function () {
2303 - this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2304 - this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2305 - if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2306 - delete this.xws.zipcancel;
2307 - delete this.xws.zipfile;
2308 - delete this.xws.zip;
2309 - });
2310 - this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2311 - this.zip.xws = this;
2312 - this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
2313 - this.zip.pipe(out);
2314 - break;
2315 - case 'cancel':
2316 - // Cancel zip operation if present
2317 - try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2318 - this.zip = null;
2319 - break;
2320 - default:
2321 - // Unknown action, ignore it.
2322 - break;
2323 - }
2324 - } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2325 - var cmd = null;
2326 - try { cmd = JSON.parse(data); } catch (e) { };
2327 - if (cmd == null) { return; }
2328 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2329 - if (cmd.action == undefined) return;
2330 -
2331 - switch (cmd.action) {
2332 - case 'plugin': {
2333 - try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2334 - break;
2335 - }
2336 - default: {
2337 - // probably shouldn't happen, but just in case this feature is expanded
2338 - }
2339 - }
2340 -
2341 - }
2342 - //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2343 - }
2344 - }
2345 -
2346 - // Delete a directory with a files and directories within it
2347 - function deleteFolderRecursive(path, rec) {
2348 - var count = 0;
2349 - if (fs.existsSync(path)) {
2350 - if (rec == true) {
2351 - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2352 - var curPath = obj.path.join(path, file);
2353 - if (fs.statSync(curPath).isDirectory()) { // recurse
2354 - count += deleteFolderRecursive(curPath, true);
2355 - } else { // delete file
2356 - fs.unlinkSync(curPath);
2357 - count++;
2358 - }
2359 - });
2360 - }
2361 - fs.unlinkSync(path);
2362 - count++;
2363 - }
2364 - return count;
2365 - };
2366 -
2367 - // Called when receiving control data on WebRTC
2368 - function onTunnelWebRTCControlData(data) {
2369 - if (typeof data != 'string') return;
2370 - var obj;
2371 - try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2372 - if (obj.type == 'close') {
2373 - //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2374 - try { this.close(); } catch (e) { }
2375 - try { this.xrtc.close(); } catch (e) { }
2376 - }
2377 - }
2378 -
2379 - // Called when receiving control data on websocket
2380 - function onTunnelControlData(data, ws) {
2381 - var obj;
2382 - if (ws == null) { ws = this; }
2383 - if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2384 - else if (typeof data == 'object') { obj = data; } else { return; }
2385 - //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2386 - //console.log('onTunnelControlData: ' + JSON.stringify(data));
2387 -
2388 - if (obj.action) {
2389 - switch (obj.action) {
2390 - case 'lock': {
2391 - // Lock the current user out of the desktop
2392 - try {
2393 - if (process.platform == 'win32') {
2394 - MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2395 - var child = require('child_process');
2396 - child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2397 - }
2398 - } catch (e) { }
2399 - break;
2400 - }
2401 - default:
2402 - // Unknown action, ignore it.
2403 - break;
2404 - }
2405 - return;
2406 - }
2407 -
2408 - switch (obj.type) {
2409 - case 'options': {
2410 - // These are additional connection options passed in the control channel.
2411 - //sendConsoleText('options: ' + JSON.stringify(obj));
2412 - delete obj.type;
2413 - ws.httprequest.xoptions = obj;
2414 -
2415 - // Set additional user consent options if present
2416 - if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2417 -
2418 - break;
2419 - }
2420 - case 'close': {
2421 - // We received the close on the websocket
2422 - //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2423 - try { ws.close(); } catch (e) { }
2424 - break;
2425 - }
2426 - case 'termsize': {
2427 - // Indicates a change in terminal size
2428 - if (process.platform == 'win32')
2429 - {
2430 - if (ws.httprequest._dispatcher == null) return;
2431 - //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2432 - if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2433 - } else
2434 - {
2435 - if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2436 - //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2437 -
2438 - if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2439 - }
2440 - break;
2441 - }
2442 - case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2443 - if (ws.httprequest.protocol == 1) { // Terminal
2444 - // 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
2445 - if (process.platform == 'win32') {
2446 - ws.httprequest._term.unpipe(ws);
2447 - } else {
2448 - ws.httprequest.process.stdout.unpipe(ws);
2449 - ws.httprequest.process.stderr.unpipe(ws);
2450 - }
2451 - } else if (ws.httprequest.protocol == 2) { // Desktop
2452 - // 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
2453 - ws.httprequest.desktop.kvm.unpipe(ws);
2454 - } else {
2455 - // Switch things around so all WebRTC data goes to onTunnelData().
2456 - ws.rtcchannel.httprequest = ws.httprequest;
2457 - ws.rtcchannel.removeAllListeners('data');
2458 - ws.rtcchannel.on('data', onTunnelData);
2459 - }
2460 - ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2461 - break;
2462 - }
2463 - case 'webrtc1': {
2464 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2465 - // Switch the user input from websocket to webrtc at this point.
2466 - if (process.platform == 'win32') {
2467 - ws.unpipe(ws.httprequest._term);
2468 - ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2469 - } else {
2470 - ws.unpipe(ws.httprequest.process.stdin);
2471 - ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2472 - }
2473 - ws.resume(); // Resume the websocket to keep receiving control data
2474 - } else if (ws.httprequest.protocol == 2) { // Desktop
2475 - // Switch the user input from websocket to webrtc at this point.
2476 - ws.unpipe(ws.httprequest.desktop.kvm);
2477 - try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2478 - ws.resume(); // Resume the websocket to keep receiving control data
2479 - }
2480 - ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2481 - break;
2482 - }
2483 - case 'webrtc2': {
2484 - // Other side received websocket end of data marker, start sending data on WebRTC channel
2485 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2486 - if (process.platform == 'win32') {
2487 - ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2488 - } else {
2489 - ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2490 - ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2491 - }
2492 - } else if (ws.httprequest.protocol == 2) { // Desktop
2493 - ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2494 - }
2495 - break;
2496 - }
2497 - case 'offer': {
2498 - // This is a WebRTC offer.
2499 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2500 - ws.webrtc = rtc.createConnection();
2501 - ws.webrtc.websocket = ws;
2502 - ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2503 - ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2504 - ws.webrtc.on('dataChannel', function (rtcchannel) {
2505 - //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2506 - rtcchannel.maxFragmentSize = 32768;
2507 - rtcchannel.xrtc = this;
2508 - rtcchannel.websocket = this.websocket;
2509 - this.rtcchannel = rtcchannel;
2510 - this.websocket.rtcchannel = rtcchannel;
2511 - this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2512 - this.websocket.rtcchannel.on('end', function () {
2513 - // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2514 - //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2515 - if (this.websocket.desktop && this.websocket.desktop.kvm)
2516 - {
2517 - try
2518 - {
2519 - this.unpipe(this.websocket.desktop.kvm);
2520 - this.websocket.httprequest.desktop.kvm.unpipe(this);
2521 - }
2522 - catch (e) { }
2523 - }
2524 - });
2525 - this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2526 - });
2527 - var sdp = null;
2528 - try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (e) { }
2529 - if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2530 - break;
2531 - }
2532 - case 'ping': {
2533 - ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
2534 - break;
2535 - }
2536 - case 'pong': { // NOP
2537 - break;
2538 - }
2539 - case 'rtt': {
2540 - ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2541 - break;
2542 - }
2543 - }
2544 - }
2545 -
2546 - // Console state
2547 - var consoleWebSockets = {};
2548 - var consoleHttpRequest = null;
2549 -
2550 - // Console HTTP response
2551 - function consoleHttpResponse(response) {
2552 - response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2553 - response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2554 - };
2555 -
2556 - // Open a web browser to a specified URL on current user's desktop
2557 - function openUserDesktopUrl(url) {
2558 - if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
2559 - var child = null;
2560 - try {
2561 - switch (process.platform) {
2562 - case 'win32':
2563 - var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2564 - child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2565 - child.stderr.on('data', function () { });
2566 - child.stdout.on('data', function () { });
2567 - 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');
2568 - child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2569 - child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2570 - child.stdin.write('exit\r\n');
2571 - child.waitExit();
2572 - break;
2573 - case 'linux':
2574 - child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2575 - break;
2576 - case 'darwin':
2577 - child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2578 - break;
2579 - default:
2580 - // Unknown platform, ignore this command.
2581 - break;
2582 - }
2583 - } catch (e) { }
2584 - return child;
2585 - }
2586 -
2587 - // Process a mesh agent console command
2588 - function processConsoleCommand(cmd, args, rights, sessionid) {
2589 - try {
2590 - var response = null;
2591 - switch (cmd) {
2592 - case 'help': { // Displays available commands
2593 - var fin = '', f = '', availcommands = 'amtconfig,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,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
2594 - if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2595 - if (process.platform != 'freebsd') { availcommands += ',vm';}
2596 - if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2597 - try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
2598 -
2599 - availcommands = availcommands.split(',').sort();
2600 - while (availcommands.length > 0) {
2601 - if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2602 - f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2603 - }
2604 - if (f != '') { fin += f; }
2605 - response = "Available commands: \r\n" + fin + ".";
2606 - break;
2607 - }
2608 - case 'agentmsg': {
2609 - if (args['_'].length == 0) {
2610 - response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2611 - } else {
2612 - if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
2613 - var msgIndex = 1, iconIndex = 0;
2614 - while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
2615 - if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
2616 - if (typeof iconIndex != 'number') { iconIndex = 0; }
2617 - tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
2618 - response = 'Agent message ' + msgIndex + ' added.';
2619 - } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
2620 - var msgIndex = 0;
2621 - try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
2622 - if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2623 - } else if (args['_'][0] == 'list') {
2624 - response = JSON.stringify(tunnelUserCount.msg, null, 2);
2625 - }
2626 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2627 - broadcastSessionsToRegisteredApps();
2628 - }
2629 - break;
2630 - }
2631 - case 'clearagentmsg': {
2632 - tunnelUserCount.msg = {};
2633 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2634 - broadcastSessionsToRegisteredApps();
2635 - break;
2636 - }
2637 - case 'coredump':
2638 - if (args['_'].length != 1) {
2639 - response = "Proper usage: coredump on|off|status|clear"; // Display usage
2640 - } else {
2641 - switch (args['_'][0].toLowerCase())
2642 - {
2643 - case 'on':
2644 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2645 - response = 'coredump is now on';
2646 - break;
2647 - case 'off':
2648 - process.coreDumpLocation = null;
2649 - response = 'coredump is now off';
2650 - break;
2651 - case 'status':
2652 - response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2653 - if (process.coreDumpLocation != null) {
2654 - if (process.platform == 'win32') {
2655 - if (fs.existsSync(process.coreDumpLocation)) { response += '\r\n CoreDump present at: ' + process.coreDumpLocation; }
2656 - } else {
2657 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { response += '\r\n CoreDump present at: ' + process.cwd() + 'core'; }
2658 - }
2659 - }
2660 - break;
2661 - case 'clear':
2662 - db.Put('CoreDumpTime', null);
2663 - response = 'coredump db cleared';
2664 - break;
2665 - default:
2666 - response = "Proper usage: coredump on|off|status"; // Display usage
2667 - break;
2668 - }
2669 - }
2670 - break;
2671 - case 'service':
2672 - if (args['_'].length != 1)
2673 - {
2674 - response = "Proper usage: service status|restart"; // Display usage
2675 - }
2676 - else
2677 - {
2678 - var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2679 - switch(args['_'][0].toLowerCase())
2680 - {
2681 - case 'status':
2682 - response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2683 - break;
2684 - case 'restart':
2685 - if (s.isMe())
2686 - {
2687 - s.restart();
2688 - }
2689 - else
2690 - {
2691 - response = 'Restarting another agent instance is not allowed';
2692 - }
2693 - break;
2694 - default:
2695 - response = "Proper usage: service status|restart"; // Display usage
2696 - break;
2697 - }
2698 - if (process.platform == 'win32') { s.close(); }
2699 - }
2700 - break;
2701 - case 'zip':
2702 - if (args['_'].length == 0) {
2703 - response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2704 - } else {
2705 - var p = args['_'].join(' ').split(',');
2706 - var ofile = p.shift();
2707 - sendConsoleText('Writing ' + ofile + '...');
2708 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2709 - out.fname = ofile;
2710 - out.sessionid = sessionid;
2711 - out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2712 - var zip = require('zip-writer').write({ files: p });
2713 - zip.pipe(out);
2714 - }
2715 - break;
2716 - case 'unzip':
2717 - if (args['_'].length == 0) {
2718 - response = "Proper usage: unzip input, destination"; // Display usage
2719 - } else {
2720 - var p = args['_'].join(' ').split(',');
2721 - if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
2722 - var prom = require('zip-reader').read(p[0]);
2723 - prom._dest = p[1];
2724 - prom.self = this;
2725 - prom.sessionid = sessionid;
2726 - prom.then(function (zipped) {
2727 - sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2728 - zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2729 - }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2730 - }
2731 - break;
2732 - case 'setbattery':
2733 - // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2734 - if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2735 - var b = { action: 'battery', state: args['_'][0] };
2736 - if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2737 - require('MeshAgent').SendCommand(b);
2738 - } else {
2739 - require('MeshAgent').SendCommand({ action: 'battery' });
2740 - }
2741 - break;
2742 - case 'fdsnapshot':
2743 - require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2744 - break;
2745 - case 'fdcount':
2746 - require('DescriptorEvents').getDescriptorCount().then(
2747 - function (c)
2748 - {
2749 - sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2750 - }, function (e)
2751 - {
2752 - sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2753 - }).parentPromise.sessionid = sessionid;
2754 - break;
2755 - case 'uac':
2756 - if (process.platform != 'win32')
2757 - {
2758 - response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2759 - break;
2760 - }
2761 - if (args['_'].length != 1)
2762 - {
2763 - response = 'Proper usage: uac [get|interactive|secure]';
2764 - }
2765 - else
2766 - {
2767 - switch(args['_'][0].toUpperCase())
2768 - {
2769 - case 'GET':
2770 - var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2771 - response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2772 - break;
2773 - case 'INTERACTIVE':
2774 - try
2775 - {
2776 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2777 - response = 'UAC mode changed to: Interactive Desktop';
2778 - }
2779 - catch (e)
2780 - {
2781 - response = "Unable to change UAC Mode";
2782 - }
2783 - break;
2784 - case 'SECURE':
2785 - try
2786 - {
2787 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2788 - response = 'UAC mode changed to: Secure Desktop';
2789 - }
2790 - catch(e)
2791 - {
2792 - response = "Unable to change UAC Mode";
2793 - }
2794 - break;
2795 - default:
2796 - response = 'Proper usage: uac [get|interactive|secure]';
2797 - break;
2798 - }
2799 - }
2800 - break;
2801 - case 'vm':
2802 - response = 'Virtual Machine = ' + require('identifiers').isVM();
2803 - break;
2804 - case 'startupoptions':
2805 - response = JSON.stringify(require('MeshAgent').getStartupOptions());
2806 - break;
2807 - case 'kvmmode':
2808 - if (require('MeshAgent').maxKvmTileSize == null)
2809 - {
2810 - response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2811 - }
2812 - else
2813 - {
2814 - if(require('MeshAgent').maxKvmTileSize == 0)
2815 - {
2816 - response = 'KVM Mode: Full JUMBO';
2817 - }
2818 - else
2819 - {
2820 - response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2821 - response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2822 - }
2823 - }
2824 - break;
2825 - case 'alert':
2826 - if (args['_'].length == 0)
2827 - {
2828 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2829 - }
2830 - else
2831 - {
2832 - var p = args['_'].join(' ').split(',');
2833 - if(p.length<2)
2834 - {
2835 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2836 - }
2837 - else
2838 - {
2839 - this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2840 - }
2841 - }
2842 - break;
2843 - case 'agentsize':
2844 - var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2845 - if (process.platform == 'win32') {
2846 - // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2847 - var writtenSize = 0;
2848 - try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
2849 - if (writtenSize != actualSize) {
2850 - response = "Size updated from: " + writtenSize + " to: " + actualSize;
2851 - try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
2852 - } else { response = "Agent Size: " + actualSize + " kb"; }
2853 - } else { response = "Agent Size: " + actualSize + " kb"; }
2854 - break;
2855 - case 'versions':
2856 - response = JSON.stringify(process.versions, null, ' ');
2857 - break;
2858 - case 'wpfhwacceleration':
2859 - if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2860 - if (args['_'].length != 1) {
2861 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2862 - }
2863 - else {
2864 - var reg = require('win-registry');
2865 - var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2866 - var key = reg.usernameToUserKey(uname);
2867 -
2868 - switch (args['_'][0].toUpperCase()) {
2869 - default:
2870 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2871 - break;
2872 - case 'ON':
2873 - try {
2874 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2875 - response = "OK";
2876 - } catch (e) { response = "FAILED"; }
2877 - break;
2878 - case 'OFF':
2879 - try {
2880 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2881 - response = 'OK';
2882 - } catch (e) { response = 'FAILED'; }
2883 - break;
2884 - case 'STATUS':
2885 - var s;
2886 - try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (e) { s = 'DEFAULT'; }
2887 - response = "WPF Hardware Acceleration: " + s;
2888 - break;
2889 - case 'DEFAULT':
2890 - try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (e) { }
2891 - response = 'OK';
2892 - break;
2893 - }
2894 - }
2895 - break;
2896 - case 'tsid':
2897 - if (process.platform == 'win32') {
2898 - if (args['_'].length != 1) {
2899 - response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2900 - } else {
2901 - var i = parseInt(args['_'][0]);
2902 - require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2903 - response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2904 - }
2905 - } else { response = "TSID command only supported on Windows"; }
2906 - break;
2907 - case 'activeusers':
2908 - if (process.platform == 'win32') {
2909 - var p = require('user-sessions').enumerateUsers();
2910 - p.sessionid = sessionid;
2911 - p.then(function (u) {
2912 - var v = [];
2913 - for (var i in u) {
2914 - if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2915 - }
2916 - sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2917 - });
2918 - } else { response = "activeusers command only supported on Windows"; }
2919 - break;
2920 - case 'wallpaper':
2921 - if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2922 - response = "wallpaper command not supported on this platform";
2923 - }
2924 - else {
2925 - if (args['_'].length != 1) {
2926 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2927 - }
2928 - else {
2929 - switch (args['_'][0].toUpperCase()) {
2930 - default:
2931 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2932 - break;
2933 - case 'GET':
2934 - case 'TOGGLE':
2935 - if (process.platform == 'win32') {
2936 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2937 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2938 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2939 - child.stderr.on('data', function () { });
2940 - child.waitExit();
2941 - var current = child.stdout.str.trim();
2942 - if (args['_'][0].toUpperCase() == 'GET') {
2943 - response = current;
2944 - break;
2945 - }
2946 - if (current != '') {
2947 - require('MeshAgent')._wallpaper = current;
2948 - response = 'Wallpaper cleared';
2949 - } else {
2950 - response = 'Wallpaper restored';
2951 - }
2952 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2953 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2954 - child.stderr.on('data', function () { });
2955 - child.waitExit();
2956 - }
2957 - else {
2958 - var id = require('user-sessions').consoleUid();
2959 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2960 - if (args['_'][0].toUpperCase() == 'GET') {
2961 - response = current;
2962 - break;
2963 - }
2964 - if (current != '/dev/null') {
2965 - require('MeshAgent')._wallpaper = current;
2966 - response = 'Wallpaper cleared';
2967 - } else {
2968 - response = 'Wallpaper restored';
2969 - }
2970 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2971 - }
2972 - break;
2973 - }
2974 - }
2975 - }
2976 - break;
2977 - case 'safemode':
2978 - if (process.platform != 'win32') {
2979 - response = 'safemode only supported on Windows Platforms'
2980 - }
2981 - else {
2982 - if (args['_'].length != 1) {
2983 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2984 - }
2985 - else {
2986 - switch (args['_'][0].toUpperCase()) {
2987 - default:
2988 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2989 - break;
2990 - case 'ON':
2991 - require('win-bcd').setKey('safeboot', 'Network');
2992 - require('win-bcd').enableSafeModeService('Mesh Agent');
2993 - break;
2994 - case 'OFF':
2995 - require('win-bcd').deleteKey('safeboot');
2996 - break;
2997 - case 'STATUS':
2998 - var nextboot = require('win-bcd').getKey('safeboot');
2999 - if (nextboot) {
3000 - switch (nextboot) {
3001 - case 'Network':
3002 - case 'network':
3003 - nextboot = 'SAFE_MODE_NETWORK';
3004 - break;
3005 - default:
3006 - nextboot = 'SAFE_MODE';
3007 - break;
3008 - }
3009 - }
3010 - response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
3011 - break;
3012 - }
3013 - }
3014 - }
3015 - break;
3016 - /*
3017 - case 'border':
3018 - {
3019 - if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
3020 - if (meshCoreObj.users.length > 0) {
3021 - obj.borderManager.Start(meshCoreObj.users[0]);
3022 - response = 'Border blinking is on.';
3023 - } else {
3024 - response = 'Cannot turn on border blinking, no logged in users.';
3025 - }
3026 - } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3027 - obj.borderManager.Stop();
3028 - response = 'Border blinking is off.';
3029 - } else {
3030 - response = 'Proper usage: border "on|off"'; // Display correct command usage
3031 - }
3032 - }
3033 - break;
3034 - */
3035 - case 'av':
3036 - if (process.platform == 'win32') {
3037 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3038 - response = JSON.stringify(require('win-info').av(), null, 1);
3039 - } else {
3040 - response = 'Not supported on the platform';
3041 - }
3042 - break;
3043 - case 'log':
3044 - if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
3045 - break;
3046 - case 'getclip':
3047 - if (require('MeshAgent').isService) {
3048 - require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
3049 - } else {
3050 - require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
3051 - }
3052 - break;
3053 - case 'setclip': {
3054 - if (args['_'].length != 1) {
3055 - response = 'Proper usage: setclip "sample text"';
3056 - } else {
3057 - if (require('MeshAgent').isService) {
3058 - require('clipboard').dispatchWrite(args['_'][0]);
3059 - response = 'Setting clipboard to: "' + args['_'][0] + '"';
3060 - }
3061 - else {
3062 - require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3063 - }
3064 - }
3065 - break;
3066 - }
3067 - /*
3068 - case 'amtreset': {
3069 - if (amt != null) { amt.reset(); response = 'Done.'; }
3070 - break;
3071 - }
3072 - case 'amtlmsreset': {
3073 - if (amt != null) { amt.lmsreset(); response = 'Done.'; }
3074 - break;
3075 - }
3076 - case 'amtccm': {
3077 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
3078 - if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
3079 - else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
3080 - }
3081 - break;
3082 - }
3083 - case 'amtacm': {
3084 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
3085 - amt.setPolicy({ type: 0 });
3086 - amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
3087 - }
3088 - break;
3089 - }
3090 - case 'amtdeactivate': {
3091 - if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
3092 - break;
3093 - }
3094 - */
3095 - case 'openurl': {
3096 - if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
3097 - else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
3098 - break;
3099 - }
3100 - case 'users': {
3101 - if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
3102 - require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
3103 - break;
3104 - }
3105 - case 'toast': {
3106 - if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
3107 - if (require('MeshAgent')._tsid == null) {
3108 - require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
3109 - }
3110 - else {
3111 - require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
3112 - }
3113 - }
3114 - break;
3115 - }
3116 - case 'setdebug': {
3117 - if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
3118 - else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
3119 - break;
3120 - }
3121 - case 'ps': {
3122 - processManager.getProcesses(function (plist) {
3123 - var x = '';
3124 - for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
3125 - sendConsoleText(x, sessionid);
3126 - });
3127 - break;
3128 - }
3129 - case 'kill': {
3130 - if ((args['_'].length < 1)) {
3131 - response = 'Proper usage: kill [pid]'; // Display correct command usage
3132 - } else {
3133 - process.kill(parseInt(args['_'][0]));
3134 - response = 'Killed process ' + args['_'][0] + '.';
3135 - }
3136 - break;
3137 - }
3138 - case 'smbios': {
3139 - if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
3140 - break;
3141 - }
3142 - case 'rawsmbios': {
3143 - if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
3144 - response = '';
3145 - for (var i in SMBiosTablesRaw) {
3146 - var header = false;
3147 - for (var j in SMBiosTablesRaw[i]) {
3148 - if (SMBiosTablesRaw[i][j].length > 0) {
3149 - if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
3150 - response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
3151 - }
3152 - }
3153 - }
3154 - }
3155 - break;
3156 - }
3157 - case 'eval': { // Eval JavaScript
3158 - if (args['_'].length < 1) {
3159 - response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3160 - } else {
3161 - response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3162 - }
3163 - break;
3164 - }
3165 - case 'uninstallagent': // Uninstall this agent
3166 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
3167 - if (!require('service-manager').manager.getService(agentName).isMe()) {
3168 - response = 'Uininstall failed, this instance is not the service instance';
3169 - } else {
3170 - try { diagnosticAgent_uninstall(); } catch (e) { }
3171 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
3172 - 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 });
3173 - }
3174 - break;
3175 - case 'notify': { // Send a notification message to the mesh
3176 - if (args['_'].length != 1) {
3177 - response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
3178 - } else {
3179 - var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
3180 - if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
3181 - mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
3182 - response = "ok";
3183 - }
3184 - break;
3185 - }
3186 - case 'cpuinfo': { // Return system information
3187 - // CPU & memory utilization
3188 - pr = require('sysinfo').cpuUtilization();
3189 - pr.sessionid = sessionid;
3190 - pr.then(function (data) {
3191 - sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
3192 - }, function (e) {
3193 - sendConsoleText(e);
3194 - });
3195 - break;
3196 - }
3197 - case 'sysinfo': { // Return system information
3198 - getSystemInformation(function (results, err) {
3199 - if (results == null) { sendConsoleText(err, this.sessionid); } else {
3200 - sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
3201 - }
3202 - });
3203 - break;
3204 - }
3205 - case 'info': { // Return information about the agent and agent core module
3206 - 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 + '.';
3207 - if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
3208 - if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
3209 - response += '\r\nModules: ' + addedModules.join(', ') + '.';
3210 - response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
3211 - response += '\r\lastMeInfo: ' + lastMeInfo + '.';
3212 - var oldNodeId = db.Get('OldNodeId');
3213 - if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
3214 - if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
3215 - break;
3216 - }
3217 - case 'osinfo': { // Return the operating system information
3218 - var i = 1;
3219 - if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
3220 - for (var j = 0; j < i; j++) {
3221 - var pr = require('os').name();
3222 - pr.sessionid = sessionid;
3223 - pr.then(function (v) {
3224 - sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
3225 - });
3226 - }
3227 - break;
3228 - }
3229 - case 'sendcaps': { // Send capability flags to the server
3230 - if (args['_'].length == 0) {
3231 - response = 'Proper usage: sendcaps (number)'; // Display correct command usage
3232 - } else {
3233 - meshCoreObj.caps = parseInt(args['_'][0]);
3234 - mesh.SendCommand(meshCoreObj);
3235 - response = JSON.stringify(meshCoreObj);
3236 - }
3237 - break;
3238 - }
3239 - case 'sendosdesc': { // Send OS description
3240 - if (args['_'].length > 0) {
3241 - meshCoreObj.osdesc = args['_'][0];
3242 - mesh.SendCommand(meshCoreObj);
3243 - response = JSON.stringify(meshCoreObj);
3244 - } else {
3245 - response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
3246 - }
3247 - break;
3248 - }
3249 - case 'args': { // Displays parsed command arguments
3250 - response = 'args ' + objToString(args, 0, ' ', true);
3251 - break;
3252 - }
3253 - case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
3254 - var r = [];
3255 - for (var i in args['_']) { r.push(args['_'][i]); }
3256 - console.log(r.join(' '));
3257 - response = 'Message printed on agent console.';
3258 - break;
3259 - }
3260 - case 'type': { // Returns the content of a file
3261 - if (args['_'].length == 0) {
3262 - response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
3263 - } else {
3264 - var max = 4096;
3265 - if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
3266 - if (max > 4096) max = 4096;
3267 - var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
3268 - response = buf.toString();
3269 - var i = response.indexOf('\n');
3270 - if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
3271 - if (r == max) response += '...';
3272 - fs.closeSync(fd);
3273 - }
3274 - break;
3275 - }
3276 - case 'dbkeys': { // Return all data store keys
3277 - response = JSON.stringify(db.Keys);
3278 - break;
3279 - }
3280 - case 'dbget': { // Return the data store value for a given key
3281 - if (db == null) { response = 'Database not accessible.'; break; }
3282 - if (args['_'].length != 1) {
3283 - response = 'Proper usage: dbget (key)'; // Display the value for a given database key
3284 - } else {
3285 - response = db.Get(args['_'][0]);
3286 - }
3287 - break;
3288 - }
3289 - case 'dbset': { // Set a data store key and value pair
3290 - if (db == null) { response = 'Database not accessible.'; break; }
3291 - if (args['_'].length != 2) {
3292 - response = 'Proper usage: dbset (key) (value)'; // Set a database key
3293 - } else {
3294 - var r = db.Put(args['_'][0], args['_'][1]);
3295 - response = 'Key set: ' + r;
3296 - }
3297 - break;
3298 - }
3299 - case 'dbcompact': { // Compact the data store
3300 - if (db == null) { response = 'Database not accessible.'; break; }
3301 - var r = db.Compact();
3302 - response = 'Database compacted: ' + r;
3303 - break;
3304 - }
3305 - case 'httpget': {
3306 - if (consoleHttpRequest != null) {
3307 - response = 'HTTP operation already in progress.';
3308 - } else {
3309 - if (args['_'].length != 1) {
3310 - response = 'Proper usage: httpget (url)';
3311 - } else {
3312 - var options = http.parseUri(args['_'][0]);
3313 - options.method = 'GET';
3314 - if (options == null) {
3315 - response = 'Invalid url.';
3316 - } else {
3317 - try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
3318 - consoleHttpRequest.sessionid = sessionid;
3319 - if (consoleHttpRequest != null) {
3320 - consoleHttpRequest.end();
3321 - response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
3322 - }
3323 - }
3324 - }
3325 - }
3326 - break;
3327 - }
3328 - case 'wslist': { // List all web sockets
3329 - response = '';
3330 - for (var i in consoleWebSockets) {
3331 - var httprequest = consoleWebSockets[i];
3332 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3333 - }
3334 - if (response == '') { response = 'no websocket sessions.'; }
3335 - break;
3336 - }
3337 - case 'wsconnect': { // Setup a web socket
3338 - if (args['_'].length == 0) {
3339 - response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3340 - } else {
3341 - var httprequest = null;
3342 - try {
3343 - var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3344 - options.rejectUnauthorized = 0;
3345 - httprequest = http.request(options);
3346 - } catch (e) { response = 'Invalid HTTP websocket request'; }
3347 - if (httprequest != null) {
3348 - httprequest.upgrade = onWebSocketUpgrade;
3349 - httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3350 -
3351 - var index = 1;
3352 - while (consoleWebSockets[index]) { index++; }
3353 - httprequest.sessionid = sessionid;
3354 - httprequest.index = index;
3355 - httprequest.url = args['_'][0];
3356 - consoleWebSockets[index] = httprequest;
3357 - response = 'New websocket session #' + index;
3358 - }
3359 - }
3360 - break;
3361 - }
3362 - case 'wssend': { // Send data on a web socket
3363 - if (args['_'].length == 0) {
3364 - response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3365 - for (var i in consoleWebSockets) {
3366 - var httprequest = consoleWebSockets[i];
3367 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3368 - }
3369 - } else {
3370 - var i = parseInt(args['_'][0]);
3371 - var httprequest = consoleWebSockets[i];
3372 - if (httprequest != undefined) {
3373 - httprequest.s.write(args['_'][1]);
3374 - response = 'ok';
3375 - } else {
3376 - response = 'Invalid web socket number';
3377 - }
3378 - }
3379 - break;
3380 - }
3381 - case 'wsclose': { // Close a websocket
3382 - if (args['_'].length == 0) {
3383 - response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3384 - } else {
3385 - var i = parseInt(args['_'][0]);
3386 - var httprequest = consoleWebSockets[i];
3387 - if (httprequest != undefined) {
3388 - if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3389 - response = 'ok';
3390 - } else {
3391 - response = 'Invalid web socket number';
3392 - }
3393 - }
3394 - break;
3395 - }
3396 - case 'tunnels': { // Show the list of current tunnels
3397 - response = '';
3398 - for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3399 - if (response == '') { response = 'No websocket sessions.'; }
3400 - break;
3401 - }
3402 - case 'ls': { // Show list of files and folders
3403 - response = '';
3404 - var xpath = '*';
3405 - if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3406 - response = 'List of ' + xpath + '\r\n';
3407 - var results = fs.readdirSync(xpath);
3408 - for (var i = 0; i < results.length; ++i) {
3409 - var stat = null, p = obj.path.join(args['_'][0], results[i]);
3410 - try { stat = fs.statSync(p); } catch (e) { }
3411 - if ((stat == null) || (stat == undefined)) {
3412 - response += (results[i] + "\r\n");
3413 - } else {
3414 - response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3415 - }
3416 - }
3417 - break;
3418 - }
3419 - case 'lsx': { // Show list of files and folders
3420 - response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3421 - break;
3422 - }
3423 - case 'lock': { // Lock the current user out of the desktop
3424 - 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'; }
3425 - else { response = 'Not supported on the platform'; }
3426 - break;
3427 - }
3428 - case 'amt': { // Show Intel AMT status
3429 - if (amt != null) {
3430 - amt.getAmtInfo(function (state) {
3431 - var resp = 'Intel AMT not detected.';
3432 - if (state != null) { resp = objToString(state, 0, ' ', true); }
3433 - sendConsoleText(resp, sessionid);
3434 - });
3435 - } else {
3436 - response = 'Intel AMT not detected.';
3437 - }
3438 - break;
3439 - }
3440 - case 'netinfo': { // Show network interface information
3441 - var interfaces = require('os').networkInterfaces();
3442 - response = objToString(interfaces, 0, ' ', true);
3443 - break;
3444 - }
3445 - case 'wakeonlan': { // Send wake-on-lan
3446 - if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3447 - response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3448 - } else {
3449 - var count = sendWakeOnLan(args['_'][0]);
3450 - response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3451 - }
3452 - break;
3453 - }
3454 - case 'sendall': { // Send a message to all consoles on this mesh
3455 - sendConsoleText(args['_'].join(' '));
3456 - break;
3457 - }
3458 - case 'power': { // Execute a power action on this computer
3459 - if (mesh.ExecPowerState == undefined) {
3460 - response = 'Power command not supported on this agent.';
3461 - } else {
3462 - if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3463 - 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
3464 - } else {
3465 - var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3466 - response = 'Power action executed with return code: ' + r + '.';
3467 - }
3468 - }
3469 - break;
3470 - }
3471 - case 'location': {
3472 - getIpLocationData(function (location) {
3473 - sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3474 - });
3475 - break;
3476 - }
3477 - case 'parseuri': {
3478 - response = JSON.stringify(http.parseUri(args['_'][0]));
3479 - break;
3480 - }
3481 - case 'scanwifi': {
3482 - if (wifiScanner != null) {
3483 - var wifiPresent = wifiScanner.hasWireless;
3484 - if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3485 - } else { response = "Wifi module not present."; }
3486 - break;
3487 - }
3488 - case 'scanamt': {
3489 - if (amtscanner != null) {
3490 - if (args['_'].length != 1) {
3491 - 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
3492 - } else {
3493 - response = 'Scanning: ' + args['_'][0] + '...';
3494 - amtscanner.scan(args['_'][0], 2000, function (data) {
3495 - if (data.length > 0) {
3496 - var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3497 - for (var i in data) {
3498 - var x = data[i];
3499 - if (r != '') { r += '\r\n'; }
3500 - r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3501 - if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3502 - if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3503 - r += '.';
3504 - }
3505 - } else {
3506 - r = 'No Intel AMT found.';
3507 - }
3508 - sendConsoleText(r);
3509 - });
3510 - }
3511 - } else { response = "Intel AMT scanner module not present."; }
3512 - break;
3513 - }
3514 - case 'modules': {
3515 - response = JSON.stringify(addedModules);
3516 - break;
3517 - }
3518 - case 'listservices': {
3519 - var services = require('service-manager').manager.enumerateService();
3520 - response = JSON.stringify(services, null, 1);
3521 - break;
3522 - }
3523 - case 'getscript': {
3524 - if (args['_'].length != 1) {
3525 - response = "Proper usage: getscript [scriptNumber].";
3526 - } else {
3527 - mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3528 - }
3529 - break;
3530 - }
3531 - case 'diagnostic':
3532 - {
3533 - if (!mesh.DAIPC.listening) {
3534 - response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3535 - break;
3536 - }
3537 - var diag = diagnosticAgent_installCheck();
3538 - if (diag) {
3539 - if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3540 - diagnosticAgent_uninstall();
3541 - response = 'Diagnostic Agent uninstalled';
3542 - }
3543 - else {
3544 - response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3545 - }
3546 - }
3547 - else {
3548 - if (args['_'].length == 1 && args['_'][0] == 'install') {
3549 - diag = diagnosticAgent_installCheck(true);
3550 - if (diag) {
3551 - response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3552 - }
3553 - else {
3554 - response = 'Diagnostic agent installation failed';
3555 - }
3556 - }
3557 - else {
3558 - response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3559 - }
3560 - }
3561 - if (diag) { diag.close(); diag = null; }
3562 - break;
3563 - }
3564 - case 'amtconfig': {
3565 - if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
3566 - if (amt == null) { response = "No Intel AMT support delected"; break; }
3567 - getMeiState(15, function (state) {
3568 - var rx = '';
3569 - if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
3570 - var apfarg = {
3571 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3572 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3573 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3574 - mpskeepalive: 60000,
3575 - clientname: state.OsHostname,
3576 - clientaddress: '127.0.0.1',
3577 - clientuuid: state.UUID,
3578 - conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3579 - meiState: state // MEI state will be passed to MPS server
3580 - };
3581 - if ((state.UUID == null) || (state.UUID.length != 36)) {
3582 - rx = "Unable to get Intel AMT UUID";
3583 - } else {
3584 - apftunnel = require('apfclient')({ debug: false }, apfarg);
3585 - apftunnel.onJsonControl = function (data) {
3586 - if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
3587 - if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3588 - if (data.action == 'deactivate') { // Request CCM deactivation
3589 - var amtMeiModule, amtMei;
3590 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); break; }
3591 - amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
3592 - amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
3593 - }
3594 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
3595 - }
3596 - apftunnel.onChannelClosed = function () { apftunnel = null; }
3597 - try {
3598 - apftunnel.connect();
3599 - rx = "Started Intel AMT configuration";
3600 - } catch (ex) {
3601 - rx = JSON.stringify(ex);
3602 - }
3603 - }
3604 - }
3605 - if (rx != '') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: rx }); }
3606 - });
3607 - break;
3608 - }
3609 - case 'apf': {
3610 - if (meshCoreObj.intelamt !== null) {
3611 - if (args['_'].length == 1) {
3612 - var connType = -1, connTypeStr = args['_'][0].toLowerCase();
3613 - if (connTypeStr == 'lms') { connType = 2; }
3614 - if (connTypeStr == 'relay') { connType = 1; }
3615 - if (connTypeStr == 'cira') { connType = 0; }
3616 - if (connTypeStr == 'off') { connType = -2; }
3617 - if (connType >= 0) { // Connect
3618 - var apfarg = {
3619 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3620 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3621 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3622 - mpskeepalive: 60000,
3623 - clientname: require('os').hostname(),
3624 - clientaddress: '127.0.0.1',
3625 - clientuuid: meshCoreObj.intelamt.uuid,
3626 - conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3627 - };
3628 - if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
3629 - response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
3630 - } else {
3631 - apftunnel = require('apfclient')({ debug: false }, apfarg);
3632 - apftunnel.onJsonControl = function (data) {
3633 - if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
3634 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
3635 - }
3636 - apftunnel.onChannelClosed = function () { apftunnel = null; }
3637 - try {
3638 - apftunnel.connect();
3639 - response = "Started APF tunnel";
3640 - } catch (e) {
3641 - response = JSON.stringify(e);
3642 - }
3643 - }
3644 - } else if (connType == -2) { // Disconnect
3645 - try {
3646 - apftunnel.disconnect();
3647 - response = "Stopped APF tunnel";
3648 - } catch (e) {
3649 - response = JSON.stringify(e);
3650 - }
3651 - apftunnel = null;
3652 - } else {
3653 - response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
3654 - }
3655 - } else {
3656 - response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
3657 - }
3658 - } else {
3659 - response = "APF tunnel requires Intel AMT";
3660 - }
3661 - break;
3662 - }
3663 - case 'plugin': {
3664 - if (typeof args['_'][0] == 'string') {
3665 - try {
3666 - // Pass off the action to the plugin
3667 - // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3668 - // to control the output / actions here.
3669 - response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3670 - } catch (e) {
3671 - response = "There was an error in the plugin (" + e + ")";
3672 - }
3673 - } else {
3674 - response = "Proper usage: plugin [pluginName] [args].";
3675 - }
3676 - break;
3677 - }
3678 - default: { // This is an unknown command, return an error message
3679 - response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3680 - break;
3681 - }
3682 - }
3683 - } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3684 - if (response != null) { sendConsoleText(response, sessionid); }
3685 - }
3686 -
3687 - // Send a mesh agent console command
3688 - function sendConsoleText(text, sessionid) {
3689 - if (typeof text == 'object') { text = JSON.stringify(text); }
3690 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3691 - }
3692 -
3693 - // Called before the process exits
3694 - //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3695 -
3696 - // Called when the server connection state changes
3697 - function handleServerConnection(state) {
3698 - meshServerConnectionState = state;
3699 - if (meshServerConnectionState == 0) {
3700 - // Server disconnected
3701 - if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3702 - lastSelfInfo = null;
3703 - } else {
3704 - // Server connected, send mesh core information
3705 - var oldNodeId = db.Get('OldNodeId');
3706 - if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3707 -
3708 - // Update the server with basic info, logged in users and more.
3709 - mesh.SendCommand(meshCoreObj);
3710 -
3711 - // Send SMBios tables if present
3712 - if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3713 -
3714 - // Update the server on more advanced stuff, like Intel ME and Network Settings
3715 - meInfoStr = null;
3716 - sendPeriodicServerUpdate();
3717 - if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3718 -
3719 - // Send any state messages
3720 - if (Object.keys(tunnelUserCount.msg).length > 0) {
3721 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
3722 - broadcastSessionsToRegisteredApps();
3723 - }
3724 -
3725 - // Send update of registered applications to the server
3726 - updateRegisteredAppsToServer();
3727 - }
3728 -
3729 - // Send server state update to registered applications
3730 - broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
3731 - }
3732 -
3733 - // Update the server with the latest network interface information
3734 - var sendNetworkUpdateNagleTimer = null;
3735 - function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3736 - function sendNetworkUpdate(force) {
3737 - sendNetworkUpdateNagleTimer = null;
3738 -
3739 - // Update the network interfaces information data
3740 - var netInfo = { netif2: require('os').networkInterfaces() };
3741 - if (netInfo.netif2) {
3742 - netInfo.action = 'netinfo';
3743 - var netInfoStr = JSON.stringify(netInfo);
3744 - if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3745 - }
3746 - }
3747 -
3748 - // Called periodically to check if we need to send updates to the server
3749 - function sendPeriodicServerUpdate(flags) {
3750 - if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3751 - if (!flags) { flags = 0xFFFFFFFF; }
3752 -
3753 - if ((flags & 1) && (amt != null)) {
3754 - // If we have a connected MEI, get Intel ME information
3755 - amt.getAmtInfo(function (meinfo) {
3756 - try {
3757 - if (meinfo == null) return;
3758 - var intelamt = {};
3759 - if (amt != null)
3760 - {
3761 - switch(amt.lmsstate)
3762 - {
3763 - case 0: intelamt.microlms = 'DISABLED'; break;
3764 - case 1: intelamt.microlms = 'CONNECTING'; break;
3765 - case 2: intelamt.microlms = 'CONNECTED'; break;
3766 - default: intelamt.microlms = 'unknown'; break;
3767 - }
3768 - }
3769 - var p = false;
3770 - 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); } }
3771 - if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3772 - if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3773 - if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3774 - if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3775 - if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3776 - // Not activated and we have wired ethernet, look for the trusted DNS
3777 - var dns = meinfo.DNS;
3778 - if (dns == null) {
3779 - // Trusted DNS not set, let's look for the OS network DNS suffix
3780 - var interfaces = require('os').networkInterfaces();
3781 - for (var i in interfaces) {
3782 - for (var j in interfaces[i]) {
3783 - if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3784 - }
3785 - }
3786 - }
3787 - if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3788 - } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3789 - if (p == true) {
3790 - var meInfoStr = JSON.stringify(intelamt);
3791 - if (meInfoStr != lastMeInfo) {
3792 - meshCoreObj.intelamt = intelamt;
3793 - mesh.SendCommand(meshCoreObj);
3794 - lastMeInfo = meInfoStr;
3795 - }
3796 - }
3797 - } catch (e) { }
3798 - });
3799 - }
3800 -
3801 - if (flags & 2) {
3802 - // Update network information
3803 - sendNetworkUpdateNagle(false);
3804 - }
3805 -
3806 - if ((flags & 4) && (process.platform == 'win32')) {
3807 - // Update anti-virus information
3808 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3809 - var av, pr;
3810 - try { av = require('win-info').av(); } catch (e) { av = null; } // Antivirus
3811 - //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (e) { pr = null; } } // Pending reboot
3812 - if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3813 - }
3814 - }
3815 -
3816 - // Starting function
3817 - obj.start = function () {
3818 - // Setup the mesh agent event handlers
3819 - mesh.AddCommandHandler(handleServerCommand);
3820 - mesh.AddConnectHandler(handleServerConnection);
3821 -
3822 - // Parse input arguments
3823 - //var args = parseArgs(process.argv);
3824 - //console.log(args);
3825 -
3826 - //resetMicroLms();
3827 -
3828 - // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3829 - try {
3830 - var userSession = require('user-sessions');
3831 - userSession.on('changed', function onUserSessionChanged() {
3832 - userSession.enumerateUsers().then(function (users) {
3833 - var u = [], a = users.Active;
3834 - for (var i = 0; i < a.length; i++) {
3835 - var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3836 - if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3837 - }
3838 - meshCoreObj.users = u;
3839 - mesh.SendCommand(meshCoreObj);
3840 - });
3841 - });
3842 - userSession.emit('changed');
3843 - //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3844 - //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3845 - } catch (e) { }
3846 - }
3847 -
3848 - obj.stop = function () {
3849 - mesh.AddCommandHandler(null);
3850 - mesh.AddConnectHandler(null);
3851 - }
3852 -
3853 - function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3854 - function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3855 - function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3856 -
3857 - function onWebSocketUpgrade(response, s, head) {
3858 - sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3859 - this.s = s;
3860 - s.httprequest = this;
3861 - s.end = onWebSocketClosed;
3862 - s.data = onWebSocketData;
3863 - }
3864 -
3865 - // Get Intel MEI State in a flexible way
3866 - // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network
3867 - function getMeiState(flags, func) {
3868 - var amtMeiModule, amtMei;
3869 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { func(null); return; }
3870 - amtMei.on('error', function (e) { func(null); return; });
3871 - try {
3872 - var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
3873 - amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
3874 - if ((flags & 1) != 0) { amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); }
3875 - amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
3876 - amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated"
3877 - amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
3878 - amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } }); // Flag 2 = CCM, 4 = ACM
3879 - //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
3880 - if ((flags & 8) != 0) {
3881 - amtMei.getLanInterfaceSettings(0, function (result) {
3882 - if (result) {
3883 - amtMeiTmpState.net0 = result;
3884 - var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
3885 - for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3886 - }
3887 - });
3888 - amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
3889 - }
3890 - amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { amtMeiTmpState.UUID = result.uuid; } });
3891 - if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); }
3892 - amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } });
3893 - if ((flags & 4) != 0) {
3894 - amtMei.getHashHandles(function (handles) {
3895 - if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); }
3896 - var exitOnCount = handles.length;
3897 - for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); }
3898 - });
3899 - }
3900 - } catch (e) { if (func != null) { func(null); } return; }
3901 - }
3902 -
3903 - return obj;
3904 -}
3905 -
3906 -//
3907 -// Module startup
3908 -//
3909 -
3910 -try {
3911 - var xexports = null, mainMeshCore = null;
3912 - try { xexports = module.exports; } catch (e) { }
3913 -
3914 - if (xexports != null) {
3915 - // If we are running within NodeJS, export the core
3916 - module.exports.createMeshCore = createMeshCore;
3917 - } else {
3918 - // If we are not running in NodeJS, launch the core
3919 - mainMeshCore = createMeshCore();
3920 - mainMeshCore.start(null);
3921 - }
3922 -} catch (e) {
3923 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3924 -}
agents/meshcore-old.js deleted
-3903
@@ -1,3903 +0,0 @@
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 (e) { }
51 - if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
52 - } catch (x) { }
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 (e) { }
58 - if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59 - } catch (e) { }
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 (e) { } }
140 - obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141 - try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
142 - obj.DAIPC._daipc = [];
143 - obj.DAIPC.on('connection', function (c) {
144 - c._send = function (j) {
145 - var data = JSON.stringify(j);
146 - var packet = Buffer.alloc(data.length + 4);
147 - packet.writeUInt32LE(data.length + 4, 0);
148 - Buffer.from(data).copy(packet, 4);
149 - this.write(packet);
150 - };
151 - this._daipc.push(c);
152 - c.parent = this;
153 - c.on('end', function () { removeRegisteredApp(this); });
154 - c.on('data', function (chunk) {
155 - if (chunk.length < 4) { this.unshift(chunk); return; }
156 - var len = chunk.readUInt32LE(0);
157 - if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
158 - if (chunk.length < len) { this.unshift(chunk); return; }
159 -
160 - var data = chunk.slice(4, len);
161 - try { data = JSON.parse(data.toString()); } catch (e) { }
162 - if ((data == null) || (typeof data.cmd != 'string')) return;
163 -
164 - try {
165 - switch (data.cmd) {
166 - case 'requesthelp':
167 - if (this._registered == null) return;
168 - sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
169 - var help = {};
170 - help[this._registered] = data.value;
171 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
172 - MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
173 - break;
174 - case 'cancelhelp':
175 - if (this._registered == null) return;
176 - sendConsoleText('Cancel Help (' + this._registered + ')');
177 - try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
178 - break;
179 - case 'register':
180 - if (typeof data.value == 'string') {
181 - this._registered = data.value;
182 - var apps = {};
183 - apps[data.value] = 1;
184 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
185 - this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
186 - }
187 - break;
188 - case 'query':
189 - switch (data.value) {
190 - case 'connection':
191 - data.result = require('MeshAgent').ConnectedServer;
192 - this._send(data);
193 - break;
194 - case 'descriptors':
195 - require('ChainViewer').getSnapshot().then(function (f) {
196 - this.tag.payload.result = f;
197 - this.tag.ipc._send(this.tag.payload);
198 - }).parentPromise.tag = { ipc: this, payload: data };
199 - break;
200 - }
201 - break;
202 - case 'amtstate':
203 - if (amt == null) return;
204 - var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
205 - func.pipe = this;
206 - amt.getAmtInfo(func);
207 - break;
208 - case 'sessions':
209 - this._send({ cmd: 'sessions', sessions: tunnelUserCount });
210 - break;
211 - }
212 - }
213 - catch (e) { removeRegisteredApp(this); this.end(); return; }
214 - });
215 - });
216 -
217 - // Send current sessions to registered apps
218 - function broadcastSessionsToRegisteredApps(x) {
219 - broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
220 - }
221 -
222 - // Send this object to all registered local applications
223 - function broadcastToRegisteredApps(x) {
224 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
225 - for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
226 - }
227 -
228 - // Send list of registered apps to the server
229 - function updateRegisteredAppsToServer() {
230 - if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
231 - var apps = {};
232 - for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
233 - try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
234 - }
235 -
236 - // Remove a registered app
237 - function removeRegisteredApp(pipe) {
238 - for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
239 - if (pipe._registered != null) updateRegisteredAppsToServer();
240 - }
241 -
242 - function diagnosticAgent_uninstall() {
243 - require('service-manager').manager.uninstallService('meshagentDiagnostic');
244 - require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
245 - };
246 - function diagnosticAgent_installCheck(install) {
247 - try {
248 - var diag = require('service-manager').manager.getService('meshagentDiagnostic');
249 - return (diag);
250 - }
251 - catch (e) {
252 - }
253 - if (!install) { return (null); }
254 -
255 - var svc = null;
256 - try {
257 - require('service-manager').manager.installService(
258 - {
259 - name: 'meshagentDiagnostic',
260 - displayName: "Mesh Agent Diagnostic Service",
261 - description: "Mesh Agent Diagnostic Service",
262 - servicePath: process.execPath,
263 - parameters: ['-recovery']
264 - //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
265 - });
266 - svc = require('service-manager').manager.getService('meshagentDiagnostic');
267 - }
268 - catch (e) {
269 - return (null);
270 - }
271 - var proxyConfig = require('global-tunnel').proxyConfig;
272 - var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
273 - var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
274 - ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
275 - ddb.Put('disableUpdate', '1');
276 - ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
277 - ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
278 - ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
279 - if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
280 - if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
281 - if (proxyConfig) {
282 - ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
283 - } else {
284 - ddb.Put('ignoreProxyFile', '1');
285 - }
286 -
287 - require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
288 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
289 -
290 - delete ddb;
291 -
292 - // Set a recurrent task, to run the Diagnostic Agent every 2 days
293 - 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' });
294 - //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
295 -
296 - return (svc);
297 - }
298 -
299 - // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
300 - if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
301 - // Setup manual battery monitoring
302 - require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
303 - if (require('MeshAgent')._batteryFileTimer != null) return;
304 - require('MeshAgent')._batteryFileTimer = setTimeout(function () {
305 - try {
306 - require('MeshAgent')._batteryFileTimer = null;
307 - var data = null;
308 - try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
309 - if ((data != null) && (data.length < 10)) {
310 - data = data.split(',');
311 - if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
312 - var level = parseInt(data[1]);
313 - if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
314 - }
315 - }
316 - } catch (e) { }
317 - }, 1000);
318 - });
319 - } else {
320 - // Setup normal battery monitoring
321 - if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
322 - require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
323 - _battLevelChanged.self._currentBatteryLevel = val;
324 - _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
325 - };
326 - require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
327 - require('MeshAgent')._powerChanged = function _powerChanged(val) {
328 - _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
329 - _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
330 - };
331 - require('MeshAgent')._powerChanged.self = require('MeshAgent');
332 - require('MeshAgent').on('Connected', function (status) {
333 - if (status == 0) {
334 - require('power-monitor').removeListener('acdc', this._powerChanged);
335 - require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
336 - } else {
337 - require('power-monitor').on('acdc', this._powerChanged);
338 - require('power-monitor').on('batteryLevel', this._battLevelChanged);
339 - }
340 - });
341 - }
342 - }
343 -
344 -
345 - /*
346 - function borderController() {
347 - this.container = null;
348 - this.Start = function Start(user) {
349 - if (this.container == null) {
350 - if (process.platform == 'win32') {
351 - try {
352 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
353 - } catch (e) {
354 - this.container = require('ScriptContainer').Create({ processIsolation: 1 });
355 - }
356 - } else {
357 - this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
358 - }
359 - this.container.parent = this;
360 - this.container.addModule('monitor-info', getJSModule('monitor-info'));
361 - this.container.addModule('monitor-border', getJSModule('monitor-border'));
362 - this.container.addModule('promise', getJSModule('promise'));
363 - this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
364 - this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
365 - }
366 - }
367 - this.Stop = function Stop() {
368 - if (this.container != null) {
369 - this._container = this.container;
370 - this._container.parent = this;
371 - this.container = null;
372 - this._container.exit();
373 - }
374 - }
375 - }
376 - obj.borderManager = new borderController();
377 - */
378 -
379 - // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
380 - var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
381 -
382 -
383 - // Get the operating system description string
384 - try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (e) { }
385 -
386 - var meshServerConnectionState = 0;
387 - var tunnels = {};
388 - var lastMeInfo = null;
389 - var lastNetworkInfo = null;
390 - var lastPublicLocationInfo = null;
391 - var selfInfoUpdateTimer = null;
392 - var http = require('http');
393 - var net = require('net');
394 - var fs = require('fs');
395 - var rtc = require('ILibWebRTC');
396 - var amt = null;
397 - var processManager = require('process-manager');
398 - var wifiScannerLib = null;
399 - var wifiScanner = null;
400 - var networkMonitor = null;
401 - var amtscanner = null;
402 - var nextTunnelIndex = 1;
403 - var amtPolicy = null;
404 - var apftunnel = null;
405 - var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
406 -
407 - // Add to the server event log
408 - function MeshServerLog(msg, state) {
409 - if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
410 - if (state) {
411 - if (state.userid) { msg.userid = state.userid; }
412 - if (state.username) { msg.username = state.username; }
413 - if (state.sessionid) { msg.sessionid = state.sessionid; }
414 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
415 - }
416 - mesh.SendCommand(msg);
417 - }
418 -
419 - // Add to the server event log, use internationalized events
420 - function MeshServerLogEx(id, args, msg, state) {
421 - var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
422 - if (state) {
423 - if (state.userid) { msg.userid = state.userid; }
424 - if (state.username) { msg.username = state.username; }
425 - if (state.sessionid) { msg.sessionid = state.sessionid; }
426 - if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
427 - }
428 - mesh.SendCommand(msg);
429 - }
430 -
431 - // If we are running in Duktape, agent will be null
432 - if (agent == null) {
433 - // Running in native agent, Import libraries
434 - db = require('SimpleDataStore').Shared();
435 - sha = require('SHA256Stream');
436 - mesh = require('MeshAgent');
437 - childProcess = require('child_process');
438 - if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
439 - // Check if this computer supports a desktop
440 - try
441 - {
442 - if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
443 - {
444 - meshCoreObj.caps |= 1;
445 - }
446 - else if(process.platform == 'linux' || process.platform == 'freebsd')
447 - {
448 - require('monitor-info').on('kvmSupportDetected', function (value)
449 - {
450 - meshCoreObj.caps |= 1;
451 - mesh.SendCommand(meshCoreObj);
452 - });
453 - }
454 - } catch (e) { }
455 - }
456 - } else {
457 - // Running in nodejs
458 - meshCoreObj.value += '-NodeJS';
459 - meshCoreObj.caps = 8;
460 - mesh = agent.getMeshApi();
461 - }
462 -
463 - mesh.DAIPC = obj.DAIPC;
464 -
465 - /*
466 - var AMTScanner = require("AMTScanner");
467 - var scan = new AMTScanner();
468 -
469 - scan.on("found", function (data) {
470 - if (typeof data === 'string') {
471 - console.log(data);
472 - } else {
473 - console.log(JSON.stringify(data, null, " "));
474 - }
475 - });
476 - scan.scan("10.2.55.140", 1000);
477 - scan.scan("10.2.55.139-10.2.55.145", 1000);
478 - scan.scan("10.2.55.128/25", 2000);
479 - */
480 -
481 - /*
482 - // Try to load up the network monitor
483 - try {
484 - networkMonitor = require('NetworkMonitor');
485 - networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
486 - networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
487 - networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
488 - } catch (e) { networkMonitor = null; }
489 - */
490 -
491 - // Try to load up the Intel AMT scanner
492 - try {
493 - var AMTScannerModule = require('amt-scanner');
494 - amtscanner = new AMTScannerModule();
495 - //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
496 - } catch (e) { amtscanner = null; }
497 -
498 - // Fetch the SMBios Tables
499 - var SMBiosTables = null;
500 - var SMBiosTablesRaw = null;
501 - try {
502 - var SMBiosModule = null;
503 - try { SMBiosModule = require('smbios'); } catch (e) { }
504 - if (SMBiosModule != null) {
505 - SMBiosModule.get(function (data) {
506 - if (data != null) {
507 - SMBiosTablesRaw = data;
508 - SMBiosTables = require('smbios').parse(data)
509 - if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
510 -
511 - // If SMBios tables say that Intel AMT is present, try to connect MEI
512 - if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
513 - {
514 - var amtmodule = require('amt-manage');
515 - amt = new amtmodule(mesh, db, false);
516 - amt.on('portBinding_LMS', function (map)
517 - {
518 - var j = { action: 'lmsinfo', value: { ports: map.keys() } };
519 - mesh.SendCommand(j);
520 - });
521 - amt.on('stateChange_LMS', function (v)
522 - {
523 - if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; }
524 - switch(v)
525 - {
526 - case 0:
527 - meshCoreObj.intelamt.microlms = 'DISABLED';
528 - break;
529 - case 1:
530 - meshCoreObj.intelamt.microlms = 'CONNECTING';
531 - break;
532 - case 2:
533 - meshCoreObj.intelamt.microlms = 'CONNECTED';
534 - break;
535 - default:
536 - break;
537 - }
538 - mesh.SendCommand(meshCoreObj);
539 - });
540 - amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
541 - if (amtPolicy != null) { amt.setPolicy(amtPolicy); }
542 - amt.start();
543 - }
544 - }
545 - });
546 - }
547 - } catch (e) { sendConsoleText("ex1: " + e); }
548 -
549 - // Try to load up the WIFI scanner
550 - try {
551 - var wifiScannerLib = require('wifi-scanner');
552 - wifiScanner = new wifiScannerLib();
553 - wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
554 - } catch (e) { wifiScannerLib = null; wifiScanner = null; }
555 -
556 - // Get our location (lat/long) using our public IP address
557 - var getIpLocationDataExInProgress = false;
558 - var getIpLocationDataExCounts = [0, 0];
559 - function getIpLocationDataEx(func) {
560 - if (getIpLocationDataExInProgress == true) { return false; }
561 - try {
562 - getIpLocationDataExInProgress = true;
563 - getIpLocationDataExCounts[0]++;
564 - var options = http.parseUri("http://ipinfo.io/json");
565 - options.method = 'GET';
566 - http.request(options, function (resp) {
567 - if (resp.statusCode == 200) {
568 - var geoData = '';
569 - resp.data = function (geoipdata) { geoData += geoipdata; };
570 - resp.end = function () {
571 - var location = null;
572 - try {
573 - if (typeof geoData == 'string') {
574 - var result = JSON.parse(geoData);
575 - if (result.ip && result.loc) { location = result; }
576 - }
577 - } catch (e) { }
578 - if (func) { getIpLocationDataExCounts[1]++; func(location); }
579 - }
580 - } else { func(null); }
581 - getIpLocationDataExInProgress = false;
582 - }).end();
583 - return true;
584 - }
585 - catch (e) { return false; }
586 - }
587 -
588 - // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
589 - function clearGatewayMac(str) {
590 - if (str == null) return null;
591 - var x = JSON.parse(str);
592 - for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
593 - return JSON.stringify(x);
594 - }
595 -
596 - function getIpLocationData(func) {
597 - // Get the location information for the cache if possible
598 - var publicLocationInfo = db.Get('publicLocationInfo');
599 - if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
600 - if (publicLocationInfo == null) {
601 - // Nothing in the cache, fetch the data
602 - getIpLocationDataEx(function (locationData) {
603 - if (locationData != null) {
604 - publicLocationInfo = {};
605 - publicLocationInfo.netInfoStr = lastNetworkInfo;
606 - publicLocationInfo.locationData = locationData;
607 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
608 - if (func) func(locationData); // Report the new location
609 - } else {
610 - if (func) func(null); // Report no location
611 - }
612 - });
613 - } else {
614 - // Check the cache
615 - if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
616 - // Cache match
617 - if (func) func(publicLocationInfo.locationData);
618 - } else {
619 - // Cache mismatch
620 - getIpLocationDataEx(function (locationData) {
621 - if (locationData != null) {
622 - publicLocationInfo = {};
623 - publicLocationInfo.netInfoStr = lastNetworkInfo;
624 - publicLocationInfo.locationData = locationData;
625 - var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
626 - if (func) func(locationData); // Report the new location
627 - } else {
628 - if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
629 - }
630 - });
631 - }
632 - }
633 - }
634 -
635 - // Polyfill String.endsWith
636 - if (!String.prototype.endsWith) {
637 - String.prototype.endsWith = function (searchString, position) {
638 - var subjectString = this.toString();
639 - if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
640 - position -= searchString.length;
641 - var lastIndex = subjectString.lastIndexOf(searchString, position);
642 - return lastIndex !== -1 && lastIndex === position;
643 - };
644 - }
645 -
646 - // Polyfill path.join
647 - obj.path = {
648 - join: function () {
649 - var x = [];
650 - for (var i in arguments) {
651 - var w = arguments[i];
652 - if (w != null) {
653 - while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
654 - if (i != 0) {
655 - while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
656 - }
657 - x.push(w);
658 - }
659 - }
660 - if (x.length == 0) return '/';
661 - return x.join('/');
662 - }
663 - };
664 -
665 - // Replace a string with a number if the string is an exact number
666 - function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
667 -
668 - // Convert decimal to hex
669 - function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
670 -
671 - // Convert a raw string to a hex string
672 - function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
673 -
674 - // Convert a buffer into a string
675 - function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
676 -
677 - // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
678 - function hex2rstr(d) {
679 - if (typeof d != "string" || d.length == 0) return '';
680 - var r = '', m = ('' + d).match(/../g), t;
681 - while (t = m.shift()) r += String.fromCharCode('0x' + t);
682 - return r
683 - }
684 -
685 - // Convert an object to string with all functions
686 - function objToString(x, p, pad, ret) {
687 - if (ret == undefined) ret = '';
688 - if (p == undefined) p = 0;
689 - if (x == null) { return '[null]'; }
690 - if (p > 8) { return '[...]'; }
691 - if (x == undefined) { return '[undefined]'; }
692 - if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
693 - if (typeof x == 'buffer') { return '[buffer]'; }
694 - if (typeof x != 'object') { return x; }
695 - var r = '{' + (ret ? '\r\n' : ' ');
696 - for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
697 - return r + addPad(p, pad) + '}';
698 - }
699 -
700 - // Return p number of spaces
701 - function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
702 -
703 - // Split a string taking into account the quoats. Used for command line parsing
704 - function splitArgs(str) {
705 - var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
706 - do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
707 - return myArray;
708 - }
709 -
710 - // Parse arguments string array into an object
711 - function parseArgs(argv) {
712 - var results = { '_': [] }, current = null;
713 - for (var i = 1, len = argv.length; i < len; i++) {
714 - var x = argv[i];
715 - if (x.length > 2 && x[0] == '-' && x[1] == '-') {
716 - if (current != null) { results[current] = true; }
717 - current = x.substring(2);
718 - } else {
719 - if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
720 - }
721 - }
722 - if (current != null) { results[current] = true; }
723 - return results;
724 - }
725 -
726 - // Get server target url with a custom path
727 - function getServerTargetUrl(path) {
728 - var x = mesh.ServerUrl;
729 - //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
730 - if (x == null) { return null; }
731 - if (path == null) { path = ''; }
732 - x = http.parseUri(x);
733 - if (x == null) return null;
734 - return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
735 - }
736 -
737 - // Get server url. If the url starts with "*/..." change it, it not use the url as is.
738 - function getServerTargetUrlEx(url) {
739 - if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
740 - return url;
741 - }
742 -
743 - // Send a wake-on-lan packet
744 - function sendWakeOnLan(hexMac) {
745 - hexMac = hexMac.split(':').join('');
746 - var count = 0;
747 - try {
748 - var interfaces = require('os').networkInterfaces();
749 - var magic = 'FFFFFFFFFFFF';
750 - for (var x = 1; x <= 16; ++x) { magic += hexMac; }
751 - var magicbin = Buffer.from(magic, 'hex');
752 -
753 - for (var adapter in interfaces) {
754 - if (interfaces.hasOwnProperty(adapter)) {
755 - for (var i = 0; i < interfaces[adapter].length; ++i) {
756 - var addr = interfaces[adapter][i];
757 - if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
758 - try {
759 - var socket = require('dgram').createSocket({ type: 'udp4' });
760 - socket.bind({ address: addr.address });
761 - socket.setBroadcast(true);
762 - socket.setMulticastInterface(addr.address);
763 - socket.setMulticastTTL(1);
764 - socket.send(magicbin, 7, '255.255.255.255');
765 - socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
766 - count++;
767 - }
768 - catch (e) { }
769 - }
770 - }
771 - }
772 - }
773 - } catch (e) { }
774 - return count;
775 - }
776 -
777 - // Handle a mesh agent command
778 - function handleServerCommand(data) {
779 - if (typeof data == 'object') {
780 - // If this is a console command, parse it and call the console handler
781 - switch (data.action) {
782 - case 'msg': {
783 - switch (data.type) {
784 - case 'console': { // Process a console command
785 - if (data.value && data.sessionid) {
786 - MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
787 - var args = splitArgs(data.value);
788 - processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
789 - }
790 - break;
791 - }
792 - case 'tunnel': {
793 - if (data.value != null) { // Process a new tunnel connection request
794 - // Create a new tunnel object
795 - var xurl = getServerTargetUrlEx(data.value);
796 - if (xurl != null) {
797 - xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
798 - var woptions = http.parseUri(xurl);
799 - woptions.perMessageDeflate = false;
800 - if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
801 - woptions.rejectUnauthorized = 0;
802 - //sendConsoleText(JSON.stringify(woptions));
803 - //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
804 - var tunnel = http.request(woptions);
805 - tunnel.upgrade = onTunnelUpgrade;
806 - tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
807 - tunnel.sessionid = data.sessionid;
808 - tunnel.rights = data.rights;
809 - tunnel.consent = data.consent;
810 - tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
811 - tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
812 - tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
813 - tunnel.userid = data.userid;
814 - tunnel.remoteaddr = data.remoteaddr;
815 - tunnel.state = 0;
816 - tunnel.url = xurl;
817 - tunnel.protocol = 0;
818 - tunnel.soptions = data.soptions;
819 - tunnel.tcpaddr = data.tcpaddr;
820 - tunnel.tcpport = data.tcpport;
821 - tunnel.udpaddr = data.udpaddr;
822 - tunnel.udpport = data.udpport;
823 - tunnel.end();
824 - // Put the tunnel in the tunnels list
825 - var index = nextTunnelIndex++;
826 - tunnel.index = index;
827 - tunnels[index] = tunnel;
828 -
829 - //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
830 - }
831 - }
832 - break;
833 - }
834 - case 'messagebox': {
835 - // Display a message box
836 - if (data.title && data.msg) {
837 - MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
838 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
839 - try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
840 - }
841 - break;
842 - }
843 - case 'ps': {
844 - // Return the list of running processes
845 - if (data.sessionid) {
846 - processManager.getProcesses(function (plist) {
847 - mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
848 - });
849 - }
850 - break;
851 - }
852 - case 'pskill': {
853 - // Kill a process
854 - if (data.value) {
855 - MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
856 - try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
857 - }
858 - break;
859 - }
860 - case 'services': {
861 - // Return the list of installed services
862 - var services = null;
863 - try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
864 - if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
865 - break;
866 - }
867 - case 'serviceStop': {
868 - // Stop a service
869 - try {
870 - var service = require('service-manager').manager.getService(data.serviceName);
871 - if (service != null) { service.stop(); }
872 - } catch (e) { }
873 - break;
874 - }
875 - case 'serviceStart': {
876 - // Start a service
877 - try {
878 - var service = require('service-manager').manager.getService(data.serviceName);
879 - if (service != null) { service.start(); }
880 - } catch (e) { }
881 - break;
882 - }
883 - case 'serviceRestart': {
884 - // Restart a service
885 - try {
886 - var service = require('service-manager').manager.getService(data.serviceName);
887 - if (service != null) { service.restart(); }
888 - } catch (e) { }
889 - break;
890 - }
891 - case 'deskBackground':
892 - {
893 - // Toggle desktop background
894 - try {
895 - if (process.platform == 'win32') {
896 - var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
897 - var sid = undefined;
898 - if (stype == 1) {
899 - if (require('MeshAgent')._tsid != null) {
900 - stype = 5;
901 - sid = require('MeshAgent')._tsid;
902 - }
903 - }
904 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
905 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
906 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
907 - child.stderr.on('data', function () { });
908 - child.waitExit();
909 - var current = child.stdout.str.trim();
910 - if (current != '') { require('MeshAgent')._wallpaper = current; }
911 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
912 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
913 - child.stderr.on('data', function () { });
914 - child.waitExit();
915 - } else {
916 - var id = require('user-sessions').consoleUid();
917 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
918 - if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
919 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
920 - }
921 - } catch (e) {
922 - sendConsoleText(e);
923 - }
924 - break;
925 - }
926 - case 'openUrl': {
927 - // Open a local web browser and return success/fail
928 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
929 - sendConsoleText("OpenURL: " + data.url);
930 - if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
931 - break;
932 - }
933 - case 'getclip': {
934 - // Send the load clipboard back to the user
935 - //sendConsoleText('getClip: ' + JSON.stringify(data));
936 - if (require('MeshAgent').isService) {
937 - require('clipboard').dispatchRead().then(function (str) {
938 - if (str) {
939 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
940 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
941 - }
942 - });
943 - } else {
944 - require("clipboard").read().then(function (str) {
945 - if (str) {
946 - MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
947 - mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
948 - }
949 - });
950 - }
951 - break;
952 - }
953 - case 'setclip': {
954 - // Set the load clipboard to a user value
955 - //sendConsoleText('setClip: ' + JSON.stringify(data));
956 - if (typeof data.data == 'string') {
957 - MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
958 - if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
959 - mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
960 - }
961 - break;
962 - }
963 - case 'userSessions': {
964 - // Send back current user sessions list, this is Windows only.
965 - //sendConsoleText('userSessions: ' + JSON.stringify(data));
966 - if (process.platform != 'win32') break;
967 - var p = require('user-sessions').enumerateUsers();
968 - p.sessionid = data.sessionid;
969 - p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
970 - break;
971 - }
972 - default:
973 - // Unknown action, ignore it.
974 - break;
975 - }
976 - break;
977 - }
978 - case 'acmactivate': {
979 - if (amt != null) {
980 - MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
981 - amt.setAcmResponse(data);
982 - }
983 - break;
984 - }
985 - case 'wakeonlan': {
986 - // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
987 - sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
988 - for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
989 - break;
990 - }
991 - case 'runcommands': {
992 - if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
993 - sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
994 -
995 - // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
996 - var options = {};
997 - if (data.runAsUser > 0) {
998 - try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
999 - options.type = require('child_process').SpawnTypes.TERM;
1000 - }
1001 - if (data.runAsUser == 2) {
1002 - if (options.uid == null) break;
1003 - if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1004 - }
1005 -
1006 - if (process.platform == 'win32') {
1007 - if (data.type == 1) {
1008 - // Windows command shell
1009 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1010 - mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1011 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1012 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1013 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1014 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1015 - } else if (data.type == 2) {
1016 - // Windows Powershell
1017 - mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1018 - mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1019 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1020 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1021 - mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1022 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1023 - }
1024 - } else if (data.type == 3) {
1025 - // Linux shell
1026 - mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1027 - mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1028 - mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1029 - mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1030 - mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1031 - mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1032 - }
1033 - break;
1034 - }
1035 - case 'uninstallagent':
1036 - // Uninstall this agent
1037 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1038 - if (require('service-manager').manager.getService(agentName).isMe()) {
1039 - try { diagnosticAgent_uninstall(); } catch (e) { }
1040 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1041 - 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 });
1042 - }
1043 - break;
1044 - case 'poweraction': {
1045 - // Server telling us to execute a power action
1046 - if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1047 - var forced = 0;
1048 - if (data.forced == 1) { forced = 1; }
1049 - data.actiontype = parseInt(data.actiontype);
1050 - MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1051 - sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1052 - var r = mesh.ExecPowerState(data.actiontype, forced);
1053 - sendConsoleText("ExecPowerState returned code: " + r);
1054 - }
1055 - break;
1056 - }
1057 - case 'iplocation': {
1058 - // 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
1059 - getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1060 - break;
1061 - }
1062 - case 'toast': {
1063 - // Display a toast message
1064 - if (data.title && data.msg) {
1065 - MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1066 - data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1067 - try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1068 - }
1069 - break;
1070 - }
1071 - case 'openUrl': {
1072 - // Open a local web browser and return success/fail
1073 - //sendConsoleText('OpenURL: ' + data.url);
1074 - MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1075 - if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1076 - break;
1077 - }
1078 - case 'amtPolicy': {
1079 - // Store the latest Intel AMT policy
1080 - amtPolicy = data.amtPolicy;
1081 - if (data.amtPolicy != null) { db.Put('amtPolicy', JSON.stringify(data.amtPolicy)); } else { db.Put('amtPolicy', null); }
1082 - if (amt != null) { amt.setPolicy(amtPolicy, true); }
1083 - break;
1084 - }
1085 - case 'getScript': {
1086 - // Received a configuration script from the server
1087 - sendConsoleText('getScript: ' + JSON.stringify(data));
1088 - break;
1089 - }
1090 - case 'sysinfo': {
1091 - // Fetch system information
1092 - getSystemInformation(function (results) {
1093 - if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1094 - });
1095 - break;
1096 - }
1097 - case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1098 - case 'pong': { break; }
1099 - case 'plugin': {
1100 - try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1101 - break;
1102 - }
1103 - case 'coredump':
1104 - // Set the current agent coredump situation.
1105 - if (data.value === true) {
1106 - // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1107 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
1108 - } else if (data.value === false) {
1109 - process.coreDumpLocation = null;
1110 - }
1111 - break;
1112 - case 'getcoredump':
1113 - // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1114 - var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1115 - var coreDumpPath = null;
1116 - if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1117 - if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) { r.exists = (db.Get('CoreDumpTime') != require('fs').statSync(coreDumpPath).mtime); }
1118 - if (r.exists == true) { r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); }
1119 - mesh.SendCommand(JSON.stringify(r));
1120 - default:
1121 - // Unknown action, ignore it.
1122 - break;
1123 - }
1124 - }
1125 - }
1126 -
1127 - // Called when a file changed in the file system
1128 - /*
1129 - function onFileWatcher(a, b) {
1130 - console.log('onFileWatcher', a, b, this.path);
1131 - var response = getDirectoryInfo(this.path);
1132 - if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1133 - }
1134 - */
1135 -
1136 - function getSystemInformation(func) {
1137 - try {
1138 - var results = { hardware: require('identifiers').get() }; // Hardware info
1139 - if (results.hardware && results.hardware.windows) {
1140 - // Remove extra entries and things that change quickly
1141 - var x = results.hardware.windows.osinfo;
1142 - try { delete x.FreePhysicalMemory; } catch (e) { }
1143 - try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1144 - try { delete x.FreeVirtualMemory; } catch (e) { }
1145 - try { delete x.LocalDateTime; } catch (e) { }
1146 - try { delete x.MaxProcessMemorySize; } catch (e) { }
1147 - try { delete x.TotalVirtualMemorySize; } catch (e) { }
1148 - try { delete x.TotalVisibleMemorySize; } catch (e) { }
1149 - try {
1150 - if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1151 - if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1152 - if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1153 - } catch (e) { }
1154 - }
1155 - if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1156 - /*
1157 - if (process.platform == 'win32') {
1158 - var defragResult = function (r) {
1159 - if (typeof r == 'object') { results[this.callname] = r; }
1160 - if (this.callname == 'defrag') {
1161 - var pr = require('win-info').installedApps(); // Installed apps
1162 - pr.callname = 'installedApps';
1163 - pr.sessionid = data.sessionid;
1164 - pr.then(defragResult, defragResult);
1165 - }
1166 - else {
1167 - results.winpatches = require('win-info').qfe(); // Windows patches
1168 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1169 - func(results);
1170 - }
1171 - }
1172 - var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1173 - pr.callname = 'defrag';
1174 - pr.sessionid = data.sessionid;
1175 - pr.then(defragResult, defragResult);
1176 - } else {
1177 - */
1178 - results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1179 - func(results);
1180 - //}
1181 - } catch (e) { func(null, e); }
1182 - }
1183 -
1184 - // Get a formated response for a given directory path
1185 - function getDirectoryInfo(reqpath) {
1186 - var response = { path: reqpath, dir: [] };
1187 - if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1188 - // List all the drives in the root, or the root itself
1189 - var results = null;
1190 - try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1191 - if (results != null) {
1192 - for (var i = 0; i < results.length; ++i) {
1193 - var drive = { n: results[i].name, t: 1 };
1194 - if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1195 - response.dir.push(drive);
1196 - }
1197 - }
1198 - } else {
1199 - // List all the files and folders in this path
1200 - if (reqpath == '') { reqpath = '/'; }
1201 - var results = null, xpath = obj.path.join(reqpath, '*');
1202 - //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1203 - try { results = fs.readdirSync(xpath); } catch (e) { }
1204 - if (results != null) {
1205 - for (var i = 0; i < results.length; ++i) {
1206 - if ((results[i] != '.') && (results[i] != '..')) {
1207 - var stat = null, p = obj.path.join(reqpath, results[i]);
1208 - //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1209 - try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1210 - if ((stat != null) && (stat != undefined)) {
1211 - if (stat.isDirectory() == true) {
1212 - response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1213 - } else {
1214 - response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1215 - }
1216 - }
1217 - }
1218 - }
1219 - }
1220 - }
1221 - return response;
1222 - }
1223 -
1224 - // Tunnel callback operations
1225 - function onTunnelUpgrade(response, s, head) {
1226 - this.s = s;
1227 - s.httprequest = this;
1228 - s.end = onTunnelClosed;
1229 - s.tunnel = this;
1230 - s.descriptorMetadata = "MeshAgent_relayTunnel";
1231 -
1232 - if (require('MeshAgent').idleTimeout != null)
1233 - {
1234 - s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1235 - s.on('timeout', function ()
1236 - {
1237 - this.ping();
1238 - this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1239 - });
1240 - }
1241 -
1242 - //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1243 -
1244 - if (this.tcpport != null) {
1245 - // This is a TCP relay connection, pause now and try to connect to the target.
1246 - s.pause();
1247 - s.data = onTcpRelayServerTunnelData;
1248 - var connectionOptions = { port: parseInt(this.tcpport) };
1249 - if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1250 - s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1251 - s.tcprelay.peerindex = this.index;
1252 -
1253 - // Add the TCP session to the count and update the server
1254 - if (s.httprequest.userid != null) {
1255 - if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1256 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1257 - broadcastSessionsToRegisteredApps();
1258 - }
1259 - } if (this.udpport != null) {
1260 - // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1261 - s.data = onUdpRelayServerTunnelData;
1262 - s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1263 - s.udprelay.bind({ port: 0 });
1264 - s.udprelay.peerindex = this.index;
1265 - s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1266 - s.udprelay.udpport = this.udpport;
1267 - s.udprelay.udpaddr = this.udpaddr;
1268 - s.udprelay.first = true;
1269 -
1270 - // Add the UDP session to the count and update the server
1271 - if (s.httprequest.userid != null) {
1272 - if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1273 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1274 - broadcastSessionsToRegisteredApps();
1275 - }
1276 - } else {
1277 - // This is a normal connect for KVM/Terminal/Files
1278 - s.data = onTunnelData;
1279 - }
1280 - }
1281 -
1282 - // Called when UDP relay data is received // TODO****
1283 - function onUdpRelayTargetTunnelConnect(data) {
1284 - var peerTunnel = tunnels[this.peerindex];
1285 - peerTunnel.s.write(data);
1286 - }
1287 -
1288 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1289 - function onUdpRelayServerTunnelData(data) {
1290 - if (this.udprelay.first === true) {
1291 - delete this.udprelay.first; // Skip the first 'c' that is received.
1292 - } else {
1293 - this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1294 - }
1295 - }
1296 -
1297 - // Called when the TCP relay target is connected
1298 - function onTcpRelayTargetTunnelConnect() {
1299 - var peerTunnel = tunnels[this.peerindex];
1300 - this.pipe(peerTunnel.s); // Pipe Target --> Server
1301 - peerTunnel.s.first = true;
1302 - peerTunnel.s.resume();
1303 - }
1304 -
1305 - // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1306 - function onTcpRelayServerTunnelData(data) {
1307 - if (this.first == true) {
1308 - this.first = false;
1309 - this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1310 - }
1311 - }
1312 -
1313 - function onTunnelClosed() {
1314 - var tunnel = tunnels[this.httprequest.index];
1315 - if (tunnel == null) return; // Stop duplicate calls.
1316 -
1317 - // If this is a routing session, clean up and send the new session counts.
1318 - if (this.httprequest.userid != null) {
1319 - if (this.httprequest.tcpport != null) {
1320 - if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
1321 - try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1322 - broadcastSessionsToRegisteredApps();
1323 - } else if (this.httprequest.udpport != null) {
1324 - if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
1325 - try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1326 - broadcastSessionsToRegisteredApps();
1327 - }
1328 - }
1329 -
1330 - // Sent tunnel statistics to the server, only send this if compression was used.
1331 - if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1332 - mesh.SendCommand({
1333 - action: 'tunnelCloseStats',
1334 - url: tunnel.url,
1335 - userid: tunnel.userid,
1336 - protocol: tunnel.protocol,
1337 - sessionid: tunnel.sessionid,
1338 - sent: this.bytesSent_uncompressed.toString(),
1339 - sentActual: this.bytesSent_actual.toString(),
1340 - sentRatio: this.bytesSent_ratio,
1341 - received: this.bytesReceived_uncompressed.toString(),
1342 - receivedActual: this.bytesReceived_actual.toString(),
1343 - receivedRatio: this.bytesReceived_ratio
1344 - });
1345 - }
1346 -
1347 - //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1348 - if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1349 -
1350 - /*
1351 - // Close the watcher if required
1352 - if (this.httprequest.watcher != undefined) {
1353 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1354 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1355 - delete this.httprequest.watcher;
1356 - }
1357 - */
1358 -
1359 - // If there is a upload or download active on this connection, close the file
1360 - if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1361 - if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1362 -
1363 - // Clean up WebRTC
1364 - if (this.webrtc != null) {
1365 - 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; }
1366 - if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1367 - try { this.webrtc.close(); } catch (e) { }
1368 - this.webrtc.removeAllListeners('connected');
1369 - this.webrtc.removeAllListeners('disconnected');
1370 - this.webrtc.removeAllListeners('dataChannel');
1371 - delete this.webrtc;
1372 - }
1373 -
1374 - // Clean up WebSocket
1375 - this.removeAllListeners('data');
1376 - }
1377 - function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1378 - function onTunnelData(data) {
1379 - //console.log("OnTunnelData");
1380 - //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1381 -
1382 - // If this is upload data, save it to file
1383 - if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1384 - // Save the data to file being uploaded.
1385 - if (data[0] == 0) {
1386 - // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1387 - try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1388 - } else {
1389 - // If data does not start with zero, save as-is.
1390 - try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1391 - }
1392 - this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1393 - return;
1394 - }
1395 -
1396 - if (this.httprequest.state == 0) {
1397 - // Check if this is a relay connection
1398 - if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1399 - }
1400 - else
1401 - {
1402 - // Handle tunnel data
1403 - if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1404 - // Take a look at the protocol
1405 - if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1406 - this.httprequest.protocol = parseInt(data);
1407 - if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1408 - if (this.httprequest.protocol == 10) {
1409 - //
1410 - // Basic file transfer
1411 - //
1412 - var stats = null;
1413 - if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1414 - try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1415 - try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1416 - if (this.httprequest.downloadFile) {
1417 - //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1418 - this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1419 - this.httprequest.downloadFile.pipe(this);
1420 - this.httprequest.downloadFile.end = function () { }
1421 - } else {
1422 - //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1423 - this.write(JSON.stringify({ op: 'cancel' }));
1424 - }
1425 - }
1426 - else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1427 - {
1428 - //
1429 - // Remote Terminal
1430 - //
1431 -
1432 - // Check user access rights for terminal
1433 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1434 - // Disengage this tunnel, user does not have the rights to do this!!
1435 - this.httprequest.protocol = 999999;
1436 - this.httprequest.s.end();
1437 - sendConsoleText("Error: No Terminal Control Rights.");
1438 - return;
1439 - }
1440 -
1441 - this.descriptorMetadata = "Remote Terminal";
1442 -
1443 - if (process.platform == 'win32')
1444 - {
1445 - if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1446 - {
1447 - this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1448 - this.httprequest.s.end();
1449 - return;
1450 - }
1451 - }
1452 -
1453 - var prom = require('promise');
1454 - this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1455 - this.httprequest.tpromise.that = this;
1456 - this.httprequest.tpromise.httprequest = this.httprequest;
1457 -
1458 - this.end = function ()
1459 - {
1460 - if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1461 - if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1462 -
1463 - // Remove the terminal session to the count to update the server
1464 - if (this.httprequest.userid != null)
1465 - {
1466 - 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]; } }
1467 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1468 - broadcastSessionsToRegisteredApps();
1469 - }
1470 -
1471 - if (process.platform == 'win32')
1472 - {
1473 - // Unpipe the web socket
1474 - this.unpipe(this.httprequest._term);
1475 - if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1476 -
1477 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1478 - if (this.rtcchannel)
1479 - {
1480 - this.rtcchannel.unpipe(this.httprequest._term);
1481 - if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1482 - }
1483 -
1484 - // Clean up
1485 - if (this.httprequest._term) { this.httprequest._term.end(); }
1486 - this.httprequest._term = null;
1487 - }
1488 - };
1489 -
1490 - // Perform User-Consent if needed.
1491 - if (this.httprequest.consent && (this.httprequest.consent & 16))
1492 - {
1493 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1494 - var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1495 - if (this.httprequest.soptions != null) {
1496 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1497 - if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1498 - }
1499 - this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1500 - this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1501 - this.httprequest.tpromise._consent.then(
1502 - function ()
1503 - {
1504 - // Success
1505 - MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1506 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1507 - this.retPromise._consent = null;
1508 - this.retPromise._res();
1509 - },
1510 - function (e)
1511 - {
1512 - // Denied
1513 - MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1514 - this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1515 - this.retPromise._rej(e.toString());
1516 - });
1517 - }
1518 - else
1519 - {
1520 - // User-Consent is not required, so just resolve this promise
1521 - this.httprequest.tpromise._res();
1522 - }
1523 -
1524 -
1525 - this.httprequest.tpromise.then(
1526 - function ()
1527 - {
1528 - this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1529 - this.httprequest.connectionPromise.ws = this.that;
1530 -
1531 - // Start Terminal
1532 - if(process.platform == 'win32')
1533 - {
1534 - try
1535 - {
1536 - var cols = 80, rows = 25;
1537 - if (this.httprequest.xoptions)
1538 - {
1539 - if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1540 - if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1541 - }
1542 -
1543 - if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1544 - {
1545 - // Admin Terminal
1546 - if (require('win-virtual-terminal').supported)
1547 - {
1548 - // ConPTY PseudoTerminal
1549 - // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1550 -
1551 - // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1552 - 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] } });
1553 - this.httprequest._dispatcher.httprequest = this.httprequest;
1554 - this.httprequest._dispatcher.on('connection', function (c)
1555 - {
1556 - if (this.httprequest.connectionPromise.completed)
1557 - {
1558 - c.end();
1559 - }
1560 - else
1561 - {
1562 - this.httprequest.connectionPromise._res(c);
1563 - }
1564 - });
1565 - }
1566 - else
1567 - {
1568 - // Legacy Terminal
1569 - this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1570 - }
1571 - }
1572 - else
1573 - {
1574 - // Logged in user
1575 - var userPromise = require('user-sessions').enumerateUsers();
1576 - userPromise.that = this;
1577 - userPromise.then(function (u)
1578 - {
1579 - var that = this.that;
1580 - if (u.Active.length > 0)
1581 - {
1582 - var username = u.Active[0].Username;
1583 - if (require('win-virtual-terminal').supported)
1584 - {
1585 - // ConPTY PseudoTerminal
1586 - 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] } });
1587 - }
1588 - else
1589 - {
1590 - // Legacy Terminal
1591 - 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] } });
1592 - }
1593 - that.httprequest._dispatcher.ws = that;
1594 - that.httprequest._dispatcher.on('connection', function (c)
1595 - {
1596 - if (this.ws.httprequest.connectionPromise.completed)
1597 - {
1598 - c.end();
1599 - }
1600 - else
1601 - {
1602 - this.ws.httprequest.connectionPromise._res(c);
1603 - }
1604 - });
1605 - }
1606 - });
1607 - }
1608 - }
1609 - catch (e)
1610 - {
1611 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1612 - }
1613 - }
1614 - else
1615 - {
1616 - try
1617 - {
1618 - var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1619 - var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1620 - var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1621 -
1622 - var env = { HISTCONTROL: 'ignoreboth' };
1623 - if (this.httprequest.xoptions)
1624 - {
1625 - if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1626 - if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1627 - }
1628 - var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1629 - if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1630 - {
1631 - if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1632 - this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1633 - }
1634 - else if (bash)
1635 - {
1636 - var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1637 - // Spaces at the beginning of lines are needed to hide commands from the command history
1638 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1639 - this.httprequest.connectionPromise._res(p);
1640 - }
1641 - else if (sh)
1642 - {
1643 - var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1644 - // Spaces at the beginning of lines are needed to hide commands from the command history
1645 - if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1646 - this.httprequest.connectionPromise._res(p);
1647 - }
1648 - else
1649 - {
1650 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1651 - }
1652 - }
1653 - catch (e)
1654 - {
1655 - this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1656 - }
1657 - }
1658 -
1659 - this.httprequest.connectionPromise.then(
1660 - function (term)
1661 - {
1662 - // SUCCESS
1663 - var stdoutstream;
1664 - var stdinstream;
1665 - if (process.platform == 'win32')
1666 - {
1667 - this.ws.httprequest._term = term;
1668 - this.ws.httprequest._term.tunnel = this.ws;
1669 - stdoutstream = stdinstream = term;
1670 - }
1671 - else
1672 - {
1673 - term.descriptorMetadata = 'Remote Terminal';
1674 - this.ws.httprequest.process = term;
1675 - this.ws.httprequest.process.tunnel = this.ws;
1676 - term.stderr.stdout = term.stdout;
1677 - term.stderr.on('data', function (c) { this.stdout.write(c); });
1678 - stdoutstream = term.stdout;
1679 - stdinstream = term.stdin;
1680 - this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1681 - term.prependListener('exit', function () { this.tunnel.end(); });
1682 - }
1683 -
1684 - this.ws.removeAllListeners('data');
1685 - this.ws.on('data', onTunnelControlData);
1686 -
1687 - stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1688 - this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1689 -
1690 - // Add the terminal session to the count to update the server
1691 - if (this.ws.httprequest.userid != null)
1692 - {
1693 - if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1694 - try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1695 - broadcastSessionsToRegisteredApps();
1696 - }
1697 -
1698 - // Toast Notification, if required
1699 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1700 - {
1701 - // User Notifications is required
1702 - var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1703 - if (this.ws.httprequest.soptions != null) {
1704 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1705 - if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1706 - }
1707 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
1708 - }
1709 - },
1710 - function (e)
1711 - {
1712 - // FAILED to connect terminal
1713 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1714 - this.ws.end();
1715 - });
1716 - },
1717 - function (e)
1718 - {
1719 - // DO NOT start terminal
1720 - this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1721 - this.that.end();
1722 - });
1723 - }
1724 - else if (this.httprequest.protocol == 2)
1725 - {
1726 - //
1727 - // Remote KVM
1728 - //
1729 -
1730 - // Check user access rights for desktop
1731 - if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1732 - // Disengage this tunnel, user does not have the rights to do this!!
1733 - this.httprequest.protocol = 999999;
1734 - this.httprequest.s.end();
1735 - sendConsoleText("Error: No Desktop Control Rights.");
1736 - return;
1737 - }
1738 -
1739 - this.descriptorMetadata = "Remote KVM";
1740 -
1741 - // Look for a TSID
1742 - var tsid = null;
1743 - if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1744 - require('MeshAgent')._tsid = tsid;
1745 -
1746 - // Remote desktop using native pipes
1747 - this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1748 - this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1749 - this.desktop = this.httprequest.desktop;
1750 -
1751 - // Add ourself to the list of remote desktop sessions
1752 - if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1753 - this.httprequest.desktop.kvm.tunnels.push(this);
1754 -
1755 - // Send a metadata update to all desktop sessions
1756 - var users = {};
1757 - if (this.httprequest.desktop.kvm.tunnels != null) {
1758 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1759 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1760 - tunnelUserCount.desktop = users;
1761 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1762 - broadcastSessionsToRegisteredApps();
1763 - }
1764 -
1765 - this.end = function () {
1766 - --this.desktop.kvm.connectionCount;
1767 -
1768 - // Remove ourself from the list of remote desktop session
1769 - var i = this.desktop.kvm.tunnels.indexOf(this);
1770 - if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1771 -
1772 - // Send a metadata update to all desktop sessions
1773 - var users = {};
1774 - if (this.httprequest.desktop.kvm.tunnels != null) {
1775 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1776 - for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1777 - tunnelUserCount.desktop = users;
1778 - try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1779 - broadcastSessionsToRegisteredApps();
1780 - }
1781 -
1782 - // Unpipe the web socket
1783 - try
1784 - {
1785 - this.unpipe(this.httprequest.desktop.kvm);
1786 - this.httprequest.desktop.kvm.unpipe(this);
1787 - }
1788 - catch(e) { }
1789 -
1790 - // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1791 - if (this.rtcchannel)
1792 - {
1793 - try
1794 - {
1795 - this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1796 - this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1797 - }
1798 - catch(e) { }
1799 - }
1800 -
1801 - // Place wallpaper back if needed
1802 - // TODO
1803 -
1804 - if (this.desktop.kvm.connectionCount == 0) {
1805 - // Display a toast message. This may not be supported on all platforms.
1806 - // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
1807 -
1808 - this.httprequest.desktop.kvm.end();
1809 - if (this.httprequest.desktop.kvm.connectionBar) {
1810 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1811 - this.httprequest.desktop.kvm.connectionBar.close();
1812 - this.httprequest.desktop.kvm.connectionBar = null;
1813 - }
1814 - } else {
1815 - for (var i in this.httprequest.desktop.kvm.users) {
1816 - if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
1817 - for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
1818 - this.httprequest.desktop.kvm.users.splice(i, 1);
1819 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1820 - this.httprequest.desktop.kvm.connectionBar.close();
1821 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1822 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1823 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1824 - MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1825 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1826 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1827 - }
1828 - this.httprequest.desktop.kvm.end();
1829 - });
1830 - break;
1831 - }
1832 - }
1833 - }
1834 - };
1835 - if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1836 - this.httprequest.desktop.kvm.connectionCount++;
1837 - this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
1838 - this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1839 - this.httprequest.desktop.kvm.rusers.sort();
1840 - this.httprequest.desktop.kvm.users.sort();
1841 - } else {
1842 - this.httprequest.desktop.kvm.connectionCount = 1;
1843 - this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
1844 - this.httprequest.desktop.kvm.users = [this.httprequest.username];
1845 - }
1846 -
1847 - if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1848 - // If we have remote control rights, pipe the KVM input
1849 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1850 - } else {
1851 - // We need to only pipe non-mouse & non-keyboard inputs.
1852 - //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1853 - // TODO!!!
1854 - }
1855 -
1856 - // Perform notification if needed. Toast messages may not be supported on all platforms.
1857 - if (this.httprequest.consent && (this.httprequest.consent & 8))
1858 - {
1859 - // User Consent Prompt is required
1860 - // Send a console message back using the console channel, "\n" is supported.
1861 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1862 - var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1863 - if (this.httprequest.soptions != null) {
1864 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1865 - if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1866 - }
1867 - var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1868 - pr.ws = this;
1869 - this.pause();
1870 - this._consentpromise = pr;
1871 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1872 - pr.then(
1873 - function ()
1874 - {
1875 - // Success
1876 - this.ws._consentpromise = null;
1877 - MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1878 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1879 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1880 - // User Notifications is required
1881 - var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1882 - if (this.ws.httprequest.soptions != null) {
1883 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1884 - if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1885 - }
1886 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1887 - }
1888 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1889 - // Connection Bar is required
1890 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1891 - this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1892 - this.ws.httprequest.desktop.kvm.connectionBar.close();
1893 - }
1894 - try {
1895 - this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1896 - MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1897 - }
1898 - catch (e) {
1899 - if (process.platform != 'darwin') {
1900 - MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1901 - }
1902 - }
1903 - if (this.ws.httprequest.desktop.kvm.connectionBar) {
1904 - this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1905 - this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1906 - MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1907 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1908 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1909 - }
1910 - this.httprequest.desktop.kvm.end();
1911 - });
1912 - }
1913 - }
1914 - this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1915 - this.ws.resume();
1916 - },
1917 - function (e)
1918 - {
1919 - // User Consent Denied/Failed
1920 - this.ws._consentpromise = null;
1921 - MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1922 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1923 - });
1924 - } else {
1925 - // User Consent Prompt is not required
1926 - if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1927 - // User Notifications is required
1928 - MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1929 - var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1930 - if (this.httprequest.soptions != null) {
1931 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1932 - if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1933 - }
1934 - try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1935 - } else {
1936 - MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1937 - }
1938 - if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1939 - // Connection Bar is required
1940 - if (this.httprequest.desktop.kvm.connectionBar) {
1941 - this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1942 - this.httprequest.desktop.kvm.connectionBar.close();
1943 - }
1944 - try {
1945 - this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
1946 - MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1947 - }
1948 - catch (e) {
1949 - MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1950 - }
1951 - if (this.httprequest.desktop.kvm.connectionBar) {
1952 - this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1953 - this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1954 - MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1955 - for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1956 - this.httprequest.desktop.kvm._pipedStreams[i].end();
1957 - }
1958 - this.httprequest.desktop.kvm.end();
1959 - });
1960 - }
1961 - }
1962 - this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1963 - }
1964 -
1965 - this.removeAllListeners('data');
1966 - this.on('data', onTunnelControlData);
1967 - //this.write('MeshCore KVM Hello!1');
1968 -
1969 - } else if (this.httprequest.protocol == 5) {
1970 - //
1971 - // Remote Files
1972 - //
1973 -
1974 - // Check user access rights for files
1975 - if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
1976 - // Disengage this tunnel, user does not have the rights to do this!!
1977 - this.httprequest.protocol = 999999;
1978 - this.httprequest.s.end();
1979 - sendConsoleText("Error: No files control rights.");
1980 - return;
1981 - }
1982 -
1983 - this.descriptorMetadata = "Remote Files";
1984 -
1985 - // Add the files session to the count to update the server
1986 - if (this.httprequest.userid != null) {
1987 - if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
1988 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
1989 - broadcastSessionsToRegisteredApps();
1990 - }
1991 -
1992 - this.end = function () {
1993 - // Remove the files session from the count to update the server
1994 - if (this.httprequest.userid != null) {
1995 - 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]; } }
1996 - try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
1997 - broadcastSessionsToRegisteredApps();
1998 - }
1999 - };
2000 -
2001 - // Perform notification if needed. Toast messages may not be supported on all platforms.
2002 - if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2003 - // User Consent Prompt is required
2004 - // Send a console message back using the console channel, "\n" is supported.
2005 - this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2006 - var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2007 - if (this.httprequest.soptions != null) {
2008 - if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2009 - if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2010 - }
2011 - var pr = require('message-box').create(consentTitle, consentMessage, 30);
2012 - pr.ws = this;
2013 - this.pause();
2014 - this._consentpromise = pr;
2015 - this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2016 - pr.then(
2017 - function ()
2018 - {
2019 - // Success
2020 - this.ws._consentpromise = null;
2021 - MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2022 - this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2023 - if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2024 - // User Notifications is required
2025 - var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2026 - if (this.ws.httprequest.soptions != null) {
2027 - if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2028 - if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2029 - }
2030 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2031 - }
2032 - this.ws.resume();
2033 - },
2034 - function (e)
2035 - {
2036 - // User Consent Denied/Failed
2037 - this.ws._consentpromise = null;
2038 - MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2039 - this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2040 - });
2041 - } else {
2042 - // User Consent Prompt is not required
2043 - if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2044 - // User Notifications is required
2045 - MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2046 - var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2047 - if (this.httprequest.soptions != null) {
2048 - if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2049 - if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2050 - }
2051 - try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2052 - } else {
2053 - MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2054 - }
2055 - this.resume();
2056 - }
2057 -
2058 - // Setup files
2059 - // NOP
2060 - }
2061 - } else if (this.httprequest.protocol == 1) {
2062 - // Send data into terminal stdin
2063 - //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2064 - } else if (this.httprequest.protocol == 2) {
2065 - // Send data into remote desktop
2066 - if (this.httprequest.desktop.state == 0) {
2067 - this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2068 - this.httprequest.desktop.state = 1;
2069 - } else {
2070 - this.httprequest.desktop.write(data);
2071 - }
2072 - } else if (this.httprequest.protocol == 5) {
2073 - // Process files commands
2074 - var cmd = null;
2075 - try { cmd = JSON.parse(data); } catch (e) { };
2076 - if (cmd == null) { return; }
2077 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2078 - if (cmd.action == undefined) { return; }
2079 - //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2080 -
2081 - if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2082 - //console.log(objToString(cmd, 0, ' '));
2083 - switch (cmd.action) {
2084 - case 'ls': {
2085 - /*
2086 - // Close the watcher if required
2087 - var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2088 - if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2089 - //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2090 - //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2091 - delete this.httprequest.watcher;
2092 - }
2093 - */
2094 -
2095 - // Send the folder content to the browser
2096 - var response = getDirectoryInfo(cmd.path);
2097 - if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2098 - this.write(Buffer.from(JSON.stringify(response)));
2099 -
2100 - /*
2101 - // Start the directory watcher
2102 - if ((cmd.path != '') && (samepath == false)) {
2103 - var watcher = fs.watch(cmd.path, onFileWatcher);
2104 - watcher.tunnel = this.httprequest;
2105 - watcher.path = cmd.path;
2106 - this.httprequest.watcher = watcher;
2107 - //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2108 - }
2109 - */
2110 - break;
2111 - }
2112 - case 'mkdir': {
2113 - // Create a new empty folder
2114 - fs.mkdirSync(cmd.path);
2115 - MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2116 - break;
2117 - }
2118 - case 'rm': {
2119 - // Delete, possibly recursive delete
2120 - for (var i in cmd.delfiles) {
2121 - var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2122 - try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2123 - if ((delcount == 1) && !cmd.rec) {
2124 - MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2125 - } else {
2126 - if (cmd.rec) {
2127 - MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2128 - } else {
2129 - MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2130 - }
2131 - }
2132 - }
2133 - break;
2134 - }
2135 - case 'markcoredump': {
2136 - // If we are asking for the coredump file, set the right path.
2137 - var coreDumpPath = null;
2138 - if (process.platform == 'win32') {
2139 - if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2140 - } else {
2141 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2142 - }
2143 - if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2144 - break;
2145 - }
2146 - case 'rename': {
2147 - // Rename a file or folder
2148 - var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2149 - var newfullpath = obj.path.join(cmd.path, cmd.newname);
2150 - MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2151 - try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2152 - break;
2153 - }
2154 - case 'download': {
2155 - // Download a file
2156 - var sendNextBlock = 0;
2157 - if (cmd.sub == 'start') { // Setup the download
2158 - if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2159 - if (process.platform == 'win32') {
2160 - if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2161 - } else {
2162 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2163 - }
2164 - }
2165 - MeshServerLogEx(49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2166 - if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2167 - this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2168 - 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; }
2169 - if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2170 - } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2171 - if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2172 - }
2173 - // Send the next download block(s)
2174 - while (sendNextBlock > 0) {
2175 - sendNextBlock--;
2176 - var buf = Buffer.alloc(16384);
2177 - var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2178 - this.filedownload.ptr += len;
2179 - if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2180 - this.write(buf.slice(0, len + 4)); // Write as binary
2181 - }
2182 - break;
2183 - }
2184 - /*
2185 - case 'download': {
2186 - // Packet download of a file, agent to browser
2187 - if (cmd.path == undefined) break;
2188 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2189 - //console.log('Download: ' + filepath);
2190 - try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2191 - this.httprequest.downloadFileId = cmd.reqid;
2192 - this.httprequest.downloadFilePtr = 0;
2193 - if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2194 - break;
2195 - }
2196 - case 'download2': {
2197 - // Stream download of a file, agent to browser
2198 - if (cmd.path == undefined) break;
2199 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2200 - try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2201 - this.httprequest.downloadFile.pipe(this);
2202 - this.httprequest.downloadFile.end = function () { }
2203 - break;
2204 - }
2205 - */
2206 - case 'upload': {
2207 - // Upload a file, browser to agent
2208 - if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2209 - if (cmd.path == undefined) break;
2210 - var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2211 - this.httprequest.uploadFilePath = filepath;
2212 - MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2213 - try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2214 - this.httprequest.uploadFileid = cmd.reqid;
2215 - if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2216 - break;
2217 - }
2218 - case 'uploaddone': {
2219 - // Indicates that an upload is done
2220 - if (this.httprequest.uploadFile) {
2221 - fs.closeSync(this.httprequest.uploadFile);
2222 - this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2223 - delete this.httprequest.uploadFile;
2224 - delete this.httprequest.uploadFileid;
2225 - delete this.httprequest.uploadFilePath;
2226 - }
2227 - break;
2228 - }
2229 - case 'uploadcancel': {
2230 - // Indicates that an upload is canceled
2231 - if (this.httprequest.uploadFile) {
2232 - fs.closeSync(this.httprequest.uploadFile);
2233 - fs.unlinkSync(this.httprequest.uploadFilePath);
2234 - this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2235 - delete this.httprequest.uploadFile;
2236 - delete this.httprequest.uploadFileid;
2237 - delete this.httprequest.uploadFilePath;
2238 - }
2239 - break;
2240 - }
2241 - case 'copy': {
2242 - // Copy a bunch of files from scpath to dspath
2243 - for (var i in cmd.names) {
2244 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2245 - MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2246 - if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2247 - }
2248 - break;
2249 - }
2250 - case 'move': {
2251 - // Move a bunch of files from scpath to dspath
2252 - for (var i in cmd.names) {
2253 - var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2254 - MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2255 - if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2256 - }
2257 - break;
2258 - }
2259 - case 'zip':
2260 - // Zip a bunch of files
2261 - if (this.zip != null) return; // Zip operating is currently running, exit now.
2262 -
2263 - // Check that the specified files exist & build full paths
2264 - var fp, stat, p = [];
2265 - for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (e) { } if (stat != null) { p.push(fp); } }
2266 - if (p.length == 0) return; // No files, quit now.
2267 -
2268 - // Setup file compression
2269 - var ofile = cmd.path + '/' + cmd.output;
2270 - this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2271 - this.zipfile = ofile;
2272 - delete this.zipcancel;
2273 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2274 - out.xws = this;
2275 - out.on('close', function () {
2276 - this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2277 - this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2278 - if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2279 - delete this.xws.zipcancel;
2280 - delete this.xws.zipfile;
2281 - delete this.xws.zip;
2282 - });
2283 - this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2284 - this.zip.xws = this;
2285 - this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
2286 - this.zip.pipe(out);
2287 - break;
2288 - case 'cancel':
2289 - // Cancel zip operation if present
2290 - try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2291 - this.zip = null;
2292 - break;
2293 - default:
2294 - // Unknown action, ignore it.
2295 - break;
2296 - }
2297 - } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2298 - var cmd = null;
2299 - try { cmd = JSON.parse(data); } catch (e) { };
2300 - if (cmd == null) { return; }
2301 - if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2302 - if (cmd.action == undefined) return;
2303 -
2304 - switch (cmd.action) {
2305 - case 'plugin': {
2306 - try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2307 - break;
2308 - }
2309 - default: {
2310 - // probably shouldn't happen, but just in case this feature is expanded
2311 - }
2312 - }
2313 -
2314 - }
2315 - //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2316 - }
2317 - }
2318 -
2319 - // Delete a directory with a files and directories within it
2320 - function deleteFolderRecursive(path, rec) {
2321 - var count = 0;
2322 - if (fs.existsSync(path)) {
2323 - if (rec == true) {
2324 - fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2325 - var curPath = obj.path.join(path, file);
2326 - if (fs.statSync(curPath).isDirectory()) { // recurse
2327 - count += deleteFolderRecursive(curPath, true);
2328 - } else { // delete file
2329 - fs.unlinkSync(curPath);
2330 - count++;
2331 - }
2332 - });
2333 - }
2334 - fs.unlinkSync(path);
2335 - count++;
2336 - }
2337 - return count;
2338 - };
2339 -
2340 - // Called when receiving control data on WebRTC
2341 - function onTunnelWebRTCControlData(data) {
2342 - if (typeof data != 'string') return;
2343 - var obj;
2344 - try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2345 - if (obj.type == 'close') {
2346 - //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2347 - try { this.close(); } catch (e) { }
2348 - try { this.xrtc.close(); } catch (e) { }
2349 - }
2350 - }
2351 -
2352 - // Called when receiving control data on websocket
2353 - function onTunnelControlData(data, ws) {
2354 - var obj;
2355 - if (ws == null) { ws = this; }
2356 - if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2357 - else if (typeof data == 'object') { obj = data; } else { return; }
2358 - //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2359 - //console.log('onTunnelControlData: ' + JSON.stringify(data));
2360 -
2361 - if (obj.action) {
2362 - switch (obj.action) {
2363 - case 'lock': {
2364 - // Lock the current user out of the desktop
2365 - try {
2366 - if (process.platform == 'win32') {
2367 - MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2368 - var child = require('child_process');
2369 - child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2370 - }
2371 - } catch (e) { }
2372 - break;
2373 - }
2374 - default:
2375 - // Unknown action, ignore it.
2376 - break;
2377 - }
2378 - return;
2379 - }
2380 -
2381 - switch (obj.type) {
2382 - case 'options': {
2383 - // These are additional connection options passed in the control channel.
2384 - //sendConsoleText('options: ' + JSON.stringify(obj));
2385 - delete obj.type;
2386 - ws.httprequest.xoptions = obj;
2387 -
2388 - // Set additional user consent options if present
2389 - if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2390 -
2391 - break;
2392 - }
2393 - case 'close': {
2394 - // We received the close on the websocket
2395 - //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2396 - try { ws.close(); } catch (e) { }
2397 - break;
2398 - }
2399 - case 'termsize': {
2400 - // Indicates a change in terminal size
2401 - if (process.platform == 'win32')
2402 - {
2403 - if (ws.httprequest._dispatcher == null) return;
2404 - //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2405 - if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2406 - } else
2407 - {
2408 - if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2409 - //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2410 -
2411 - if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2412 - }
2413 - break;
2414 - }
2415 - case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2416 - if (ws.httprequest.protocol == 1) { // Terminal
2417 - // 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
2418 - if (process.platform == 'win32') {
2419 - ws.httprequest._term.unpipe(ws);
2420 - } else {
2421 - ws.httprequest.process.stdout.unpipe(ws);
2422 - ws.httprequest.process.stderr.unpipe(ws);
2423 - }
2424 - } else if (ws.httprequest.protocol == 2) { // Desktop
2425 - // 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
2426 - ws.httprequest.desktop.kvm.unpipe(ws);
2427 - } else {
2428 - // Switch things around so all WebRTC data goes to onTunnelData().
2429 - ws.rtcchannel.httprequest = ws.httprequest;
2430 - ws.rtcchannel.removeAllListeners('data');
2431 - ws.rtcchannel.on('data', onTunnelData);
2432 - }
2433 - ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2434 - break;
2435 - }
2436 - case 'webrtc1': {
2437 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2438 - // Switch the user input from websocket to webrtc at this point.
2439 - if (process.platform == 'win32') {
2440 - ws.unpipe(ws.httprequest._term);
2441 - ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2442 - } else {
2443 - ws.unpipe(ws.httprequest.process.stdin);
2444 - ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2445 - }
2446 - ws.resume(); // Resume the websocket to keep receiving control data
2447 - } else if (ws.httprequest.protocol == 2) { // Desktop
2448 - // Switch the user input from websocket to webrtc at this point.
2449 - ws.unpipe(ws.httprequest.desktop.kvm);
2450 - try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2451 - ws.resume(); // Resume the websocket to keep receiving control data
2452 - }
2453 - ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2454 - break;
2455 - }
2456 - case 'webrtc2': {
2457 - // Other side received websocket end of data marker, start sending data on WebRTC channel
2458 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2459 - if (process.platform == 'win32') {
2460 - ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2461 - } else {
2462 - ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2463 - ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2464 - }
2465 - } else if (ws.httprequest.protocol == 2) { // Desktop
2466 - ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2467 - }
2468 - break;
2469 - }
2470 - case 'offer': {
2471 - // This is a WebRTC offer.
2472 - if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2473 - ws.webrtc = rtc.createConnection();
2474 - ws.webrtc.websocket = ws;
2475 - ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2476 - ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2477 - ws.webrtc.on('dataChannel', function (rtcchannel) {
2478 - //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2479 - rtcchannel.maxFragmentSize = 32768;
2480 - rtcchannel.xrtc = this;
2481 - rtcchannel.websocket = this.websocket;
2482 - this.rtcchannel = rtcchannel;
2483 - this.websocket.rtcchannel = rtcchannel;
2484 - this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2485 - this.websocket.rtcchannel.on('end', function () {
2486 - // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2487 - //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2488 - if (this.websocket.desktop && this.websocket.desktop.kvm)
2489 - {
2490 - try
2491 - {
2492 - this.unpipe(this.websocket.desktop.kvm);
2493 - this.websocket.httprequest.desktop.kvm.unpipe(this);
2494 - }
2495 - catch (e) { }
2496 - }
2497 - });
2498 - this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2499 - });
2500 - var sdp = null;
2501 - try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (e) { }
2502 - if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2503 - break;
2504 - }
2505 - case 'ping': {
2506 - ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
2507 - break;
2508 - }
2509 - case 'pong': { // NOP
2510 - break;
2511 - }
2512 - case 'rtt': {
2513 - ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2514 - break;
2515 - }
2516 - }
2517 - }
2518 -
2519 - // Console state
2520 - var consoleWebSockets = {};
2521 - var consoleHttpRequest = null;
2522 -
2523 - // Console HTTP response
2524 - function consoleHttpResponse(response) {
2525 - response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2526 - response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2527 - };
2528 -
2529 - // Open a web browser to a specified URL on current user's desktop
2530 - function openUserDesktopUrl(url) {
2531 - if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
2532 - var child = null;
2533 - try {
2534 - switch (process.platform) {
2535 - case 'win32':
2536 - var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2537 - child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2538 - child.stderr.on('data', function () { });
2539 - child.stdout.on('data', function () { });
2540 - 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');
2541 - child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2542 - child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2543 - child.stdin.write('exit\r\n');
2544 - child.waitExit();
2545 - break;
2546 - case 'linux':
2547 - child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2548 - break;
2549 - case 'darwin':
2550 - child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2551 - break;
2552 - default:
2553 - // Unknown platform, ignore this command.
2554 - break;
2555 - }
2556 - } catch (e) { }
2557 - return child;
2558 - }
2559 -
2560 - // Process a mesh agent console command
2561 - function processConsoleCommand(cmd, args, rights, sessionid) {
2562 - try {
2563 - var response = null;
2564 - switch (cmd) {
2565 - case 'help': { // Displays available commands
2566 - var fin = '', f = '', availcommands = 'amtconfig,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,agentmsg';
2567 - if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2568 - if (process.platform != 'freebsd') { availcommands += ',vm';}
2569 - if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2570 - try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
2571 -
2572 - availcommands = availcommands.split(',').sort();
2573 - while (availcommands.length > 0) {
2574 - if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2575 - f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2576 - }
2577 - if (f != '') { fin += f; }
2578 - response = "Available commands: \r\n" + fin + ".";
2579 - break;
2580 - }
2581 - case 'agentmsg': {
2582 - if (args['_'].length == 0) {
2583 - response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2584 - } else {
2585 - if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
2586 - var msgIndex = 1, iconIndex = 0;
2587 - while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
2588 - if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
2589 - if (typeof iconIndex != 'number') { iconIndex = 0; }
2590 - tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
2591 - response = 'Agent message ' + msgIndex + ' added.';
2592 - } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
2593 - var msgIndex = 0;
2594 - try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
2595 - if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2596 - } else if (args['_'][0] == 'list') {
2597 - response = JSON.stringify(tunnelUserCount.msg, null, 2);
2598 - }
2599 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2600 - broadcastSessionsToRegisteredApps();
2601 - }
2602 - break;
2603 - }
2604 - case 'clearagentmsg': {
2605 - tunnelUserCount.msg = {};
2606 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2607 - broadcastSessionsToRegisteredApps();
2608 - break;
2609 - }
2610 - case 'coredump':
2611 - if (args['_'].length != 1) {
2612 - response = "Proper usage: coredump on|off|status|clear"; // Display usage
2613 - } else {
2614 - switch (args['_'][0].toLowerCase())
2615 - {
2616 - case 'on':
2617 - process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2618 - response = 'coredump is now on';
2619 - break;
2620 - case 'off':
2621 - process.coreDumpLocation = null;
2622 - response = 'coredump is now off';
2623 - break;
2624 - case 'status':
2625 - response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2626 - if (process.coreDumpLocation != null) {
2627 - if (process.platform == 'win32') {
2628 - if (fs.existsSync(process.coreDumpLocation)) { response += '\r\n CoreDump present at: ' + process.coreDumpLocation; }
2629 - } else {
2630 - if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { response += '\r\n CoreDump present at: ' + process.cwd() + 'core'; }
2631 - }
2632 - }
2633 - break;
2634 - case 'clear':
2635 - db.Put('CoreDumpTime', null);
2636 - response = 'coredump db cleared';
2637 - break;
2638 - default:
2639 - response = "Proper usage: coredump on|off|status"; // Display usage
2640 - break;
2641 - }
2642 - }
2643 - break;
2644 - case 'service':
2645 - if (args['_'].length != 1)
2646 - {
2647 - response = "Proper usage: service status|restart"; // Display usage
2648 - }
2649 - else
2650 - {
2651 - var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2652 - switch(args['_'][0].toLowerCase())
2653 - {
2654 - case 'status':
2655 - response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2656 - break;
2657 - case 'restart':
2658 - if (s.isMe())
2659 - {
2660 - s.restart();
2661 - }
2662 - else
2663 - {
2664 - response = 'Restarting another agent instance is not allowed';
2665 - }
2666 - break;
2667 - default:
2668 - response = "Proper usage: service status|restart"; // Display usage
2669 - break;
2670 - }
2671 - if (process.platform == 'win32') { s.close(); }
2672 - }
2673 - break;
2674 - case 'zip':
2675 - if (args['_'].length == 0) {
2676 - response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2677 - } else {
2678 - var p = args['_'].join(' ').split(',');
2679 - var ofile = p.shift();
2680 - sendConsoleText('Writing ' + ofile + '...');
2681 - var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2682 - out.fname = ofile;
2683 - out.sessionid = sessionid;
2684 - out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2685 - var zip = require('zip-writer').write({ files: p });
2686 - zip.pipe(out);
2687 - }
2688 - break;
2689 - case 'unzip':
2690 - if (args['_'].length == 0) {
2691 - response = "Proper usage: unzip input, destination"; // Display usage
2692 - } else {
2693 - var p = args['_'].join(' ').split(',');
2694 - if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
2695 - var prom = require('zip-reader').read(p[0]);
2696 - prom._dest = p[1];
2697 - prom.self = this;
2698 - prom.sessionid = sessionid;
2699 - prom.then(function (zipped) {
2700 - sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2701 - zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2702 - }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2703 - }
2704 - break;
2705 - case 'setbattery':
2706 - // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2707 - if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2708 - var b = { action: 'battery', state: args['_'][0] };
2709 - if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2710 - require('MeshAgent').SendCommand(b);
2711 - } else {
2712 - require('MeshAgent').SendCommand({ action: 'battery' });
2713 - }
2714 - break;
2715 - case 'fdsnapshot':
2716 - require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2717 - break;
2718 - case 'fdcount':
2719 - require('DescriptorEvents').getDescriptorCount().then(
2720 - function (c)
2721 - {
2722 - sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2723 - }, function (e)
2724 - {
2725 - sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2726 - }).parentPromise.sessionid = sessionid;
2727 - break;
2728 - case 'uac':
2729 - if (process.platform != 'win32')
2730 - {
2731 - response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2732 - break;
2733 - }
2734 - if (args['_'].length != 1)
2735 - {
2736 - response = 'Proper usage: uac [get|interactive|secure]';
2737 - }
2738 - else
2739 - {
2740 - switch(args['_'][0].toUpperCase())
2741 - {
2742 - case 'GET':
2743 - var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2744 - response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2745 - break;
2746 - case 'INTERACTIVE':
2747 - try
2748 - {
2749 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2750 - response = 'UAC mode changed to: Interactive Desktop';
2751 - }
2752 - catch (e)
2753 - {
2754 - response = "Unable to change UAC Mode";
2755 - }
2756 - break;
2757 - case 'SECURE':
2758 - try
2759 - {
2760 - require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2761 - response = 'UAC mode changed to: Secure Desktop';
2762 - }
2763 - catch(e)
2764 - {
2765 - response = "Unable to change UAC Mode";
2766 - }
2767 - break;
2768 - default:
2769 - response = 'Proper usage: uac [get|interactive|secure]';
2770 - break;
2771 - }
2772 - }
2773 - break;
2774 - case 'vm':
2775 - response = 'Virtual Machine = ' + require('identifiers').isVM();
2776 - break;
2777 - case 'startupoptions':
2778 - response = JSON.stringify(require('MeshAgent').getStartupOptions());
2779 - break;
2780 - case 'kvmmode':
2781 - if (require('MeshAgent').maxKvmTileSize == null)
2782 - {
2783 - response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2784 - }
2785 - else
2786 - {
2787 - if(require('MeshAgent').maxKvmTileSize == 0)
2788 - {
2789 - response = 'KVM Mode: Full JUMBO';
2790 - }
2791 - else
2792 - {
2793 - response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2794 - response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2795 - }
2796 - }
2797 - break;
2798 - case 'alert':
2799 - if (args['_'].length == 0)
2800 - {
2801 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2802 - }
2803 - else
2804 - {
2805 - var p = args['_'].join(' ').split(',');
2806 - if(p.length<2)
2807 - {
2808 - response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2809 - }
2810 - else
2811 - {
2812 - this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2813 - }
2814 - }
2815 - break;
2816 - case 'agentsize':
2817 - var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2818 - if (process.platform == 'win32') {
2819 - // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2820 - var writtenSize = 0;
2821 - try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
2822 - if (writtenSize != actualSize) {
2823 - response = "Size updated from: " + writtenSize + " to: " + actualSize;
2824 - try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
2825 - } else { response = "Agent Size: " + actualSize + " kb"; }
2826 - } else { response = "Agent Size: " + actualSize + " kb"; }
2827 - break;
2828 - case 'versions':
2829 - response = JSON.stringify(process.versions, null, ' ');
2830 - break;
2831 - case 'wpfhwacceleration':
2832 - if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2833 - if (args['_'].length != 1) {
2834 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2835 - }
2836 - else {
2837 - var reg = require('win-registry');
2838 - var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2839 - var key = reg.usernameToUserKey(uname);
2840 -
2841 - switch (args['_'][0].toUpperCase()) {
2842 - default:
2843 - response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2844 - break;
2845 - case 'ON':
2846 - try {
2847 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2848 - response = "OK";
2849 - } catch (e) { response = "FAILED"; }
2850 - break;
2851 - case 'OFF':
2852 - try {
2853 - reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2854 - response = 'OK';
2855 - } catch (e) { response = 'FAILED'; }
2856 - break;
2857 - case 'STATUS':
2858 - var s;
2859 - try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (e) { s = 'DEFAULT'; }
2860 - response = "WPF Hardware Acceleration: " + s;
2861 - break;
2862 - case 'DEFAULT':
2863 - try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (e) { }
2864 - response = 'OK';
2865 - break;
2866 - }
2867 - }
2868 - break;
2869 - case 'tsid':
2870 - if (process.platform == 'win32') {
2871 - if (args['_'].length != 1) {
2872 - response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2873 - } else {
2874 - var i = parseInt(args['_'][0]);
2875 - require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2876 - response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2877 - }
2878 - } else { response = "TSID command only supported on Windows"; }
2879 - break;
2880 - case 'activeusers':
2881 - if (process.platform == 'win32') {
2882 - var p = require('user-sessions').enumerateUsers();
2883 - p.sessionid = sessionid;
2884 - p.then(function (u) {
2885 - var v = [];
2886 - for (var i in u) {
2887 - if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2888 - }
2889 - sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2890 - });
2891 - } else { response = "activeusers command only supported on Windows"; }
2892 - break;
2893 - case 'wallpaper':
2894 - if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2895 - response = "wallpaper command not supported on this platform";
2896 - }
2897 - else {
2898 - if (args['_'].length != 1) {
2899 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2900 - }
2901 - else {
2902 - switch (args['_'][0].toUpperCase()) {
2903 - default:
2904 - response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2905 - break;
2906 - case 'GET':
2907 - case 'TOGGLE':
2908 - if (process.platform == 'win32') {
2909 - var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2910 - var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2911 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2912 - child.stderr.on('data', function () { });
2913 - child.waitExit();
2914 - var current = child.stdout.str.trim();
2915 - if (args['_'][0].toUpperCase() == 'GET') {
2916 - response = current;
2917 - break;
2918 - }
2919 - if (current != '') {
2920 - require('MeshAgent')._wallpaper = current;
2921 - response = 'Wallpaper cleared';
2922 - } else {
2923 - response = 'Wallpaper restored';
2924 - }
2925 - child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2926 - child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2927 - child.stderr.on('data', function () { });
2928 - child.waitExit();
2929 - }
2930 - else {
2931 - var id = require('user-sessions').consoleUid();
2932 - var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2933 - if (args['_'][0].toUpperCase() == 'GET') {
2934 - response = current;
2935 - break;
2936 - }
2937 - if (current != '/dev/null') {
2938 - require('MeshAgent')._wallpaper = current;
2939 - response = 'Wallpaper cleared';
2940 - } else {
2941 - response = 'Wallpaper restored';
2942 - }
2943 - require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2944 - }
2945 - break;
2946 - }
2947 - }
2948 - }
2949 - break;
2950 - case 'safemode':
2951 - if (process.platform != 'win32') {
2952 - response = 'safemode only supported on Windows Platforms'
2953 - }
2954 - else {
2955 - if (args['_'].length != 1) {
2956 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2957 - }
2958 - else {
2959 - switch (args['_'][0].toUpperCase()) {
2960 - default:
2961 - response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2962 - break;
2963 - case 'ON':
2964 - require('win-bcd').setKey('safeboot', 'Network');
2965 - require('win-bcd').enableSafeModeService('Mesh Agent');
2966 - break;
2967 - case 'OFF':
2968 - require('win-bcd').deleteKey('safeboot');
2969 - break;
2970 - case 'STATUS':
2971 - var nextboot = require('win-bcd').getKey('safeboot');
2972 - if (nextboot) {
2973 - switch (nextboot) {
2974 - case 'Network':
2975 - case 'network':
2976 - nextboot = 'SAFE_MODE_NETWORK';
2977 - break;
2978 - default:
2979 - nextboot = 'SAFE_MODE';
2980 - break;
2981 - }
2982 - }
2983 - response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
2984 - break;
2985 - }
2986 - }
2987 - }
2988 - break;
2989 - /*
2990 - case 'border':
2991 - {
2992 - if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
2993 - if (meshCoreObj.users.length > 0) {
2994 - obj.borderManager.Start(meshCoreObj.users[0]);
2995 - response = 'Border blinking is on.';
2996 - } else {
2997 - response = 'Cannot turn on border blinking, no logged in users.';
2998 - }
2999 - } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3000 - obj.borderManager.Stop();
3001 - response = 'Border blinking is off.';
3002 - } else {
3003 - response = 'Proper usage: border "on|off"'; // Display correct command usage
3004 - }
3005 - }
3006 - break;
3007 - */
3008 - case 'av':
3009 - if (process.platform == 'win32') {
3010 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3011 - response = JSON.stringify(require('win-info').av(), null, 1);
3012 - } else {
3013 - response = 'Not supported on the platform';
3014 - }
3015 - break;
3016 - case 'log':
3017 - if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
3018 - break;
3019 - case 'getclip':
3020 - if (require('MeshAgent').isService) {
3021 - require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
3022 - } else {
3023 - require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
3024 - }
3025 - break;
3026 - case 'setclip': {
3027 - if (args['_'].length != 1) {
3028 - response = 'Proper usage: setclip "sample text"';
3029 - } else {
3030 - if (require('MeshAgent').isService) {
3031 - require('clipboard').dispatchWrite(args['_'][0]);
3032 - response = 'Setting clipboard to: "' + args['_'][0] + '"';
3033 - }
3034 - else {
3035 - require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3036 - }
3037 - }
3038 - break;
3039 - }
3040 - case 'amtreset': {
3041 - if (amt != null) { amt.reset(); response = 'Done.'; }
3042 - break;
3043 - }
3044 - case 'amtlmsreset': {
3045 - if (amt != null) { amt.lmsreset(); response = 'Done.'; }
3046 - break;
3047 - }
3048 - case 'amtccm': {
3049 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
3050 - if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
3051 - else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
3052 - }
3053 - break;
3054 - }
3055 - case 'amtacm': {
3056 - if (amt == null) { response = 'Intel AMT not supported.'; } else {
3057 - amt.setPolicy({ type: 0 });
3058 - amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
3059 - }
3060 - break;
3061 - }
3062 - case 'amtdeactivate': {
3063 - if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
3064 - break;
3065 - }
3066 - case 'amtpolicy': {
3067 - if (amtPolicy == null) {
3068 - response = 'No Intel(R) AMT policy.';
3069 - } else {
3070 - response = JSON.stringify(amtPolicy);
3071 - }
3072 - break;
3073 - }
3074 - case 'openurl': {
3075 - if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
3076 - else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
3077 - break;
3078 - }
3079 - case 'users': {
3080 - if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
3081 - require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
3082 - break;
3083 - }
3084 - case 'toast': {
3085 - if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
3086 - if (require('MeshAgent')._tsid == null) {
3087 - require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
3088 - }
3089 - else {
3090 - require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
3091 - }
3092 - }
3093 - break;
3094 - }
3095 - case 'setdebug': {
3096 - if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
3097 - else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
3098 - break;
3099 - }
3100 - case 'ps': {
3101 - processManager.getProcesses(function (plist) {
3102 - var x = '';
3103 - for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
3104 - sendConsoleText(x, sessionid);
3105 - });
3106 - break;
3107 - }
3108 - case 'kill': {
3109 - if ((args['_'].length < 1)) {
3110 - response = 'Proper usage: kill [pid]'; // Display correct command usage
3111 - } else {
3112 - process.kill(parseInt(args['_'][0]));
3113 - response = 'Killed process ' + args['_'][0] + '.';
3114 - }
3115 - break;
3116 - }
3117 - case 'smbios': {
3118 - if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
3119 - break;
3120 - }
3121 - case 'rawsmbios': {
3122 - if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
3123 - response = '';
3124 - for (var i in SMBiosTablesRaw) {
3125 - var header = false;
3126 - for (var j in SMBiosTablesRaw[i]) {
3127 - if (SMBiosTablesRaw[i][j].length > 0) {
3128 - if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
3129 - response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
3130 - }
3131 - }
3132 - }
3133 - }
3134 - break;
3135 - }
3136 - case 'eval': { // Eval JavaScript
3137 - if (args['_'].length < 1) {
3138 - response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3139 - } else {
3140 - response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3141 - }
3142 - break;
3143 - }
3144 - case 'uninstallagent': // Uninstall this agent
3145 - var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
3146 - if (!require('service-manager').manager.getService(agentName).isMe()) {
3147 - response = 'Uininstall failed, this instance is not the service instance';
3148 - } else {
3149 - try { diagnosticAgent_uninstall(); } catch (e) { }
3150 - var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
3151 - 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 });
3152 - }
3153 - break;
3154 - case 'notify': { // Send a notification message to the mesh
3155 - if (args['_'].length != 1) {
3156 - response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
3157 - } else {
3158 - var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
3159 - if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
3160 - mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
3161 - response = "ok";
3162 - }
3163 - break;
3164 - }
3165 - case 'cpuinfo': { // Return system information
3166 - // CPU & memory utilization
3167 - pr = require('sysinfo').cpuUtilization();
3168 - pr.sessionid = sessionid;
3169 - pr.then(function (data) {
3170 - sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
3171 - }, function (e) {
3172 - sendConsoleText(e);
3173 - });
3174 - break;
3175 - }
3176 - case 'sysinfo': { // Return system information
3177 - getSystemInformation(function (results, err) {
3178 - if (results == null) { sendConsoleText(err, this.sessionid); } else {
3179 - sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
3180 - }
3181 - });
3182 - break;
3183 - }
3184 - case 'info': { // Return information about the agent and agent core module
3185 - 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 + '.';
3186 - if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
3187 - if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
3188 - response += '\r\nModules: ' + addedModules.join(', ') + '.';
3189 - response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
3190 - response += '\r\lastMeInfo: ' + lastMeInfo + '.';
3191 - var oldNodeId = db.Get('OldNodeId');
3192 - if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
3193 - if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
3194 - break;
3195 - }
3196 - case 'osinfo': { // Return the operating system information
3197 - var i = 1;
3198 - if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
3199 - for (var j = 0; j < i; j++) {
3200 - var pr = require('os').name();
3201 - pr.sessionid = sessionid;
3202 - pr.then(function (v) {
3203 - sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
3204 - });
3205 - }
3206 - break;
3207 - }
3208 - case 'sendcaps': { // Send capability flags to the server
3209 - if (args['_'].length == 0) {
3210 - response = 'Proper usage: sendcaps (number)'; // Display correct command usage
3211 - } else {
3212 - meshCoreObj.caps = parseInt(args['_'][0]);
3213 - mesh.SendCommand(meshCoreObj);
3214 - response = JSON.stringify(meshCoreObj);
3215 - }
3216 - break;
3217 - }
3218 - case 'sendosdesc': { // Send OS description
3219 - if (args['_'].length > 0) {
3220 - meshCoreObj.osdesc = args['_'][0];
3221 - mesh.SendCommand(meshCoreObj);
3222 - response = JSON.stringify(meshCoreObj);
3223 - } else {
3224 - response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
3225 - }
3226 - break;
3227 - }
3228 - case 'args': { // Displays parsed command arguments
3229 - response = 'args ' + objToString(args, 0, ' ', true);
3230 - break;
3231 - }
3232 - case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
3233 - var r = [];
3234 - for (var i in args['_']) { r.push(args['_'][i]); }
3235 - console.log(r.join(' '));
3236 - response = 'Message printed on agent console.';
3237 - break;
3238 - }
3239 - case 'type': { // Returns the content of a file
3240 - if (args['_'].length == 0) {
3241 - response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
3242 - } else {
3243 - var max = 4096;
3244 - if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
3245 - if (max > 4096) max = 4096;
3246 - var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
3247 - response = buf.toString();
3248 - var i = response.indexOf('\n');
3249 - if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
3250 - if (r == max) response += '...';
3251 - fs.closeSync(fd);
3252 - }
3253 - break;
3254 - }
3255 - case 'dbkeys': { // Return all data store keys
3256 - response = JSON.stringify(db.Keys);
3257 - break;
3258 - }
3259 - case 'dbget': { // Return the data store value for a given key
3260 - if (db == null) { response = 'Database not accessible.'; break; }
3261 - if (args['_'].length != 1) {
3262 - response = 'Proper usage: dbget (key)'; // Display the value for a given database key
3263 - } else {
3264 - response = db.Get(args['_'][0]);
3265 - }
3266 - break;
3267 - }
3268 - case 'dbset': { // Set a data store key and value pair
3269 - if (db == null) { response = 'Database not accessible.'; break; }
3270 - if (args['_'].length != 2) {
3271 - response = 'Proper usage: dbset (key) (value)'; // Set a database key
3272 - } else {
3273 - var r = db.Put(args['_'][0], args['_'][1]);
3274 - response = 'Key set: ' + r;
3275 - }
3276 - break;
3277 - }
3278 - case 'dbcompact': { // Compact the data store
3279 - if (db == null) { response = 'Database not accessible.'; break; }
3280 - var r = db.Compact();
3281 - response = 'Database compacted: ' + r;
3282 - break;
3283 - }
3284 - case 'httpget': {
3285 - if (consoleHttpRequest != null) {
3286 - response = 'HTTP operation already in progress.';
3287 - } else {
3288 - if (args['_'].length != 1) {
3289 - response = 'Proper usage: httpget (url)';
3290 - } else {
3291 - var options = http.parseUri(args['_'][0]);
3292 - options.method = 'GET';
3293 - if (options == null) {
3294 - response = 'Invalid url.';
3295 - } else {
3296 - try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
3297 - consoleHttpRequest.sessionid = sessionid;
3298 - if (consoleHttpRequest != null) {
3299 - consoleHttpRequest.end();
3300 - response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
3301 - }
3302 - }
3303 - }
3304 - }
3305 - break;
3306 - }
3307 - case 'wslist': { // List all web sockets
3308 - response = '';
3309 - for (var i in consoleWebSockets) {
3310 - var httprequest = consoleWebSockets[i];
3311 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3312 - }
3313 - if (response == '') { response = 'no websocket sessions.'; }
3314 - break;
3315 - }
3316 - case 'wsconnect': { // Setup a web socket
3317 - if (args['_'].length == 0) {
3318 - response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3319 - } else {
3320 - var httprequest = null;
3321 - try {
3322 - var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3323 - options.rejectUnauthorized = 0;
3324 - httprequest = http.request(options);
3325 - } catch (e) { response = 'Invalid HTTP websocket request'; }
3326 - if (httprequest != null) {
3327 - httprequest.upgrade = onWebSocketUpgrade;
3328 - httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3329 -
3330 - var index = 1;
3331 - while (consoleWebSockets[index]) { index++; }
3332 - httprequest.sessionid = sessionid;
3333 - httprequest.index = index;
3334 - httprequest.url = args['_'][0];
3335 - consoleWebSockets[index] = httprequest;
3336 - response = 'New websocket session #' + index;
3337 - }
3338 - }
3339 - break;
3340 - }
3341 - case 'wssend': { // Send data on a web socket
3342 - if (args['_'].length == 0) {
3343 - response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3344 - for (var i in consoleWebSockets) {
3345 - var httprequest = consoleWebSockets[i];
3346 - response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3347 - }
3348 - } else {
3349 - var i = parseInt(args['_'][0]);
3350 - var httprequest = consoleWebSockets[i];
3351 - if (httprequest != undefined) {
3352 - httprequest.s.write(args['_'][1]);
3353 - response = 'ok';
3354 - } else {
3355 - response = 'Invalid web socket number';
3356 - }
3357 - }
3358 - break;
3359 - }
3360 - case 'wsclose': { // Close a websocket
3361 - if (args['_'].length == 0) {
3362 - response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3363 - } else {
3364 - var i = parseInt(args['_'][0]);
3365 - var httprequest = consoleWebSockets[i];
3366 - if (httprequest != undefined) {
3367 - if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3368 - response = 'ok';
3369 - } else {
3370 - response = 'Invalid web socket number';
3371 - }
3372 - }
3373 - break;
3374 - }
3375 - case 'tunnels': { // Show the list of current tunnels
3376 - response = '';
3377 - for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3378 - if (response == '') { response = 'No websocket sessions.'; }
3379 - break;
3380 - }
3381 - case 'ls': { // Show list of files and folders
3382 - response = '';
3383 - var xpath = '*';
3384 - if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3385 - response = 'List of ' + xpath + '\r\n';
3386 - var results = fs.readdirSync(xpath);
3387 - for (var i = 0; i < results.length; ++i) {
3388 - var stat = null, p = obj.path.join(args['_'][0], results[i]);
3389 - try { stat = fs.statSync(p); } catch (e) { }
3390 - if ((stat == null) || (stat == undefined)) {
3391 - response += (results[i] + "\r\n");
3392 - } else {
3393 - response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3394 - }
3395 - }
3396 - break;
3397 - }
3398 - case 'lsx': { // Show list of files and folders
3399 - response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3400 - break;
3401 - }
3402 - case 'lock': { // Lock the current user out of the desktop
3403 - 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'; }
3404 - else { response = 'Not supported on the platform'; }
3405 - break;
3406 - }
3407 - case 'amt': { // Show Intel AMT status
3408 - if (amt != null) {
3409 - amt.getAmtInfo(function (state) {
3410 - var resp = 'Intel AMT not detected.';
3411 - if (state != null) { resp = objToString(state, 0, ' ', true); }
3412 - sendConsoleText(resp, sessionid);
3413 - });
3414 - } else {
3415 - response = 'Intel AMT not detected.';
3416 - }
3417 - break;
3418 - }
3419 - case 'netinfo': { // Show network interface information
3420 - var interfaces = require('os').networkInterfaces();
3421 - response = objToString(interfaces, 0, ' ', true);
3422 - break;
3423 - }
3424 - case 'wakeonlan': { // Send wake-on-lan
3425 - if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3426 - response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3427 - } else {
3428 - var count = sendWakeOnLan(args['_'][0]);
3429 - response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3430 - }
3431 - break;
3432 - }
3433 - case 'sendall': { // Send a message to all consoles on this mesh
3434 - sendConsoleText(args['_'].join(' '));
3435 - break;
3436 - }
3437 - case 'power': { // Execute a power action on this computer
3438 - if (mesh.ExecPowerState == undefined) {
3439 - response = 'Power command not supported on this agent.';
3440 - } else {
3441 - if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3442 - 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
3443 - } else {
3444 - var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3445 - response = 'Power action executed with return code: ' + r + '.';
3446 - }
3447 - }
3448 - break;
3449 - }
3450 - case 'location': {
3451 - getIpLocationData(function (location) {
3452 - sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3453 - });
3454 - break;
3455 - }
3456 - case 'parseuri': {
3457 - response = JSON.stringify(http.parseUri(args['_'][0]));
3458 - break;
3459 - }
3460 - case 'scanwifi': {
3461 - if (wifiScanner != null) {
3462 - var wifiPresent = wifiScanner.hasWireless;
3463 - if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3464 - } else { response = "Wifi module not present."; }
3465 - break;
3466 - }
3467 - case 'scanamt': {
3468 - if (amtscanner != null) {
3469 - if (args['_'].length != 1) {
3470 - 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
3471 - } else {
3472 - response = 'Scanning: ' + args['_'][0] + '...';
3473 - amtscanner.scan(args['_'][0], 2000, function (data) {
3474 - if (data.length > 0) {
3475 - var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3476 - for (var i in data) {
3477 - var x = data[i];
3478 - if (r != '') { r += '\r\n'; }
3479 - r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3480 - if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3481 - if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3482 - r += '.';
3483 - }
3484 - } else {
3485 - r = 'No Intel AMT found.';
3486 - }
3487 - sendConsoleText(r);
3488 - });
3489 - }
3490 - } else { response = "Intel AMT scanner module not present."; }
3491 - break;
3492 - }
3493 - case 'modules': {
3494 - response = JSON.stringify(addedModules);
3495 - break;
3496 - }
3497 - case 'listservices': {
3498 - var services = require('service-manager').manager.enumerateService();
3499 - response = JSON.stringify(services, null, 1);
3500 - break;
3501 - }
3502 - case 'getscript': {
3503 - if (args['_'].length != 1) {
3504 - response = "Proper usage: getscript [scriptNumber].";
3505 - } else {
3506 - mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3507 - }
3508 - break;
3509 - }
3510 - case 'diagnostic':
3511 - {
3512 - if (!mesh.DAIPC.listening) {
3513 - response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3514 - break;
3515 - }
3516 - var diag = diagnosticAgent_installCheck();
3517 - if (diag) {
3518 - if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3519 - diagnosticAgent_uninstall();
3520 - response = 'Diagnostic Agent uninstalled';
3521 - }
3522 - else {
3523 - response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3524 - }
3525 - }
3526 - else {
3527 - if (args['_'].length == 1 && args['_'][0] == 'install') {
3528 - diag = diagnosticAgent_installCheck(true);
3529 - if (diag) {
3530 - response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3531 - }
3532 - else {
3533 - response = 'Diagnostic agent installation failed';
3534 - }
3535 - }
3536 - else {
3537 - response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3538 - }
3539 - }
3540 - if (diag) { diag.close(); diag = null; }
3541 - break;
3542 - }
3543 - case 'amtconfig': {
3544 - if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
3545 - if (amt == null) { response = "No Intel AMT support delected"; break; }
3546 - getMeiState(15, function (state) {
3547 - var rx = '';
3548 - if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
3549 - var apfarg = {
3550 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3551 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3552 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3553 - mpskeepalive: 60000,
3554 - clientname: state.OsHostname,
3555 - clientaddress: '127.0.0.1',
3556 - clientuuid: state.UUID,
3557 - conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3558 - meiState: state // MEI state will be passed to MPS server
3559 - };
3560 - if ((state.UUID == null) || (state.UUID.length != 36)) {
3561 - rx = "Unable to get Intel AMT UUID";
3562 - } else {
3563 - apftunnel = require('apfclient')({ debug: false }, apfarg);
3564 - apftunnel.onJsonControl = function (data) {
3565 - if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
3566 - if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3567 - if (data.action == 'deactivate') { // Request CCM deactivation
3568 - var amtMeiModule, amtMei;
3569 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
3570 - amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
3571 - amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
3572 - }
3573 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
3574 - }
3575 - apftunnel.onChannelClosed = function () { apftunnel = null; }
3576 - try {
3577 - apftunnel.connect();
3578 - rx = "Started Intel AMT configuration";
3579 - } catch (ex) {
3580 - rx = JSON.stringify(ex);
3581 - }
3582 - }
3583 - }
3584 - if (rx != '') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: rx }); }
3585 - });
3586 - break;
3587 - }
3588 - case 'apf': {
3589 - if (meshCoreObj.intelamt !== null) {
3590 - if (args['_'].length == 1) {
3591 - var connType = -1, connTypeStr = args['_'][0].toLowerCase();
3592 - if (connTypeStr == 'lms') { connType = 2; }
3593 - if (connTypeStr == 'relay') { connType = 1; }
3594 - if (connTypeStr == 'cira') { connType = 0; }
3595 - if (connTypeStr == 'off') { connType = -2; }
3596 - if (connType >= 0) { // Connect
3597 - var apfarg = {
3598 - mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3599 - mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3600 - mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3601 - mpskeepalive: 60000,
3602 - clientname: require('os').hostname(),
3603 - clientaddress: '127.0.0.1',
3604 - clientuuid: meshCoreObj.intelamt.uuid,
3605 - conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3606 - };
3607 - if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
3608 - response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
3609 - } else {
3610 - apftunnel = require('apfclient')({ debug: false }, apfarg);
3611 - apftunnel.onJsonControl = function (data) {
3612 - if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
3613 - if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
3614 - }
3615 - apftunnel.onChannelClosed = function () { apftunnel = null; }
3616 - try {
3617 - apftunnel.connect();
3618 - response = "Started APF tunnel";
3619 - } catch (e) {
3620 - response = JSON.stringify(e);
3621 - }
3622 - }
3623 - } else if (connType == -2) { // Disconnect
3624 - try {
3625 - apftunnel.disconnect();
3626 - response = "Stopped APF tunnel";
3627 - } catch (e) {
3628 - response = JSON.stringify(e);
3629 - }
3630 - apftunnel = null;
3631 - } else {
3632 - response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
3633 - }
3634 - } else {
3635 - response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
3636 - }
3637 - } else {
3638 - response = "APF tunnel requires Intel AMT";
3639 - }
3640 - break;
3641 - }
3642 - case 'plugin': {
3643 - if (typeof args['_'][0] == 'string') {
3644 - try {
3645 - // Pass off the action to the plugin
3646 - // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3647 - // to control the output / actions here.
3648 - response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3649 - } catch (e) {
3650 - response = "There was an error in the plugin (" + e + ")";
3651 - }
3652 - } else {
3653 - response = "Proper usage: plugin [pluginName] [args].";
3654 - }
3655 - break;
3656 - }
3657 - default: { // This is an unknown command, return an error message
3658 - response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3659 - break;
3660 - }
3661 - }
3662 - } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3663 - if (response != null) { sendConsoleText(response, sessionid); }
3664 - }
3665 -
3666 - // Send a mesh agent console command
3667 - function sendConsoleText(text, sessionid) {
3668 - if (typeof text == 'object') { text = JSON.stringify(text); }
3669 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3670 - }
3671 -
3672 - // Called before the process exits
3673 - //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3674 -
3675 - // Called when the server connection state changes
3676 - function handleServerConnection(state) {
3677 - meshServerConnectionState = state;
3678 - if (meshServerConnectionState == 0) {
3679 - // Server disconnected
3680 - if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3681 - lastSelfInfo = null;
3682 - } else {
3683 - // Server connected, send mesh core information
3684 - var oldNodeId = db.Get('OldNodeId');
3685 - if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3686 -
3687 - // Update the server with basic info, logged in users and more.
3688 - mesh.SendCommand(meshCoreObj);
3689 -
3690 - // Send SMBios tables if present
3691 - if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3692 -
3693 - // Update the server on more advanced stuff, like Intel ME and Network Settings
3694 - meInfoStr = null;
3695 - sendPeriodicServerUpdate();
3696 - if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3697 -
3698 - // Send any state messages
3699 - if (Object.keys(tunnelUserCount.msg).length > 0) {
3700 - try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
3701 - broadcastSessionsToRegisteredApps();
3702 - }
3703 -
3704 - // Send update of registered applications to the server
3705 - updateRegisteredAppsToServer();
3706 - }
3707 -
3708 - // Send server state update to registered applications
3709 - broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
3710 - }
3711 -
3712 - // Update the server with the latest network interface information
3713 - var sendNetworkUpdateNagleTimer = null;
3714 - function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3715 - function sendNetworkUpdate(force) {
3716 - sendNetworkUpdateNagleTimer = null;
3717 -
3718 - // Update the network interfaces information data
3719 - var netInfo = { netif2: require('os').networkInterfaces() };
3720 - if (netInfo.netif2) {
3721 - netInfo.action = 'netinfo';
3722 - var netInfoStr = JSON.stringify(netInfo);
3723 - if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3724 - }
3725 - }
3726 -
3727 - // Called periodically to check if we need to send updates to the server
3728 - function sendPeriodicServerUpdate(flags) {
3729 - if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3730 - if (!flags) { flags = 0xFFFFFFFF; }
3731 -
3732 - if ((flags & 1) && (amt != null)) {
3733 - // If we have a connected MEI, get Intel ME information
3734 - amt.getAmtInfo(function (meinfo) {
3735 - try {
3736 - if (meinfo == null) return;
3737 - var intelamt = {};
3738 - if (amt != null)
3739 - {
3740 - switch(amt.lmsstate)
3741 - {
3742 - case 0: intelamt.microlms = 'DISABLED'; break;
3743 - case 1: intelamt.microlms = 'CONNECTING'; break;
3744 - case 2: intelamt.microlms = 'CONNECTED'; break;
3745 - default: intelamt.microlms = 'unknown'; break;
3746 - }
3747 - }
3748 - var p = false;
3749 - 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); } }
3750 - if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3751 - if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3752 - if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3753 - if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3754 - if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3755 - // Not activated and we have wired ethernet, look for the trusted DNS
3756 - var dns = meinfo.DNS;
3757 - if (dns == null) {
3758 - // Trusted DNS not set, let's look for the OS network DNS suffix
3759 - var interfaces = require('os').networkInterfaces();
3760 - for (var i in interfaces) {
3761 - for (var j in interfaces[i]) {
3762 - if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3763 - }
3764 - }
3765 - }
3766 - if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3767 - } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3768 - if (p == true) {
3769 - var meInfoStr = JSON.stringify(intelamt);
3770 - if (meInfoStr != lastMeInfo) {
3771 - meshCoreObj.intelamt = intelamt;
3772 - mesh.SendCommand(meshCoreObj);
3773 - lastMeInfo = meInfoStr;
3774 - }
3775 - }
3776 - } catch (e) { }
3777 - });
3778 - }
3779 -
3780 - if (flags & 2) {
3781 - // Update network information
3782 - sendNetworkUpdateNagle(false);
3783 - }
3784 -
3785 - if ((flags & 4) && (process.platform == 'win32')) {
3786 - // Update anti-virus information
3787 - // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3788 - var av, pr;
3789 - try { av = require('win-info').av(); } catch (e) { av = null; } // Antivirus
3790 - //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (e) { pr = null; } } // Pending reboot
3791 - if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3792 - }
3793 - }
3794 -
3795 - // Starting function
3796 - obj.start = function () {
3797 - // Setup the mesh agent event handlers
3798 - mesh.AddCommandHandler(handleServerCommand);
3799 - mesh.AddConnectHandler(handleServerConnection);
3800 -
3801 - // Parse input arguments
3802 - //var args = parseArgs(process.argv);
3803 - //console.log(args);
3804 -
3805 - //resetMicroLms();
3806 -
3807 - // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3808 - try {
3809 - var userSession = require('user-sessions');
3810 - userSession.on('changed', function onUserSessionChanged() {
3811 - userSession.enumerateUsers().then(function (users) {
3812 - var u = [], a = users.Active;
3813 - for (var i = 0; i < a.length; i++) {
3814 - var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3815 - if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3816 - }
3817 - meshCoreObj.users = u;
3818 - mesh.SendCommand(meshCoreObj);
3819 - });
3820 - });
3821 - userSession.emit('changed');
3822 - //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3823 - //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3824 - } catch (e) { }
3825 - }
3826 -
3827 - obj.stop = function () {
3828 - mesh.AddCommandHandler(null);
3829 - mesh.AddConnectHandler(null);
3830 - }
3831 -
3832 - function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3833 - function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3834 - function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3835 -
3836 - function onWebSocketUpgrade(response, s, head) {
3837 - sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3838 - this.s = s;
3839 - s.httprequest = this;
3840 - s.end = onWebSocketClosed;
3841 - s.data = onWebSocketData;
3842 - }
3843 -
3844 - // Get Intel MEI State in a flexible way
3845 - // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network
3846 - function getMeiState(flags, func) {
3847 - var amtMeiModule, amtMei;
3848 - try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { func(null); return; }
3849 - amtMei.on('error', function (e) { func(null); return; });
3850 - try {
3851 - var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
3852 - amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
3853 - if ((flags & 1) != 0) { amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); }
3854 - amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
3855 - amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated"
3856 - amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
3857 - amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } }); // Flag 2 = CCM, 4 = ACM
3858 - //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
3859 - if ((flags & 8) != 0) {
3860 - amtMei.getLanInterfaceSettings(0, function (result) {
3861 - if (result) {
3862 - amtMeiTmpState.net0 = result;
3863 - var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
3864 - for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3865 - }
3866 - });
3867 - amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
3868 - }
3869 - amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { amtMeiTmpState.UUID = result.uuid; } });
3870 - if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); }
3871 - amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } });
3872 - if ((flags & 4) != 0) {
3873 - amtMei.getHashHandles(function (handles) {
3874 - if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); }
3875 - var exitOnCount = handles.length;
3876 - for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); }
3877 - });
3878 - }
3879 - } catch (e) { if (func != null) { func(null); } return; }
3880 - }
3881 -
3882 - return obj;
3883 -}
3884 -
3885 -//
3886 -// Module startup
3887 -//
3888 -
3889 -try {
3890 - var xexports = null, mainMeshCore = null;
3891 - try { xexports = module.exports; } catch (e) { }
3892 -
3893 - if (xexports != null) {
3894 - // If we are running within NodeJS, export the core
3895 - module.exports.createMeshCore = createMeshCore;
3896 - } else {
3897 - // If we are not running in NodeJS, launch the core
3898 - mainMeshCore = createMeshCore();
3899 - mainMeshCore.start(null);
3900 - }
3901 -} catch (e) {
3902 - require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3903 -}