Updated Windows agents, meshcore we better pipe support.

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