Intel AMT configuration now automated thru agent.
Ylian Saint-Hilaire committed
Oct 21, 2020 at 15:36 UTC
b04d1b7d4a581e1b3d59653f98a624ebc77fca9b
7 files changed
+8651
-853
agents/meshcore-bad.js
new
+3924
@@ -0,0 +1,3924 @@
1
+/*
2
+Copyright 2018-2020 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
+process.on('uncaughtException', function (ex) {
18
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
19
+});
20
+
21
+// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
22
+//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
23
+
24
+// Mesh Rights
25
+var MNG_ERROR = 65;
26
+var MESHRIGHT_EDITMESH = 1;
27
+var MESHRIGHT_MANAGEUSERS = 2;
28
+var MESHRIGHT_MANAGECOMPUTERS = 4;
29
+var MESHRIGHT_REMOTECONTROL = 8;
30
+var MESHRIGHT_AGENTCONSOLE = 16;
31
+var MESHRIGHT_SERVERFILES = 32;
32
+var MESHRIGHT_WAKEDEVICE = 64;
33
+var MESHRIGHT_SETNOTES = 128;
34
+var MESHRIGHT_REMOTEVIEW = 256;
35
+var MESHRIGHT_NOTERMINAL = 512;
36
+var MESHRIGHT_NOFILES = 1024;
37
+var MESHRIGHT_NOAMT = 2048;
38
+var MESHRIGHT_LIMITEDINPUT = 4096;
39
+var MESHRIGHT_LIMITEVENTS = 8192;
40
+var MESHRIGHT_CHATNOTIFY = 16384;
41
+var MESHRIGHT_UNINSTALL = 32768;
42
+var MESHRIGHT_NODESKTOP = 65536;
43
+
44
+function createMeshCore(agent) {
45
+ var obj = {};
46
+ if (process.platform == 'win32' && require('user-sessions').isRoot()) {
47
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
48
+ try {
49
+ var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
50
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
51
+ if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
52
+ } catch (x) { }
53
+
54
+ // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
55
+ try {
56
+ var meshCheck = false;
57
+ try { meshCheck = require('service-manager').manager.getService('Mesh Agent').isMe(); } catch (e) { }
58
+ if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59
+ } catch (e) { }
60
+ }
61
+
62
+ if (process.platform == 'darwin' && !process.versions) {
63
+ // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
64
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
65
+ child.stdout.str = '';
66
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
67
+ child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); ");
68
+ child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
69
+ child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
70
+ child.waitExit();
71
+ if (child.stdout.str.trim() == 'Crashed') {
72
+ child = require('child_process').execFile('/bin/sh', ['sh']);
73
+ child.stdout.str = '';
74
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
75
+ child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
76
+ child.waitExit();
77
+
78
+ if (parseInt(child.stdout.str.trim()) == process.pid) {
79
+ // The currently running MeshAgent is us, so we can continue with the update
80
+ var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
81
+ var tokens = plist.split('<key>KeepAlive</key>');
82
+ if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
83
+ var tmp = tokens[1].split('</dict>');
84
+ tmp.shift();
85
+ tokens[1] = '\n <true/>' + tmp.join('</dict>');
86
+ tokens = tokens.join('<key>KeepAlive</key>');
87
+
88
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
89
+
90
+ var fix = '';
91
+ fix += ("function macosRepair()\n");
92
+ fix += ("{\n");
93
+ fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
94
+ fix += (" child.stdout.str = '';\n");
95
+ fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
96
+ fix += (" child.stderr.on('data', function (chunk) { });\n");
97
+ fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
98
+ fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
99
+ fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
100
+ fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
101
+ fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
102
+ fix += (" child.waitExit();\n");
103
+ fix += ("}\n");
104
+ fix += ("macosRepair();\n");
105
+ fix += ("process.exit();\n");
106
+ require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
107
+
108
+ var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
109
+ plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
110
+ plist += '<plist version="1.0">\n';
111
+ plist += ' <dict>\n';
112
+ plist += ' <key>Label</key>\n';
113
+ plist += (' <string>meshagentRepair</string>\n');
114
+ plist += ' <key>ProgramArguments</key>\n';
115
+ plist += ' <array>\n';
116
+ plist += (' <string>' + process.execPath + '</string>\n');
117
+ plist += ' <string>macosRepair.js</string>\n';
118
+ plist += ' </array>\n';
119
+ plist += ' <key>WorkingDirectory</key>\n';
120
+ plist += (' <string>' + process.cwd() + '</string>\n');
121
+ plist += ' <key>RunAtLoad</key>\n';
122
+ plist += ' <true/>\n';
123
+ plist += ' </dict>\n';
124
+ plist += '</plist>';
125
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
126
+
127
+ child = require('child_process').execFile('/bin/sh', ['sh']);
128
+ child.stdout.str = '';
129
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
130
+ child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
131
+ child.waitExit();
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // Create Secure IPC for Diagnostic Agent Communications
138
+ obj.DAIPC = require('net').createServer();
139
+ if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (e) { } }
140
+ obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141
+ try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
142
+ obj.DAIPC._daipc = [];
143
+ obj.DAIPC.on('connection', function (c) {
144
+ c._send = function (j) {
145
+ var data = JSON.stringify(j);
146
+ var packet = Buffer.alloc(data.length + 4);
147
+ packet.writeUInt32LE(data.length + 4, 0);
148
+ Buffer.from(data).copy(packet, 4);
149
+ this.write(packet);
150
+ };
151
+ this._daipc.push(c);
152
+ c.parent = this;
153
+ c.on('end', function () { removeRegisteredApp(this); });
154
+ c.on('data', function (chunk) {
155
+ if (chunk.length < 4) { this.unshift(chunk); return; }
156
+ var len = chunk.readUInt32LE(0);
157
+ if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
158
+ if (chunk.length < len) { this.unshift(chunk); return; }
159
+
160
+ var data = chunk.slice(4, len);
161
+ try { data = JSON.parse(data.toString()); } catch (e) { }
162
+ if ((data == null) || (typeof data.cmd != 'string')) return;
163
+
164
+ try {
165
+ switch (data.cmd) {
166
+ case 'requesthelp':
167
+ if (this._registered == null) return;
168
+ sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
169
+ var help = {};
170
+ help[this._registered] = data.value;
171
+ try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
172
+ MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
173
+ break;
174
+ case 'cancelhelp':
175
+ if (this._registered == null) return;
176
+ sendConsoleText('Cancel Help (' + this._registered + ')');
177
+ try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
178
+ break;
179
+ case 'register':
180
+ if (typeof data.value == 'string') {
181
+ this._registered = data.value;
182
+ var apps = {};
183
+ apps[data.value] = 1;
184
+ try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
185
+ this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
186
+ }
187
+ break;
188
+ case 'query':
189
+ switch (data.value) {
190
+ case 'connection':
191
+ data.result = require('MeshAgent').ConnectedServer;
192
+ this._send(data);
193
+ break;
194
+ case 'descriptors':
195
+ require('ChainViewer').getSnapshot().then(function (f) {
196
+ this.tag.payload.result = f;
197
+ this.tag.ipc._send(this.tag.payload);
198
+ }).parentPromise.tag = { ipc: this, payload: data };
199
+ break;
200
+ }
201
+ break;
202
+ case 'amtstate':
203
+ if (amt == null) return;
204
+ var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
205
+ func.pipe = this;
206
+ amt.getAmtInfo(func);
207
+ break;
208
+ case 'sessions':
209
+ this._send({ cmd: 'sessions', sessions: tunnelUserCount });
210
+ break;
211
+ }
212
+ }
213
+ catch (e) { removeRegisteredApp(this); this.end(); return; }
214
+ });
215
+ });
216
+
217
+ // Send current sessions to registered apps
218
+ function broadcastSessionsToRegisteredApps(x) {
219
+ broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
220
+ }
221
+
222
+ // Send this object to all registered local applications
223
+ function broadcastToRegisteredApps(x) {
224
+ if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
225
+ for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
226
+ }
227
+
228
+ // Send list of registered apps to the server
229
+ function updateRegisteredAppsToServer() {
230
+ if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
231
+ var apps = {};
232
+ for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
233
+ try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
234
+ }
235
+
236
+ // Remove a registered app
237
+ function removeRegisteredApp(pipe) {
238
+ for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
239
+ if (pipe._registered != null) updateRegisteredAppsToServer();
240
+ }
241
+
242
+ function diagnosticAgent_uninstall() {
243
+ require('service-manager').manager.uninstallService('meshagentDiagnostic');
244
+ require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
245
+ };
246
+ function diagnosticAgent_installCheck(install) {
247
+ try {
248
+ var diag = require('service-manager').manager.getService('meshagentDiagnostic');
249
+ return (diag);
250
+ }
251
+ catch (e) {
252
+ }
253
+ if (!install) { return (null); }
254
+
255
+ var svc = null;
256
+ try {
257
+ require('service-manager').manager.installService(
258
+ {
259
+ name: 'meshagentDiagnostic',
260
+ displayName: "Mesh Agent Diagnostic Service",
261
+ description: "Mesh Agent Diagnostic Service",
262
+ servicePath: process.execPath,
263
+ parameters: ['-recovery']
264
+ //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
265
+ });
266
+ svc = require('service-manager').manager.getService('meshagentDiagnostic');
267
+ }
268
+ catch (e) {
269
+ return (null);
270
+ }
271
+ var proxyConfig = require('global-tunnel').proxyConfig;
272
+ var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
273
+ var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
274
+ ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
275
+ ddb.Put('disableUpdate', '1');
276
+ ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
277
+ ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
278
+ ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
279
+ if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
280
+ if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
281
+ if (proxyConfig) {
282
+ ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
283
+ } else {
284
+ ddb.Put('ignoreProxyFile', '1');
285
+ }
286
+
287
+ require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
288
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
289
+
290
+ delete ddb;
291
+
292
+ // Set a recurrent task, to run the Diagnostic Agent every 2 days
293
+ require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
294
+ //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
295
+
296
+ return (svc);
297
+ }
298
+
299
+ // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
300
+ if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
301
+ // Setup manual battery monitoring
302
+ require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
303
+ if (require('MeshAgent')._batteryFileTimer != null) return;
304
+ require('MeshAgent')._batteryFileTimer = setTimeout(function () {
305
+ try {
306
+ require('MeshAgent')._batteryFileTimer = null;
307
+ var data = null;
308
+ try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
309
+ if ((data != null) && (data.length < 10)) {
310
+ data = data.split(',');
311
+ if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
312
+ var level = parseInt(data[1]);
313
+ if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
314
+ }
315
+ }
316
+ } catch (e) { }
317
+ }, 1000);
318
+ });
319
+ } else {
320
+ // Setup normal battery monitoring
321
+ if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
322
+ require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
323
+ _battLevelChanged.self._currentBatteryLevel = val;
324
+ _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
325
+ };
326
+ require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
327
+ require('MeshAgent')._powerChanged = function _powerChanged(val) {
328
+ _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
329
+ _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
330
+ };
331
+ require('MeshAgent')._powerChanged.self = require('MeshAgent');
332
+ require('MeshAgent').on('Connected', function (status) {
333
+ if (status == 0) {
334
+ require('power-monitor').removeListener('acdc', this._powerChanged);
335
+ require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
336
+ } else {
337
+ require('power-monitor').on('acdc', this._powerChanged);
338
+ require('power-monitor').on('batteryLevel', this._battLevelChanged);
339
+ }
340
+ });
341
+ }
342
+ }
343
+
344
+
345
+ /*
346
+ function borderController() {
347
+ this.container = null;
348
+ this.Start = function Start(user) {
349
+ if (this.container == null) {
350
+ if (process.platform == 'win32') {
351
+ try {
352
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
353
+ } catch (e) {
354
+ this.container = require('ScriptContainer').Create({ processIsolation: 1 });
355
+ }
356
+ } else {
357
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
358
+ }
359
+ this.container.parent = this;
360
+ this.container.addModule('monitor-info', getJSModule('monitor-info'));
361
+ this.container.addModule('monitor-border', getJSModule('monitor-border'));
362
+ this.container.addModule('promise', getJSModule('promise'));
363
+ this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
364
+ this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
365
+ }
366
+ }
367
+ this.Stop = function Stop() {
368
+ if (this.container != null) {
369
+ this._container = this.container;
370
+ this._container.parent = this;
371
+ this.container = null;
372
+ this._container.exit();
373
+ }
374
+ }
375
+ }
376
+ obj.borderManager = new borderController();
377
+ */
378
+
379
+ // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
380
+ var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
381
+
382
+
383
+ // Get the operating system description string
384
+ try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (e) { }
385
+
386
+ var meshServerConnectionState = 0;
387
+ var tunnels = {};
388
+ var lastMeInfo = null;
389
+ var lastNetworkInfo = null;
390
+ var lastPublicLocationInfo = null;
391
+ var selfInfoUpdateTimer = null;
392
+ var http = require('http');
393
+ var net = require('net');
394
+ var fs = require('fs');
395
+ var rtc = require('ILibWebRTC');
396
+ var amt = null;
397
+ var processManager = require('process-manager');
398
+ var wifiScannerLib = null;
399
+ var wifiScanner = null;
400
+ var networkMonitor = null;
401
+ var amtscanner = null;
402
+ var nextTunnelIndex = 1;
403
+ var apftunnel = null;
404
+ var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
405
+
406
+ // Add to the server event log
407
+ function MeshServerLog(msg, state) {
408
+ if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
409
+ if (state) {
410
+ if (state.userid) { msg.userid = state.userid; }
411
+ if (state.username) { msg.username = state.username; }
412
+ if (state.sessionid) { msg.sessionid = state.sessionid; }
413
+ if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
414
+ }
415
+ mesh.SendCommand(msg);
416
+ }
417
+
418
+ // Add to the server event log, use internationalized events
419
+ function MeshServerLogEx(id, args, msg, state) {
420
+ var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
421
+ if (state) {
422
+ if (state.userid) { msg.userid = state.userid; }
423
+ if (state.username) { msg.username = state.username; }
424
+ if (state.sessionid) { msg.sessionid = state.sessionid; }
425
+ if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
426
+ }
427
+ mesh.SendCommand(msg);
428
+ }
429
+
430
+ // If we are running in Duktape, agent will be null
431
+ if (agent == null) {
432
+ // Running in native agent, Import libraries
433
+ db = require('SimpleDataStore').Shared();
434
+ sha = require('SHA256Stream');
435
+ mesh = require('MeshAgent');
436
+ childProcess = require('child_process');
437
+ if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
438
+ // Check if this computer supports a desktop
439
+ try
440
+ {
441
+ if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
442
+ {
443
+ meshCoreObj.caps |= 1;
444
+ }
445
+ else if(process.platform == 'linux' || process.platform == 'freebsd')
446
+ {
447
+ require('monitor-info').on('kvmSupportDetected', function (value)
448
+ {
449
+ meshCoreObj.caps |= 1;
450
+ mesh.SendCommand(meshCoreObj);
451
+ });
452
+ }
453
+ } catch (e) { }
454
+ }
455
+ } else {
456
+ // Running in nodejs
457
+ meshCoreObj.value += '-NodeJS';
458
+ meshCoreObj.caps = 8;
459
+ mesh = agent.getMeshApi();
460
+ }
461
+
462
+ mesh.DAIPC = obj.DAIPC;
463
+
464
+ /*
465
+ var AMTScanner = require("AMTScanner");
466
+ var scan = new AMTScanner();
467
+
468
+ scan.on("found", function (data) {
469
+ if (typeof data === 'string') {
470
+ console.log(data);
471
+ } else {
472
+ console.log(JSON.stringify(data, null, " "));
473
+ }
474
+ });
475
+ scan.scan("10.2.55.140", 1000);
476
+ scan.scan("10.2.55.139-10.2.55.145", 1000);
477
+ scan.scan("10.2.55.128/25", 2000);
478
+ */
479
+
480
+ /*
481
+ // Try to load up the network monitor
482
+ try {
483
+ networkMonitor = require('NetworkMonitor');
484
+ networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
485
+ networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
486
+ networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
487
+ } catch (e) { networkMonitor = null; }
488
+ */
489
+
490
+ // Try to load up the Intel AMT scanner
491
+ try {
492
+ var AMTScannerModule = require('amt-scanner');
493
+ amtscanner = new AMTScannerModule();
494
+ //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
495
+ } catch (e) { amtscanner = null; }
496
+
497
+ // Fetch the SMBios Tables
498
+ var SMBiosTables = null;
499
+ var SMBiosTablesRaw = null;
500
+ try {
501
+ var SMBiosModule = null;
502
+ try { SMBiosModule = require('smbios'); } catch (e) { }
503
+ if (SMBiosModule != null) {
504
+ SMBiosModule.get(function (data) {
505
+ if (data != null) {
506
+ SMBiosTablesRaw = data;
507
+ SMBiosTables = require('smbios').parse(data)
508
+ if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
509
+
510
+ // If SMBios tables say that Intel AMT is present, try to connect MEI
511
+ if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
512
+ {
513
+ var amtmodule = require('amt-manage');
514
+ amt = new amtmodule(mesh, db, false);
515
+ amt.on('portBinding_LMS', function (map)
516
+ {
517
+ var j = { action: 'lmsinfo', value: { ports: map.keys() } };
518
+ mesh.SendCommand(j);
519
+ });
520
+ amt.on('stateChange_LMS', function (v)
521
+ {
522
+ if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; }
523
+ switch(v)
524
+ {
525
+ case 0:
526
+ meshCoreObj.intelamt.microlms = 'DISABLED';
527
+ break;
528
+ case 1:
529
+ meshCoreObj.intelamt.microlms = 'CONNECTING';
530
+ break;
531
+ case 2:
532
+ meshCoreObj.intelamt.microlms = 'CONNECTED';
533
+ break;
534
+ default:
535
+ break;
536
+ }
537
+ mesh.SendCommand(meshCoreObj);
538
+ });
539
+ amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
540
+ amt.start();
541
+ }
542
+ }
543
+ });
544
+ }
545
+ } catch (e) { sendConsoleText("ex1: " + e); }
546
+
547
+ // Try to load up the WIFI scanner
548
+ try {
549
+ var wifiScannerLib = require('wifi-scanner');
550
+ wifiScanner = new wifiScannerLib();
551
+ wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
552
+ } catch (e) { wifiScannerLib = null; wifiScanner = null; }
553
+
554
+ // Get our location (lat/long) using our public IP address
555
+ var getIpLocationDataExInProgress = false;
556
+ var getIpLocationDataExCounts = [0, 0];
557
+ function getIpLocationDataEx(func) {
558
+ if (getIpLocationDataExInProgress == true) { return false; }
559
+ try {
560
+ getIpLocationDataExInProgress = true;
561
+ getIpLocationDataExCounts[0]++;
562
+ var options = http.parseUri("http://ipinfo.io/json");
563
+ options.method = 'GET';
564
+ http.request(options, function (resp) {
565
+ if (resp.statusCode == 200) {
566
+ var geoData = '';
567
+ resp.data = function (geoipdata) { geoData += geoipdata; };
568
+ resp.end = function () {
569
+ var location = null;
570
+ try {
571
+ if (typeof geoData == 'string') {
572
+ var result = JSON.parse(geoData);
573
+ if (result.ip && result.loc) { location = result; }
574
+ }
575
+ } catch (e) { }
576
+ if (func) { getIpLocationDataExCounts[1]++; func(location); }
577
+ }
578
+ } else { func(null); }
579
+ getIpLocationDataExInProgress = false;
580
+ }).end();
581
+ return true;
582
+ }
583
+ catch (e) { return false; }
584
+ }
585
+
586
+ // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
587
+ function clearGatewayMac(str) {
588
+ if (str == null) return null;
589
+ var x = JSON.parse(str);
590
+ for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
591
+ return JSON.stringify(x);
592
+ }
593
+
594
+ function getIpLocationData(func) {
595
+ // Get the location information for the cache if possible
596
+ var publicLocationInfo = db.Get('publicLocationInfo');
597
+ if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
598
+ if (publicLocationInfo == null) {
599
+ // Nothing in the cache, fetch the data
600
+ getIpLocationDataEx(function (locationData) {
601
+ if (locationData != null) {
602
+ publicLocationInfo = {};
603
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
604
+ publicLocationInfo.locationData = locationData;
605
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
606
+ if (func) func(locationData); // Report the new location
607
+ } else {
608
+ if (func) func(null); // Report no location
609
+ }
610
+ });
611
+ } else {
612
+ // Check the cache
613
+ if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
614
+ // Cache match
615
+ if (func) func(publicLocationInfo.locationData);
616
+ } else {
617
+ // Cache mismatch
618
+ getIpLocationDataEx(function (locationData) {
619
+ if (locationData != null) {
620
+ publicLocationInfo = {};
621
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
622
+ publicLocationInfo.locationData = locationData;
623
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
624
+ if (func) func(locationData); // Report the new location
625
+ } else {
626
+ if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
627
+ }
628
+ });
629
+ }
630
+ }
631
+ }
632
+
633
+ // Polyfill String.endsWith
634
+ if (!String.prototype.endsWith) {
635
+ String.prototype.endsWith = function (searchString, position) {
636
+ var subjectString = this.toString();
637
+ if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
638
+ position -= searchString.length;
639
+ var lastIndex = subjectString.lastIndexOf(searchString, position);
640
+ return lastIndex !== -1 && lastIndex === position;
641
+ };
642
+ }
643
+
644
+ // Polyfill path.join
645
+ obj.path = {
646
+ join: function () {
647
+ var x = [];
648
+ for (var i in arguments) {
649
+ var w = arguments[i];
650
+ if (w != null) {
651
+ while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
652
+ if (i != 0) {
653
+ while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
654
+ }
655
+ x.push(w);
656
+ }
657
+ }
658
+ if (x.length == 0) return '/';
659
+ return x.join('/');
660
+ }
661
+ };
662
+
663
+ // Replace a string with a number if the string is an exact number
664
+ function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
665
+
666
+ // Convert decimal to hex
667
+ function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
668
+
669
+ // Convert a raw string to a hex string
670
+ function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
671
+
672
+ // Convert a buffer into a string
673
+ function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
674
+
675
+ // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
676
+ function hex2rstr(d) {
677
+ if (typeof d != "string" || d.length == 0) return '';
678
+ var r = '', m = ('' + d).match(/../g), t;
679
+ while (t = m.shift()) r += String.fromCharCode('0x' + t);
680
+ return r
681
+ }
682
+
683
+ // Convert an object to string with all functions
684
+ function objToString(x, p, pad, ret) {
685
+ if (ret == undefined) ret = '';
686
+ if (p == undefined) p = 0;
687
+ if (x == null) { return '[null]'; }
688
+ if (p > 8) { return '[...]'; }
689
+ if (x == undefined) { return '[undefined]'; }
690
+ if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
691
+ if (typeof x == 'buffer') { return '[buffer]'; }
692
+ if (typeof x != 'object') { return x; }
693
+ var r = '{' + (ret ? '\r\n' : ' ');
694
+ for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
695
+ return r + addPad(p, pad) + '}';
696
+ }
697
+
698
+ // Return p number of spaces
699
+ function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
700
+
701
+ // Split a string taking into account the quoats. Used for command line parsing
702
+ function splitArgs(str) {
703
+ var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
704
+ do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
705
+ return myArray;
706
+ }
707
+
708
+ // Parse arguments string array into an object
709
+ function parseArgs(argv) {
710
+ var results = { '_': [] }, current = null;
711
+ for (var i = 1, len = argv.length; i < len; i++) {
712
+ var x = argv[i];
713
+ if (x.length > 2 && x[0] == '-' && x[1] == '-') {
714
+ if (current != null) { results[current] = true; }
715
+ current = x.substring(2);
716
+ } else {
717
+ if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
718
+ }
719
+ }
720
+ if (current != null) { results[current] = true; }
721
+ return results;
722
+ }
723
+
724
+ // Get server target url with a custom path
725
+ function getServerTargetUrl(path) {
726
+ var x = mesh.ServerUrl;
727
+ //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
728
+ if (x == null) { return null; }
729
+ if (path == null) { path = ''; }
730
+ x = http.parseUri(x);
731
+ if (x == null) return null;
732
+ return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
733
+ }
734
+
735
+ // Get server url. If the url starts with "*/..." change it, it not use the url as is.
736
+ function getServerTargetUrlEx(url) {
737
+ if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
738
+ return url;
739
+ }
740
+
741
+ // Send a wake-on-lan packet
742
+ function sendWakeOnLan(hexMac) {
743
+ hexMac = hexMac.split(':').join('');
744
+ var count = 0;
745
+ try {
746
+ var interfaces = require('os').networkInterfaces();
747
+ var magic = 'FFFFFFFFFFFF';
748
+ for (var x = 1; x <= 16; ++x) { magic += hexMac; }
749
+ var magicbin = Buffer.from(magic, 'hex');
750
+
751
+ for (var adapter in interfaces) {
752
+ if (interfaces.hasOwnProperty(adapter)) {
753
+ for (var i = 0; i < interfaces[adapter].length; ++i) {
754
+ var addr = interfaces[adapter][i];
755
+ if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
756
+ try {
757
+ var socket = require('dgram').createSocket({ type: 'udp4' });
758
+ socket.bind({ address: addr.address });
759
+ socket.setBroadcast(true);
760
+ socket.setMulticastInterface(addr.address);
761
+ socket.setMulticastTTL(1);
762
+ socket.send(magicbin, 7, '255.255.255.255');
763
+ socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
764
+ count++;
765
+ }
766
+ catch (e) { }
767
+ }
768
+ }
769
+ }
770
+ }
771
+ } catch (e) { }
772
+ return count;
773
+ }
774
+
775
+ // Handle a mesh agent command
776
+ function handleServerCommand(data) {
777
+ if (typeof data == 'object') {
778
+ // If this is a console command, parse it and call the console handler
779
+ switch (data.action) {
780
+ case 'msg': {
781
+ switch (data.type) {
782
+ case 'console': { // Process a console command
783
+ if (data.value && data.sessionid) {
784
+ MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
785
+ var args = splitArgs(data.value);
786
+ processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
787
+ }
788
+ break;
789
+ }
790
+ case 'tunnel': {
791
+ if (data.value != null) { // Process a new tunnel connection request
792
+ // Create a new tunnel object
793
+ var xurl = getServerTargetUrlEx(data.value);
794
+ if (xurl != null) {
795
+ xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
796
+ var woptions = http.parseUri(xurl);
797
+ woptions.perMessageDeflate = false;
798
+ if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
799
+ woptions.rejectUnauthorized = 0;
800
+ //sendConsoleText(JSON.stringify(woptions));
801
+ //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
802
+ var tunnel = http.request(woptions);
803
+ tunnel.upgrade = onTunnelUpgrade;
804
+ tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
805
+ tunnel.sessionid = data.sessionid;
806
+ tunnel.rights = data.rights;
807
+ tunnel.consent = data.consent;
808
+ tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
809
+ tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
810
+ tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
811
+ tunnel.userid = data.userid;
812
+ tunnel.remoteaddr = data.remoteaddr;
813
+ tunnel.state = 0;
814
+ tunnel.url = xurl;
815
+ tunnel.protocol = 0;
816
+ tunnel.soptions = data.soptions;
817
+ tunnel.tcpaddr = data.tcpaddr;
818
+ tunnel.tcpport = data.tcpport;
819
+ tunnel.udpaddr = data.udpaddr;
820
+ tunnel.udpport = data.udpport;
821
+ tunnel.end();
822
+ // Put the tunnel in the tunnels list
823
+ var index = nextTunnelIndex++;
824
+ tunnel.index = index;
825
+ tunnels[index] = tunnel;
826
+
827
+ //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
828
+ }
829
+ }
830
+ break;
831
+ }
832
+ case 'messagebox': {
833
+ // Display a message box
834
+ if (data.title && data.msg) {
835
+ MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
836
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
837
+ try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
838
+ }
839
+ break;
840
+ }
841
+ case 'ps': {
842
+ // Return the list of running processes
843
+ if (data.sessionid) {
844
+ processManager.getProcesses(function (plist) {
845
+ mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
846
+ });
847
+ }
848
+ break;
849
+ }
850
+ case 'pskill': {
851
+ // Kill a process
852
+ if (data.value) {
853
+ MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
854
+ try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
855
+ }
856
+ break;
857
+ }
858
+ case 'services': {
859
+ // Return the list of installed services
860
+ var services = null;
861
+ try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
862
+ if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
863
+ break;
864
+ }
865
+ case 'serviceStop': {
866
+ // Stop a service
867
+ try {
868
+ var service = require('service-manager').manager.getService(data.serviceName);
869
+ if (service != null) { service.stop(); }
870
+ } catch (e) { }
871
+ break;
872
+ }
873
+ case 'serviceStart': {
874
+ // Start a service
875
+ try {
876
+ var service = require('service-manager').manager.getService(data.serviceName);
877
+ if (service != null) { service.start(); }
878
+ } catch (e) { }
879
+ break;
880
+ }
881
+ case 'serviceRestart': {
882
+ // Restart a service
883
+ try {
884
+ var service = require('service-manager').manager.getService(data.serviceName);
885
+ if (service != null) { service.restart(); }
886
+ } catch (e) { }
887
+ break;
888
+ }
889
+ case 'deskBackground':
890
+ {
891
+ // Toggle desktop background
892
+ try {
893
+ if (process.platform == 'win32') {
894
+ var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
895
+ var sid = undefined;
896
+ if (stype == 1) {
897
+ if (require('MeshAgent')._tsid != null) {
898
+ stype = 5;
899
+ sid = require('MeshAgent')._tsid;
900
+ }
901
+ }
902
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
903
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
904
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
905
+ child.stderr.on('data', function () { });
906
+ child.waitExit();
907
+ var current = child.stdout.str.trim();
908
+ if (current != '') { require('MeshAgent')._wallpaper = current; }
909
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
910
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
911
+ child.stderr.on('data', function () { });
912
+ child.waitExit();
913
+ } else {
914
+ var id = require('user-sessions').consoleUid();
915
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
916
+ if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
917
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
918
+ }
919
+ } catch (e) {
920
+ sendConsoleText(e);
921
+ }
922
+ break;
923
+ }
924
+ case 'openUrl': {
925
+ // Open a local web browser and return success/fail
926
+ MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
927
+ sendConsoleText("OpenURL: " + data.url);
928
+ if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
929
+ break;
930
+ }
931
+ case 'getclip': {
932
+ // Send the load clipboard back to the user
933
+ //sendConsoleText('getClip: ' + JSON.stringify(data));
934
+ if (require('MeshAgent').isService) {
935
+ require('clipboard').dispatchRead().then(function (str) {
936
+ if (str) {
937
+ MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
938
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
939
+ }
940
+ });
941
+ } else {
942
+ require("clipboard").read().then(function (str) {
943
+ if (str) {
944
+ MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
945
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
946
+ }
947
+ });
948
+ }
949
+ break;
950
+ }
951
+ case 'setclip': {
952
+ // Set the load clipboard to a user value
953
+ //sendConsoleText('setClip: ' + JSON.stringify(data));
954
+ if (typeof data.data == 'string') {
955
+ MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
956
+ if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
957
+ mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
958
+ }
959
+ break;
960
+ }
961
+ case 'userSessions': {
962
+ // Send back current user sessions list, this is Windows only.
963
+ //sendConsoleText('userSessions: ' + JSON.stringify(data));
964
+ if (process.platform != 'win32') break;
965
+ var p = require('user-sessions').enumerateUsers();
966
+ p.sessionid = data.sessionid;
967
+ p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
968
+ break;
969
+ }
970
+ default:
971
+ // Unknown action, ignore it.
972
+ break;
973
+ }
974
+ break;
975
+ }
976
+ case 'acmactivate': {
977
+ if (amt != null) {
978
+ MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
979
+ amt.setAcmResponse(data);
980
+ }
981
+ break;
982
+ }
983
+ case 'wakeonlan': {
984
+ // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
985
+ sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
986
+ for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
987
+ break;
988
+ }
989
+ case 'runcommands': {
990
+ if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
991
+ sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
992
+
993
+ // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
994
+ var options = {};
995
+ if (data.runAsUser > 0) {
996
+ try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
997
+ options.type = require('child_process').SpawnTypes.TERM;
998
+ }
999
+ if (data.runAsUser == 2) {
1000
+ if (options.uid == null) break;
1001
+ if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1002
+ }
1003
+
1004
+ if (process.platform == 'win32') {
1005
+ if (data.type == 1) {
1006
+ // Windows command shell
1007
+ mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1008
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1009
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1010
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1011
+ mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1012
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1013
+ } else if (data.type == 2) {
1014
+ // Windows Powershell
1015
+ mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1016
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1017
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1018
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1019
+ mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1020
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1021
+ }
1022
+ } else if (data.type == 3) {
1023
+ // Linux shell
1024
+ mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1025
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1026
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1027
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1028
+ mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1029
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1030
+ }
1031
+ break;
1032
+ }
1033
+ case 'uninstallagent':
1034
+ // Uninstall this agent
1035
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1036
+ if (require('service-manager').manager.getService(agentName).isMe()) {
1037
+ try { diagnosticAgent_uninstall(); } catch (e) { }
1038
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1039
+ this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
1040
+ }
1041
+ break;
1042
+ case 'poweraction': {
1043
+ // Server telling us to execute a power action
1044
+ if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1045
+ var forced = 0;
1046
+ if (data.forced == 1) { forced = 1; }
1047
+ data.actiontype = parseInt(data.actiontype);
1048
+ MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1049
+ sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1050
+ var r = mesh.ExecPowerState(data.actiontype, forced);
1051
+ sendConsoleText("ExecPowerState returned code: " + r);
1052
+ }
1053
+ break;
1054
+ }
1055
+ case 'iplocation': {
1056
+ // Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
1057
+ getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1058
+ break;
1059
+ }
1060
+ case 'toast': {
1061
+ // Display a toast message
1062
+ if (data.title && data.msg) {
1063
+ MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1064
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1065
+ try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1066
+ }
1067
+ break;
1068
+ }
1069
+ case 'openUrl': {
1070
+ // Open a local web browser and return success/fail
1071
+ //sendConsoleText('OpenURL: ' + data.url);
1072
+ MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1073
+ if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1074
+ break;
1075
+ }
1076
+ case 'amtconfig': {
1077
+ // Perform Intel AMT activation and/or configuration
1078
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'amtconfig' });
1079
+ if ((apftunnel != null) || (amt == null)) break;
1080
+ getMeiState(15, function (state) {
1081
+ if ((state == null) || (state.ProvisioningState == null)) return;
1082
+ if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
1083
+ var apfarg = {
1084
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
1085
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16), // TODO: User a server provided encrypted cookie for CIRA-LMS login
1086
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
1087
+ mpskeepalive: 60000,
1088
+ clientname: state.OsHostname,
1089
+ clientaddress: '127.0.0.1',
1090
+ clientuuid: state.UUID,
1091
+ conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
1092
+ meiState: state // MEI state will be passed to MPS server
1093
+ };
1094
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'apf-on' });
1095
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
1096
+ apftunnel.onJsonControl = function (data) {
1097
+ if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message (DEBUG)
1098
+ if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1099
+ if (data.action == 'deactivate') { // Request CCM deactivation
1100
+ var amtMeiModule, amtMei;
1101
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
1102
+ amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
1103
+ amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
1104
+ }
1105
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
1106
+ }
1107
+ apftunnel.onChannelClosed = function () { apftunnel = null; require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: 'apf-off' }); }
1108
+ try { apftunnel.connect(); } catch (ex) { }
1109
+ });
1110
+ break;
1111
+ }
1112
+ case 'getScript': {
1113
+ // Received a configuration script from the server
1114
+ sendConsoleText('getScript: ' + JSON.stringify(data));
1115
+ break;
1116
+ }
1117
+ case 'sysinfo': {
1118
+ // Fetch system information
1119
+ getSystemInformation(function (results) {
1120
+ if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1121
+ });
1122
+ break;
1123
+ }
1124
+ case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1125
+ case 'pong': { break; }
1126
+ case 'plugin': {
1127
+ try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1128
+ break;
1129
+ }
1130
+ case 'coredump':
1131
+ // Set the current agent coredump situation.
1132
+ if (data.value === true) {
1133
+ // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1134
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
1135
+ } else if (data.value === false) {
1136
+ process.coreDumpLocation = null;
1137
+ }
1138
+ break;
1139
+ case 'getcoredump':
1140
+ // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1141
+ var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1142
+ var coreDumpPath = null;
1143
+ if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1144
+ if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) { r.exists = (db.Get('CoreDumpTime') != require('fs').statSync(coreDumpPath).mtime); }
1145
+ if (r.exists == true) { r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); }
1146
+ mesh.SendCommand(JSON.stringify(r));
1147
+ default:
1148
+ // Unknown action, ignore it.
1149
+ break;
1150
+ }
1151
+ }
1152
+ }
1153
+
1154
+ // Called when a file changed in the file system
1155
+ /*
1156
+ function onFileWatcher(a, b) {
1157
+ console.log('onFileWatcher', a, b, this.path);
1158
+ var response = getDirectoryInfo(this.path);
1159
+ if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1160
+ }
1161
+ */
1162
+
1163
+ function getSystemInformation(func) {
1164
+ try {
1165
+ var results = { hardware: require('identifiers').get() }; // Hardware info
1166
+ if (results.hardware && results.hardware.windows) {
1167
+ // Remove extra entries and things that change quickly
1168
+ var x = results.hardware.windows.osinfo;
1169
+ try { delete x.FreePhysicalMemory; } catch (e) { }
1170
+ try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1171
+ try { delete x.FreeVirtualMemory; } catch (e) { }
1172
+ try { delete x.LocalDateTime; } catch (e) { }
1173
+ try { delete x.MaxProcessMemorySize; } catch (e) { }
1174
+ try { delete x.TotalVirtualMemorySize; } catch (e) { }
1175
+ try { delete x.TotalVisibleMemorySize; } catch (e) { }
1176
+ try {
1177
+ if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1178
+ if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1179
+ if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1180
+ } catch (e) { }
1181
+ }
1182
+ if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1183
+ /*
1184
+ if (process.platform == 'win32') {
1185
+ var defragResult = function (r) {
1186
+ if (typeof r == 'object') { results[this.callname] = r; }
1187
+ if (this.callname == 'defrag') {
1188
+ var pr = require('win-info').installedApps(); // Installed apps
1189
+ pr.callname = 'installedApps';
1190
+ pr.sessionid = data.sessionid;
1191
+ pr.then(defragResult, defragResult);
1192
+ }
1193
+ else {
1194
+ results.winpatches = require('win-info').qfe(); // Windows patches
1195
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1196
+ func(results);
1197
+ }
1198
+ }
1199
+ var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1200
+ pr.callname = 'defrag';
1201
+ pr.sessionid = data.sessionid;
1202
+ pr.then(defragResult, defragResult);
1203
+ } else {
1204
+ */
1205
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1206
+ func(results);
1207
+ //}
1208
+ } catch (e) { func(null, e); }
1209
+ }
1210
+
1211
+ // Get a formated response for a given directory path
1212
+ function getDirectoryInfo(reqpath) {
1213
+ var response = { path: reqpath, dir: [] };
1214
+ if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1215
+ // List all the drives in the root, or the root itself
1216
+ var results = null;
1217
+ try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1218
+ if (results != null) {
1219
+ for (var i = 0; i < results.length; ++i) {
1220
+ var drive = { n: results[i].name, t: 1 };
1221
+ if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1222
+ response.dir.push(drive);
1223
+ }
1224
+ }
1225
+ } else {
1226
+ // List all the files and folders in this path
1227
+ if (reqpath == '') { reqpath = '/'; }
1228
+ var results = null, xpath = obj.path.join(reqpath, '*');
1229
+ //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1230
+ try { results = fs.readdirSync(xpath); } catch (e) { }
1231
+ if (results != null) {
1232
+ for (var i = 0; i < results.length; ++i) {
1233
+ if ((results[i] != '.') && (results[i] != '..')) {
1234
+ var stat = null, p = obj.path.join(reqpath, results[i]);
1235
+ //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1236
+ try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1237
+ if ((stat != null) && (stat != undefined)) {
1238
+ if (stat.isDirectory() == true) {
1239
+ response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1240
+ } else {
1241
+ response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ }
1247
+ }
1248
+ return response;
1249
+ }
1250
+
1251
+ // Tunnel callback operations
1252
+ function onTunnelUpgrade(response, s, head) {
1253
+ this.s = s;
1254
+ s.httprequest = this;
1255
+ s.end = onTunnelClosed;
1256
+ s.tunnel = this;
1257
+ s.descriptorMetadata = "MeshAgent_relayTunnel";
1258
+
1259
+ if (require('MeshAgent').idleTimeout != null)
1260
+ {
1261
+ s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1262
+ s.on('timeout', function ()
1263
+ {
1264
+ this.ping();
1265
+ this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1266
+ });
1267
+ }
1268
+
1269
+ //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1270
+
1271
+ if (this.tcpport != null) {
1272
+ // This is a TCP relay connection, pause now and try to connect to the target.
1273
+ s.pause();
1274
+ s.data = onTcpRelayServerTunnelData;
1275
+ var connectionOptions = { port: parseInt(this.tcpport) };
1276
+ if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1277
+ s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1278
+ s.tcprelay.peerindex = this.index;
1279
+
1280
+ // Add the TCP session to the count and update the server
1281
+ if (s.httprequest.userid != null) {
1282
+ if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1283
+ try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1284
+ broadcastSessionsToRegisteredApps();
1285
+ }
1286
+ } if (this.udpport != null) {
1287
+ // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1288
+ s.data = onUdpRelayServerTunnelData;
1289
+ s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1290
+ s.udprelay.bind({ port: 0 });
1291
+ s.udprelay.peerindex = this.index;
1292
+ s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1293
+ s.udprelay.udpport = this.udpport;
1294
+ s.udprelay.udpaddr = this.udpaddr;
1295
+ s.udprelay.first = true;
1296
+
1297
+ // Add the UDP session to the count and update the server
1298
+ if (s.httprequest.userid != null) {
1299
+ if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1300
+ try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1301
+ broadcastSessionsToRegisteredApps();
1302
+ }
1303
+ } else {
1304
+ // This is a normal connect for KVM/Terminal/Files
1305
+ s.data = onTunnelData;
1306
+ }
1307
+ }
1308
+
1309
+ // Called when UDP relay data is received // TODO****
1310
+ function onUdpRelayTargetTunnelConnect(data) {
1311
+ var peerTunnel = tunnels[this.peerindex];
1312
+ peerTunnel.s.write(data);
1313
+ }
1314
+
1315
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1316
+ function onUdpRelayServerTunnelData(data) {
1317
+ if (this.udprelay.first === true) {
1318
+ delete this.udprelay.first; // Skip the first 'c' that is received.
1319
+ } else {
1320
+ this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1321
+ }
1322
+ }
1323
+
1324
+ // Called when the TCP relay target is connected
1325
+ function onTcpRelayTargetTunnelConnect() {
1326
+ var peerTunnel = tunnels[this.peerindex];
1327
+ this.pipe(peerTunnel.s); // Pipe Target --> Server
1328
+ peerTunnel.s.first = true;
1329
+ peerTunnel.s.resume();
1330
+ }
1331
+
1332
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1333
+ function onTcpRelayServerTunnelData(data) {
1334
+ if (this.first == true) {
1335
+ this.first = false;
1336
+ this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1337
+ }
1338
+ }
1339
+
1340
+ function onTunnelClosed() {
1341
+ var tunnel = tunnels[this.httprequest.index];
1342
+ if (tunnel == null) return; // Stop duplicate calls.
1343
+
1344
+ // If this is a routing session, clean up and send the new session counts.
1345
+ if (this.httprequest.userid != null) {
1346
+ if (this.httprequest.tcpport != null) {
1347
+ if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
1348
+ try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1349
+ broadcastSessionsToRegisteredApps();
1350
+ } else if (this.httprequest.udpport != null) {
1351
+ if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
1352
+ try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1353
+ broadcastSessionsToRegisteredApps();
1354
+ }
1355
+ }
1356
+
1357
+ // Sent tunnel statistics to the server, only send this if compression was used.
1358
+ if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1359
+ mesh.SendCommand({
1360
+ action: 'tunnelCloseStats',
1361
+ url: tunnel.url,
1362
+ userid: tunnel.userid,
1363
+ protocol: tunnel.protocol,
1364
+ sessionid: tunnel.sessionid,
1365
+ sent: this.bytesSent_uncompressed.toString(),
1366
+ sentActual: this.bytesSent_actual.toString(),
1367
+ sentRatio: this.bytesSent_ratio,
1368
+ received: this.bytesReceived_uncompressed.toString(),
1369
+ receivedActual: this.bytesReceived_actual.toString(),
1370
+ receivedRatio: this.bytesReceived_ratio
1371
+ });
1372
+ }
1373
+
1374
+ //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1375
+ if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1376
+
1377
+ /*
1378
+ // Close the watcher if required
1379
+ if (this.httprequest.watcher != undefined) {
1380
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1381
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1382
+ delete this.httprequest.watcher;
1383
+ }
1384
+ */
1385
+
1386
+ // If there is a upload or download active on this connection, close the file
1387
+ if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1388
+ if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1389
+
1390
+ // Clean up WebRTC
1391
+ if (this.webrtc != null) {
1392
+ if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
1393
+ if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1394
+ try { this.webrtc.close(); } catch (e) { }
1395
+ this.webrtc.removeAllListeners('connected');
1396
+ this.webrtc.removeAllListeners('disconnected');
1397
+ this.webrtc.removeAllListeners('dataChannel');
1398
+ delete this.webrtc;
1399
+ }
1400
+
1401
+ // Clean up WebSocket
1402
+ this.removeAllListeners('data');
1403
+ }
1404
+ function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1405
+ function onTunnelData(data) {
1406
+ //console.log("OnTunnelData");
1407
+ //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1408
+
1409
+ // If this is upload data, save it to file
1410
+ if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1411
+ // Save the data to file being uploaded.
1412
+ if (data[0] == 0) {
1413
+ // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1414
+ try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1415
+ } else {
1416
+ // If data does not start with zero, save as-is.
1417
+ try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1418
+ }
1419
+ this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1420
+ return;
1421
+ }
1422
+
1423
+ if (this.httprequest.state == 0) {
1424
+ // Check if this is a relay connection
1425
+ if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1426
+ }
1427
+ else
1428
+ {
1429
+ // Handle tunnel data
1430
+ if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1431
+ // Take a look at the protocol
1432
+ if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1433
+ this.httprequest.protocol = parseInt(data);
1434
+ if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1435
+ if (this.httprequest.protocol == 10) {
1436
+ //
1437
+ // Basic file transfer
1438
+ //
1439
+ var stats = null;
1440
+ if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1441
+ try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1442
+ try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1443
+ if (this.httprequest.downloadFile) {
1444
+ //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1445
+ this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1446
+ this.httprequest.downloadFile.pipe(this);
1447
+ this.httprequest.downloadFile.end = function () { }
1448
+ } else {
1449
+ //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1450
+ this.write(JSON.stringify({ op: 'cancel' }));
1451
+ }
1452
+ }
1453
+ else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1454
+ {
1455
+ //
1456
+ // Remote Terminal
1457
+ //
1458
+
1459
+ // Check user access rights for terminal
1460
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1461
+ // Disengage this tunnel, user does not have the rights to do this!!
1462
+ this.httprequest.protocol = 999999;
1463
+ this.httprequest.s.end();
1464
+ sendConsoleText("Error: No Terminal Control Rights.");
1465
+ return;
1466
+ }
1467
+
1468
+ this.descriptorMetadata = "Remote Terminal";
1469
+
1470
+ if (process.platform == 'win32')
1471
+ {
1472
+ if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1473
+ {
1474
+ this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1475
+ this.httprequest.s.end();
1476
+ return;
1477
+ }
1478
+ }
1479
+
1480
+ var prom = require('promise');
1481
+ this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1482
+ this.httprequest.tpromise.that = this;
1483
+ this.httprequest.tpromise.httprequest = this.httprequest;
1484
+
1485
+ this.end = function ()
1486
+ {
1487
+ if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1488
+ if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1489
+
1490
+ // Remove the terminal session to the count to update the server
1491
+ if (this.httprequest.userid != null)
1492
+ {
1493
+ if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } }
1494
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1495
+ broadcastSessionsToRegisteredApps();
1496
+ }
1497
+
1498
+ if (process.platform == 'win32')
1499
+ {
1500
+ // Unpipe the web socket
1501
+ this.unpipe(this.httprequest._term);
1502
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1503
+
1504
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1505
+ if (this.rtcchannel)
1506
+ {
1507
+ this.rtcchannel.unpipe(this.httprequest._term);
1508
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1509
+ }
1510
+
1511
+ // Clean up
1512
+ if (this.httprequest._term) { this.httprequest._term.end(); }
1513
+ this.httprequest._term = null;
1514
+ }
1515
+ };
1516
+
1517
+ // Perform User-Consent if needed.
1518
+ if (this.httprequest.consent && (this.httprequest.consent & 16))
1519
+ {
1520
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1521
+ var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1522
+ if (this.httprequest.soptions != null) {
1523
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1524
+ if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1525
+ }
1526
+ this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1527
+ this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1528
+ this.httprequest.tpromise._consent.then(
1529
+ function ()
1530
+ {
1531
+ // Success
1532
+ MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1533
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1534
+ this.retPromise._consent = null;
1535
+ this.retPromise._res();
1536
+ },
1537
+ function (e)
1538
+ {
1539
+ // Denied
1540
+ MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1541
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1542
+ this.retPromise._rej(e.toString());
1543
+ });
1544
+ }
1545
+ else
1546
+ {
1547
+ // User-Consent is not required, so just resolve this promise
1548
+ this.httprequest.tpromise._res();
1549
+ }
1550
+
1551
+
1552
+ this.httprequest.tpromise.then(
1553
+ function ()
1554
+ {
1555
+ this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1556
+ this.httprequest.connectionPromise.ws = this.that;
1557
+
1558
+ // Start Terminal
1559
+ if(process.platform == 'win32')
1560
+ {
1561
+ try
1562
+ {
1563
+ var cols = 80, rows = 25;
1564
+ if (this.httprequest.xoptions)
1565
+ {
1566
+ if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1567
+ if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1568
+ }
1569
+
1570
+ if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1571
+ {
1572
+ // Admin Terminal
1573
+ if (require('win-virtual-terminal').supported)
1574
+ {
1575
+ // ConPTY PseudoTerminal
1576
+ // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1577
+
1578
+ // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1579
+ this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1580
+ this.httprequest._dispatcher.httprequest = this.httprequest;
1581
+ this.httprequest._dispatcher.on('connection', function (c)
1582
+ {
1583
+ if (this.httprequest.connectionPromise.completed)
1584
+ {
1585
+ c.end();
1586
+ }
1587
+ else
1588
+ {
1589
+ this.httprequest.connectionPromise._res(c);
1590
+ }
1591
+ });
1592
+ }
1593
+ else
1594
+ {
1595
+ // Legacy Terminal
1596
+ this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1597
+ }
1598
+ }
1599
+ else
1600
+ {
1601
+ // Logged in user
1602
+ var userPromise = require('user-sessions').enumerateUsers();
1603
+ userPromise.that = this;
1604
+ userPromise.then(function (u)
1605
+ {
1606
+ var that = this.that;
1607
+ if (u.Active.length > 0)
1608
+ {
1609
+ var username = u.Active[0].Username;
1610
+ if (require('win-virtual-terminal').supported)
1611
+ {
1612
+ // ConPTY PseudoTerminal
1613
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1614
+ }
1615
+ else
1616
+ {
1617
+ // Legacy Terminal
1618
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1619
+ }
1620
+ that.httprequest._dispatcher.ws = that;
1621
+ that.httprequest._dispatcher.on('connection', function (c)
1622
+ {
1623
+ if (this.ws.httprequest.connectionPromise.completed)
1624
+ {
1625
+ c.end();
1626
+ }
1627
+ else
1628
+ {
1629
+ this.ws.httprequest.connectionPromise._res(c);
1630
+ }
1631
+ });
1632
+ }
1633
+ });
1634
+ }
1635
+ }
1636
+ catch (e)
1637
+ {
1638
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1639
+ }
1640
+ }
1641
+ else
1642
+ {
1643
+ try
1644
+ {
1645
+ var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1646
+ var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1647
+ var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1648
+
1649
+ var env = { HISTCONTROL: 'ignoreboth' };
1650
+ if (this.httprequest.xoptions)
1651
+ {
1652
+ if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1653
+ if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1654
+ }
1655
+ var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1656
+ if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1657
+ {
1658
+ if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1659
+ this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1660
+ }
1661
+ else if (bash)
1662
+ {
1663
+ var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1664
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1665
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1666
+ this.httprequest.connectionPromise._res(p);
1667
+ }
1668
+ else if (sh)
1669
+ {
1670
+ var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1671
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1672
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1673
+ this.httprequest.connectionPromise._res(p);
1674
+ }
1675
+ else
1676
+ {
1677
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1678
+ }
1679
+ }
1680
+ catch (e)
1681
+ {
1682
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1683
+ }
1684
+ }
1685
+
1686
+ this.httprequest.connectionPromise.then(
1687
+ function (term)
1688
+ {
1689
+ // SUCCESS
1690
+ var stdoutstream;
1691
+ var stdinstream;
1692
+ if (process.platform == 'win32')
1693
+ {
1694
+ this.ws.httprequest._term = term;
1695
+ this.ws.httprequest._term.tunnel = this.ws;
1696
+ stdoutstream = stdinstream = term;
1697
+ }
1698
+ else
1699
+ {
1700
+ term.descriptorMetadata = 'Remote Terminal';
1701
+ this.ws.httprequest.process = term;
1702
+ this.ws.httprequest.process.tunnel = this.ws;
1703
+ term.stderr.stdout = term.stdout;
1704
+ term.stderr.on('data', function (c) { this.stdout.write(c); });
1705
+ stdoutstream = term.stdout;
1706
+ stdinstream = term.stdin;
1707
+ this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1708
+ term.prependListener('exit', function () { this.tunnel.end(); });
1709
+ }
1710
+
1711
+ this.ws.removeAllListeners('data');
1712
+ this.ws.on('data', onTunnelControlData);
1713
+
1714
+ stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1715
+ this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1716
+
1717
+ // Add the terminal session to the count to update the server
1718
+ if (this.ws.httprequest.userid != null)
1719
+ {
1720
+ if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1721
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1722
+ broadcastSessionsToRegisteredApps();
1723
+ }
1724
+
1725
+ // Toast Notification, if required
1726
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1727
+ {
1728
+ // User Notifications is required
1729
+ var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1730
+ if (this.ws.httprequest.soptions != null) {
1731
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1732
+ if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1733
+ }
1734
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
1735
+ }
1736
+ },
1737
+ function (e)
1738
+ {
1739
+ // FAILED to connect terminal
1740
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1741
+ this.ws.end();
1742
+ });
1743
+ },
1744
+ function (e)
1745
+ {
1746
+ // DO NOT start terminal
1747
+ this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1748
+ this.that.end();
1749
+ });
1750
+ }
1751
+ else if (this.httprequest.protocol == 2)
1752
+ {
1753
+ //
1754
+ // Remote KVM
1755
+ //
1756
+
1757
+ // Check user access rights for desktop
1758
+ if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1759
+ // Disengage this tunnel, user does not have the rights to do this!!
1760
+ this.httprequest.protocol = 999999;
1761
+ this.httprequest.s.end();
1762
+ sendConsoleText("Error: No Desktop Control Rights.");
1763
+ return;
1764
+ }
1765
+
1766
+ this.descriptorMetadata = "Remote KVM";
1767
+
1768
+ // Look for a TSID
1769
+ var tsid = null;
1770
+ if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1771
+ require('MeshAgent')._tsid = tsid;
1772
+
1773
+ // Remote desktop using native pipes
1774
+ this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1775
+ this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1776
+ this.desktop = this.httprequest.desktop;
1777
+
1778
+ // Add ourself to the list of remote desktop sessions
1779
+ if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1780
+ this.httprequest.desktop.kvm.tunnels.push(this);
1781
+
1782
+ // Send a metadata update to all desktop sessions
1783
+ var users = {};
1784
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1785
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1786
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1787
+ tunnelUserCount.desktop = users;
1788
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1789
+ broadcastSessionsToRegisteredApps();
1790
+ }
1791
+
1792
+ this.end = function () {
1793
+ --this.desktop.kvm.connectionCount;
1794
+
1795
+ // Remove ourself from the list of remote desktop session
1796
+ var i = this.desktop.kvm.tunnels.indexOf(this);
1797
+ if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1798
+
1799
+ // Send a metadata update to all desktop sessions
1800
+ var users = {};
1801
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1802
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1803
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1804
+ tunnelUserCount.desktop = users;
1805
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1806
+ broadcastSessionsToRegisteredApps();
1807
+ }
1808
+
1809
+ // Unpipe the web socket
1810
+ try
1811
+ {
1812
+ this.unpipe(this.httprequest.desktop.kvm);
1813
+ this.httprequest.desktop.kvm.unpipe(this);
1814
+ }
1815
+ catch(e) { }
1816
+
1817
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1818
+ if (this.rtcchannel)
1819
+ {
1820
+ try
1821
+ {
1822
+ this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1823
+ this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1824
+ }
1825
+ catch(e) { }
1826
+ }
1827
+
1828
+ // Place wallpaper back if needed
1829
+ // TODO
1830
+
1831
+ if (this.desktop.kvm.connectionCount == 0) {
1832
+ // Display a toast message. This may not be supported on all platforms.
1833
+ // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
1834
+
1835
+ this.httprequest.desktop.kvm.end();
1836
+ if (this.httprequest.desktop.kvm.connectionBar) {
1837
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1838
+ this.httprequest.desktop.kvm.connectionBar.close();
1839
+ this.httprequest.desktop.kvm.connectionBar = null;
1840
+ }
1841
+ } else {
1842
+ for (var i in this.httprequest.desktop.kvm.users) {
1843
+ if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
1844
+ for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
1845
+ this.httprequest.desktop.kvm.users.splice(i, 1);
1846
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1847
+ this.httprequest.desktop.kvm.connectionBar.close();
1848
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1849
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1850
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1851
+ MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1852
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1853
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1854
+ }
1855
+ this.httprequest.desktop.kvm.end();
1856
+ });
1857
+ break;
1858
+ }
1859
+ }
1860
+ }
1861
+ };
1862
+ if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1863
+ this.httprequest.desktop.kvm.connectionCount++;
1864
+ this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
1865
+ this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1866
+ this.httprequest.desktop.kvm.rusers.sort();
1867
+ this.httprequest.desktop.kvm.users.sort();
1868
+ } else {
1869
+ this.httprequest.desktop.kvm.connectionCount = 1;
1870
+ this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
1871
+ this.httprequest.desktop.kvm.users = [this.httprequest.username];
1872
+ }
1873
+
1874
+ if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1875
+ // If we have remote control rights, pipe the KVM input
1876
+ this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1877
+ } else {
1878
+ // We need to only pipe non-mouse & non-keyboard inputs.
1879
+ //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1880
+ // TODO!!!
1881
+ }
1882
+
1883
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
1884
+ if (this.httprequest.consent && (this.httprequest.consent & 8))
1885
+ {
1886
+ // User Consent Prompt is required
1887
+ // Send a console message back using the console channel, "\n" is supported.
1888
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1889
+ var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1890
+ if (this.httprequest.soptions != null) {
1891
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1892
+ if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1893
+ }
1894
+ var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1895
+ pr.ws = this;
1896
+ this.pause();
1897
+ this._consentpromise = pr;
1898
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1899
+ pr.then(
1900
+ function ()
1901
+ {
1902
+ // Success
1903
+ this.ws._consentpromise = null;
1904
+ MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1905
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1906
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1907
+ // User Notifications is required
1908
+ var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1909
+ if (this.ws.httprequest.soptions != null) {
1910
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1911
+ if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1912
+ }
1913
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1914
+ }
1915
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1916
+ // Connection Bar is required
1917
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1918
+ this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1919
+ this.ws.httprequest.desktop.kvm.connectionBar.close();
1920
+ }
1921
+ try {
1922
+ this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1923
+ MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1924
+ }
1925
+ catch (e) {
1926
+ if (process.platform != 'darwin') {
1927
+ MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1928
+ }
1929
+ }
1930
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1931
+ this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1932
+ this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1933
+ MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1934
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1935
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1936
+ }
1937
+ this.httprequest.desktop.kvm.end();
1938
+ });
1939
+ }
1940
+ }
1941
+ this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1942
+ this.ws.resume();
1943
+ },
1944
+ function (e)
1945
+ {
1946
+ // User Consent Denied/Failed
1947
+ this.ws._consentpromise = null;
1948
+ MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1949
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1950
+ });
1951
+ } else {
1952
+ // User Consent Prompt is not required
1953
+ if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1954
+ // User Notifications is required
1955
+ MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1956
+ var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1957
+ if (this.httprequest.soptions != null) {
1958
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1959
+ if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1960
+ }
1961
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1962
+ } else {
1963
+ MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1964
+ }
1965
+ if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1966
+ // Connection Bar is required
1967
+ if (this.httprequest.desktop.kvm.connectionBar) {
1968
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1969
+ this.httprequest.desktop.kvm.connectionBar.close();
1970
+ }
1971
+ try {
1972
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
1973
+ MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1974
+ }
1975
+ catch (e) {
1976
+ MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1977
+ }
1978
+ if (this.httprequest.desktop.kvm.connectionBar) {
1979
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1980
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1981
+ MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1982
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1983
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1984
+ }
1985
+ this.httprequest.desktop.kvm.end();
1986
+ });
1987
+ }
1988
+ }
1989
+ this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1990
+ }
1991
+
1992
+ this.removeAllListeners('data');
1993
+ this.on('data', onTunnelControlData);
1994
+ //this.write('MeshCore KVM Hello!1');
1995
+
1996
+ } else if (this.httprequest.protocol == 5) {
1997
+ //
1998
+ // Remote Files
1999
+ //
2000
+
2001
+ // Check user access rights for files
2002
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
2003
+ // Disengage this tunnel, user does not have the rights to do this!!
2004
+ this.httprequest.protocol = 999999;
2005
+ this.httprequest.s.end();
2006
+ sendConsoleText("Error: No files control rights.");
2007
+ return;
2008
+ }
2009
+
2010
+ this.descriptorMetadata = "Remote Files";
2011
+
2012
+ // Add the files session to the count to update the server
2013
+ if (this.httprequest.userid != null) {
2014
+ if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
2015
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2016
+ broadcastSessionsToRegisteredApps();
2017
+ }
2018
+
2019
+ this.end = function () {
2020
+ // Remove the files session from the count to update the server
2021
+ if (this.httprequest.userid != null) {
2022
+ if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } }
2023
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
2024
+ broadcastSessionsToRegisteredApps();
2025
+ }
2026
+ };
2027
+
2028
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
2029
+ if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2030
+ // User Consent Prompt is required
2031
+ // Send a console message back using the console channel, "\n" is supported.
2032
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2033
+ var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2034
+ if (this.httprequest.soptions != null) {
2035
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2036
+ if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2037
+ }
2038
+ var pr = require('message-box').create(consentTitle, consentMessage, 30);
2039
+ pr.ws = this;
2040
+ this.pause();
2041
+ this._consentpromise = pr;
2042
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2043
+ pr.then(
2044
+ function ()
2045
+ {
2046
+ // Success
2047
+ this.ws._consentpromise = null;
2048
+ MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2049
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2050
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2051
+ // User Notifications is required
2052
+ var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2053
+ if (this.ws.httprequest.soptions != null) {
2054
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2055
+ if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2056
+ }
2057
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2058
+ }
2059
+ this.ws.resume();
2060
+ },
2061
+ function (e)
2062
+ {
2063
+ // User Consent Denied/Failed
2064
+ this.ws._consentpromise = null;
2065
+ MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2066
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2067
+ });
2068
+ } else {
2069
+ // User Consent Prompt is not required
2070
+ if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2071
+ // User Notifications is required
2072
+ MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2073
+ var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2074
+ if (this.httprequest.soptions != null) {
2075
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2076
+ if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2077
+ }
2078
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2079
+ } else {
2080
+ MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2081
+ }
2082
+ this.resume();
2083
+ }
2084
+
2085
+ // Setup files
2086
+ // NOP
2087
+ }
2088
+ } else if (this.httprequest.protocol == 1) {
2089
+ // Send data into terminal stdin
2090
+ //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2091
+ } else if (this.httprequest.protocol == 2) {
2092
+ // Send data into remote desktop
2093
+ if (this.httprequest.desktop.state == 0) {
2094
+ this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2095
+ this.httprequest.desktop.state = 1;
2096
+ } else {
2097
+ this.httprequest.desktop.write(data);
2098
+ }
2099
+ } else if (this.httprequest.protocol == 5) {
2100
+ // Process files commands
2101
+ var cmd = null;
2102
+ try { cmd = JSON.parse(data); } catch (e) { };
2103
+ if (cmd == null) { return; }
2104
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2105
+ if (cmd.action == undefined) { return; }
2106
+ //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2107
+
2108
+ if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2109
+ //console.log(objToString(cmd, 0, ' '));
2110
+ switch (cmd.action) {
2111
+ case 'ls': {
2112
+ /*
2113
+ // Close the watcher if required
2114
+ var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2115
+ if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2116
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2117
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2118
+ delete this.httprequest.watcher;
2119
+ }
2120
+ */
2121
+
2122
+ // Send the folder content to the browser
2123
+ var response = getDirectoryInfo(cmd.path);
2124
+ if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2125
+ this.write(Buffer.from(JSON.stringify(response)));
2126
+
2127
+ /*
2128
+ // Start the directory watcher
2129
+ if ((cmd.path != '') && (samepath == false)) {
2130
+ var watcher = fs.watch(cmd.path, onFileWatcher);
2131
+ watcher.tunnel = this.httprequest;
2132
+ watcher.path = cmd.path;
2133
+ this.httprequest.watcher = watcher;
2134
+ //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2135
+ }
2136
+ */
2137
+ break;
2138
+ }
2139
+ case 'mkdir': {
2140
+ // Create a new empty folder
2141
+ fs.mkdirSync(cmd.path);
2142
+ MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2143
+ break;
2144
+ }
2145
+ case 'rm': {
2146
+ // Delete, possibly recursive delete
2147
+ for (var i in cmd.delfiles) {
2148
+ var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2149
+ try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2150
+ if ((delcount == 1) && !cmd.rec) {
2151
+ MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2152
+ } else {
2153
+ if (cmd.rec) {
2154
+ MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2155
+ } else {
2156
+ MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2157
+ }
2158
+ }
2159
+ }
2160
+ break;
2161
+ }
2162
+ case 'markcoredump': {
2163
+ // If we are asking for the coredump file, set the right path.
2164
+ var coreDumpPath = null;
2165
+ if (process.platform == 'win32') {
2166
+ if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2167
+ } else {
2168
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2169
+ }
2170
+ if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2171
+ break;
2172
+ }
2173
+ case 'rename': {
2174
+ // Rename a file or folder
2175
+ var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2176
+ var newfullpath = obj.path.join(cmd.path, cmd.newname);
2177
+ MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2178
+ try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2179
+ break;
2180
+ }
2181
+ case 'download': {
2182
+ // Download a file
2183
+ var sendNextBlock = 0;
2184
+ if (cmd.sub == 'start') { // Setup the download
2185
+ if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2186
+ if (process.platform == 'win32') {
2187
+ if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2188
+ } else {
2189
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2190
+ }
2191
+ }
2192
+ MeshServerLogEx(49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2193
+ if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2194
+ this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2195
+ try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2196
+ if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2197
+ } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2198
+ if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2199
+ }
2200
+ // Send the next download block(s)
2201
+ while (sendNextBlock > 0) {
2202
+ sendNextBlock--;
2203
+ var buf = Buffer.alloc(16384);
2204
+ var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2205
+ this.filedownload.ptr += len;
2206
+ if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2207
+ this.write(buf.slice(0, len + 4)); // Write as binary
2208
+ }
2209
+ break;
2210
+ }
2211
+ /*
2212
+ case 'download': {
2213
+ // Packet download of a file, agent to browser
2214
+ if (cmd.path == undefined) break;
2215
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2216
+ //console.log('Download: ' + filepath);
2217
+ try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2218
+ this.httprequest.downloadFileId = cmd.reqid;
2219
+ this.httprequest.downloadFilePtr = 0;
2220
+ if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2221
+ break;
2222
+ }
2223
+ case 'download2': {
2224
+ // Stream download of a file, agent to browser
2225
+ if (cmd.path == undefined) break;
2226
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2227
+ try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2228
+ this.httprequest.downloadFile.pipe(this);
2229
+ this.httprequest.downloadFile.end = function () { }
2230
+ break;
2231
+ }
2232
+ */
2233
+ case 'upload': {
2234
+ // Upload a file, browser to agent
2235
+ if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2236
+ if (cmd.path == undefined) break;
2237
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2238
+ this.httprequest.uploadFilePath = filepath;
2239
+ MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2240
+ try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2241
+ this.httprequest.uploadFileid = cmd.reqid;
2242
+ if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2243
+ break;
2244
+ }
2245
+ case 'uploaddone': {
2246
+ // Indicates that an upload is done
2247
+ if (this.httprequest.uploadFile) {
2248
+ fs.closeSync(this.httprequest.uploadFile);
2249
+ this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2250
+ delete this.httprequest.uploadFile;
2251
+ delete this.httprequest.uploadFileid;
2252
+ delete this.httprequest.uploadFilePath;
2253
+ }
2254
+ break;
2255
+ }
2256
+ case 'uploadcancel': {
2257
+ // Indicates that an upload is canceled
2258
+ if (this.httprequest.uploadFile) {
2259
+ fs.closeSync(this.httprequest.uploadFile);
2260
+ fs.unlinkSync(this.httprequest.uploadFilePath);
2261
+ this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2262
+ delete this.httprequest.uploadFile;
2263
+ delete this.httprequest.uploadFileid;
2264
+ delete this.httprequest.uploadFilePath;
2265
+ }
2266
+ break;
2267
+ }
2268
+ case 'copy': {
2269
+ // Copy a bunch of files from scpath to dspath
2270
+ for (var i in cmd.names) {
2271
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2272
+ MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2273
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2274
+ }
2275
+ break;
2276
+ }
2277
+ case 'move': {
2278
+ // Move a bunch of files from scpath to dspath
2279
+ for (var i in cmd.names) {
2280
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2281
+ MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2282
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2283
+ }
2284
+ break;
2285
+ }
2286
+ case 'zip':
2287
+ // Zip a bunch of files
2288
+ if (this.zip != null) return; // Zip operating is currently running, exit now.
2289
+
2290
+ // Check that the specified files exist & build full paths
2291
+ var fp, stat, p = [];
2292
+ for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (e) { } if (stat != null) { p.push(fp); } }
2293
+ if (p.length == 0) return; // No files, quit now.
2294
+
2295
+ // Setup file compression
2296
+ var ofile = cmd.path + '/' + cmd.output;
2297
+ this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2298
+ this.zipfile = ofile;
2299
+ delete this.zipcancel;
2300
+ var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2301
+ out.xws = this;
2302
+ out.on('close', function () {
2303
+ this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2304
+ this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2305
+ if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2306
+ delete this.xws.zipcancel;
2307
+ delete this.xws.zipfile;
2308
+ delete this.xws.zip;
2309
+ });
2310
+ this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2311
+ this.zip.xws = this;
2312
+ this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
2313
+ this.zip.pipe(out);
2314
+ break;
2315
+ case 'cancel':
2316
+ // Cancel zip operation if present
2317
+ try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2318
+ this.zip = null;
2319
+ break;
2320
+ default:
2321
+ // Unknown action, ignore it.
2322
+ break;
2323
+ }
2324
+ } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2325
+ var cmd = null;
2326
+ try { cmd = JSON.parse(data); } catch (e) { };
2327
+ if (cmd == null) { return; }
2328
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2329
+ if (cmd.action == undefined) return;
2330
+
2331
+ switch (cmd.action) {
2332
+ case 'plugin': {
2333
+ try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2334
+ break;
2335
+ }
2336
+ default: {
2337
+ // probably shouldn't happen, but just in case this feature is expanded
2338
+ }
2339
+ }
2340
+
2341
+ }
2342
+ //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2343
+ }
2344
+ }
2345
+
2346
+ // Delete a directory with a files and directories within it
2347
+ function deleteFolderRecursive(path, rec) {
2348
+ var count = 0;
2349
+ if (fs.existsSync(path)) {
2350
+ if (rec == true) {
2351
+ fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2352
+ var curPath = obj.path.join(path, file);
2353
+ if (fs.statSync(curPath).isDirectory()) { // recurse
2354
+ count += deleteFolderRecursive(curPath, true);
2355
+ } else { // delete file
2356
+ fs.unlinkSync(curPath);
2357
+ count++;
2358
+ }
2359
+ });
2360
+ }
2361
+ fs.unlinkSync(path);
2362
+ count++;
2363
+ }
2364
+ return count;
2365
+ };
2366
+
2367
+ // Called when receiving control data on WebRTC
2368
+ function onTunnelWebRTCControlData(data) {
2369
+ if (typeof data != 'string') return;
2370
+ var obj;
2371
+ try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2372
+ if (obj.type == 'close') {
2373
+ //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2374
+ try { this.close(); } catch (e) { }
2375
+ try { this.xrtc.close(); } catch (e) { }
2376
+ }
2377
+ }
2378
+
2379
+ // Called when receiving control data on websocket
2380
+ function onTunnelControlData(data, ws) {
2381
+ var obj;
2382
+ if (ws == null) { ws = this; }
2383
+ if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2384
+ else if (typeof data == 'object') { obj = data; } else { return; }
2385
+ //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2386
+ //console.log('onTunnelControlData: ' + JSON.stringify(data));
2387
+
2388
+ if (obj.action) {
2389
+ switch (obj.action) {
2390
+ case 'lock': {
2391
+ // Lock the current user out of the desktop
2392
+ try {
2393
+ if (process.platform == 'win32') {
2394
+ MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2395
+ var child = require('child_process');
2396
+ child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2397
+ }
2398
+ } catch (e) { }
2399
+ break;
2400
+ }
2401
+ default:
2402
+ // Unknown action, ignore it.
2403
+ break;
2404
+ }
2405
+ return;
2406
+ }
2407
+
2408
+ switch (obj.type) {
2409
+ case 'options': {
2410
+ // These are additional connection options passed in the control channel.
2411
+ //sendConsoleText('options: ' + JSON.stringify(obj));
2412
+ delete obj.type;
2413
+ ws.httprequest.xoptions = obj;
2414
+
2415
+ // Set additional user consent options if present
2416
+ if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2417
+
2418
+ break;
2419
+ }
2420
+ case 'close': {
2421
+ // We received the close on the websocket
2422
+ //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2423
+ try { ws.close(); } catch (e) { }
2424
+ break;
2425
+ }
2426
+ case 'termsize': {
2427
+ // Indicates a change in terminal size
2428
+ if (process.platform == 'win32')
2429
+ {
2430
+ if (ws.httprequest._dispatcher == null) return;
2431
+ //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2432
+ if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2433
+ } else
2434
+ {
2435
+ if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2436
+ //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2437
+
2438
+ if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2439
+ }
2440
+ break;
2441
+ }
2442
+ case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2443
+ if (ws.httprequest.protocol == 1) { // Terminal
2444
+ // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
2445
+ if (process.platform == 'win32') {
2446
+ ws.httprequest._term.unpipe(ws);
2447
+ } else {
2448
+ ws.httprequest.process.stdout.unpipe(ws);
2449
+ ws.httprequest.process.stderr.unpipe(ws);
2450
+ }
2451
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2452
+ // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
2453
+ ws.httprequest.desktop.kvm.unpipe(ws);
2454
+ } else {
2455
+ // Switch things around so all WebRTC data goes to onTunnelData().
2456
+ ws.rtcchannel.httprequest = ws.httprequest;
2457
+ ws.rtcchannel.removeAllListeners('data');
2458
+ ws.rtcchannel.on('data', onTunnelData);
2459
+ }
2460
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2461
+ break;
2462
+ }
2463
+ case 'webrtc1': {
2464
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2465
+ // Switch the user input from websocket to webrtc at this point.
2466
+ if (process.platform == 'win32') {
2467
+ ws.unpipe(ws.httprequest._term);
2468
+ ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2469
+ } else {
2470
+ ws.unpipe(ws.httprequest.process.stdin);
2471
+ ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2472
+ }
2473
+ ws.resume(); // Resume the websocket to keep receiving control data
2474
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2475
+ // Switch the user input from websocket to webrtc at this point.
2476
+ ws.unpipe(ws.httprequest.desktop.kvm);
2477
+ try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2478
+ ws.resume(); // Resume the websocket to keep receiving control data
2479
+ }
2480
+ ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2481
+ break;
2482
+ }
2483
+ case 'webrtc2': {
2484
+ // Other side received websocket end of data marker, start sending data on WebRTC channel
2485
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2486
+ if (process.platform == 'win32') {
2487
+ ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2488
+ } else {
2489
+ ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2490
+ ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2491
+ }
2492
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2493
+ ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2494
+ }
2495
+ break;
2496
+ }
2497
+ case 'offer': {
2498
+ // This is a WebRTC offer.
2499
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2500
+ ws.webrtc = rtc.createConnection();
2501
+ ws.webrtc.websocket = ws;
2502
+ ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2503
+ ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2504
+ ws.webrtc.on('dataChannel', function (rtcchannel) {
2505
+ //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2506
+ rtcchannel.maxFragmentSize = 32768;
2507
+ rtcchannel.xrtc = this;
2508
+ rtcchannel.websocket = this.websocket;
2509
+ this.rtcchannel = rtcchannel;
2510
+ this.websocket.rtcchannel = rtcchannel;
2511
+ this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2512
+ this.websocket.rtcchannel.on('end', function () {
2513
+ // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2514
+ //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2515
+ if (this.websocket.desktop && this.websocket.desktop.kvm)
2516
+ {
2517
+ try
2518
+ {
2519
+ this.unpipe(this.websocket.desktop.kvm);
2520
+ this.websocket.httprequest.desktop.kvm.unpipe(this);
2521
+ }
2522
+ catch (e) { }
2523
+ }
2524
+ });
2525
+ this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2526
+ });
2527
+ var sdp = null;
2528
+ try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (e) { }
2529
+ if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2530
+ break;
2531
+ }
2532
+ case 'ping': {
2533
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
2534
+ break;
2535
+ }
2536
+ case 'pong': { // NOP
2537
+ break;
2538
+ }
2539
+ case 'rtt': {
2540
+ ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2541
+ break;
2542
+ }
2543
+ }
2544
+ }
2545
+
2546
+ // Console state
2547
+ var consoleWebSockets = {};
2548
+ var consoleHttpRequest = null;
2549
+
2550
+ // Console HTTP response
2551
+ function consoleHttpResponse(response) {
2552
+ response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2553
+ response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2554
+ };
2555
+
2556
+ // Open a web browser to a specified URL on current user's desktop
2557
+ function openUserDesktopUrl(url) {
2558
+ if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
2559
+ var child = null;
2560
+ try {
2561
+ switch (process.platform) {
2562
+ case 'win32':
2563
+ var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2564
+ child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2565
+ child.stderr.on('data', function () { });
2566
+ child.stdout.on('data', function () { });
2567
+ child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process.env['windir'] + '\\system32\\cmd.exe /C START ' + url + '"\r\n');
2568
+ child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2569
+ child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2570
+ child.stdin.write('exit\r\n');
2571
+ child.waitExit();
2572
+ break;
2573
+ case 'linux':
2574
+ child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2575
+ break;
2576
+ case 'darwin':
2577
+ child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2578
+ break;
2579
+ default:
2580
+ // Unknown platform, ignore this command.
2581
+ break;
2582
+ }
2583
+ } catch (e) { }
2584
+ return child;
2585
+ }
2586
+
2587
+ // Process a mesh agent console command
2588
+ function processConsoleCommand(cmd, args, rights, sessionid) {
2589
+ try {
2590
+ var response = null;
2591
+ switch (cmd) {
2592
+ case 'help': { // Displays available commands
2593
+ var fin = '', f = '', availcommands = 'amtconfig,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
2594
+ if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2595
+ if (process.platform != 'freebsd') { availcommands += ',vm';}
2596
+ if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2597
+ try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
2598
+
2599
+ availcommands = availcommands.split(',').sort();
2600
+ while (availcommands.length > 0) {
2601
+ if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2602
+ f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2603
+ }
2604
+ if (f != '') { fin += f; }
2605
+ response = "Available commands: \r\n" + fin + ".";
2606
+ break;
2607
+ }
2608
+ case 'agentmsg': {
2609
+ if (args['_'].length == 0) {
2610
+ response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2611
+ } else {
2612
+ if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
2613
+ var msgIndex = 1, iconIndex = 0;
2614
+ while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
2615
+ if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
2616
+ if (typeof iconIndex != 'number') { iconIndex = 0; }
2617
+ tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
2618
+ response = 'Agent message ' + msgIndex + ' added.';
2619
+ } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
2620
+ var msgIndex = 0;
2621
+ try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
2622
+ if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2623
+ } else if (args['_'][0] == 'list') {
2624
+ response = JSON.stringify(tunnelUserCount.msg, null, 2);
2625
+ }
2626
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2627
+ broadcastSessionsToRegisteredApps();
2628
+ }
2629
+ break;
2630
+ }
2631
+ case 'clearagentmsg': {
2632
+ tunnelUserCount.msg = {};
2633
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2634
+ broadcastSessionsToRegisteredApps();
2635
+ break;
2636
+ }
2637
+ case 'coredump':
2638
+ if (args['_'].length != 1) {
2639
+ response = "Proper usage: coredump on|off|status|clear"; // Display usage
2640
+ } else {
2641
+ switch (args['_'][0].toLowerCase())
2642
+ {
2643
+ case 'on':
2644
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2645
+ response = 'coredump is now on';
2646
+ break;
2647
+ case 'off':
2648
+ process.coreDumpLocation = null;
2649
+ response = 'coredump is now off';
2650
+ break;
2651
+ case 'status':
2652
+ response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2653
+ if (process.coreDumpLocation != null) {
2654
+ if (process.platform == 'win32') {
2655
+ if (fs.existsSync(process.coreDumpLocation)) { response += '\r\n CoreDump present at: ' + process.coreDumpLocation; }
2656
+ } else {
2657
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { response += '\r\n CoreDump present at: ' + process.cwd() + 'core'; }
2658
+ }
2659
+ }
2660
+ break;
2661
+ case 'clear':
2662
+ db.Put('CoreDumpTime', null);
2663
+ response = 'coredump db cleared';
2664
+ break;
2665
+ default:
2666
+ response = "Proper usage: coredump on|off|status"; // Display usage
2667
+ break;
2668
+ }
2669
+ }
2670
+ break;
2671
+ case 'service':
2672
+ if (args['_'].length != 1)
2673
+ {
2674
+ response = "Proper usage: service status|restart"; // Display usage
2675
+ }
2676
+ else
2677
+ {
2678
+ var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2679
+ switch(args['_'][0].toLowerCase())
2680
+ {
2681
+ case 'status':
2682
+ response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2683
+ break;
2684
+ case 'restart':
2685
+ if (s.isMe())
2686
+ {
2687
+ s.restart();
2688
+ }
2689
+ else
2690
+ {
2691
+ response = 'Restarting another agent instance is not allowed';
2692
+ }
2693
+ break;
2694
+ default:
2695
+ response = "Proper usage: service status|restart"; // Display usage
2696
+ break;
2697
+ }
2698
+ if (process.platform == 'win32') { s.close(); }
2699
+ }
2700
+ break;
2701
+ case 'zip':
2702
+ if (args['_'].length == 0) {
2703
+ response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2704
+ } else {
2705
+ var p = args['_'].join(' ').split(',');
2706
+ var ofile = p.shift();
2707
+ sendConsoleText('Writing ' + ofile + '...');
2708
+ var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2709
+ out.fname = ofile;
2710
+ out.sessionid = sessionid;
2711
+ out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2712
+ var zip = require('zip-writer').write({ files: p });
2713
+ zip.pipe(out);
2714
+ }
2715
+ break;
2716
+ case 'unzip':
2717
+ if (args['_'].length == 0) {
2718
+ response = "Proper usage: unzip input, destination"; // Display usage
2719
+ } else {
2720
+ var p = args['_'].join(' ').split(',');
2721
+ if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
2722
+ var prom = require('zip-reader').read(p[0]);
2723
+ prom._dest = p[1];
2724
+ prom.self = this;
2725
+ prom.sessionid = sessionid;
2726
+ prom.then(function (zipped) {
2727
+ sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2728
+ zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2729
+ }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2730
+ }
2731
+ break;
2732
+ case 'setbattery':
2733
+ // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2734
+ if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2735
+ var b = { action: 'battery', state: args['_'][0] };
2736
+ if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2737
+ require('MeshAgent').SendCommand(b);
2738
+ } else {
2739
+ require('MeshAgent').SendCommand({ action: 'battery' });
2740
+ }
2741
+ break;
2742
+ case 'fdsnapshot':
2743
+ require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2744
+ break;
2745
+ case 'fdcount':
2746
+ require('DescriptorEvents').getDescriptorCount().then(
2747
+ function (c)
2748
+ {
2749
+ sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2750
+ }, function (e)
2751
+ {
2752
+ sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2753
+ }).parentPromise.sessionid = sessionid;
2754
+ break;
2755
+ case 'uac':
2756
+ if (process.platform != 'win32')
2757
+ {
2758
+ response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2759
+ break;
2760
+ }
2761
+ if (args['_'].length != 1)
2762
+ {
2763
+ response = 'Proper usage: uac [get|interactive|secure]';
2764
+ }
2765
+ else
2766
+ {
2767
+ switch(args['_'][0].toUpperCase())
2768
+ {
2769
+ case 'GET':
2770
+ var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2771
+ response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2772
+ break;
2773
+ case 'INTERACTIVE':
2774
+ try
2775
+ {
2776
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2777
+ response = 'UAC mode changed to: Interactive Desktop';
2778
+ }
2779
+ catch (e)
2780
+ {
2781
+ response = "Unable to change UAC Mode";
2782
+ }
2783
+ break;
2784
+ case 'SECURE':
2785
+ try
2786
+ {
2787
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2788
+ response = 'UAC mode changed to: Secure Desktop';
2789
+ }
2790
+ catch(e)
2791
+ {
2792
+ response = "Unable to change UAC Mode";
2793
+ }
2794
+ break;
2795
+ default:
2796
+ response = 'Proper usage: uac [get|interactive|secure]';
2797
+ break;
2798
+ }
2799
+ }
2800
+ break;
2801
+ case 'vm':
2802
+ response = 'Virtual Machine = ' + require('identifiers').isVM();
2803
+ break;
2804
+ case 'startupoptions':
2805
+ response = JSON.stringify(require('MeshAgent').getStartupOptions());
2806
+ break;
2807
+ case 'kvmmode':
2808
+ if (require('MeshAgent').maxKvmTileSize == null)
2809
+ {
2810
+ response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2811
+ }
2812
+ else
2813
+ {
2814
+ if(require('MeshAgent').maxKvmTileSize == 0)
2815
+ {
2816
+ response = 'KVM Mode: Full JUMBO';
2817
+ }
2818
+ else
2819
+ {
2820
+ response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2821
+ response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2822
+ }
2823
+ }
2824
+ break;
2825
+ case 'alert':
2826
+ if (args['_'].length == 0)
2827
+ {
2828
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2829
+ }
2830
+ else
2831
+ {
2832
+ var p = args['_'].join(' ').split(',');
2833
+ if(p.length<2)
2834
+ {
2835
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2836
+ }
2837
+ else
2838
+ {
2839
+ this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2840
+ }
2841
+ }
2842
+ break;
2843
+ case 'agentsize':
2844
+ var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2845
+ if (process.platform == 'win32') {
2846
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2847
+ var writtenSize = 0;
2848
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
2849
+ if (writtenSize != actualSize) {
2850
+ response = "Size updated from: " + writtenSize + " to: " + actualSize;
2851
+ try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
2852
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2853
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2854
+ break;
2855
+ case 'versions':
2856
+ response = JSON.stringify(process.versions, null, ' ');
2857
+ break;
2858
+ case 'wpfhwacceleration':
2859
+ if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2860
+ if (args['_'].length != 1) {
2861
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2862
+ }
2863
+ else {
2864
+ var reg = require('win-registry');
2865
+ var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2866
+ var key = reg.usernameToUserKey(uname);
2867
+
2868
+ switch (args['_'][0].toUpperCase()) {
2869
+ default:
2870
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2871
+ break;
2872
+ case 'ON':
2873
+ try {
2874
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2875
+ response = "OK";
2876
+ } catch (e) { response = "FAILED"; }
2877
+ break;
2878
+ case 'OFF':
2879
+ try {
2880
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2881
+ response = 'OK';
2882
+ } catch (e) { response = 'FAILED'; }
2883
+ break;
2884
+ case 'STATUS':
2885
+ var s;
2886
+ try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (e) { s = 'DEFAULT'; }
2887
+ response = "WPF Hardware Acceleration: " + s;
2888
+ break;
2889
+ case 'DEFAULT':
2890
+ try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (e) { }
2891
+ response = 'OK';
2892
+ break;
2893
+ }
2894
+ }
2895
+ break;
2896
+ case 'tsid':
2897
+ if (process.platform == 'win32') {
2898
+ if (args['_'].length != 1) {
2899
+ response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2900
+ } else {
2901
+ var i = parseInt(args['_'][0]);
2902
+ require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2903
+ response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2904
+ }
2905
+ } else { response = "TSID command only supported on Windows"; }
2906
+ break;
2907
+ case 'activeusers':
2908
+ if (process.platform == 'win32') {
2909
+ var p = require('user-sessions').enumerateUsers();
2910
+ p.sessionid = sessionid;
2911
+ p.then(function (u) {
2912
+ var v = [];
2913
+ for (var i in u) {
2914
+ if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2915
+ }
2916
+ sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2917
+ });
2918
+ } else { response = "activeusers command only supported on Windows"; }
2919
+ break;
2920
+ case 'wallpaper':
2921
+ if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2922
+ response = "wallpaper command not supported on this platform";
2923
+ }
2924
+ else {
2925
+ if (args['_'].length != 1) {
2926
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2927
+ }
2928
+ else {
2929
+ switch (args['_'][0].toUpperCase()) {
2930
+ default:
2931
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2932
+ break;
2933
+ case 'GET':
2934
+ case 'TOGGLE':
2935
+ if (process.platform == 'win32') {
2936
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2937
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2938
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2939
+ child.stderr.on('data', function () { });
2940
+ child.waitExit();
2941
+ var current = child.stdout.str.trim();
2942
+ if (args['_'][0].toUpperCase() == 'GET') {
2943
+ response = current;
2944
+ break;
2945
+ }
2946
+ if (current != '') {
2947
+ require('MeshAgent')._wallpaper = current;
2948
+ response = 'Wallpaper cleared';
2949
+ } else {
2950
+ response = 'Wallpaper restored';
2951
+ }
2952
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2953
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2954
+ child.stderr.on('data', function () { });
2955
+ child.waitExit();
2956
+ }
2957
+ else {
2958
+ var id = require('user-sessions').consoleUid();
2959
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2960
+ if (args['_'][0].toUpperCase() == 'GET') {
2961
+ response = current;
2962
+ break;
2963
+ }
2964
+ if (current != '/dev/null') {
2965
+ require('MeshAgent')._wallpaper = current;
2966
+ response = 'Wallpaper cleared';
2967
+ } else {
2968
+ response = 'Wallpaper restored';
2969
+ }
2970
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2971
+ }
2972
+ break;
2973
+ }
2974
+ }
2975
+ }
2976
+ break;
2977
+ case 'safemode':
2978
+ if (process.platform != 'win32') {
2979
+ response = 'safemode only supported on Windows Platforms'
2980
+ }
2981
+ else {
2982
+ if (args['_'].length != 1) {
2983
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2984
+ }
2985
+ else {
2986
+ switch (args['_'][0].toUpperCase()) {
2987
+ default:
2988
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2989
+ break;
2990
+ case 'ON':
2991
+ require('win-bcd').setKey('safeboot', 'Network');
2992
+ require('win-bcd').enableSafeModeService('Mesh Agent');
2993
+ break;
2994
+ case 'OFF':
2995
+ require('win-bcd').deleteKey('safeboot');
2996
+ break;
2997
+ case 'STATUS':
2998
+ var nextboot = require('win-bcd').getKey('safeboot');
2999
+ if (nextboot) {
3000
+ switch (nextboot) {
3001
+ case 'Network':
3002
+ case 'network':
3003
+ nextboot = 'SAFE_MODE_NETWORK';
3004
+ break;
3005
+ default:
3006
+ nextboot = 'SAFE_MODE';
3007
+ break;
3008
+ }
3009
+ }
3010
+ response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
3011
+ break;
3012
+ }
3013
+ }
3014
+ }
3015
+ break;
3016
+ /*
3017
+ case 'border':
3018
+ {
3019
+ if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
3020
+ if (meshCoreObj.users.length > 0) {
3021
+ obj.borderManager.Start(meshCoreObj.users[0]);
3022
+ response = 'Border blinking is on.';
3023
+ } else {
3024
+ response = 'Cannot turn on border blinking, no logged in users.';
3025
+ }
3026
+ } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3027
+ obj.borderManager.Stop();
3028
+ response = 'Border blinking is off.';
3029
+ } else {
3030
+ response = 'Proper usage: border "on|off"'; // Display correct command usage
3031
+ }
3032
+ }
3033
+ break;
3034
+ */
3035
+ case 'av':
3036
+ if (process.platform == 'win32') {
3037
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3038
+ response = JSON.stringify(require('win-info').av(), null, 1);
3039
+ } else {
3040
+ response = 'Not supported on the platform';
3041
+ }
3042
+ break;
3043
+ case 'log':
3044
+ if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
3045
+ break;
3046
+ case 'getclip':
3047
+ if (require('MeshAgent').isService) {
3048
+ require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
3049
+ } else {
3050
+ require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
3051
+ }
3052
+ break;
3053
+ case 'setclip': {
3054
+ if (args['_'].length != 1) {
3055
+ response = 'Proper usage: setclip "sample text"';
3056
+ } else {
3057
+ if (require('MeshAgent').isService) {
3058
+ require('clipboard').dispatchWrite(args['_'][0]);
3059
+ response = 'Setting clipboard to: "' + args['_'][0] + '"';
3060
+ }
3061
+ else {
3062
+ require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3063
+ }
3064
+ }
3065
+ break;
3066
+ }
3067
+ /*
3068
+ case 'amtreset': {
3069
+ if (amt != null) { amt.reset(); response = 'Done.'; }
3070
+ break;
3071
+ }
3072
+ case 'amtlmsreset': {
3073
+ if (amt != null) { amt.lmsreset(); response = 'Done.'; }
3074
+ break;
3075
+ }
3076
+ case 'amtccm': {
3077
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
3078
+ if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
3079
+ else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
3080
+ }
3081
+ break;
3082
+ }
3083
+ case 'amtacm': {
3084
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
3085
+ amt.setPolicy({ type: 0 });
3086
+ amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
3087
+ }
3088
+ break;
3089
+ }
3090
+ case 'amtdeactivate': {
3091
+ if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
3092
+ break;
3093
+ }
3094
+ */
3095
+ case 'openurl': {
3096
+ if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
3097
+ else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
3098
+ break;
3099
+ }
3100
+ case 'users': {
3101
+ if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
3102
+ require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
3103
+ break;
3104
+ }
3105
+ case 'toast': {
3106
+ if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
3107
+ if (require('MeshAgent')._tsid == null) {
3108
+ require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
3109
+ }
3110
+ else {
3111
+ require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
3112
+ }
3113
+ }
3114
+ break;
3115
+ }
3116
+ case 'setdebug': {
3117
+ if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
3118
+ else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
3119
+ break;
3120
+ }
3121
+ case 'ps': {
3122
+ processManager.getProcesses(function (plist) {
3123
+ var x = '';
3124
+ for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
3125
+ sendConsoleText(x, sessionid);
3126
+ });
3127
+ break;
3128
+ }
3129
+ case 'kill': {
3130
+ if ((args['_'].length < 1)) {
3131
+ response = 'Proper usage: kill [pid]'; // Display correct command usage
3132
+ } else {
3133
+ process.kill(parseInt(args['_'][0]));
3134
+ response = 'Killed process ' + args['_'][0] + '.';
3135
+ }
3136
+ break;
3137
+ }
3138
+ case 'smbios': {
3139
+ if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
3140
+ break;
3141
+ }
3142
+ case 'rawsmbios': {
3143
+ if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
3144
+ response = '';
3145
+ for (var i in SMBiosTablesRaw) {
3146
+ var header = false;
3147
+ for (var j in SMBiosTablesRaw[i]) {
3148
+ if (SMBiosTablesRaw[i][j].length > 0) {
3149
+ if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
3150
+ response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
3151
+ }
3152
+ }
3153
+ }
3154
+ }
3155
+ break;
3156
+ }
3157
+ case 'eval': { // Eval JavaScript
3158
+ if (args['_'].length < 1) {
3159
+ response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3160
+ } else {
3161
+ response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3162
+ }
3163
+ break;
3164
+ }
3165
+ case 'uninstallagent': // Uninstall this agent
3166
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
3167
+ if (!require('service-manager').manager.getService(agentName).isMe()) {
3168
+ response = 'Uininstall failed, this instance is not the service instance';
3169
+ } else {
3170
+ try { diagnosticAgent_uninstall(); } catch (e) { }
3171
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
3172
+ this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
3173
+ }
3174
+ break;
3175
+ case 'notify': { // Send a notification message to the mesh
3176
+ if (args['_'].length != 1) {
3177
+ response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
3178
+ } else {
3179
+ var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
3180
+ if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
3181
+ mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
3182
+ response = "ok";
3183
+ }
3184
+ break;
3185
+ }
3186
+ case 'cpuinfo': { // Return system information
3187
+ // CPU & memory utilization
3188
+ pr = require('sysinfo').cpuUtilization();
3189
+ pr.sessionid = sessionid;
3190
+ pr.then(function (data) {
3191
+ sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
3192
+ }, function (e) {
3193
+ sendConsoleText(e);
3194
+ });
3195
+ break;
3196
+ }
3197
+ case 'sysinfo': { // Return system information
3198
+ getSystemInformation(function (results, err) {
3199
+ if (results == null) { sendConsoleText(err, this.sessionid); } else {
3200
+ sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
3201
+ }
3202
+ });
3203
+ break;
3204
+ }
3205
+ case 'info': { // Return information about the agent and agent core module
3206
+ response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
3207
+ if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
3208
+ if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
3209
+ response += '\r\nModules: ' + addedModules.join(', ') + '.';
3210
+ response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
3211
+ response += '\r\lastMeInfo: ' + lastMeInfo + '.';
3212
+ var oldNodeId = db.Get('OldNodeId');
3213
+ if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
3214
+ if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
3215
+ break;
3216
+ }
3217
+ case 'osinfo': { // Return the operating system information
3218
+ var i = 1;
3219
+ if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
3220
+ for (var j = 0; j < i; j++) {
3221
+ var pr = require('os').name();
3222
+ pr.sessionid = sessionid;
3223
+ pr.then(function (v) {
3224
+ sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
3225
+ });
3226
+ }
3227
+ break;
3228
+ }
3229
+ case 'sendcaps': { // Send capability flags to the server
3230
+ if (args['_'].length == 0) {
3231
+ response = 'Proper usage: sendcaps (number)'; // Display correct command usage
3232
+ } else {
3233
+ meshCoreObj.caps = parseInt(args['_'][0]);
3234
+ mesh.SendCommand(meshCoreObj);
3235
+ response = JSON.stringify(meshCoreObj);
3236
+ }
3237
+ break;
3238
+ }
3239
+ case 'sendosdesc': { // Send OS description
3240
+ if (args['_'].length > 0) {
3241
+ meshCoreObj.osdesc = args['_'][0];
3242
+ mesh.SendCommand(meshCoreObj);
3243
+ response = JSON.stringify(meshCoreObj);
3244
+ } else {
3245
+ response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
3246
+ }
3247
+ break;
3248
+ }
3249
+ case 'args': { // Displays parsed command arguments
3250
+ response = 'args ' + objToString(args, 0, ' ', true);
3251
+ break;
3252
+ }
3253
+ case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
3254
+ var r = [];
3255
+ for (var i in args['_']) { r.push(args['_'][i]); }
3256
+ console.log(r.join(' '));
3257
+ response = 'Message printed on agent console.';
3258
+ break;
3259
+ }
3260
+ case 'type': { // Returns the content of a file
3261
+ if (args['_'].length == 0) {
3262
+ response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
3263
+ } else {
3264
+ var max = 4096;
3265
+ if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
3266
+ if (max > 4096) max = 4096;
3267
+ var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
3268
+ response = buf.toString();
3269
+ var i = response.indexOf('\n');
3270
+ if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
3271
+ if (r == max) response += '...';
3272
+ fs.closeSync(fd);
3273
+ }
3274
+ break;
3275
+ }
3276
+ case 'dbkeys': { // Return all data store keys
3277
+ response = JSON.stringify(db.Keys);
3278
+ break;
3279
+ }
3280
+ case 'dbget': { // Return the data store value for a given key
3281
+ if (db == null) { response = 'Database not accessible.'; break; }
3282
+ if (args['_'].length != 1) {
3283
+ response = 'Proper usage: dbget (key)'; // Display the value for a given database key
3284
+ } else {
3285
+ response = db.Get(args['_'][0]);
3286
+ }
3287
+ break;
3288
+ }
3289
+ case 'dbset': { // Set a data store key and value pair
3290
+ if (db == null) { response = 'Database not accessible.'; break; }
3291
+ if (args['_'].length != 2) {
3292
+ response = 'Proper usage: dbset (key) (value)'; // Set a database key
3293
+ } else {
3294
+ var r = db.Put(args['_'][0], args['_'][1]);
3295
+ response = 'Key set: ' + r;
3296
+ }
3297
+ break;
3298
+ }
3299
+ case 'dbcompact': { // Compact the data store
3300
+ if (db == null) { response = 'Database not accessible.'; break; }
3301
+ var r = db.Compact();
3302
+ response = 'Database compacted: ' + r;
3303
+ break;
3304
+ }
3305
+ case 'httpget': {
3306
+ if (consoleHttpRequest != null) {
3307
+ response = 'HTTP operation already in progress.';
3308
+ } else {
3309
+ if (args['_'].length != 1) {
3310
+ response = 'Proper usage: httpget (url)';
3311
+ } else {
3312
+ var options = http.parseUri(args['_'][0]);
3313
+ options.method = 'GET';
3314
+ if (options == null) {
3315
+ response = 'Invalid url.';
3316
+ } else {
3317
+ try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
3318
+ consoleHttpRequest.sessionid = sessionid;
3319
+ if (consoleHttpRequest != null) {
3320
+ consoleHttpRequest.end();
3321
+ response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
3322
+ }
3323
+ }
3324
+ }
3325
+ }
3326
+ break;
3327
+ }
3328
+ case 'wslist': { // List all web sockets
3329
+ response = '';
3330
+ for (var i in consoleWebSockets) {
3331
+ var httprequest = consoleWebSockets[i];
3332
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3333
+ }
3334
+ if (response == '') { response = 'no websocket sessions.'; }
3335
+ break;
3336
+ }
3337
+ case 'wsconnect': { // Setup a web socket
3338
+ if (args['_'].length == 0) {
3339
+ response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3340
+ } else {
3341
+ var httprequest = null;
3342
+ try {
3343
+ var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3344
+ options.rejectUnauthorized = 0;
3345
+ httprequest = http.request(options);
3346
+ } catch (e) { response = 'Invalid HTTP websocket request'; }
3347
+ if (httprequest != null) {
3348
+ httprequest.upgrade = onWebSocketUpgrade;
3349
+ httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3350
+
3351
+ var index = 1;
3352
+ while (consoleWebSockets[index]) { index++; }
3353
+ httprequest.sessionid = sessionid;
3354
+ httprequest.index = index;
3355
+ httprequest.url = args['_'][0];
3356
+ consoleWebSockets[index] = httprequest;
3357
+ response = 'New websocket session #' + index;
3358
+ }
3359
+ }
3360
+ break;
3361
+ }
3362
+ case 'wssend': { // Send data on a web socket
3363
+ if (args['_'].length == 0) {
3364
+ response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3365
+ for (var i in consoleWebSockets) {
3366
+ var httprequest = consoleWebSockets[i];
3367
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3368
+ }
3369
+ } else {
3370
+ var i = parseInt(args['_'][0]);
3371
+ var httprequest = consoleWebSockets[i];
3372
+ if (httprequest != undefined) {
3373
+ httprequest.s.write(args['_'][1]);
3374
+ response = 'ok';
3375
+ } else {
3376
+ response = 'Invalid web socket number';
3377
+ }
3378
+ }
3379
+ break;
3380
+ }
3381
+ case 'wsclose': { // Close a websocket
3382
+ if (args['_'].length == 0) {
3383
+ response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3384
+ } else {
3385
+ var i = parseInt(args['_'][0]);
3386
+ var httprequest = consoleWebSockets[i];
3387
+ if (httprequest != undefined) {
3388
+ if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3389
+ response = 'ok';
3390
+ } else {
3391
+ response = 'Invalid web socket number';
3392
+ }
3393
+ }
3394
+ break;
3395
+ }
3396
+ case 'tunnels': { // Show the list of current tunnels
3397
+ response = '';
3398
+ for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3399
+ if (response == '') { response = 'No websocket sessions.'; }
3400
+ break;
3401
+ }
3402
+ case 'ls': { // Show list of files and folders
3403
+ response = '';
3404
+ var xpath = '*';
3405
+ if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3406
+ response = 'List of ' + xpath + '\r\n';
3407
+ var results = fs.readdirSync(xpath);
3408
+ for (var i = 0; i < results.length; ++i) {
3409
+ var stat = null, p = obj.path.join(args['_'][0], results[i]);
3410
+ try { stat = fs.statSync(p); } catch (e) { }
3411
+ if ((stat == null) || (stat == undefined)) {
3412
+ response += (results[i] + "\r\n");
3413
+ } else {
3414
+ response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3415
+ }
3416
+ }
3417
+ break;
3418
+ }
3419
+ case 'lsx': { // Show list of files and folders
3420
+ response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3421
+ break;
3422
+ }
3423
+ case 'lock': { // Lock the current user out of the desktop
3424
+ if (process.platform == 'win32') { var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); response = 'Ok'; }
3425
+ else { response = 'Not supported on the platform'; }
3426
+ break;
3427
+ }
3428
+ case 'amt': { // Show Intel AMT status
3429
+ if (amt != null) {
3430
+ amt.getAmtInfo(function (state) {
3431
+ var resp = 'Intel AMT not detected.';
3432
+ if (state != null) { resp = objToString(state, 0, ' ', true); }
3433
+ sendConsoleText(resp, sessionid);
3434
+ });
3435
+ } else {
3436
+ response = 'Intel AMT not detected.';
3437
+ }
3438
+ break;
3439
+ }
3440
+ case 'netinfo': { // Show network interface information
3441
+ var interfaces = require('os').networkInterfaces();
3442
+ response = objToString(interfaces, 0, ' ', true);
3443
+ break;
3444
+ }
3445
+ case 'wakeonlan': { // Send wake-on-lan
3446
+ if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3447
+ response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3448
+ } else {
3449
+ var count = sendWakeOnLan(args['_'][0]);
3450
+ response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3451
+ }
3452
+ break;
3453
+ }
3454
+ case 'sendall': { // Send a message to all consoles on this mesh
3455
+ sendConsoleText(args['_'].join(' '));
3456
+ break;
3457
+ }
3458
+ case 'power': { // Execute a power action on this computer
3459
+ if (mesh.ExecPowerState == undefined) {
3460
+ response = 'Power command not supported on this agent.';
3461
+ } else {
3462
+ if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3463
+ response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
3464
+ } else {
3465
+ var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3466
+ response = 'Power action executed with return code: ' + r + '.';
3467
+ }
3468
+ }
3469
+ break;
3470
+ }
3471
+ case 'location': {
3472
+ getIpLocationData(function (location) {
3473
+ sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3474
+ });
3475
+ break;
3476
+ }
3477
+ case 'parseuri': {
3478
+ response = JSON.stringify(http.parseUri(args['_'][0]));
3479
+ break;
3480
+ }
3481
+ case 'scanwifi': {
3482
+ if (wifiScanner != null) {
3483
+ var wifiPresent = wifiScanner.hasWireless;
3484
+ if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3485
+ } else { response = "Wifi module not present."; }
3486
+ break;
3487
+ }
3488
+ case 'scanamt': {
3489
+ if (amtscanner != null) {
3490
+ if (args['_'].length != 1) {
3491
+ response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n'; // Display correct command usage
3492
+ } else {
3493
+ response = 'Scanning: ' + args['_'][0] + '...';
3494
+ amtscanner.scan(args['_'][0], 2000, function (data) {
3495
+ if (data.length > 0) {
3496
+ var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3497
+ for (var i in data) {
3498
+ var x = data[i];
3499
+ if (r != '') { r += '\r\n'; }
3500
+ r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3501
+ if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3502
+ if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3503
+ r += '.';
3504
+ }
3505
+ } else {
3506
+ r = 'No Intel AMT found.';
3507
+ }
3508
+ sendConsoleText(r);
3509
+ });
3510
+ }
3511
+ } else { response = "Intel AMT scanner module not present."; }
3512
+ break;
3513
+ }
3514
+ case 'modules': {
3515
+ response = JSON.stringify(addedModules);
3516
+ break;
3517
+ }
3518
+ case 'listservices': {
3519
+ var services = require('service-manager').manager.enumerateService();
3520
+ response = JSON.stringify(services, null, 1);
3521
+ break;
3522
+ }
3523
+ case 'getscript': {
3524
+ if (args['_'].length != 1) {
3525
+ response = "Proper usage: getscript [scriptNumber].";
3526
+ } else {
3527
+ mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3528
+ }
3529
+ break;
3530
+ }
3531
+ case 'diagnostic':
3532
+ {
3533
+ if (!mesh.DAIPC.listening) {
3534
+ response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3535
+ break;
3536
+ }
3537
+ var diag = diagnosticAgent_installCheck();
3538
+ if (diag) {
3539
+ if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3540
+ diagnosticAgent_uninstall();
3541
+ response = 'Diagnostic Agent uninstalled';
3542
+ }
3543
+ else {
3544
+ response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3545
+ }
3546
+ }
3547
+ else {
3548
+ if (args['_'].length == 1 && args['_'][0] == 'install') {
3549
+ diag = diagnosticAgent_installCheck(true);
3550
+ if (diag) {
3551
+ response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3552
+ }
3553
+ else {
3554
+ response = 'Diagnostic agent installation failed';
3555
+ }
3556
+ }
3557
+ else {
3558
+ response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3559
+ }
3560
+ }
3561
+ if (diag) { diag.close(); diag = null; }
3562
+ break;
3563
+ }
3564
+ case 'amtconfig': {
3565
+ if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
3566
+ if (amt == null) { response = "No Intel AMT support delected"; break; }
3567
+ getMeiState(15, function (state) {
3568
+ var rx = '';
3569
+ if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
3570
+ var apfarg = {
3571
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3572
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3573
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3574
+ mpskeepalive: 60000,
3575
+ clientname: state.OsHostname,
3576
+ clientaddress: '127.0.0.1',
3577
+ clientuuid: state.UUID,
3578
+ conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3579
+ meiState: state // MEI state will be passed to MPS server
3580
+ };
3581
+ if ((state.UUID == null) || (state.UUID.length != 36)) {
3582
+ rx = "Unable to get Intel AMT UUID";
3583
+ } else {
3584
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
3585
+ apftunnel.onJsonControl = function (data) {
3586
+ if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
3587
+ if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3588
+ if (data.action == 'deactivate') { // Request CCM deactivation
3589
+ var amtMeiModule, amtMei;
3590
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); break; }
3591
+ amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
3592
+ amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
3593
+ }
3594
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
3595
+ }
3596
+ apftunnel.onChannelClosed = function () { apftunnel = null; }
3597
+ try {
3598
+ apftunnel.connect();
3599
+ rx = "Started Intel AMT configuration";
3600
+ } catch (ex) {
3601
+ rx = JSON.stringify(ex);
3602
+ }
3603
+ }
3604
+ }
3605
+ if (rx != '') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: rx }); }
3606
+ });
3607
+ break;
3608
+ }
3609
+ case 'apf': {
3610
+ if (meshCoreObj.intelamt !== null) {
3611
+ if (args['_'].length == 1) {
3612
+ var connType = -1, connTypeStr = args['_'][0].toLowerCase();
3613
+ if (connTypeStr == 'lms') { connType = 2; }
3614
+ if (connTypeStr == 'relay') { connType = 1; }
3615
+ if (connTypeStr == 'cira') { connType = 0; }
3616
+ if (connTypeStr == 'off') { connType = -2; }
3617
+ if (connType >= 0) { // Connect
3618
+ var apfarg = {
3619
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3620
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3621
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3622
+ mpskeepalive: 60000,
3623
+ clientname: require('os').hostname(),
3624
+ clientaddress: '127.0.0.1',
3625
+ clientuuid: meshCoreObj.intelamt.uuid,
3626
+ conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3627
+ };
3628
+ if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
3629
+ response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
3630
+ } else {
3631
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
3632
+ apftunnel.onJsonControl = function (data) {
3633
+ if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
3634
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
3635
+ }
3636
+ apftunnel.onChannelClosed = function () { apftunnel = null; }
3637
+ try {
3638
+ apftunnel.connect();
3639
+ response = "Started APF tunnel";
3640
+ } catch (e) {
3641
+ response = JSON.stringify(e);
3642
+ }
3643
+ }
3644
+ } else if (connType == -2) { // Disconnect
3645
+ try {
3646
+ apftunnel.disconnect();
3647
+ response = "Stopped APF tunnel";
3648
+ } catch (e) {
3649
+ response = JSON.stringify(e);
3650
+ }
3651
+ apftunnel = null;
3652
+ } else {
3653
+ response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
3654
+ }
3655
+ } else {
3656
+ response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
3657
+ }
3658
+ } else {
3659
+ response = "APF tunnel requires Intel AMT";
3660
+ }
3661
+ break;
3662
+ }
3663
+ case 'plugin': {
3664
+ if (typeof args['_'][0] == 'string') {
3665
+ try {
3666
+ // Pass off the action to the plugin
3667
+ // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3668
+ // to control the output / actions here.
3669
+ response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3670
+ } catch (e) {
3671
+ response = "There was an error in the plugin (" + e + ")";
3672
+ }
3673
+ } else {
3674
+ response = "Proper usage: plugin [pluginName] [args].";
3675
+ }
3676
+ break;
3677
+ }
3678
+ default: { // This is an unknown command, return an error message
3679
+ response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3680
+ break;
3681
+ }
3682
+ }
3683
+ } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3684
+ if (response != null) { sendConsoleText(response, sessionid); }
3685
+ }
3686
+
3687
+ // Send a mesh agent console command
3688
+ function sendConsoleText(text, sessionid) {
3689
+ if (typeof text == 'object') { text = JSON.stringify(text); }
3690
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3691
+ }
3692
+
3693
+ // Called before the process exits
3694
+ //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3695
+
3696
+ // Called when the server connection state changes
3697
+ function handleServerConnection(state) {
3698
+ meshServerConnectionState = state;
3699
+ if (meshServerConnectionState == 0) {
3700
+ // Server disconnected
3701
+ if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3702
+ lastSelfInfo = null;
3703
+ } else {
3704
+ // Server connected, send mesh core information
3705
+ var oldNodeId = db.Get('OldNodeId');
3706
+ if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3707
+
3708
+ // Update the server with basic info, logged in users and more.
3709
+ mesh.SendCommand(meshCoreObj);
3710
+
3711
+ // Send SMBios tables if present
3712
+ if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3713
+
3714
+ // Update the server on more advanced stuff, like Intel ME and Network Settings
3715
+ meInfoStr = null;
3716
+ sendPeriodicServerUpdate();
3717
+ if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3718
+
3719
+ // Send any state messages
3720
+ if (Object.keys(tunnelUserCount.msg).length > 0) {
3721
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
3722
+ broadcastSessionsToRegisteredApps();
3723
+ }
3724
+
3725
+ // Send update of registered applications to the server
3726
+ updateRegisteredAppsToServer();
3727
+ }
3728
+
3729
+ // Send server state update to registered applications
3730
+ broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
3731
+ }
3732
+
3733
+ // Update the server with the latest network interface information
3734
+ var sendNetworkUpdateNagleTimer = null;
3735
+ function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3736
+ function sendNetworkUpdate(force) {
3737
+ sendNetworkUpdateNagleTimer = null;
3738
+
3739
+ // Update the network interfaces information data
3740
+ var netInfo = { netif2: require('os').networkInterfaces() };
3741
+ if (netInfo.netif2) {
3742
+ netInfo.action = 'netinfo';
3743
+ var netInfoStr = JSON.stringify(netInfo);
3744
+ if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3745
+ }
3746
+ }
3747
+
3748
+ // Called periodically to check if we need to send updates to the server
3749
+ function sendPeriodicServerUpdate(flags) {
3750
+ if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3751
+ if (!flags) { flags = 0xFFFFFFFF; }
3752
+
3753
+ if ((flags & 1) && (amt != null)) {
3754
+ // If we have a connected MEI, get Intel ME information
3755
+ amt.getAmtInfo(function (meinfo) {
3756
+ try {
3757
+ if (meinfo == null) return;
3758
+ var intelamt = {};
3759
+ if (amt != null)
3760
+ {
3761
+ switch(amt.lmsstate)
3762
+ {
3763
+ case 0: intelamt.microlms = 'DISABLED'; break;
3764
+ case 1: intelamt.microlms = 'CONNECTING'; break;
3765
+ case 2: intelamt.microlms = 'CONNECTED'; break;
3766
+ default: intelamt.microlms = 'unknown'; break;
3767
+ }
3768
+ }
3769
+ var p = false;
3770
+ if ((meinfo.Versions != null) && (meinfo.Versions.AMT != null)) { intelamt.ver = meinfo.Versions.AMT; p = true; if (meinfo.Versions.Sku != null) { intelamt.sku = parseInt(meinfo.Versions.Sku); } }
3771
+ if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3772
+ if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3773
+ if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3774
+ if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3775
+ if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3776
+ // Not activated and we have wired ethernet, look for the trusted DNS
3777
+ var dns = meinfo.DNS;
3778
+ if (dns == null) {
3779
+ // Trusted DNS not set, let's look for the OS network DNS suffix
3780
+ var interfaces = require('os').networkInterfaces();
3781
+ for (var i in interfaces) {
3782
+ for (var j in interfaces[i]) {
3783
+ if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3784
+ }
3785
+ }
3786
+ }
3787
+ if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3788
+ } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3789
+ if (p == true) {
3790
+ var meInfoStr = JSON.stringify(intelamt);
3791
+ if (meInfoStr != lastMeInfo) {
3792
+ meshCoreObj.intelamt = intelamt;
3793
+ mesh.SendCommand(meshCoreObj);
3794
+ lastMeInfo = meInfoStr;
3795
+ }
3796
+ }
3797
+ } catch (e) { }
3798
+ });
3799
+ }
3800
+
3801
+ if (flags & 2) {
3802
+ // Update network information
3803
+ sendNetworkUpdateNagle(false);
3804
+ }
3805
+
3806
+ if ((flags & 4) && (process.platform == 'win32')) {
3807
+ // Update anti-virus information
3808
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3809
+ var av, pr;
3810
+ try { av = require('win-info').av(); } catch (e) { av = null; } // Antivirus
3811
+ //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (e) { pr = null; } } // Pending reboot
3812
+ if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3813
+ }
3814
+ }
3815
+
3816
+ // Starting function
3817
+ obj.start = function () {
3818
+ // Setup the mesh agent event handlers
3819
+ mesh.AddCommandHandler(handleServerCommand);
3820
+ mesh.AddConnectHandler(handleServerConnection);
3821
+
3822
+ // Parse input arguments
3823
+ //var args = parseArgs(process.argv);
3824
+ //console.log(args);
3825
+
3826
+ //resetMicroLms();
3827
+
3828
+ // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3829
+ try {
3830
+ var userSession = require('user-sessions');
3831
+ userSession.on('changed', function onUserSessionChanged() {
3832
+ userSession.enumerateUsers().then(function (users) {
3833
+ var u = [], a = users.Active;
3834
+ for (var i = 0; i < a.length; i++) {
3835
+ var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3836
+ if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3837
+ }
3838
+ meshCoreObj.users = u;
3839
+ mesh.SendCommand(meshCoreObj);
3840
+ });
3841
+ });
3842
+ userSession.emit('changed');
3843
+ //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3844
+ //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3845
+ } catch (e) { }
3846
+ }
3847
+
3848
+ obj.stop = function () {
3849
+ mesh.AddCommandHandler(null);
3850
+ mesh.AddConnectHandler(null);
3851
+ }
3852
+
3853
+ function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3854
+ function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3855
+ function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3856
+
3857
+ function onWebSocketUpgrade(response, s, head) {
3858
+ sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3859
+ this.s = s;
3860
+ s.httprequest = this;
3861
+ s.end = onWebSocketClosed;
3862
+ s.data = onWebSocketData;
3863
+ }
3864
+
3865
+ // Get Intel MEI State in a flexible way
3866
+ // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network
3867
+ function getMeiState(flags, func) {
3868
+ var amtMeiModule, amtMei;
3869
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { func(null); return; }
3870
+ amtMei.on('error', function (e) { func(null); return; });
3871
+ try {
3872
+ var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
3873
+ amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
3874
+ if ((flags & 1) != 0) { amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); }
3875
+ amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
3876
+ amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated"
3877
+ amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
3878
+ amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } }); // Flag 2 = CCM, 4 = ACM
3879
+ //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
3880
+ if ((flags & 8) != 0) {
3881
+ amtMei.getLanInterfaceSettings(0, function (result) {
3882
+ if (result) {
3883
+ amtMeiTmpState.net0 = result;
3884
+ var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
3885
+ for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3886
+ }
3887
+ });
3888
+ amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
3889
+ }
3890
+ amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { amtMeiTmpState.UUID = result.uuid; } });
3891
+ if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); }
3892
+ amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } });
3893
+ if ((flags & 4) != 0) {
3894
+ amtMei.getHashHandles(function (handles) {
3895
+ if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); }
3896
+ var exitOnCount = handles.length;
3897
+ for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); }
3898
+ });
3899
+ }
3900
+ } catch (e) { if (func != null) { func(null); } return; }
3901
+ }
3902
+
3903
+ return obj;
3904
+}
3905
+
3906
+//
3907
+// Module startup
3908
+//
3909
+
3910
+try {
3911
+ var xexports = null, mainMeshCore = null;
3912
+ try { xexports = module.exports; } catch (e) { }
3913
+
3914
+ if (xexports != null) {
3915
+ // If we are running within NodeJS, export the core
3916
+ module.exports.createMeshCore = createMeshCore;
3917
+ } else {
3918
+ // If we are not running in NodeJS, launch the core
3919
+ mainMeshCore = createMeshCore();
3920
+ mainMeshCore.start(null);
3921
+ }
3922
+} catch (e) {
3923
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3924
+}
agents/meshcore-old.js
new
+3903
@@ -0,0 +1,3903 @@
1
+/*
2
+Copyright 2018-2020 Intel Corporation
3
+
4
+Licensed under the Apache License, Version 2.0 (the "License");
5
+you may not use this file except in compliance with the License.
6
+You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+Unless required by applicable law or agreed to in writing, software
11
+distributed under the License is distributed on an "AS IS" BASIS,
12
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+See the License for the specific language governing permissions and
14
+limitations under the License.
15
+*/
16
+
17
+process.on('uncaughtException', function (ex) {
18
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException1: " + ex });
19
+});
20
+
21
+// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
22
+//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
23
+
24
+// Mesh Rights
25
+var MNG_ERROR = 65;
26
+var MESHRIGHT_EDITMESH = 1;
27
+var MESHRIGHT_MANAGEUSERS = 2;
28
+var MESHRIGHT_MANAGECOMPUTERS = 4;
29
+var MESHRIGHT_REMOTECONTROL = 8;
30
+var MESHRIGHT_AGENTCONSOLE = 16;
31
+var MESHRIGHT_SERVERFILES = 32;
32
+var MESHRIGHT_WAKEDEVICE = 64;
33
+var MESHRIGHT_SETNOTES = 128;
34
+var MESHRIGHT_REMOTEVIEW = 256;
35
+var MESHRIGHT_NOTERMINAL = 512;
36
+var MESHRIGHT_NOFILES = 1024;
37
+var MESHRIGHT_NOAMT = 2048;
38
+var MESHRIGHT_LIMITEDINPUT = 4096;
39
+var MESHRIGHT_LIMITEVENTS = 8192;
40
+var MESHRIGHT_CHATNOTIFY = 16384;
41
+var MESHRIGHT_UNINSTALL = 32768;
42
+var MESHRIGHT_NODESKTOP = 65536;
43
+
44
+function createMeshCore(agent) {
45
+ var obj = {};
46
+ if (process.platform == 'win32' && require('user-sessions').isRoot()) {
47
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
48
+ try {
49
+ var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
50
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { }
51
+ if (writtenSize != actualSize) { try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { } }
52
+ } catch (x) { }
53
+
54
+ // Check to see if we are the Installed Mesh Agent Service, if we are, make sure we can run in Safe Mode
55
+ try {
56
+ var meshCheck = false;
57
+ try { meshCheck = require('service-manager').manager.getService('Mesh Agent').isMe(); } catch (e) { }
58
+ if (meshCheck && require('win-bcd').isSafeModeService && !require('win-bcd').isSafeModeService('Mesh Agent')) { require('win-bcd').enableSafeModeService('Mesh Agent'); }
59
+ } catch (e) { }
60
+ }
61
+
62
+ if (process.platform == 'darwin' && !process.versions) {
63
+ // This is an older MacOS Agent, so we'll need to check the service definition so that Auto-Update will function correctly
64
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
65
+ child.stdout.str = '';
66
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
67
+ child.stdin.write("cat /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist | tr '\n' '\.' | awk '{split($0, a, \"<key>KeepAlive</key>\"); split(a[2], b, \"<\"); split(b[2], c, \">\"); ");
68
+ child.stdin.write(" if(c[1]==\"dict\"){ split(a[2], d, \"</dict>\"); if(split(d[1], truval, \"<true/>\")>1) { split(truval[1], kn1, \"<key>\"); split(kn1[2], kn2, \"</key>\"); print kn2[1]; } }");
69
+ child.stdin.write(" else { split(c[1], ka, \"/\"); if(ka[1]==\"true\") {print \"ALWAYS\";} } }'\nexit\n");
70
+ child.waitExit();
71
+ if (child.stdout.str.trim() == 'Crashed') {
72
+ child = require('child_process').execFile('/bin/sh', ['sh']);
73
+ child.stdout.str = '';
74
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
75
+ child.stdin.write("launchctl list | grep 'meshagent' | awk '{ if($3==\"meshagent\"){print $1;}}'\nexit\n");
76
+ child.waitExit();
77
+
78
+ if (parseInt(child.stdout.str.trim()) == process.pid) {
79
+ // The currently running MeshAgent is us, so we can continue with the update
80
+ var plist = require('fs').readFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist').toString();
81
+ var tokens = plist.split('<key>KeepAlive</key>');
82
+ if (tokens[1].split('>')[0].split('<')[1] == 'dict') {
83
+ var tmp = tokens[1].split('</dict>');
84
+ tmp.shift();
85
+ tokens[1] = '\n <true/>' + tmp.join('</dict>');
86
+ tokens = tokens.join('<key>KeepAlive</key>');
87
+
88
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist', tokens);
89
+
90
+ var fix = '';
91
+ fix += ("function macosRepair()\n");
92
+ fix += ("{\n");
93
+ fix += (" var child = require('child_process').execFile('/bin/sh', ['sh']);\n");
94
+ fix += (" child.stdout.str = '';\n");
95
+ fix += (" child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });\n");
96
+ fix += (" child.stderr.on('data', function (chunk) { });\n");
97
+ fix += (" child.stdin.write('launchctl unload /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
98
+ fix += (" child.stdin.write('launchctl load /Library/LaunchDaemons/meshagent_osx64_LaunchDaemon.plist\\n');\n");
99
+ fix += (" child.stdin.write('rm /Library/LaunchDaemons/meshagentRepair.plist\\n');\n");
100
+ fix += (" child.stdin.write('rm " + process.cwd() + "/macosRepair.js\\n');\n");
101
+ fix += (" child.stdin.write('launchctl stop meshagentRepair\\nexit\\n');\n");
102
+ fix += (" child.waitExit();\n");
103
+ fix += ("}\n");
104
+ fix += ("macosRepair();\n");
105
+ fix += ("process.exit();\n");
106
+ require('fs').writeFileSync(process.cwd() + '/macosRepair.js', fix);
107
+
108
+ var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
109
+ plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
110
+ plist += '<plist version="1.0">\n';
111
+ plist += ' <dict>\n';
112
+ plist += ' <key>Label</key>\n';
113
+ plist += (' <string>meshagentRepair</string>\n');
114
+ plist += ' <key>ProgramArguments</key>\n';
115
+ plist += ' <array>\n';
116
+ plist += (' <string>' + process.execPath + '</string>\n');
117
+ plist += ' <string>macosRepair.js</string>\n';
118
+ plist += ' </array>\n';
119
+ plist += ' <key>WorkingDirectory</key>\n';
120
+ plist += (' <string>' + process.cwd() + '</string>\n');
121
+ plist += ' <key>RunAtLoad</key>\n';
122
+ plist += ' <true/>\n';
123
+ plist += ' </dict>\n';
124
+ plist += '</plist>';
125
+ require('fs').writeFileSync('/Library/LaunchDaemons/meshagentRepair.plist', plist);
126
+
127
+ child = require('child_process').execFile('/bin/sh', ['sh']);
128
+ child.stdout.str = '';
129
+ child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
130
+ child.stdin.write("launchctl load /Library/LaunchDaemons/meshagentRepair.plist\nexit\n");
131
+ child.waitExit();
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ // Create Secure IPC for Diagnostic Agent Communications
138
+ obj.DAIPC = require('net').createServer();
139
+ if (process.platform != 'win32') { try { require('fs').unlinkSync(process.cwd() + '/DAIPC'); } catch (e) { } }
140
+ obj.DAIPC.IPCPATH = process.platform == 'win32' ? ('\\\\.\\pipe\\' + require('_agentNodeId')() + '-DAIPC') : (process.cwd() + '/DAIPC');
141
+ try { obj.DAIPC.listen({ path: obj.DAIPC.IPCPATH, writableAll: true, maxConnections: 5 }); } catch (e) { }
142
+ obj.DAIPC._daipc = [];
143
+ obj.DAIPC.on('connection', function (c) {
144
+ c._send = function (j) {
145
+ var data = JSON.stringify(j);
146
+ var packet = Buffer.alloc(data.length + 4);
147
+ packet.writeUInt32LE(data.length + 4, 0);
148
+ Buffer.from(data).copy(packet, 4);
149
+ this.write(packet);
150
+ };
151
+ this._daipc.push(c);
152
+ c.parent = this;
153
+ c.on('end', function () { removeRegisteredApp(this); });
154
+ c.on('data', function (chunk) {
155
+ if (chunk.length < 4) { this.unshift(chunk); return; }
156
+ var len = chunk.readUInt32LE(0);
157
+ if (len > 8192) { removeRegisteredApp(this); this.end(); return; }
158
+ if (chunk.length < len) { this.unshift(chunk); return; }
159
+
160
+ var data = chunk.slice(4, len);
161
+ try { data = JSON.parse(data.toString()); } catch (e) { }
162
+ if ((data == null) || (typeof data.cmd != 'string')) return;
163
+
164
+ try {
165
+ switch (data.cmd) {
166
+ case 'requesthelp':
167
+ if (this._registered == null) return;
168
+ sendConsoleText('Request Help (' + this._registered + '): ' + data.value);
169
+ var help = {};
170
+ help[this._registered] = data.value;
171
+ try { mesh.SendCommand({ action: 'sessions', type: 'help', value: help }); } catch (e) { }
172
+ MeshServerLogEx(98, [this._registered, data.value], "Help Requested, user: " + this._registered + ", details: " + data.value, null);
173
+ break;
174
+ case 'cancelhelp':
175
+ if (this._registered == null) return;
176
+ sendConsoleText('Cancel Help (' + this._registered + ')');
177
+ try { mesh.SendCommand({ action: 'sessions', type: 'help', value: {} }); } catch (e) { }
178
+ break;
179
+ case 'register':
180
+ if (typeof data.value == 'string') {
181
+ this._registered = data.value;
182
+ var apps = {};
183
+ apps[data.value] = 1;
184
+ try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
185
+ this._send({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer, amt: (amt != null) });
186
+ }
187
+ break;
188
+ case 'query':
189
+ switch (data.value) {
190
+ case 'connection':
191
+ data.result = require('MeshAgent').ConnectedServer;
192
+ this._send(data);
193
+ break;
194
+ case 'descriptors':
195
+ require('ChainViewer').getSnapshot().then(function (f) {
196
+ this.tag.payload.result = f;
197
+ this.tag.ipc._send(this.tag.payload);
198
+ }).parentPromise.tag = { ipc: this, payload: data };
199
+ break;
200
+ }
201
+ break;
202
+ case 'amtstate':
203
+ if (amt == null) return;
204
+ var func = function amtStateFunc(state) { if (state != null) { amtStateFunc.pipe._send({ cmd: 'amtstate', value: state }); } }
205
+ func.pipe = this;
206
+ amt.getAmtInfo(func);
207
+ break;
208
+ case 'sessions':
209
+ this._send({ cmd: 'sessions', sessions: tunnelUserCount });
210
+ break;
211
+ }
212
+ }
213
+ catch (e) { removeRegisteredApp(this); this.end(); return; }
214
+ });
215
+ });
216
+
217
+ // Send current sessions to registered apps
218
+ function broadcastSessionsToRegisteredApps(x) {
219
+ broadcastToRegisteredApps({ cmd: 'sessions', sessions: tunnelUserCount });
220
+ }
221
+
222
+ // Send this object to all registered local applications
223
+ function broadcastToRegisteredApps(x) {
224
+ if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
225
+ for (var i in obj.DAIPC._daipc) { if (obj.DAIPC._daipc[i]._registered != null) { obj.DAIPC._daipc[i]._send(x); } }
226
+ }
227
+
228
+ // Send list of registered apps to the server
229
+ function updateRegisteredAppsToServer() {
230
+ if ((obj.DAIPC == null) || (obj.DAIPC._daipc == null)) return;
231
+ var apps = {};
232
+ for (var i in obj.DAIPC._daipc) { if (apps[obj.DAIPC._daipc[i]._registered] == null) { apps[obj.DAIPC._daipc[i]._registered] = 1; } else { apps[obj.DAIPC._daipc[i]._registered]++; } }
233
+ try { mesh.SendCommand({ action: 'sessions', type: 'app', value: apps }); } catch (e) { }
234
+ }
235
+
236
+ // Remove a registered app
237
+ function removeRegisteredApp(pipe) {
238
+ for (var i = obj.DAIPC._daipc.length - 1; i >= 0; i--) { if (obj.DAIPC._daipc[i] === pipe) { obj.DAIPC._daipc.splice(i, 1); } }
239
+ if (pipe._registered != null) updateRegisteredAppsToServer();
240
+ }
241
+
242
+ function diagnosticAgent_uninstall() {
243
+ require('service-manager').manager.uninstallService('meshagentDiagnostic');
244
+ require('task-scheduler').delete('meshagentDiagnostic/periodicStart');
245
+ };
246
+ function diagnosticAgent_installCheck(install) {
247
+ try {
248
+ var diag = require('service-manager').manager.getService('meshagentDiagnostic');
249
+ return (diag);
250
+ }
251
+ catch (e) {
252
+ }
253
+ if (!install) { return (null); }
254
+
255
+ var svc = null;
256
+ try {
257
+ require('service-manager').manager.installService(
258
+ {
259
+ name: 'meshagentDiagnostic',
260
+ displayName: "Mesh Agent Diagnostic Service",
261
+ description: "Mesh Agent Diagnostic Service",
262
+ servicePath: process.execPath,
263
+ parameters: ['-recovery']
264
+ //files: [{ newName: 'diagnostic.js', _buffer: Buffer.from('LyoNCkNvcHlyaWdodCAyMDE5IEludGVsIENvcnBvcmF0aW9uDQoNCkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSAiTGljZW5zZSIpOw0KeW91IG1heSBub3QgdXNlIHRoaXMgZmlsZSBleGNlcHQgaW4gY29tcGxpYW5jZSB3aXRoIHRoZSBMaWNlbnNlLg0KWW91IG1heSBvYnRhaW4gYSBjb3B5IG9mIHRoZSBMaWNlbnNlIGF0DQoNCiAgICBodHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjANCg0KVW5sZXNzIHJlcXVpcmVkIGJ5IGFwcGxpY2FibGUgbGF3IG9yIGFncmVlZCB0byBpbiB3cml0aW5nLCBzb2Z0d2FyZQ0KZGlzdHJpYnV0ZWQgdW5kZXIgdGhlIExpY2Vuc2UgaXMgZGlzdHJpYnV0ZWQgb24gYW4gIkFTIElTIiBCQVNJUywNCldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLg0KU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZA0KbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuDQoqLw0KDQp2YXIgaG9zdCA9IHJlcXVpcmUoJ3NlcnZpY2UtaG9zdCcpLmNyZWF0ZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpOw0KdmFyIFJlY292ZXJ5QWdlbnQgPSByZXF1aXJlKCdNZXNoQWdlbnQnKTsNCg0KaG9zdC5vbignc2VydmljZVN0YXJ0JywgZnVuY3Rpb24gKCkNCnsNCiAgICBjb25zb2xlLnNldERlc3RpbmF0aW9uKGNvbnNvbGUuRGVzdGluYXRpb25zLkxPR0ZJTEUpOw0KICAgIGhvc3Quc3RvcCA9IGZ1bmN0aW9uKCkNCiAgICB7DQogICAgICAgIHJlcXVpcmUoJ3NlcnZpY2UtbWFuYWdlcicpLm1hbmFnZXIuZ2V0U2VydmljZSgnbWVzaGFnZW50RGlhZ25vc3RpYycpLnN0b3AoKTsNCiAgICB9DQogICAgUmVjb3ZlcnlBZ2VudC5vbignQ29ubmVjdGVkJywgZnVuY3Rpb24gKHN0YXR1cykNCiAgICB7DQogICAgICAgIGlmIChzdGF0dXMgPT0gMCkNCiAgICAgICAgew0KICAgICAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IFNlcnZlciBjb25uZWN0aW9uIGxvc3QuLi4nKTsNCiAgICAgICAgICAgIHJldHVybjsNCiAgICAgICAgfQ0KICAgICAgICBjb25zb2xlLmxvZygnRGlhZ25vc3RpYyBBZ2VudDogQ29ubmVjdGlvbiBFc3RhYmxpc2hlZCB3aXRoIFNlcnZlcicpOw0KICAgICAgICBzdGFydCgpOw0KICAgIH0pOw0KfSk7DQpob3N0Lm9uKCdub3JtYWxTdGFydCcsIGZ1bmN0aW9uICgpDQp7DQogICAgaG9zdC5zdG9wID0gZnVuY3Rpb24gKCkNCiAgICB7DQogICAgICAgIHByb2Nlc3MuZXhpdCgpOw0KICAgIH0NCiAgICBjb25zb2xlLmxvZygnTm9uIFNlcnZpY2UgTW9kZScpOw0KICAgIFJlY292ZXJ5QWdlbnQub24oJ0Nvbm5lY3RlZCcsIGZ1bmN0aW9uIChzdGF0dXMpDQogICAgew0KICAgICAgICBpZiAoc3RhdHVzID09IDApDQogICAgICAgIHsNCiAgICAgICAgICAgIGNvbnNvbGUubG9nKCdEaWFnbm9zdGljIEFnZW50OiBTZXJ2ZXIgY29ubmVjdGlvbiBsb3N0Li4uJyk7DQogICAgICAgICAgICByZXR1cm47DQogICAgICAgIH0NCiAgICAgICAgY29uc29sZS5sb2coJ0RpYWdub3N0aWMgQWdlbnQ6IENvbm5lY3Rpb24gRXN0YWJsaXNoZWQgd2l0aCBTZXJ2ZXInKTsNCiAgICAgICAgc3RhcnQoKTsNCiAgICB9KTsNCn0pOw0KaG9zdC5vbignc2VydmljZVN0b3AnLCBmdW5jdGlvbiAoKSB7IHByb2Nlc3MuZXhpdCgpOyB9KTsNCmhvc3QucnVuKCk7DQoNCg0KZnVuY3Rpb24gc3RhcnQoKQ0Kew0KDQp9Ow0K', 'base64') }]
265
+ });
266
+ svc = require('service-manager').manager.getService('meshagentDiagnostic');
267
+ }
268
+ catch (e) {
269
+ return (null);
270
+ }
271
+ var proxyConfig = require('global-tunnel').proxyConfig;
272
+ var cert = require('MeshAgent').GenerateAgentCertificate('CN=MeshNodeDiagnosticCertificate');
273
+ var nodeid = require('tls').loadCertificate(cert.root).getKeyHash().toString('base64');
274
+ ddb = require('SimpleDataStore').Create(svc.appWorkingDirectory().replace('\\', '/') + '/meshagentDiagnostic.db');
275
+ ddb.Put('disableUpdate', '1');
276
+ ddb.Put('MeshID', Buffer.from(require('MeshAgent').ServerInfo.MeshID, 'hex'));
277
+ ddb.Put('ServerID', require('MeshAgent').ServerInfo.ServerID);
278
+ ddb.Put('MeshServer', require('MeshAgent').ServerInfo.ServerUri);
279
+ if (cert.root.pfx) { ddb.Put('SelfNodeCert', cert.root.pfx); }
280
+ if (cert.tls) { ddb.Put('SelfNodeTlsCert', cert.tls.pfx); }
281
+ if (proxyConfig) {
282
+ ddb.Put('WebProxy', proxyConfig.host + ':' + proxyConfig.port);
283
+ } else {
284
+ ddb.Put('ignoreProxyFile', '1');
285
+ }
286
+
287
+ require('MeshAgent').SendCommand({ action: 'diagnostic', value: { command: 'register', value: nodeid } });
288
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "Diagnostic Agent Registered [" + nodeid.length + "/" + nodeid + "]" });
289
+
290
+ delete ddb;
291
+
292
+ // Set a recurrent task, to run the Diagnostic Agent every 2 days
293
+ require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: 2, time: require('tls').generateRandomInteger('0', '23') + ':' + require('tls').generateRandomInteger('0', '59').padStart(2, '0'), service: 'meshagentDiagnostic' });
294
+ //require('task-scheduler').create({ name: 'meshagentDiagnostic/periodicStart', daily: '1', time: '17:16', service: 'meshagentDiagnostic' });
295
+
296
+ return (svc);
297
+ }
298
+
299
+ // Monitor the file 'batterystate.txt' in the agent's folder and sends battery update when this file is changed.
300
+ if ((require('fs').existsSync(process.cwd() + 'batterystate.txt')) && (require('fs').watch != null)) {
301
+ // Setup manual battery monitoring
302
+ require('MeshAgent')._batteryFileWatcher = require('fs').watch(process.cwd(), function () {
303
+ if (require('MeshAgent')._batteryFileTimer != null) return;
304
+ require('MeshAgent')._batteryFileTimer = setTimeout(function () {
305
+ try {
306
+ require('MeshAgent')._batteryFileTimer = null;
307
+ var data = null;
308
+ try { data = require('fs').readFileSync(process.cwd() + 'batterystate.txt').toString(); } catch (e) { }
309
+ if ((data != null) && (data.length < 10)) {
310
+ data = data.split(',');
311
+ if ((data.length == 2) && ((data[0] == 'ac') || (data[0] == 'dc'))) {
312
+ var level = parseInt(data[1]);
313
+ if ((level >= 0) && (level <= 100)) { require('MeshAgent').SendCommand({ action: 'battery', state: data[0], level: level }); }
314
+ }
315
+ }
316
+ } catch (e) { }
317
+ }, 1000);
318
+ });
319
+ } else {
320
+ // Setup normal battery monitoring
321
+ if (require('identifiers').isBatteryPowered && require('identifiers').isBatteryPowered()) {
322
+ require('MeshAgent')._battLevelChanged = function _battLevelChanged(val) {
323
+ _battLevelChanged.self._currentBatteryLevel = val;
324
+ _battLevelChanged.self.SendCommand({ action: 'battery', state: _battLevelChanged.self._currentPowerState, level: val });
325
+ };
326
+ require('MeshAgent')._battLevelChanged.self = require('MeshAgent');
327
+ require('MeshAgent')._powerChanged = function _powerChanged(val) {
328
+ _powerChanged.self._currentPowerState = (val == 'AC' ? 'ac' : 'dc');
329
+ _powerChanged.self.SendCommand({ action: 'battery', state: (val == 'AC' ? 'ac' : 'dc'), level: _powerChanged.self._currentBatteryLevel });
330
+ };
331
+ require('MeshAgent')._powerChanged.self = require('MeshAgent');
332
+ require('MeshAgent').on('Connected', function (status) {
333
+ if (status == 0) {
334
+ require('power-monitor').removeListener('acdc', this._powerChanged);
335
+ require('power-monitor').removeListener('batteryLevel', this._battLevelChanged);
336
+ } else {
337
+ require('power-monitor').on('acdc', this._powerChanged);
338
+ require('power-monitor').on('batteryLevel', this._battLevelChanged);
339
+ }
340
+ });
341
+ }
342
+ }
343
+
344
+
345
+ /*
346
+ function borderController() {
347
+ this.container = null;
348
+ this.Start = function Start(user) {
349
+ if (this.container == null) {
350
+ if (process.platform == 'win32') {
351
+ try {
352
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
353
+ } catch (e) {
354
+ this.container = require('ScriptContainer').Create({ processIsolation: 1 });
355
+ }
356
+ } else {
357
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
358
+ }
359
+ this.container.parent = this;
360
+ this.container.addModule('monitor-info', getJSModule('monitor-info'));
361
+ this.container.addModule('monitor-border', getJSModule('monitor-border'));
362
+ this.container.addModule('promise', getJSModule('promise'));
363
+ this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
364
+ this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
365
+ }
366
+ }
367
+ this.Stop = function Stop() {
368
+ if (this.container != null) {
369
+ this._container = this.container;
370
+ this._container.parent = this;
371
+ this.container = null;
372
+ this._container.exit();
373
+ }
374
+ }
375
+ }
376
+ obj.borderManager = new borderController();
377
+ */
378
+
379
+ // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
380
+ var meshCoreObj = { action: 'coreinfo', value: (require('MeshAgent').coreHash ? ('MeshCore CRC-' + crc32c(require('MeshAgent').coreHash)) : ('MeshCore v6')), caps: 14, root: require('user-sessions').isRoot() }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript, 32 = Temporary Agent, 64 = Recovery Agent
381
+
382
+
383
+ // Get the operating system description string
384
+ try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (e) { }
385
+
386
+ var meshServerConnectionState = 0;
387
+ var tunnels = {};
388
+ var lastMeInfo = null;
389
+ var lastNetworkInfo = null;
390
+ var lastPublicLocationInfo = null;
391
+ var selfInfoUpdateTimer = null;
392
+ var http = require('http');
393
+ var net = require('net');
394
+ var fs = require('fs');
395
+ var rtc = require('ILibWebRTC');
396
+ var amt = null;
397
+ var processManager = require('process-manager');
398
+ var wifiScannerLib = null;
399
+ var wifiScanner = null;
400
+ var networkMonitor = null;
401
+ var amtscanner = null;
402
+ var nextTunnelIndex = 1;
403
+ var amtPolicy = null;
404
+ var apftunnel = null;
405
+ var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
406
+
407
+ // Add to the server event log
408
+ function MeshServerLog(msg, state) {
409
+ if (typeof msg == 'string') { msg = { action: 'log', msg: msg }; } else { msg.action = 'log'; }
410
+ if (state) {
411
+ if (state.userid) { msg.userid = state.userid; }
412
+ if (state.username) { msg.username = state.username; }
413
+ if (state.sessionid) { msg.sessionid = state.sessionid; }
414
+ if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
415
+ }
416
+ mesh.SendCommand(msg);
417
+ }
418
+
419
+ // Add to the server event log, use internationalized events
420
+ function MeshServerLogEx(id, args, msg, state) {
421
+ var msg = { action: 'log', msgid: id, msgArgs: args, msg: msg };
422
+ if (state) {
423
+ if (state.userid) { msg.userid = state.userid; }
424
+ if (state.username) { msg.username = state.username; }
425
+ if (state.sessionid) { msg.sessionid = state.sessionid; }
426
+ if (state.remoteaddr) { msg.remoteaddr = state.remoteaddr; }
427
+ }
428
+ mesh.SendCommand(msg);
429
+ }
430
+
431
+ // If we are running in Duktape, agent will be null
432
+ if (agent == null) {
433
+ // Running in native agent, Import libraries
434
+ db = require('SimpleDataStore').Shared();
435
+ sha = require('SHA256Stream');
436
+ mesh = require('MeshAgent');
437
+ childProcess = require('child_process');
438
+ if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
439
+ // Check if this computer supports a desktop
440
+ try
441
+ {
442
+ if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support))
443
+ {
444
+ meshCoreObj.caps |= 1;
445
+ }
446
+ else if(process.platform == 'linux' || process.platform == 'freebsd')
447
+ {
448
+ require('monitor-info').on('kvmSupportDetected', function (value)
449
+ {
450
+ meshCoreObj.caps |= 1;
451
+ mesh.SendCommand(meshCoreObj);
452
+ });
453
+ }
454
+ } catch (e) { }
455
+ }
456
+ } else {
457
+ // Running in nodejs
458
+ meshCoreObj.value += '-NodeJS';
459
+ meshCoreObj.caps = 8;
460
+ mesh = agent.getMeshApi();
461
+ }
462
+
463
+ mesh.DAIPC = obj.DAIPC;
464
+
465
+ /*
466
+ var AMTScanner = require("AMTScanner");
467
+ var scan = new AMTScanner();
468
+
469
+ scan.on("found", function (data) {
470
+ if (typeof data === 'string') {
471
+ console.log(data);
472
+ } else {
473
+ console.log(JSON.stringify(data, null, " "));
474
+ }
475
+ });
476
+ scan.scan("10.2.55.140", 1000);
477
+ scan.scan("10.2.55.139-10.2.55.145", 1000);
478
+ scan.scan("10.2.55.128/25", 2000);
479
+ */
480
+
481
+ /*
482
+ // Try to load up the network monitor
483
+ try {
484
+ networkMonitor = require('NetworkMonitor');
485
+ networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
486
+ networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
487
+ networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
488
+ } catch (e) { networkMonitor = null; }
489
+ */
490
+
491
+ // Try to load up the Intel AMT scanner
492
+ try {
493
+ var AMTScannerModule = require('amt-scanner');
494
+ amtscanner = new AMTScannerModule();
495
+ //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
496
+ } catch (e) { amtscanner = null; }
497
+
498
+ // Fetch the SMBios Tables
499
+ var SMBiosTables = null;
500
+ var SMBiosTablesRaw = null;
501
+ try {
502
+ var SMBiosModule = null;
503
+ try { SMBiosModule = require('smbios'); } catch (e) { }
504
+ if (SMBiosModule != null) {
505
+ SMBiosModule.get(function (data) {
506
+ if (data != null) {
507
+ SMBiosTablesRaw = data;
508
+ SMBiosTables = require('smbios').parse(data)
509
+ if (mesh.isControlChannelConnected) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
510
+
511
+ // If SMBios tables say that Intel AMT is present, try to connect MEI
512
+ if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true))
513
+ {
514
+ var amtmodule = require('amt-manage');
515
+ amt = new amtmodule(mesh, db, false);
516
+ amt.on('portBinding_LMS', function (map)
517
+ {
518
+ var j = { action: 'lmsinfo', value: { ports: map.keys() } };
519
+ mesh.SendCommand(j);
520
+ });
521
+ amt.on('stateChange_LMS', function (v)
522
+ {
523
+ if (!meshCoreObj.intelamt) { meshCoreObj.intelamt = {}; }
524
+ switch(v)
525
+ {
526
+ case 0:
527
+ meshCoreObj.intelamt.microlms = 'DISABLED';
528
+ break;
529
+ case 1:
530
+ meshCoreObj.intelamt.microlms = 'CONNECTING';
531
+ break;
532
+ case 2:
533
+ meshCoreObj.intelamt.microlms = 'CONNECTED';
534
+ break;
535
+ default:
536
+ break;
537
+ }
538
+ mesh.SendCommand(meshCoreObj);
539
+ });
540
+ amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
541
+ if (amtPolicy != null) { amt.setPolicy(amtPolicy); }
542
+ amt.start();
543
+ }
544
+ }
545
+ });
546
+ }
547
+ } catch (e) { sendConsoleText("ex1: " + e); }
548
+
549
+ // Try to load up the WIFI scanner
550
+ try {
551
+ var wifiScannerLib = require('wifi-scanner');
552
+ wifiScanner = new wifiScannerLib();
553
+ wifiScanner.on('accessPoint', function (data) { sendConsoleText("wifiScanner: " + data); });
554
+ } catch (e) { wifiScannerLib = null; wifiScanner = null; }
555
+
556
+ // Get our location (lat/long) using our public IP address
557
+ var getIpLocationDataExInProgress = false;
558
+ var getIpLocationDataExCounts = [0, 0];
559
+ function getIpLocationDataEx(func) {
560
+ if (getIpLocationDataExInProgress == true) { return false; }
561
+ try {
562
+ getIpLocationDataExInProgress = true;
563
+ getIpLocationDataExCounts[0]++;
564
+ var options = http.parseUri("http://ipinfo.io/json");
565
+ options.method = 'GET';
566
+ http.request(options, function (resp) {
567
+ if (resp.statusCode == 200) {
568
+ var geoData = '';
569
+ resp.data = function (geoipdata) { geoData += geoipdata; };
570
+ resp.end = function () {
571
+ var location = null;
572
+ try {
573
+ if (typeof geoData == 'string') {
574
+ var result = JSON.parse(geoData);
575
+ if (result.ip && result.loc) { location = result; }
576
+ }
577
+ } catch (e) { }
578
+ if (func) { getIpLocationDataExCounts[1]++; func(location); }
579
+ }
580
+ } else { func(null); }
581
+ getIpLocationDataExInProgress = false;
582
+ }).end();
583
+ return true;
584
+ }
585
+ catch (e) { return false; }
586
+ }
587
+
588
+ // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
589
+ function clearGatewayMac(str) {
590
+ if (str == null) return null;
591
+ var x = JSON.parse(str);
592
+ for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
593
+ return JSON.stringify(x);
594
+ }
595
+
596
+ function getIpLocationData(func) {
597
+ // Get the location information for the cache if possible
598
+ var publicLocationInfo = db.Get('publicLocationInfo');
599
+ if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
600
+ if (publicLocationInfo == null) {
601
+ // Nothing in the cache, fetch the data
602
+ getIpLocationDataEx(function (locationData) {
603
+ if (locationData != null) {
604
+ publicLocationInfo = {};
605
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
606
+ publicLocationInfo.locationData = locationData;
607
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
608
+ if (func) func(locationData); // Report the new location
609
+ } else {
610
+ if (func) func(null); // Report no location
611
+ }
612
+ });
613
+ } else {
614
+ // Check the cache
615
+ if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
616
+ // Cache match
617
+ if (func) func(publicLocationInfo.locationData);
618
+ } else {
619
+ // Cache mismatch
620
+ getIpLocationDataEx(function (locationData) {
621
+ if (locationData != null) {
622
+ publicLocationInfo = {};
623
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
624
+ publicLocationInfo.locationData = locationData;
625
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
626
+ if (func) func(locationData); // Report the new location
627
+ } else {
628
+ if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
629
+ }
630
+ });
631
+ }
632
+ }
633
+ }
634
+
635
+ // Polyfill String.endsWith
636
+ if (!String.prototype.endsWith) {
637
+ String.prototype.endsWith = function (searchString, position) {
638
+ var subjectString = this.toString();
639
+ if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
640
+ position -= searchString.length;
641
+ var lastIndex = subjectString.lastIndexOf(searchString, position);
642
+ return lastIndex !== -1 && lastIndex === position;
643
+ };
644
+ }
645
+
646
+ // Polyfill path.join
647
+ obj.path = {
648
+ join: function () {
649
+ var x = [];
650
+ for (var i in arguments) {
651
+ var w = arguments[i];
652
+ if (w != null) {
653
+ while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
654
+ if (i != 0) {
655
+ while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
656
+ }
657
+ x.push(w);
658
+ }
659
+ }
660
+ if (x.length == 0) return '/';
661
+ return x.join('/');
662
+ }
663
+ };
664
+
665
+ // Replace a string with a number if the string is an exact number
666
+ function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
667
+
668
+ // Convert decimal to hex
669
+ function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
670
+
671
+ // Convert a raw string to a hex string
672
+ function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
673
+
674
+ // Convert a buffer into a string
675
+ function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
676
+
677
+ // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
678
+ function hex2rstr(d) {
679
+ if (typeof d != "string" || d.length == 0) return '';
680
+ var r = '', m = ('' + d).match(/../g), t;
681
+ while (t = m.shift()) r += String.fromCharCode('0x' + t);
682
+ return r
683
+ }
684
+
685
+ // Convert an object to string with all functions
686
+ function objToString(x, p, pad, ret) {
687
+ if (ret == undefined) ret = '';
688
+ if (p == undefined) p = 0;
689
+ if (x == null) { return '[null]'; }
690
+ if (p > 8) { return '[...]'; }
691
+ if (x == undefined) { return '[undefined]'; }
692
+ if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
693
+ if (typeof x == 'buffer') { return '[buffer]'; }
694
+ if (typeof x != 'object') { return x; }
695
+ var r = '{' + (ret ? '\r\n' : ' ');
696
+ for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
697
+ return r + addPad(p, pad) + '}';
698
+ }
699
+
700
+ // Return p number of spaces
701
+ function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
702
+
703
+ // Split a string taking into account the quoats. Used for command line parsing
704
+ function splitArgs(str) {
705
+ var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
706
+ do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
707
+ return myArray;
708
+ }
709
+
710
+ // Parse arguments string array into an object
711
+ function parseArgs(argv) {
712
+ var results = { '_': [] }, current = null;
713
+ for (var i = 1, len = argv.length; i < len; i++) {
714
+ var x = argv[i];
715
+ if (x.length > 2 && x[0] == '-' && x[1] == '-') {
716
+ if (current != null) { results[current] = true; }
717
+ current = x.substring(2);
718
+ } else {
719
+ if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
720
+ }
721
+ }
722
+ if (current != null) { results[current] = true; }
723
+ return results;
724
+ }
725
+
726
+ // Get server target url with a custom path
727
+ function getServerTargetUrl(path) {
728
+ var x = mesh.ServerUrl;
729
+ //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
730
+ if (x == null) { return null; }
731
+ if (path == null) { path = ''; }
732
+ x = http.parseUri(x);
733
+ if (x == null) return null;
734
+ return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
735
+ }
736
+
737
+ // Get server url. If the url starts with "*/..." change it, it not use the url as is.
738
+ function getServerTargetUrlEx(url) {
739
+ if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
740
+ return url;
741
+ }
742
+
743
+ // Send a wake-on-lan packet
744
+ function sendWakeOnLan(hexMac) {
745
+ hexMac = hexMac.split(':').join('');
746
+ var count = 0;
747
+ try {
748
+ var interfaces = require('os').networkInterfaces();
749
+ var magic = 'FFFFFFFFFFFF';
750
+ for (var x = 1; x <= 16; ++x) { magic += hexMac; }
751
+ var magicbin = Buffer.from(magic, 'hex');
752
+
753
+ for (var adapter in interfaces) {
754
+ if (interfaces.hasOwnProperty(adapter)) {
755
+ for (var i = 0; i < interfaces[adapter].length; ++i) {
756
+ var addr = interfaces[adapter][i];
757
+ if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
758
+ try {
759
+ var socket = require('dgram').createSocket({ type: 'udp4' });
760
+ socket.bind({ address: addr.address });
761
+ socket.setBroadcast(true);
762
+ socket.setMulticastInterface(addr.address);
763
+ socket.setMulticastTTL(1);
764
+ socket.send(magicbin, 7, '255.255.255.255');
765
+ socket.descriptorMetadata = 'WoL (' + addr.address + ' => ' + hexMac + ')';
766
+ count++;
767
+ }
768
+ catch (e) { }
769
+ }
770
+ }
771
+ }
772
+ }
773
+ } catch (e) { }
774
+ return count;
775
+ }
776
+
777
+ // Handle a mesh agent command
778
+ function handleServerCommand(data) {
779
+ if (typeof data == 'object') {
780
+ // If this is a console command, parse it and call the console handler
781
+ switch (data.action) {
782
+ case 'msg': {
783
+ switch (data.type) {
784
+ case 'console': { // Process a console command
785
+ if (data.value && data.sessionid) {
786
+ MeshServerLogEx(17, [data.value], "Processing console command: " + data.value, data);
787
+ var args = splitArgs(data.value);
788
+ processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
789
+ }
790
+ break;
791
+ }
792
+ case 'tunnel': {
793
+ if (data.value != null) { // Process a new tunnel connection request
794
+ // Create a new tunnel object
795
+ var xurl = getServerTargetUrlEx(data.value);
796
+ if (xurl != null) {
797
+ xurl = xurl.split('$').join('%24').split('@').join('%40'); // Escape the $ and @ characters
798
+ var woptions = http.parseUri(xurl);
799
+ woptions.perMessageDeflate = false;
800
+ if (typeof data.perMessageDeflate == 'boolean') { woptions.perMessageDeflate = data.perMessageDeflate; }
801
+ woptions.rejectUnauthorized = 0;
802
+ //sendConsoleText(JSON.stringify(woptions));
803
+ //sendConsoleText('TUNNEL: ' + JSON.stringify(data));
804
+ var tunnel = http.request(woptions);
805
+ tunnel.upgrade = onTunnelUpgrade;
806
+ tunnel.on('error', function (e) { sendConsoleText("ERROR: Unable to connect relay tunnel to: " + this.url + ", " + JSON.stringify(e)); });
807
+ tunnel.sessionid = data.sessionid;
808
+ tunnel.rights = data.rights;
809
+ tunnel.consent = data.consent;
810
+ tunnel.privacybartext = data.privacybartext ? data.privacybartext : "Sharing desktop with: {0}";
811
+ tunnel.username = data.username + (data.guestname ? (' - ' + data.guestname) : '');
812
+ tunnel.realname = (data.realname ? data.realname : data.username) + (data.guestname ? (' - ' + data.guestname) : '');
813
+ tunnel.userid = data.userid;
814
+ tunnel.remoteaddr = data.remoteaddr;
815
+ tunnel.state = 0;
816
+ tunnel.url = xurl;
817
+ tunnel.protocol = 0;
818
+ tunnel.soptions = data.soptions;
819
+ tunnel.tcpaddr = data.tcpaddr;
820
+ tunnel.tcpport = data.tcpport;
821
+ tunnel.udpaddr = data.udpaddr;
822
+ tunnel.udpport = data.udpport;
823
+ tunnel.end();
824
+ // Put the tunnel in the tunnels list
825
+ var index = nextTunnelIndex++;
826
+ tunnel.index = index;
827
+ tunnels[index] = tunnel;
828
+
829
+ //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
830
+ }
831
+ }
832
+ break;
833
+ }
834
+ case 'messagebox': {
835
+ // Display a message box
836
+ if (data.title && data.msg) {
837
+ MeshServerLogEx(18, [data.title, data.msg], "Displaying message box, title=" + data.title + ", message=" + data.msg, data);
838
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
839
+ try { require('message-box').create(data.title, data.msg, 120); } catch (e) { }
840
+ }
841
+ break;
842
+ }
843
+ case 'ps': {
844
+ // Return the list of running processes
845
+ if (data.sessionid) {
846
+ processManager.getProcesses(function (plist) {
847
+ mesh.SendCommand({ action: 'msg', type: 'ps', value: JSON.stringify(plist), sessionid: data.sessionid });
848
+ });
849
+ }
850
+ break;
851
+ }
852
+ case 'pskill': {
853
+ // Kill a process
854
+ if (data.value) {
855
+ MeshServerLogEx(19, [data.value], "Killing process " + data.value, data);
856
+ try { process.kill(data.value); } catch (e) { sendConsoleText("pskill: " + JSON.stringify(e)); }
857
+ }
858
+ break;
859
+ }
860
+ case 'services': {
861
+ // Return the list of installed services
862
+ var services = null;
863
+ try { services = require('service-manager').manager.enumerateService(); } catch (e) { }
864
+ if (services != null) { mesh.SendCommand({ action: 'msg', type: 'services', value: JSON.stringify(services), sessionid: data.sessionid }); }
865
+ break;
866
+ }
867
+ case 'serviceStop': {
868
+ // Stop a service
869
+ try {
870
+ var service = require('service-manager').manager.getService(data.serviceName);
871
+ if (service != null) { service.stop(); }
872
+ } catch (e) { }
873
+ break;
874
+ }
875
+ case 'serviceStart': {
876
+ // Start a service
877
+ try {
878
+ var service = require('service-manager').manager.getService(data.serviceName);
879
+ if (service != null) { service.start(); }
880
+ } catch (e) { }
881
+ break;
882
+ }
883
+ case 'serviceRestart': {
884
+ // Restart a service
885
+ try {
886
+ var service = require('service-manager').manager.getService(data.serviceName);
887
+ if (service != null) { service.restart(); }
888
+ } catch (e) { }
889
+ break;
890
+ }
891
+ case 'deskBackground':
892
+ {
893
+ // Toggle desktop background
894
+ try {
895
+ if (process.platform == 'win32') {
896
+ var stype = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
897
+ var sid = undefined;
898
+ if (stype == 1) {
899
+ if (require('MeshAgent')._tsid != null) {
900
+ stype = 5;
901
+ sid = require('MeshAgent')._tsid;
902
+ }
903
+ }
904
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
905
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: stype, uid: sid });
906
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
907
+ child.stderr.on('data', function () { });
908
+ child.waitExit();
909
+ var current = child.stdout.str.trim();
910
+ if (current != '') { require('MeshAgent')._wallpaper = current; }
911
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: stype, uid: sid });
912
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
913
+ child.stderr.on('data', function () { });
914
+ child.waitExit();
915
+ } else {
916
+ var id = require('user-sessions').consoleUid();
917
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
918
+ if (current != '/dev/null') { require('MeshAgent')._wallpaper = current; }
919
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
920
+ }
921
+ } catch (e) {
922
+ sendConsoleText(e);
923
+ }
924
+ break;
925
+ }
926
+ case 'openUrl': {
927
+ // Open a local web browser and return success/fail
928
+ MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
929
+ sendConsoleText("OpenURL: " + data.url);
930
+ if (data.url) { mesh.SendCommand({ action: 'msg', type: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
931
+ break;
932
+ }
933
+ case 'getclip': {
934
+ // Send the load clipboard back to the user
935
+ //sendConsoleText('getClip: ' + JSON.stringify(data));
936
+ if (require('MeshAgent').isService) {
937
+ require('clipboard').dispatchRead().then(function (str) {
938
+ if (str) {
939
+ MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
940
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
941
+ }
942
+ });
943
+ } else {
944
+ require("clipboard").read().then(function (str) {
945
+ if (str) {
946
+ MeshServerLogEx(21, [str.length], "Getting clipboard content, " + str.length + " byte(s)", data);
947
+ mesh.SendCommand({ action: 'msg', type: 'getclip', sessionid: data.sessionid, data: str, tag: data.tag });
948
+ }
949
+ });
950
+ }
951
+ break;
952
+ }
953
+ case 'setclip': {
954
+ // Set the load clipboard to a user value
955
+ //sendConsoleText('setClip: ' + JSON.stringify(data));
956
+ if (typeof data.data == 'string') {
957
+ MeshServerLogEx(22, [data.data.length], "Setting clipboard content, " + data.data.length + " byte(s)", data);
958
+ if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
959
+ mesh.SendCommand({ action: 'msg', type: 'setclip', sessionid: data.sessionid, success: true });
960
+ }
961
+ break;
962
+ }
963
+ case 'userSessions': {
964
+ // Send back current user sessions list, this is Windows only.
965
+ //sendConsoleText('userSessions: ' + JSON.stringify(data));
966
+ if (process.platform != 'win32') break;
967
+ var p = require('user-sessions').enumerateUsers();
968
+ p.sessionid = data.sessionid;
969
+ p.then(function (u) { mesh.SendCommand({ action: 'msg', type: 'userSessions', sessionid: data.sessionid, data: u, tag: data.tag }); });
970
+ break;
971
+ }
972
+ default:
973
+ // Unknown action, ignore it.
974
+ break;
975
+ }
976
+ break;
977
+ }
978
+ case 'acmactivate': {
979
+ if (amt != null) {
980
+ MeshServerLogEx(23, null, "Attempting Intel AMT ACM mode activation", data);
981
+ amt.setAcmResponse(data);
982
+ }
983
+ break;
984
+ }
985
+ case 'wakeonlan': {
986
+ // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
987
+ sendConsoleText("Server requesting wake-on-lan for: " + data.macs.join(', '));
988
+ for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
989
+ break;
990
+ }
991
+ case 'runcommands': {
992
+ if (mesh.cmdchild != null) { sendConsoleText("Run commands can't execute, already busy."); break; }
993
+ sendConsoleText("Run commands (" + data.runAsUser + "): " + data.cmds);
994
+
995
+ // data.runAsUser: 0=Agent,1=UserOrAgent,2=UserOnly
996
+ var options = {};
997
+ if (data.runAsUser > 0) {
998
+ try { options.uid = require('user-sessions').consoleUid(); } catch (e) { }
999
+ options.type = require('child_process').SpawnTypes.TERM;
1000
+ }
1001
+ if (data.runAsUser == 2) {
1002
+ if (options.uid == null) break;
1003
+ if (((require('user-sessions').minUid != null) && (options.uid < require('user-sessions').minUid()))) break; // This command can only run as user.
1004
+ }
1005
+
1006
+ if (process.platform == 'win32') {
1007
+ if (data.type == 1) {
1008
+ // Windows command shell
1009
+ mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd'], options);
1010
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1011
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1012
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1013
+ mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1014
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1015
+ } else if (data.type == 2) {
1016
+ // Windows Powershell
1017
+ mesh.cmdchild = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], options);
1018
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsPowerShell';
1019
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1020
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1021
+ mesh.cmdchild.stdin.write(data.cmds + '\r\nexit\r\n');
1022
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1023
+ }
1024
+ } else if (data.type == 3) {
1025
+ // Linux shell
1026
+ mesh.cmdchild = require('child_process').execFile('/bin/sh', ['sh'], options);
1027
+ mesh.cmdchild.descriptorMetadata = 'UserCommandsShell';
1028
+ mesh.cmdchild.stdout.on('data', function (c) { sendConsoleText(c.toString()); });
1029
+ mesh.cmdchild.stderr.on('data', function (c) { sendConsoleText(c.toString()); });
1030
+ mesh.cmdchild.stdin.write(data.cmds.split('\r').join('') + '\nexit\n');
1031
+ mesh.cmdchild.on('exit', function () { sendConsoleText("Run commands completed."); delete mesh.cmdchild; });
1032
+ }
1033
+ break;
1034
+ }
1035
+ case 'uninstallagent':
1036
+ // Uninstall this agent
1037
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
1038
+ if (require('service-manager').manager.getService(agentName).isMe()) {
1039
+ try { diagnosticAgent_uninstall(); } catch (e) { }
1040
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
1041
+ this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
1042
+ }
1043
+ break;
1044
+ case 'poweraction': {
1045
+ // Server telling us to execute a power action
1046
+ if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
1047
+ var forced = 0;
1048
+ if (data.forced == 1) { forced = 1; }
1049
+ data.actiontype = parseInt(data.actiontype);
1050
+ MeshServerLogEx(25, [data.actiontype, forced], "Performing power action=" + data.actiontype + ", forced=" + forced, data);
1051
+ sendConsoleText("Performing power action=" + data.actiontype + ", forced=" + forced + '.');
1052
+ var r = mesh.ExecPowerState(data.actiontype, forced);
1053
+ sendConsoleText("ExecPowerState returned code: " + r);
1054
+ }
1055
+ break;
1056
+ }
1057
+ case 'iplocation': {
1058
+ // Update the IP location information of this node. Only do this when requested by the server since we have a limited amount of time we can call this per day
1059
+ getIpLocationData(function (location) { mesh.SendCommand({ action: 'iplocation', type: 'publicip', value: location }); });
1060
+ break;
1061
+ }
1062
+ case 'toast': {
1063
+ // Display a toast message
1064
+ if (data.title && data.msg) {
1065
+ MeshServerLogEx(26, [data.title, data.msg], "Displaying toast message, title=" + data.title + ", message=" + data.msg, data);
1066
+ data.msg = data.msg.split('\r').join('\\r').split('\n').join('\\n');
1067
+ try { require('toaster').Toast(data.title, data.msg); } catch (e) { }
1068
+ }
1069
+ break;
1070
+ }
1071
+ case 'openUrl': {
1072
+ // Open a local web browser and return success/fail
1073
+ //sendConsoleText('OpenURL: ' + data.url);
1074
+ MeshServerLogEx(20, [data.url], "Opening: " + data.url, data);
1075
+ if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1076
+ break;
1077
+ }
1078
+ case 'amtPolicy': {
1079
+ // Store the latest Intel AMT policy
1080
+ amtPolicy = data.amtPolicy;
1081
+ if (data.amtPolicy != null) { db.Put('amtPolicy', JSON.stringify(data.amtPolicy)); } else { db.Put('amtPolicy', null); }
1082
+ if (amt != null) { amt.setPolicy(amtPolicy, true); }
1083
+ break;
1084
+ }
1085
+ case 'getScript': {
1086
+ // Received a configuration script from the server
1087
+ sendConsoleText('getScript: ' + JSON.stringify(data));
1088
+ break;
1089
+ }
1090
+ case 'sysinfo': {
1091
+ // Fetch system information
1092
+ getSystemInformation(function (results) {
1093
+ if ((results != null) && (data.hash != results.hash)) { mesh.SendCommand({ action: 'sysinfo', sessionid: this.sessionid, data: results }); }
1094
+ });
1095
+ break;
1096
+ }
1097
+ case 'ping': { mesh.SendCommand('{"action":"pong"}'); break; }
1098
+ case 'pong': { break; }
1099
+ case 'plugin': {
1100
+ try { require(data.plugin).consoleaction(data, data.rights, data.sessionid, this); } catch (e) { throw e; }
1101
+ break;
1102
+ }
1103
+ case 'coredump':
1104
+ // Set the current agent coredump situation.
1105
+ if (data.value === true) {
1106
+ // TODO: This replace() below is not ideal, would be better to remove the .exe at the end instead of replace.
1107
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
1108
+ } else if (data.value === false) {
1109
+ process.coreDumpLocation = null;
1110
+ }
1111
+ break;
1112
+ case 'getcoredump':
1113
+ // Ask the agent if a core dump is currently available, if yes, also return the hash of the agent.
1114
+ var r = { action: 'getcoredump', value: (process.coreDumpLocation != null) };
1115
+ var coreDumpPath = null;
1116
+ if (process.platform == 'win32') { coreDumpPath = process.coreDumpLocation; } else { coreDumpPath = (process.cwd() != '//') ? fs.existsSync(process.cwd() + 'core') : null; }
1117
+ if ((coreDumpPath != null) && (fs.existsSync(coreDumpPath))) { r.exists = (db.Get('CoreDumpTime') != require('fs').statSync(coreDumpPath).mtime); }
1118
+ if (r.exists == true) { r.agenthashhex = getSHA384FileHash(process.execPath).toString('hex'); }
1119
+ mesh.SendCommand(JSON.stringify(r));
1120
+ default:
1121
+ // Unknown action, ignore it.
1122
+ break;
1123
+ }
1124
+ }
1125
+ }
1126
+
1127
+ // Called when a file changed in the file system
1128
+ /*
1129
+ function onFileWatcher(a, b) {
1130
+ console.log('onFileWatcher', a, b, this.path);
1131
+ var response = getDirectoryInfo(this.path);
1132
+ if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
1133
+ }
1134
+ */
1135
+
1136
+ function getSystemInformation(func) {
1137
+ try {
1138
+ var results = { hardware: require('identifiers').get() }; // Hardware info
1139
+ if (results.hardware && results.hardware.windows) {
1140
+ // Remove extra entries and things that change quickly
1141
+ var x = results.hardware.windows.osinfo;
1142
+ try { delete x.FreePhysicalMemory; } catch (e) { }
1143
+ try { delete x.FreeSpaceInPagingFiles; } catch (e) { }
1144
+ try { delete x.FreeVirtualMemory; } catch (e) { }
1145
+ try { delete x.LocalDateTime; } catch (e) { }
1146
+ try { delete x.MaxProcessMemorySize; } catch (e) { }
1147
+ try { delete x.TotalVirtualMemorySize; } catch (e) { }
1148
+ try { delete x.TotalVisibleMemorySize; } catch (e) { }
1149
+ try {
1150
+ if (results.hardware.windows.memory) { for (var i in results.hardware.windows.memory) { delete results.hardware.windows.memory[i].Node; } }
1151
+ if (results.hardware.windows.osinfo) { delete results.hardware.windows.osinfo.Node; }
1152
+ if (results.hardware.windows.partitions) { for (var i in results.hardware.windows.partitions) { delete results.hardware.windows.partitions[i].Node; } }
1153
+ } catch (e) { }
1154
+ }
1155
+ if (process.platform == 'win32') { results.pendingReboot = require('win-info').pendingReboot(); } // Pending reboot
1156
+ /*
1157
+ if (process.platform == 'win32') {
1158
+ var defragResult = function (r) {
1159
+ if (typeof r == 'object') { results[this.callname] = r; }
1160
+ if (this.callname == 'defrag') {
1161
+ var pr = require('win-info').installedApps(); // Installed apps
1162
+ pr.callname = 'installedApps';
1163
+ pr.sessionid = data.sessionid;
1164
+ pr.then(defragResult, defragResult);
1165
+ }
1166
+ else {
1167
+ results.winpatches = require('win-info').qfe(); // Windows patches
1168
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1169
+ func(results);
1170
+ }
1171
+ }
1172
+ var pr = require('win-info').defrag({ volume: 'C:' }); // Defrag TODO
1173
+ pr.callname = 'defrag';
1174
+ pr.sessionid = data.sessionid;
1175
+ pr.then(defragResult, defragResult);
1176
+ } else {
1177
+ */
1178
+ results.hash = require('SHA384Stream').create().syncHash(JSON.stringify(results)).toString('hex');
1179
+ func(results);
1180
+ //}
1181
+ } catch (e) { func(null, e); }
1182
+ }
1183
+
1184
+ // Get a formated response for a given directory path
1185
+ function getDirectoryInfo(reqpath) {
1186
+ var response = { path: reqpath, dir: [] };
1187
+ if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
1188
+ // List all the drives in the root, or the root itself
1189
+ var results = null;
1190
+ try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
1191
+ if (results != null) {
1192
+ for (var i = 0; i < results.length; ++i) {
1193
+ var drive = { n: results[i].name, t: 1 };
1194
+ if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
1195
+ response.dir.push(drive);
1196
+ }
1197
+ }
1198
+ } else {
1199
+ // List all the files and folders in this path
1200
+ if (reqpath == '') { reqpath = '/'; }
1201
+ var results = null, xpath = obj.path.join(reqpath, '*');
1202
+ //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
1203
+ try { results = fs.readdirSync(xpath); } catch (e) { }
1204
+ if (results != null) {
1205
+ for (var i = 0; i < results.length; ++i) {
1206
+ if ((results[i] != '.') && (results[i] != '..')) {
1207
+ var stat = null, p = obj.path.join(reqpath, results[i]);
1208
+ //if (process.platform == "win32") { p = p.split('/').join('\\'); }
1209
+ try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
1210
+ if ((stat != null) && (stat != undefined)) {
1211
+ if (stat.isDirectory() == true) {
1212
+ response.dir.push({ n: results[i], t: 2, d: stat.mtime });
1213
+ } else {
1214
+ response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
1215
+ }
1216
+ }
1217
+ }
1218
+ }
1219
+ }
1220
+ }
1221
+ return response;
1222
+ }
1223
+
1224
+ // Tunnel callback operations
1225
+ function onTunnelUpgrade(response, s, head) {
1226
+ this.s = s;
1227
+ s.httprequest = this;
1228
+ s.end = onTunnelClosed;
1229
+ s.tunnel = this;
1230
+ s.descriptorMetadata = "MeshAgent_relayTunnel";
1231
+
1232
+ if (require('MeshAgent').idleTimeout != null)
1233
+ {
1234
+ s.setTimeout(require('MeshAgent').idleTimeout * 1000);
1235
+ s.on('timeout', function ()
1236
+ {
1237
+ this.ping();
1238
+ this.setTimeout(require('MeshAgent').idleTimeout * 1000);
1239
+ });
1240
+ }
1241
+
1242
+ //sendConsoleText('onTunnelUpgrade - ' + this.tcpport + ' - ' + this.udpport);
1243
+
1244
+ if (this.tcpport != null) {
1245
+ // This is a TCP relay connection, pause now and try to connect to the target.
1246
+ s.pause();
1247
+ s.data = onTcpRelayServerTunnelData;
1248
+ var connectionOptions = { port: parseInt(this.tcpport) };
1249
+ if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
1250
+ s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
1251
+ s.tcprelay.peerindex = this.index;
1252
+
1253
+ // Add the TCP session to the count and update the server
1254
+ if (s.httprequest.userid != null) {
1255
+ if (tunnelUserCount.tcp[s.httprequest.userid] == null) { tunnelUserCount.tcp[s.httprequest.userid] = 1; } else { tunnelUserCount.tcp[s.httprequest.userid]++; }
1256
+ try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1257
+ broadcastSessionsToRegisteredApps();
1258
+ }
1259
+ } if (this.udpport != null) {
1260
+ // This is a UDP relay connection, get the UDP socket setup. // TODO: ***************
1261
+ s.data = onUdpRelayServerTunnelData;
1262
+ s.udprelay = require('dgram').createSocket({ type: 'udp4' });
1263
+ s.udprelay.bind({ port: 0 });
1264
+ s.udprelay.peerindex = this.index;
1265
+ s.udprelay.on('message', onUdpRelayTargetTunnelConnect);
1266
+ s.udprelay.udpport = this.udpport;
1267
+ s.udprelay.udpaddr = this.udpaddr;
1268
+ s.udprelay.first = true;
1269
+
1270
+ // Add the UDP session to the count and update the server
1271
+ if (s.httprequest.userid != null) {
1272
+ if (tunnelUserCount.udp[s.httprequest.userid] == null) { tunnelUserCount.udp[s.httprequest.userid] = 1; } else { tunnelUserCount.udp[s.httprequest.userid]++; }
1273
+ try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.tcp }); } catch (e) { }
1274
+ broadcastSessionsToRegisteredApps();
1275
+ }
1276
+ } else {
1277
+ // This is a normal connect for KVM/Terminal/Files
1278
+ s.data = onTunnelData;
1279
+ }
1280
+ }
1281
+
1282
+ // Called when UDP relay data is received // TODO****
1283
+ function onUdpRelayTargetTunnelConnect(data) {
1284
+ var peerTunnel = tunnels[this.peerindex];
1285
+ peerTunnel.s.write(data);
1286
+ }
1287
+
1288
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1289
+ function onUdpRelayServerTunnelData(data) {
1290
+ if (this.udprelay.first === true) {
1291
+ delete this.udprelay.first; // Skip the first 'c' that is received.
1292
+ } else {
1293
+ this.udprelay.send(data, parseInt(this.udprelay.udpport), this.udprelay.udpaddr ? this.udprelay.udpaddr : '127.0.0.1');
1294
+ }
1295
+ }
1296
+
1297
+ // Called when the TCP relay target is connected
1298
+ function onTcpRelayTargetTunnelConnect() {
1299
+ var peerTunnel = tunnels[this.peerindex];
1300
+ this.pipe(peerTunnel.s); // Pipe Target --> Server
1301
+ peerTunnel.s.first = true;
1302
+ peerTunnel.s.resume();
1303
+ }
1304
+
1305
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
1306
+ function onTcpRelayServerTunnelData(data) {
1307
+ if (this.first == true) {
1308
+ this.first = false;
1309
+ this.pipe(this.tcprelay, { dataTypeSkip: 1 }); // Pipe Server --> Target (don't pipe text type websocket frames)
1310
+ }
1311
+ }
1312
+
1313
+ function onTunnelClosed() {
1314
+ var tunnel = tunnels[this.httprequest.index];
1315
+ if (tunnel == null) return; // Stop duplicate calls.
1316
+
1317
+ // If this is a routing session, clean up and send the new session counts.
1318
+ if (this.httprequest.userid != null) {
1319
+ if (this.httprequest.tcpport != null) {
1320
+ if (tunnelUserCount.tcp[this.httprequest.userid] != null) { tunnelUserCount.tcp[this.httprequest.userid]--; if (tunnelUserCount.tcp[this.httprequest.userid] <= 0) { delete tunnelUserCount.tcp[this.httprequest.userid]; } }
1321
+ try { mesh.SendCommand({ action: 'sessions', type: 'tcp', value: tunnelUserCount.tcp }); } catch (e) { }
1322
+ broadcastSessionsToRegisteredApps();
1323
+ } else if (this.httprequest.udpport != null) {
1324
+ if (tunnelUserCount.udp[this.httprequest.userid] != null) { tunnelUserCount.udp[this.httprequest.userid]--; if (tunnelUserCount.udp[this.httprequest.userid] <= 0) { delete tunnelUserCount.udp[this.httprequest.userid]; } }
1325
+ try { mesh.SendCommand({ action: 'sessions', type: 'udp', value: tunnelUserCount.udp }); } catch (e) { }
1326
+ broadcastSessionsToRegisteredApps();
1327
+ }
1328
+ }
1329
+
1330
+ // Sent tunnel statistics to the server, only send this if compression was used.
1331
+ if ((this.bytesSent_uncompressed) && (this.bytesSent_uncompressed.toString() != this.bytesSent_actual.toString())) {
1332
+ mesh.SendCommand({
1333
+ action: 'tunnelCloseStats',
1334
+ url: tunnel.url,
1335
+ userid: tunnel.userid,
1336
+ protocol: tunnel.protocol,
1337
+ sessionid: tunnel.sessionid,
1338
+ sent: this.bytesSent_uncompressed.toString(),
1339
+ sentActual: this.bytesSent_actual.toString(),
1340
+ sentRatio: this.bytesSent_ratio,
1341
+ received: this.bytesReceived_uncompressed.toString(),
1342
+ receivedActual: this.bytesReceived_actual.toString(),
1343
+ receivedRatio: this.bytesReceived_ratio
1344
+ });
1345
+ }
1346
+
1347
+ //sendConsoleText("Tunnel #" + this.httprequest.index + " closed. Sent -> " + this.bytesSent_uncompressed + ' bytes (uncompressed), ' + this.bytesSent_actual + ' bytes (actual), ' + this.bytesSent_ratio + '% compression', this.httprequest.sessionid);
1348
+ if (this.httprequest.index) { delete tunnels[this.httprequest.index]; }
1349
+
1350
+ /*
1351
+ // Close the watcher if required
1352
+ if (this.httprequest.watcher != undefined) {
1353
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1354
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1355
+ delete this.httprequest.watcher;
1356
+ }
1357
+ */
1358
+
1359
+ // If there is a upload or download active on this connection, close the file
1360
+ if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; delete this.httprequest.uploadFileid; delete this.httprequest.uploadFilePath; }
1361
+ if (this.httprequest.downloadFile) { delete this.httprequest.downloadFile; }
1362
+
1363
+ // Clean up WebRTC
1364
+ if (this.webrtc != null) {
1365
+ if (this.webrtc.rtcchannel) { try { this.webrtc.rtcchannel.close(); } catch (e) { } this.webrtc.rtcchannel.removeAllListeners('data'); this.webrtc.rtcchannel.removeAllListeners('end'); delete this.webrtc.rtcchannel; }
1366
+ if (this.webrtc.websocket) { delete this.webrtc.websocket; }
1367
+ try { this.webrtc.close(); } catch (e) { }
1368
+ this.webrtc.removeAllListeners('connected');
1369
+ this.webrtc.removeAllListeners('disconnected');
1370
+ this.webrtc.removeAllListeners('dataChannel');
1371
+ delete this.webrtc;
1372
+ }
1373
+
1374
+ // Clean up WebSocket
1375
+ this.removeAllListeners('data');
1376
+ }
1377
+ function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
1378
+ function onTunnelData(data) {
1379
+ //console.log("OnTunnelData");
1380
+ //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
1381
+
1382
+ // If this is upload data, save it to file
1383
+ if ((this.httprequest.uploadFile) && (typeof data == 'object') && (data[0] != 123)) {
1384
+ // Save the data to file being uploaded.
1385
+ if (data[0] == 0) {
1386
+ // If data starts with zero, skip the first byte. This is used to escape binary file data from JSON.
1387
+ try { fs.writeSync(this.httprequest.uploadFile, data, 1, data.length - 1); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1388
+ } else {
1389
+ // If data does not start with zero, save as-is.
1390
+ try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { sendConsoleText('FileUpload Error'); this.write(Buffer.from(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
1391
+ }
1392
+ this.write(Buffer.from(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data.
1393
+ return;
1394
+ }
1395
+
1396
+ if (this.httprequest.state == 0) {
1397
+ // Check if this is a relay connection
1398
+ if ((data == 'c') || (data == 'cr')) { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
1399
+ }
1400
+ else
1401
+ {
1402
+ // Handle tunnel data
1403
+ if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user), 10 = FileTransfer
1404
+ // Take a look at the protocol
1405
+ if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
1406
+ this.httprequest.protocol = parseInt(data);
1407
+ if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
1408
+ if (this.httprequest.protocol == 10) {
1409
+ //
1410
+ // Basic file transfer
1411
+ //
1412
+ var stats = null;
1413
+ if ((process.platform != 'win32') && (this.httprequest.xoptions.file.startsWith('/') == false)) { this.httprequest.xoptions.file = '/' + this.httprequest.xoptions.file; }
1414
+ try { stats = require('fs').statSync(this.httprequest.xoptions.file) } catch (e) { }
1415
+ try { if (stats) { this.httprequest.downloadFile = fs.createReadStream(this.httprequest.xoptions.file, { flags: 'rbN' }); } } catch (e) { }
1416
+ if (this.httprequest.downloadFile) {
1417
+ //sendConsoleText('BasicFileTransfer, ok, ' + this.httprequest.xoptions.file + ', ' + JSON.stringify(stats));
1418
+ this.write(JSON.stringify({ op: 'ok', size: stats.size }));
1419
+ this.httprequest.downloadFile.pipe(this);
1420
+ this.httprequest.downloadFile.end = function () { }
1421
+ } else {
1422
+ //sendConsoleText('BasicFileTransfer, cancel, ' + this.httprequest.xoptions.file);
1423
+ this.write(JSON.stringify({ op: 'cancel' }));
1424
+ }
1425
+ }
1426
+ else if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9))
1427
+ {
1428
+ //
1429
+ // Remote Terminal
1430
+ //
1431
+
1432
+ // Check user access rights for terminal
1433
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOTERMINAL) != 0))) {
1434
+ // Disengage this tunnel, user does not have the rights to do this!!
1435
+ this.httprequest.protocol = 999999;
1436
+ this.httprequest.s.end();
1437
+ sendConsoleText("Error: No Terminal Control Rights.");
1438
+ return;
1439
+ }
1440
+
1441
+ this.descriptorMetadata = "Remote Terminal";
1442
+
1443
+ if (process.platform == 'win32')
1444
+ {
1445
+ if (!require('win-terminal').PowerShellCapable() && (this.httprequest.protocol == 6 || this.httprequest.protocol == 9))
1446
+ {
1447
+ this.httprequest.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: 'PowerShell is not supported on this version of windows', msgid: 1 }));
1448
+ this.httprequest.s.end();
1449
+ return;
1450
+ }
1451
+ }
1452
+
1453
+ var prom = require('promise');
1454
+ this.httprequest.tpromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1455
+ this.httprequest.tpromise.that = this;
1456
+ this.httprequest.tpromise.httprequest = this.httprequest;
1457
+
1458
+ this.end = function ()
1459
+ {
1460
+ if (this.httprequest.tpromise._consent) { this.httprequest.tpromise._consent.close(); }
1461
+ if (this.httprequest.connectionPromise) { this.httprequest.connectionPromise._rej('Closed'); }
1462
+
1463
+ // Remove the terminal session to the count to update the server
1464
+ if (this.httprequest.userid != null)
1465
+ {
1466
+ if (tunnelUserCount.terminal[this.httprequest.userid] != null) { tunnelUserCount.terminal[this.httprequest.userid]--; if (tunnelUserCount.terminal[this.httprequest.userid] <= 0) { delete tunnelUserCount.terminal[this.httprequest.userid]; } }
1467
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1468
+ broadcastSessionsToRegisteredApps();
1469
+ }
1470
+
1471
+ if (process.platform == 'win32')
1472
+ {
1473
+ // Unpipe the web socket
1474
+ this.unpipe(this.httprequest._term);
1475
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this); }
1476
+
1477
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1478
+ if (this.rtcchannel)
1479
+ {
1480
+ this.rtcchannel.unpipe(this.httprequest._term);
1481
+ if (this.httprequest._term) { this.httprequest._term.unpipe(this.rtcchannel); }
1482
+ }
1483
+
1484
+ // Clean up
1485
+ if (this.httprequest._term) { this.httprequest._term.end(); }
1486
+ this.httprequest._term = null;
1487
+ }
1488
+ };
1489
+
1490
+ // Perform User-Consent if needed.
1491
+ if (this.httprequest.consent && (this.httprequest.consent & 16))
1492
+ {
1493
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1494
+ var consentMessage = this.httprequest.username + " requesting remote terminal access. Grant access?", consentTitle = 'MeshCentral';
1495
+ if (this.httprequest.soptions != null) {
1496
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1497
+ if (this.httprequest.soptions.consentMsgTerminal != null) { consentMessage = this.httprequest.soptions.consentMsgTerminal.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1498
+ }
1499
+ this.httprequest.tpromise._consent = require('message-box').create(consentTitle, consentMessage, 30);
1500
+ this.httprequest.tpromise._consent.retPromise = this.httprequest.tpromise;
1501
+ this.httprequest.tpromise._consent.then(
1502
+ function ()
1503
+ {
1504
+ // Success
1505
+ MeshServerLogEx(27, null, "Local user accepted remote terminal request (" + this.retPromise.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1506
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1507
+ this.retPromise._consent = null;
1508
+ this.retPromise._res();
1509
+ },
1510
+ function (e)
1511
+ {
1512
+ // Denied
1513
+ MeshServerLogEx(28, null, "Local user rejected remote terminal request (" + this.retPromise.that.httprequest.remoteaddr + ")", this.retPromise.that.httprequest);
1514
+ this.retPromise.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1515
+ this.retPromise._rej(e.toString());
1516
+ });
1517
+ }
1518
+ else
1519
+ {
1520
+ // User-Consent is not required, so just resolve this promise
1521
+ this.httprequest.tpromise._res();
1522
+ }
1523
+
1524
+
1525
+ this.httprequest.tpromise.then(
1526
+ function ()
1527
+ {
1528
+ this.httprequest.connectionPromise = new prom(function (res, rej) { this._res = res; this._rej = rej; });
1529
+ this.httprequest.connectionPromise.ws = this.that;
1530
+
1531
+ // Start Terminal
1532
+ if(process.platform == 'win32')
1533
+ {
1534
+ try
1535
+ {
1536
+ var cols = 80, rows = 25;
1537
+ if (this.httprequest.xoptions)
1538
+ {
1539
+ if (this.httprequest.xoptions.rows) { rows = this.httprequest.xoptions.rows; }
1540
+ if (this.httprequest.xoptions.cols) { cols = this.httprequest.xoptions.cols; }
1541
+ }
1542
+
1543
+ if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6))
1544
+ {
1545
+ // Admin Terminal
1546
+ if (require('win-virtual-terminal').supported)
1547
+ {
1548
+ // ConPTY PseudoTerminal
1549
+ // this.httprequest._term = require('win-virtual-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](80, 25);
1550
+
1551
+ // The above line is commented out, because there is a bug with ClosePseudoConsole() API, so this is the workaround
1552
+ this.httprequest._dispatcher = require('win-dispatcher').dispatch({ modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1553
+ this.httprequest._dispatcher.httprequest = this.httprequest;
1554
+ this.httprequest._dispatcher.on('connection', function (c)
1555
+ {
1556
+ if (this.httprequest.connectionPromise.completed)
1557
+ {
1558
+ c.end();
1559
+ }
1560
+ else
1561
+ {
1562
+ this.httprequest.connectionPromise._res(c);
1563
+ }
1564
+ });
1565
+ }
1566
+ else
1567
+ {
1568
+ // Legacy Terminal
1569
+ this.httprequest.connectionPromise._res(require('win-terminal')[this.httprequest.protocol == 6 ? 'StartPowerShell' : 'Start'](cols, rows));
1570
+ }
1571
+ }
1572
+ else
1573
+ {
1574
+ // Logged in user
1575
+ var userPromise = require('user-sessions').enumerateUsers();
1576
+ userPromise.that = this;
1577
+ userPromise.then(function (u)
1578
+ {
1579
+ var that = this.that;
1580
+ if (u.Active.length > 0)
1581
+ {
1582
+ var username = u.Active[0].Username;
1583
+ if (require('win-virtual-terminal').supported)
1584
+ {
1585
+ // ConPTY PseudoTerminal
1586
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1587
+ }
1588
+ else
1589
+ {
1590
+ // Legacy Terminal
1591
+ that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [cols, rows] } });
1592
+ }
1593
+ that.httprequest._dispatcher.ws = that;
1594
+ that.httprequest._dispatcher.on('connection', function (c)
1595
+ {
1596
+ if (this.ws.httprequest.connectionPromise.completed)
1597
+ {
1598
+ c.end();
1599
+ }
1600
+ else
1601
+ {
1602
+ this.ws.httprequest.connectionPromise._res(c);
1603
+ }
1604
+ });
1605
+ }
1606
+ });
1607
+ }
1608
+ }
1609
+ catch (e)
1610
+ {
1611
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1612
+ }
1613
+ }
1614
+ else
1615
+ {
1616
+ try
1617
+ {
1618
+ var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
1619
+ var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
1620
+ var login = process.platform == 'linux' ? '/bin/login' : '/usr/bin/login';
1621
+
1622
+ var env = { HISTCONTROL: 'ignoreboth' };
1623
+ if (this.httprequest.xoptions)
1624
+ {
1625
+ if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
1626
+ if (this.httprequest.xoptions.cols) { env.COLUMNS = ('' + this.httprequest.xoptions.cols); }
1627
+ }
1628
+ var options = { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1629
+ if (this.httprequest.xoptions && this.httprequest.xoptions.requireLogin)
1630
+ {
1631
+ if (!require('fs').existsSync(login)) { throw ('Unable to spawn login process'); }
1632
+ this.httprequest.connectionPromise._res(childProcess.execFile(login, ['login'], options)); // Start login shell
1633
+ }
1634
+ else if (bash)
1635
+ {
1636
+ var p = childProcess.execFile(bash, ['bash'], options); // Start bash
1637
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1638
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1639
+ this.httprequest.connectionPromise._res(p);
1640
+ }
1641
+ else if (sh)
1642
+ {
1643
+ var p = childProcess.execFile(sh, ['sh'], options); // Start sh
1644
+ // Spaces at the beginning of lines are needed to hide commands from the command history
1645
+ if (process.platform == 'linux') { p.stdin.write(' alias ls=\'ls --color=auto\';clear\n'); }
1646
+ this.httprequest.connectionPromise._res(p);
1647
+ }
1648
+ else
1649
+ {
1650
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, no shell found');
1651
+ }
1652
+ }
1653
+ catch (e)
1654
+ {
1655
+ this.httprequest.connectionPromise._rej('Failed to start remote terminal session, ' + e.toString());
1656
+ }
1657
+ }
1658
+
1659
+ this.httprequest.connectionPromise.then(
1660
+ function (term)
1661
+ {
1662
+ // SUCCESS
1663
+ var stdoutstream;
1664
+ var stdinstream;
1665
+ if (process.platform == 'win32')
1666
+ {
1667
+ this.ws.httprequest._term = term;
1668
+ this.ws.httprequest._term.tunnel = this.ws;
1669
+ stdoutstream = stdinstream = term;
1670
+ }
1671
+ else
1672
+ {
1673
+ term.descriptorMetadata = 'Remote Terminal';
1674
+ this.ws.httprequest.process = term;
1675
+ this.ws.httprequest.process.tunnel = this.ws;
1676
+ term.stderr.stdout = term.stdout;
1677
+ term.stderr.on('data', function (c) { this.stdout.write(c); });
1678
+ stdoutstream = term.stdout;
1679
+ stdinstream = term.stdin;
1680
+ this.ws.prependListener('end', function () { this.httprequest.process.kill(); });
1681
+ term.prependListener('exit', function () { this.tunnel.end(); });
1682
+ }
1683
+
1684
+ this.ws.removeAllListeners('data');
1685
+ this.ws.on('data', onTunnelControlData);
1686
+
1687
+ stdoutstream.pipe(this.ws, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1688
+ this.ws.pipe(stdinstream, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
1689
+
1690
+ // Add the terminal session to the count to update the server
1691
+ if (this.ws.httprequest.userid != null)
1692
+ {
1693
+ if (tunnelUserCount.terminal[this.ws.httprequest.userid] == null) { tunnelUserCount.terminal[this.ws.httprequest.userid] = 1; } else { tunnelUserCount.terminal[this.ws.httprequest.userid]++; }
1694
+ try { mesh.SendCommand({ action: 'sessions', type: 'terminal', value: tunnelUserCount.terminal }); } catch (e) { }
1695
+ broadcastSessionsToRegisteredApps();
1696
+ }
1697
+
1698
+ // Toast Notification, if required
1699
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 2))
1700
+ {
1701
+ // User Notifications is required
1702
+ var notifyMessage = this.ws.httprequest.username + " started a remote terminal session.", notifyTitle = "MeshCentral";
1703
+ if (this.ws.httprequest.soptions != null) {
1704
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1705
+ if (this.ws.httprequest.soptions.notifyMsgTerminal != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgTerminal.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1706
+ }
1707
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
1708
+ }
1709
+ },
1710
+ function (e)
1711
+ {
1712
+ // FAILED to connect terminal
1713
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1714
+ this.ws.end();
1715
+ });
1716
+ },
1717
+ function (e)
1718
+ {
1719
+ // DO NOT start terminal
1720
+ this.that.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1721
+ this.that.end();
1722
+ });
1723
+ }
1724
+ else if (this.httprequest.protocol == 2)
1725
+ {
1726
+ //
1727
+ // Remote KVM
1728
+ //
1729
+
1730
+ // Check user access rights for desktop
1731
+ if ((((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NODESKTOP) != 0))) {
1732
+ // Disengage this tunnel, user does not have the rights to do this!!
1733
+ this.httprequest.protocol = 999999;
1734
+ this.httprequest.s.end();
1735
+ sendConsoleText("Error: No Desktop Control Rights.");
1736
+ return;
1737
+ }
1738
+
1739
+ this.descriptorMetadata = "Remote KVM";
1740
+
1741
+ // Look for a TSID
1742
+ var tsid = null;
1743
+ if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
1744
+ require('MeshAgent')._tsid = tsid;
1745
+
1746
+ // Remote desktop using native pipes
1747
+ this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
1748
+ this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
1749
+ this.desktop = this.httprequest.desktop;
1750
+
1751
+ // Add ourself to the list of remote desktop sessions
1752
+ if (this.httprequest.desktop.kvm.tunnels == null) { this.httprequest.desktop.kvm.tunnels = []; }
1753
+ this.httprequest.desktop.kvm.tunnels.push(this);
1754
+
1755
+ // Send a metadata update to all desktop sessions
1756
+ var users = {};
1757
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1758
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1759
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1760
+ tunnelUserCount.desktop = users;
1761
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1762
+ broadcastSessionsToRegisteredApps();
1763
+ }
1764
+
1765
+ this.end = function () {
1766
+ --this.desktop.kvm.connectionCount;
1767
+
1768
+ // Remove ourself from the list of remote desktop session
1769
+ var i = this.desktop.kvm.tunnels.indexOf(this);
1770
+ if (i >= 0) { this.desktop.kvm.tunnels.splice(i, 1); }
1771
+
1772
+ // Send a metadata update to all desktop sessions
1773
+ var users = {};
1774
+ if (this.httprequest.desktop.kvm.tunnels != null) {
1775
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { var userid = this.httprequest.desktop.kvm.tunnels[i].httprequest.userid; if (users[userid] == null) { users[userid] = 1; } else { users[userid]++; } } catch (e) { } }
1776
+ for (var i in this.httprequest.desktop.kvm.tunnels) { try { this.httprequest.desktop.kvm.tunnels[i].write(JSON.stringify({ ctrlChannel: '102938', type: 'metadata', users: users })); } catch (e) { } }
1777
+ tunnelUserCount.desktop = users;
1778
+ try { mesh.SendCommand({ action: 'sessions', type: 'kvm', value: users }); } catch (e) { }
1779
+ broadcastSessionsToRegisteredApps();
1780
+ }
1781
+
1782
+ // Unpipe the web socket
1783
+ try
1784
+ {
1785
+ this.unpipe(this.httprequest.desktop.kvm);
1786
+ this.httprequest.desktop.kvm.unpipe(this);
1787
+ }
1788
+ catch(e) { }
1789
+
1790
+ // Unpipe the WebRTC channel if needed (This will also be done when the WebRTC channel ends).
1791
+ if (this.rtcchannel)
1792
+ {
1793
+ try
1794
+ {
1795
+ this.rtcchannel.unpipe(this.httprequest.desktop.kvm);
1796
+ this.httprequest.desktop.kvm.unpipe(this.rtcchannel);
1797
+ }
1798
+ catch(e) { }
1799
+ }
1800
+
1801
+ // Place wallpaper back if needed
1802
+ // TODO
1803
+
1804
+ if (this.desktop.kvm.connectionCount == 0) {
1805
+ // Display a toast message. This may not be supported on all platforms.
1806
+ // try { require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.'); } catch (e) { }
1807
+
1808
+ this.httprequest.desktop.kvm.end();
1809
+ if (this.httprequest.desktop.kvm.connectionBar) {
1810
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1811
+ this.httprequest.desktop.kvm.connectionBar.close();
1812
+ this.httprequest.desktop.kvm.connectionBar = null;
1813
+ }
1814
+ } else {
1815
+ for (var i in this.httprequest.desktop.kvm.users) {
1816
+ if ((this.httprequest.desktop.kvm.users[i] == this.httprequest.username) && this.httprequest.desktop.kvm.connectionBar) {
1817
+ for (var j in this.httprequest.desktop.kvm.rusers) { if (this.httprequest.desktop.kvm.rusers[j] == this.httprequest.realname) { this.httprequest.desktop.kvm.rusers.splice(j, 1); break; } }
1818
+ this.httprequest.desktop.kvm.users.splice(i, 1);
1819
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1820
+ this.httprequest.desktop.kvm.connectionBar.close();
1821
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1822
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1823
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1824
+ MeshServerLogEx(29, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1825
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1826
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1827
+ }
1828
+ this.httprequest.desktop.kvm.end();
1829
+ });
1830
+ break;
1831
+ }
1832
+ }
1833
+ }
1834
+ };
1835
+ if (this.httprequest.desktop.kvm.hasOwnProperty('connectionCount')) {
1836
+ this.httprequest.desktop.kvm.connectionCount++;
1837
+ this.httprequest.desktop.kvm.rusers.push(this.httprequest.realname);
1838
+ this.httprequest.desktop.kvm.users.push(this.httprequest.username);
1839
+ this.httprequest.desktop.kvm.rusers.sort();
1840
+ this.httprequest.desktop.kvm.users.sort();
1841
+ } else {
1842
+ this.httprequest.desktop.kvm.connectionCount = 1;
1843
+ this.httprequest.desktop.kvm.rusers = [this.httprequest.realname];
1844
+ this.httprequest.desktop.kvm.users = [this.httprequest.username];
1845
+ }
1846
+
1847
+ if ((this.httprequest.rights == 0xFFFFFFFF) || (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0))) {
1848
+ // If we have remote control rights, pipe the KVM input
1849
+ this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
1850
+ } else {
1851
+ // We need to only pipe non-mouse & non-keyboard inputs.
1852
+ //sendConsoleText('Warning: No Remote Desktop Input Rights.');
1853
+ // TODO!!!
1854
+ }
1855
+
1856
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
1857
+ if (this.httprequest.consent && (this.httprequest.consent & 8))
1858
+ {
1859
+ // User Consent Prompt is required
1860
+ // Send a console message back using the console channel, "\n" is supported.
1861
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
1862
+ var consentMessage = this.httprequest.realname + " requesting remote desktop access. Grant access?", consentTitle = 'MeshCentral';
1863
+ if (this.httprequest.soptions != null) {
1864
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
1865
+ if (this.httprequest.soptions.consentMsgDesktop != null) { consentMessage = this.httprequest.soptions.consentMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1866
+ }
1867
+ var pr = require('message-box').create(consentTitle, consentMessage, 30, null, tsid);
1868
+ pr.ws = this;
1869
+ this.pause();
1870
+ this._consentpromise = pr;
1871
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); }});
1872
+ pr.then(
1873
+ function ()
1874
+ {
1875
+ // Success
1876
+ this.ws._consentpromise = null;
1877
+ MeshServerLogEx(30, null, "Starting remote desktop after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1878
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null, msgid: 0 }));
1879
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 1)) {
1880
+ // User Notifications is required
1881
+ var notifyMessage = this.ws.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1882
+ if (this.ws.httprequest.soptions != null) {
1883
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
1884
+ if (this.ws.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
1885
+ }
1886
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1887
+ }
1888
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 0x40)) {
1889
+ // Connection Bar is required
1890
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1891
+ this.ws.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1892
+ this.ws.httprequest.desktop.kvm.connectionBar.close();
1893
+ }
1894
+ try {
1895
+ this.ws.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.ws.httprequest.privacybartext.replace('{0}', this.ws.httprequest.desktop.kvm.users.join(', ')).replace('{1}', this.ws.httprequest.desktop.kvm.rusers.join(', ')), require('MeshAgent')._tsid);
1896
+ MeshServerLogEx(31, null, "Remote Desktop Connection Bar Activated/Updated (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1897
+ }
1898
+ catch (e) {
1899
+ if (process.platform != 'darwin') {
1900
+ MeshServerLogEx(32, null, "Remote Desktop Connection Bar Failed or Not Supported (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1901
+ }
1902
+ }
1903
+ if (this.ws.httprequest.desktop.kvm.connectionBar) {
1904
+ this.ws.httprequest.desktop.kvm.connectionBar.httprequest = this.ws.httprequest;
1905
+ this.ws.httprequest.desktop.kvm.connectionBar.on('close', function () {
1906
+ MeshServerLogEx(33, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1907
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1908
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1909
+ }
1910
+ this.httprequest.desktop.kvm.end();
1911
+ });
1912
+ }
1913
+ }
1914
+ this.ws.httprequest.desktop.kvm.pipe(this.ws, { dataTypeSkip: 1 });
1915
+ this.ws.resume();
1916
+ },
1917
+ function (e)
1918
+ {
1919
+ // User Consent Denied/Failed
1920
+ this.ws._consentpromise = null;
1921
+ MeshServerLogEx(34, null, "Failed to start remote desktop after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
1922
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
1923
+ });
1924
+ } else {
1925
+ // User Consent Prompt is not required
1926
+ if (this.httprequest.consent && (this.httprequest.consent & 1)) {
1927
+ // User Notifications is required
1928
+ MeshServerLogEx(35, null, "Started remote desktop with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1929
+ var notifyMessage = this.httprequest.realname + " started a remote desktop session.", notifyTitle = "MeshCentral";
1930
+ if (this.httprequest.soptions != null) {
1931
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
1932
+ if (this.httprequest.soptions.notifyMsgDesktop != null) { notifyMessage = this.httprequest.soptions.notifyMsgDesktop.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
1933
+ }
1934
+ try { require('toaster').Toast(notifyTitle, notifyMessage, tsid); } catch (e) { }
1935
+ } else {
1936
+ MeshServerLogEx(36, null, "Started remote desktop without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
1937
+ }
1938
+ if (this.httprequest.consent && (this.httprequest.consent & 0x40)) {
1939
+ // Connection Bar is required
1940
+ if (this.httprequest.desktop.kvm.connectionBar) {
1941
+ this.httprequest.desktop.kvm.connectionBar.removeAllListeners('close');
1942
+ this.httprequest.desktop.kvm.connectionBar.close();
1943
+ }
1944
+ try {
1945
+ this.httprequest.desktop.kvm.connectionBar = require('notifybar-desktop')(this.httprequest.privacybartext.replace('{0}', this.httprequest.desktop.kvm.rusers.join(', ')).replace('{1}', this.httprequest.desktop.kvm.users.join(', ')), require('MeshAgent')._tsid);
1946
+ MeshServerLogEx(37, null, "Remote Desktop Connection Bar Activated/Updated (" + this.httprequest.remoteaddr + ")", this.httprequest);
1947
+ }
1948
+ catch (e) {
1949
+ MeshServerLogEx(38, null, "Remote Desktop Connection Bar Failed or not Supported (" + this.httprequest.remoteaddr + ")", this.httprequest);
1950
+ }
1951
+ if (this.httprequest.desktop.kvm.connectionBar) {
1952
+ this.httprequest.desktop.kvm.connectionBar.httprequest = this.httprequest;
1953
+ this.httprequest.desktop.kvm.connectionBar.on('close', function () {
1954
+ MeshServerLogEx(39, null, "Remote Desktop Connection forcefully closed by local user (" + this.httprequest.remoteaddr + ")", this.httprequest);
1955
+ for (var i in this.httprequest.desktop.kvm._pipedStreams) {
1956
+ this.httprequest.desktop.kvm._pipedStreams[i].end();
1957
+ }
1958
+ this.httprequest.desktop.kvm.end();
1959
+ });
1960
+ }
1961
+ }
1962
+ this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 });
1963
+ }
1964
+
1965
+ this.removeAllListeners('data');
1966
+ this.on('data', onTunnelControlData);
1967
+ //this.write('MeshCore KVM Hello!1');
1968
+
1969
+ } else if (this.httprequest.protocol == 5) {
1970
+ //
1971
+ // Remote Files
1972
+ //
1973
+
1974
+ // Check user access rights for files
1975
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) || ((this.httprequest.rights != 0xFFFFFFFF) && ((this.httprequest.rights & MESHRIGHT_NOFILES) != 0))) {
1976
+ // Disengage this tunnel, user does not have the rights to do this!!
1977
+ this.httprequest.protocol = 999999;
1978
+ this.httprequest.s.end();
1979
+ sendConsoleText("Error: No files control rights.");
1980
+ return;
1981
+ }
1982
+
1983
+ this.descriptorMetadata = "Remote Files";
1984
+
1985
+ // Add the files session to the count to update the server
1986
+ if (this.httprequest.userid != null) {
1987
+ if (tunnelUserCount.files[this.httprequest.userid] == null) { tunnelUserCount.files[this.httprequest.userid] = 1; } else { tunnelUserCount.files[this.httprequest.userid]++; }
1988
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
1989
+ broadcastSessionsToRegisteredApps();
1990
+ }
1991
+
1992
+ this.end = function () {
1993
+ // Remove the files session from the count to update the server
1994
+ if (this.httprequest.userid != null) {
1995
+ if (tunnelUserCount.files[this.httprequest.userid] != null) { tunnelUserCount.files[this.httprequest.userid]--; if (tunnelUserCount.files[this.httprequest.userid] <= 0) { delete tunnelUserCount.files[this.httprequest.userid]; } }
1996
+ try { mesh.SendCommand({ action: 'sessions', type: 'files', value: tunnelUserCount.files }); } catch (e) { }
1997
+ broadcastSessionsToRegisteredApps();
1998
+ }
1999
+ };
2000
+
2001
+ // Perform notification if needed. Toast messages may not be supported on all platforms.
2002
+ if (this.httprequest.consent && (this.httprequest.consent & 32)) {
2003
+ // User Consent Prompt is required
2004
+ // Send a console message back using the console channel, "\n" is supported.
2005
+ this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: "Waiting for user to grant access...", msgid: 1 }));
2006
+ var consentMessage = this.httprequest.realname + " requesting remote file Access. Grant access?", consentTitle = 'MeshCentral';
2007
+ if (this.httprequest.soptions != null) {
2008
+ if (this.httprequest.soptions.consentTitle != null) { consentTitle = this.httprequest.soptions.consentTitle; }
2009
+ if (this.httprequest.soptions.consentMsgFiles != null) { consentMessage = this.httprequest.soptions.consentMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2010
+ }
2011
+ var pr = require('message-box').create(consentTitle, consentMessage, 30);
2012
+ pr.ws = this;
2013
+ this.pause();
2014
+ this._consentpromise = pr;
2015
+ this.prependOnceListener('end', function () { if (this._consentpromise && this._consentpromise.close) { this._consentpromise.close(); } });
2016
+ pr.then(
2017
+ function ()
2018
+ {
2019
+ // Success
2020
+ this.ws._consentpromise = null;
2021
+ MeshServerLogEx(40, null, "Starting remote files after local user accepted (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2022
+ this.ws.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: null }));
2023
+ if (this.ws.httprequest.consent && (this.ws.httprequest.consent & 4)) {
2024
+ // User Notifications is required
2025
+ var notifyMessage = this.ws.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2026
+ if (this.ws.httprequest.soptions != null) {
2027
+ if (this.ws.httprequest.soptions.notifyTitle != null) { notifyTitle = this.ws.httprequest.soptions.notifyTitle; }
2028
+ if (this.ws.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.ws.httprequest.soptions.notifyMsgFiles.replace('{0}', this.ws.httprequest.realname).replace('{1}', this.ws.httprequest.username); }
2029
+ }
2030
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2031
+ }
2032
+ this.ws.resume();
2033
+ },
2034
+ function (e)
2035
+ {
2036
+ // User Consent Denied/Failed
2037
+ this.ws._consentpromise = null;
2038
+ MeshServerLogEx(41, null, "Failed to start remote files after local user rejected (" + this.ws.httprequest.remoteaddr + ")", this.ws.httprequest);
2039
+ this.ws.end(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString(), msgid: 2 }));
2040
+ });
2041
+ } else {
2042
+ // User Consent Prompt is not required
2043
+ if (this.httprequest.consent && (this.httprequest.consent & 4)) {
2044
+ // User Notifications is required
2045
+ MeshServerLogEx(42, null, "Started remote files with toast notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2046
+ var notifyMessage = this.httprequest.realname + " started a remote file session.", notifyTitle = "MeshCentral";
2047
+ if (this.httprequest.soptions != null) {
2048
+ if (this.httprequest.soptions.notifyTitle != null) { notifyTitle = this.httprequest.soptions.notifyTitle; }
2049
+ if (this.httprequest.soptions.notifyMsgFiles != null) { notifyMessage = this.httprequest.soptions.notifyMsgFiles.replace('{0}', this.httprequest.realname).replace('{1}', this.httprequest.username); }
2050
+ }
2051
+ try { require('toaster').Toast(notifyTitle, notifyMessage); } catch (e) { }
2052
+ } else {
2053
+ MeshServerLogEx(43, null, "Started remote files without notification (" + this.httprequest.remoteaddr + ")", this.httprequest);
2054
+ }
2055
+ this.resume();
2056
+ }
2057
+
2058
+ // Setup files
2059
+ // NOP
2060
+ }
2061
+ } else if (this.httprequest.protocol == 1) {
2062
+ // Send data into terminal stdin
2063
+ //this.write(data); // Echo back the keys (Does not seem to be a good idea)
2064
+ } else if (this.httprequest.protocol == 2) {
2065
+ // Send data into remote desktop
2066
+ if (this.httprequest.desktop.state == 0) {
2067
+ this.write(Buffer.from(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
2068
+ this.httprequest.desktop.state = 1;
2069
+ } else {
2070
+ this.httprequest.desktop.write(data);
2071
+ }
2072
+ } else if (this.httprequest.protocol == 5) {
2073
+ // Process files commands
2074
+ var cmd = null;
2075
+ try { cmd = JSON.parse(data); } catch (e) { };
2076
+ if (cmd == null) { return; }
2077
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2078
+ if (cmd.action == undefined) { return; }
2079
+ //sendConsoleText('CMD: ' + JSON.stringify(cmd));
2080
+
2081
+ if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
2082
+ //console.log(objToString(cmd, 0, ' '));
2083
+ switch (cmd.action) {
2084
+ case 'ls': {
2085
+ /*
2086
+ // Close the watcher if required
2087
+ var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
2088
+ if ((this.httprequest.watcher != undefined) && (samepath == false)) {
2089
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
2090
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
2091
+ delete this.httprequest.watcher;
2092
+ }
2093
+ */
2094
+
2095
+ // Send the folder content to the browser
2096
+ var response = getDirectoryInfo(cmd.path);
2097
+ if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
2098
+ this.write(Buffer.from(JSON.stringify(response)));
2099
+
2100
+ /*
2101
+ // Start the directory watcher
2102
+ if ((cmd.path != '') && (samepath == false)) {
2103
+ var watcher = fs.watch(cmd.path, onFileWatcher);
2104
+ watcher.tunnel = this.httprequest;
2105
+ watcher.path = cmd.path;
2106
+ this.httprequest.watcher = watcher;
2107
+ //console.log('Starting watcher: ' + this.httprequest.watcher.path);
2108
+ }
2109
+ */
2110
+ break;
2111
+ }
2112
+ case 'mkdir': {
2113
+ // Create a new empty folder
2114
+ fs.mkdirSync(cmd.path);
2115
+ MeshServerLogEx(44, [cmd.path], "Create folder: \"" + cmd.path + "\"", this.httprequest);
2116
+ break;
2117
+ }
2118
+ case 'rm': {
2119
+ // Delete, possibly recursive delete
2120
+ for (var i in cmd.delfiles) {
2121
+ var p = obj.path.join(cmd.path, cmd.delfiles[i]), delcount = 0;
2122
+ try { delcount = deleteFolderRecursive(p, cmd.rec); } catch (e) { }
2123
+ if ((delcount == 1) && !cmd.rec) {
2124
+ MeshServerLogEx(45, [p], "Delete: \"" + p + "\"", this.httprequest);
2125
+ } else {
2126
+ if (cmd.rec) {
2127
+ MeshServerLogEx(46, [p, delcount], "Delete recursive: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2128
+ } else {
2129
+ MeshServerLogEx(47, [p, delcount], "Delete: \"" + p + "\", " + delcount + " element(s) removed", this.httprequest);
2130
+ }
2131
+ }
2132
+ }
2133
+ break;
2134
+ }
2135
+ case 'markcoredump': {
2136
+ // If we are asking for the coredump file, set the right path.
2137
+ var coreDumpPath = null;
2138
+ if (process.platform == 'win32') {
2139
+ if (fs.existsSync(process.coreDumpLocation)) { coreDumpPath = process.coreDumpLocation; }
2140
+ } else {
2141
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { coreDumpPath = process.cwd() + 'core'; }
2142
+ }
2143
+ if (coreDumpPath != null) { db.Put('CoreDumpTime', require('fs').statSync(coreDumpPath).mtime); }
2144
+ break;
2145
+ }
2146
+ case 'rename': {
2147
+ // Rename a file or folder
2148
+ var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
2149
+ var newfullpath = obj.path.join(cmd.path, cmd.newname);
2150
+ MeshServerLogEx(48, [oldfullpath, cmd.newname], 'Rename: \"' + oldfullpath + '\" to \"' + cmd.newname + '\"', this.httprequest);
2151
+ try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
2152
+ break;
2153
+ }
2154
+ case 'download': {
2155
+ // Download a file
2156
+ var sendNextBlock = 0;
2157
+ if (cmd.sub == 'start') { // Setup the download
2158
+ if ((cmd.path == null) && (cmd.ask == 'coredump')) { // If we are asking for the coredump file, set the right path.
2159
+ if (process.platform == 'win32') {
2160
+ if (fs.existsSync(process.coreDumpLocation)) { cmd.path = process.coreDumpLocation; }
2161
+ } else {
2162
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { cmd.path = process.cwd() + 'core'; }
2163
+ }
2164
+ }
2165
+ MeshServerLogEx(49, [cmd.path], 'Download: \"' + cmd.path + '\"', this.httprequest);
2166
+ if ((cmd.path == null) || (this.filedownload != null)) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2167
+ this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
2168
+ try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
2169
+ if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
2170
+ } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
2171
+ if (cmd.sub == 'startack') { sendNextBlock = ((typeof cmd.ack == 'number') ? cmd.ack : 8); } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
2172
+ }
2173
+ // Send the next download block(s)
2174
+ while (sendNextBlock > 0) {
2175
+ sendNextBlock--;
2176
+ var buf = Buffer.alloc(16384);
2177
+ var len = fs.readSync(this.filedownload.f, buf, 4, 16380, null);
2178
+ this.filedownload.ptr += len;
2179
+ if (len < 16380) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
2180
+ this.write(buf.slice(0, len + 4)); // Write as binary
2181
+ }
2182
+ break;
2183
+ }
2184
+ /*
2185
+ case 'download': {
2186
+ // Packet download of a file, agent to browser
2187
+ if (cmd.path == undefined) break;
2188
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2189
+ //console.log('Download: ' + filepath);
2190
+ try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
2191
+ this.httprequest.downloadFileId = cmd.reqid;
2192
+ this.httprequest.downloadFilePtr = 0;
2193
+ if (this.httprequest.downloadFile) { this.write(Buffer.from(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
2194
+ break;
2195
+ }
2196
+ case 'download2': {
2197
+ // Stream download of a file, agent to browser
2198
+ if (cmd.path == undefined) break;
2199
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2200
+ try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
2201
+ this.httprequest.downloadFile.pipe(this);
2202
+ this.httprequest.downloadFile.end = function () { }
2203
+ break;
2204
+ }
2205
+ */
2206
+ case 'upload': {
2207
+ // Upload a file, browser to agent
2208
+ if (this.httprequest.uploadFile != null) { fs.closeSync(this.httprequest.uploadFile); delete this.httprequest.uploadFile; }
2209
+ if (cmd.path == undefined) break;
2210
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
2211
+ this.httprequest.uploadFilePath = filepath;
2212
+ MeshServerLogEx(50, [filepath], 'Upload: \"' + filepath + '\"', this.httprequest);
2213
+ try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(Buffer.from(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
2214
+ this.httprequest.uploadFileid = cmd.reqid;
2215
+ if (this.httprequest.uploadFile) { this.write(Buffer.from(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
2216
+ break;
2217
+ }
2218
+ case 'uploaddone': {
2219
+ // Indicates that an upload is done
2220
+ if (this.httprequest.uploadFile) {
2221
+ fs.closeSync(this.httprequest.uploadFile);
2222
+ this.write(Buffer.from(JSON.stringify({ action: 'uploaddone', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2223
+ delete this.httprequest.uploadFile;
2224
+ delete this.httprequest.uploadFileid;
2225
+ delete this.httprequest.uploadFilePath;
2226
+ }
2227
+ break;
2228
+ }
2229
+ case 'uploadcancel': {
2230
+ // Indicates that an upload is canceled
2231
+ if (this.httprequest.uploadFile) {
2232
+ fs.closeSync(this.httprequest.uploadFile);
2233
+ fs.unlinkSync(this.httprequest.uploadFilePath);
2234
+ this.write(Buffer.from(JSON.stringify({ action: 'uploadcancel', reqid: this.httprequest.uploadFileid }))); // Indicate that we closed the file.
2235
+ delete this.httprequest.uploadFile;
2236
+ delete this.httprequest.uploadFileid;
2237
+ delete this.httprequest.uploadFilePath;
2238
+ }
2239
+ break;
2240
+ }
2241
+ case 'copy': {
2242
+ // Copy a bunch of files from scpath to dspath
2243
+ for (var i in cmd.names) {
2244
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2245
+ MeshServerLogEx(51, [sc, ds], 'Copy: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2246
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
2247
+ }
2248
+ break;
2249
+ }
2250
+ case 'move': {
2251
+ // Move a bunch of files from scpath to dspath
2252
+ for (var i in cmd.names) {
2253
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
2254
+ MeshServerLogEx(52, [sc, ds], 'Move: \"' + sc + '\" to \"' + ds + '\"', this.httprequest);
2255
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
2256
+ }
2257
+ break;
2258
+ }
2259
+ case 'zip':
2260
+ // Zip a bunch of files
2261
+ if (this.zip != null) return; // Zip operating is currently running, exit now.
2262
+
2263
+ // Check that the specified files exist & build full paths
2264
+ var fp, stat, p = [];
2265
+ for (var i in cmd.files) { fp = cmd.path + '/' + cmd.files[i]; stat = null; try { stat = fs.statSync(fp); } catch (e) { } if (stat != null) { p.push(fp); } }
2266
+ if (p.length == 0) return; // No files, quit now.
2267
+
2268
+ // Setup file compression
2269
+ var ofile = cmd.path + '/' + cmd.output;
2270
+ this.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zipping' })));
2271
+ this.zipfile = ofile;
2272
+ delete this.zipcancel;
2273
+ var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2274
+ out.xws = this;
2275
+ out.on('close', function () {
2276
+ this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: null })));
2277
+ this.xws.write(Buffer.from(JSON.stringify({ action: 'refresh' })));
2278
+ if (this.xws.zipcancel === true) { fs.unlinkSync(this.xws.zipfile); } // Delete the complete file.
2279
+ delete this.xws.zipcancel;
2280
+ delete this.xws.zipfile;
2281
+ delete this.xws.zip;
2282
+ });
2283
+ this.zip = require('zip-writer').write({ files: p, basePath: cmd.path });
2284
+ this.zip.xws = this;
2285
+ this.zip.on('progress', require('events').moderated(function (name, p) { this.xws.write(Buffer.from(JSON.stringify({ action: 'dialogmessage', msg: 'zippingFile', file: ((process.platform == 'win32') ? (name.split('/').join('\\')) : name), progress: p }))); }, 1000));
2286
+ this.zip.pipe(out);
2287
+ break;
2288
+ case 'cancel':
2289
+ // Cancel zip operation if present
2290
+ try { this.zipcancel = true; this.zip.cancel(function () { }); } catch (e) { }
2291
+ this.zip = null;
2292
+ break;
2293
+ default:
2294
+ // Unknown action, ignore it.
2295
+ break;
2296
+ }
2297
+ } else if (this.httprequest.protocol == 7) { // Plugin data exchange
2298
+ var cmd = null;
2299
+ try { cmd = JSON.parse(data); } catch (e) { };
2300
+ if (cmd == null) { return; }
2301
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
2302
+ if (cmd.action == undefined) return;
2303
+
2304
+ switch (cmd.action) {
2305
+ case 'plugin': {
2306
+ try { require(cmd.plugin).consoleaction(cmd, null, null, this); } catch (e) { throw e; }
2307
+ break;
2308
+ }
2309
+ default: {
2310
+ // probably shouldn't happen, but just in case this feature is expanded
2311
+ }
2312
+ }
2313
+
2314
+ }
2315
+ //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
2316
+ }
2317
+ }
2318
+
2319
+ // Delete a directory with a files and directories within it
2320
+ function deleteFolderRecursive(path, rec) {
2321
+ var count = 0;
2322
+ if (fs.existsSync(path)) {
2323
+ if (rec == true) {
2324
+ fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
2325
+ var curPath = obj.path.join(path, file);
2326
+ if (fs.statSync(curPath).isDirectory()) { // recurse
2327
+ count += deleteFolderRecursive(curPath, true);
2328
+ } else { // delete file
2329
+ fs.unlinkSync(curPath);
2330
+ count++;
2331
+ }
2332
+ });
2333
+ }
2334
+ fs.unlinkSync(path);
2335
+ count++;
2336
+ }
2337
+ return count;
2338
+ };
2339
+
2340
+ // Called when receiving control data on WebRTC
2341
+ function onTunnelWebRTCControlData(data) {
2342
+ if (typeof data != 'string') return;
2343
+ var obj;
2344
+ try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
2345
+ if (obj.type == 'close') {
2346
+ //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
2347
+ try { this.close(); } catch (e) { }
2348
+ try { this.xrtc.close(); } catch (e) { }
2349
+ }
2350
+ }
2351
+
2352
+ // Called when receiving control data on websocket
2353
+ function onTunnelControlData(data, ws) {
2354
+ var obj;
2355
+ if (ws == null) { ws = this; }
2356
+ if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
2357
+ else if (typeof data == 'object') { obj = data; } else { return; }
2358
+ //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
2359
+ //console.log('onTunnelControlData: ' + JSON.stringify(data));
2360
+
2361
+ if (obj.action) {
2362
+ switch (obj.action) {
2363
+ case 'lock': {
2364
+ // Lock the current user out of the desktop
2365
+ try {
2366
+ if (process.platform == 'win32') {
2367
+ MeshServerLogEx(53, null, "Locking remote user out of desktop", ws.httprequest);
2368
+ var child = require('child_process');
2369
+ child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
2370
+ }
2371
+ } catch (e) { }
2372
+ break;
2373
+ }
2374
+ default:
2375
+ // Unknown action, ignore it.
2376
+ break;
2377
+ }
2378
+ return;
2379
+ }
2380
+
2381
+ switch (obj.type) {
2382
+ case 'options': {
2383
+ // These are additional connection options passed in the control channel.
2384
+ //sendConsoleText('options: ' + JSON.stringify(obj));
2385
+ delete obj.type;
2386
+ ws.httprequest.xoptions = obj;
2387
+
2388
+ // Set additional user consent options if present
2389
+ if ((obj != null) && (typeof obj.consent == 'number')) { ws.httprequest.consent |= obj.consent; }
2390
+
2391
+ break;
2392
+ }
2393
+ case 'close': {
2394
+ // We received the close on the websocket
2395
+ //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
2396
+ try { ws.close(); } catch (e) { }
2397
+ break;
2398
+ }
2399
+ case 'termsize': {
2400
+ // Indicates a change in terminal size
2401
+ if (process.platform == 'win32')
2402
+ {
2403
+ if (ws.httprequest._dispatcher == null) return;
2404
+ //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
2405
+ if (ws.httprequest._dispatcher.invoke) { ws.httprequest._dispatcher.invoke('resizeTerminal', [obj.cols, obj.rows]); }
2406
+ } else
2407
+ {
2408
+ if (ws.httprequest.process == null || ws.httprequest.process.pty == 0) return;
2409
+ //sendConsoleText('Linux Resize: ' + obj.cols + 'x' + obj.rows);
2410
+
2411
+ if (ws.httprequest.process.tcsetsize) { ws.httprequest.process.tcsetsize(obj.rows, obj.cols); }
2412
+ }
2413
+ break;
2414
+ }
2415
+ case 'webrtc0': { // Browser indicates we can start WebRTC switch-over.
2416
+ if (ws.httprequest.protocol == 1) { // Terminal
2417
+ // This is a terminal data stream, unpipe the terminal now and indicate to the other side that terminal data will no longer be received over WebSocket
2418
+ if (process.platform == 'win32') {
2419
+ ws.httprequest._term.unpipe(ws);
2420
+ } else {
2421
+ ws.httprequest.process.stdout.unpipe(ws);
2422
+ ws.httprequest.process.stderr.unpipe(ws);
2423
+ }
2424
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2425
+ // This is a KVM data stream, unpipe the KVM now and indicate to the other side that KVM data will no longer be received over WebSocket
2426
+ ws.httprequest.desktop.kvm.unpipe(ws);
2427
+ } else {
2428
+ // Switch things around so all WebRTC data goes to onTunnelData().
2429
+ ws.rtcchannel.httprequest = ws.httprequest;
2430
+ ws.rtcchannel.removeAllListeners('data');
2431
+ ws.rtcchannel.on('data', onTunnelData);
2432
+ }
2433
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
2434
+ break;
2435
+ }
2436
+ case 'webrtc1': {
2437
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2438
+ // Switch the user input from websocket to webrtc at this point.
2439
+ if (process.platform == 'win32') {
2440
+ ws.unpipe(ws.httprequest._term);
2441
+ ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2442
+ } else {
2443
+ ws.unpipe(ws.httprequest.process.stdin);
2444
+ ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2445
+ }
2446
+ ws.resume(); // Resume the websocket to keep receiving control data
2447
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2448
+ // Switch the user input from websocket to webrtc at this point.
2449
+ ws.unpipe(ws.httprequest.desktop.kvm);
2450
+ try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
2451
+ ws.resume(); // Resume the websocket to keep receiving control data
2452
+ }
2453
+ ws.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}'); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
2454
+ break;
2455
+ }
2456
+ case 'webrtc2': {
2457
+ // Other side received websocket end of data marker, start sending data on WebRTC channel
2458
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) { // Terminal
2459
+ if (process.platform == 'win32') {
2460
+ ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2461
+ } else {
2462
+ ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2463
+ ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
2464
+ }
2465
+ } else if (ws.httprequest.protocol == 2) { // Desktop
2466
+ ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
2467
+ }
2468
+ break;
2469
+ }
2470
+ case 'offer': {
2471
+ // This is a WebRTC offer.
2472
+ if ((ws.httprequest.protocol == 1) || (ws.httprequest.protocol == 6)) return; // TODO: Terminal is currently broken with WebRTC. Reject WebRTC upgrade for now.
2473
+ ws.webrtc = rtc.createConnection();
2474
+ ws.webrtc.websocket = ws;
2475
+ ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
2476
+ ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
2477
+ ws.webrtc.on('dataChannel', function (rtcchannel) {
2478
+ //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
2479
+ rtcchannel.maxFragmentSize = 32768;
2480
+ rtcchannel.xrtc = this;
2481
+ rtcchannel.websocket = this.websocket;
2482
+ this.rtcchannel = rtcchannel;
2483
+ this.websocket.rtcchannel = rtcchannel;
2484
+ this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
2485
+ this.websocket.rtcchannel.on('end', function () {
2486
+ // The WebRTC channel closed, unpipe the KVM now. This is also done when the web socket closes.
2487
+ //sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');
2488
+ if (this.websocket.desktop && this.websocket.desktop.kvm)
2489
+ {
2490
+ try
2491
+ {
2492
+ this.unpipe(this.websocket.desktop.kvm);
2493
+ this.websocket.httprequest.desktop.kvm.unpipe(this);
2494
+ }
2495
+ catch (e) { }
2496
+ }
2497
+ });
2498
+ this.websocket.write('{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}'); // Indicate we are ready for WebRTC switch-over.
2499
+ });
2500
+ var sdp = null;
2501
+ try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (e) { }
2502
+ if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
2503
+ break;
2504
+ }
2505
+ case 'ping': {
2506
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"pong\"}"); // Send pong response
2507
+ break;
2508
+ }
2509
+ case 'pong': { // NOP
2510
+ break;
2511
+ }
2512
+ case 'rtt': {
2513
+ ws.write({ type: 'rtt', ctrlChannel: '102938', time: obj.time });
2514
+ break;
2515
+ }
2516
+ }
2517
+ }
2518
+
2519
+ // Console state
2520
+ var consoleWebSockets = {};
2521
+ var consoleHttpRequest = null;
2522
+
2523
+ // Console HTTP response
2524
+ function consoleHttpResponse(response) {
2525
+ response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
2526
+ response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
2527
+ };
2528
+
2529
+ // Open a web browser to a specified URL on current user's desktop
2530
+ function openUserDesktopUrl(url) {
2531
+ if ((url.toLowerCase().startsWith('http://') == false) && (url.toLowerCase().startsWith('https://') == false)) { return null; }
2532
+ var child = null;
2533
+ try {
2534
+ switch (process.platform) {
2535
+ case 'win32':
2536
+ var user = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2537
+ child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ['cmd']);
2538
+ child.stderr.on('data', function () { });
2539
+ child.stdout.on('data', function () { });
2540
+ child.stdin.write('SCHTASKS /CREATE /F /TN MeshChatTask /SC ONCE /ST 00:00 /RU ' + user + ' /TR "' + process.env['windir'] + '\\system32\\cmd.exe /C START ' + url + '"\r\n');
2541
+ child.stdin.write('SCHTASKS /RUN /TN MeshChatTask\r\n');
2542
+ child.stdin.write('SCHTASKS /DELETE /F /TN MeshChatTask\r\n');
2543
+ child.stdin.write('exit\r\n');
2544
+ child.waitExit();
2545
+ break;
2546
+ case 'linux':
2547
+ child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { uid: require('user-sessions').consoleUid() });
2548
+ break;
2549
+ case 'darwin':
2550
+ child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
2551
+ break;
2552
+ default:
2553
+ // Unknown platform, ignore this command.
2554
+ break;
2555
+ }
2556
+ } catch (e) { }
2557
+ return child;
2558
+ }
2559
+
2560
+ // Process a mesh agent console command
2561
+ function processConsoleCommand(cmd, args, rights, sessionid) {
2562
+ try {
2563
+ var response = null;
2564
+ switch (cmd) {
2565
+ case 'help': { // Displays available commands
2566
+ var fin = '', f = '', availcommands = 'amtconfig,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
2567
+ if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2568
+ if (process.platform != 'freebsd') { availcommands += ',vm';}
2569
+ if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
2570
+ try { require('zip-reader'); availcommands += ',zip,unzip'; } catch (e) { }
2571
+
2572
+ availcommands = availcommands.split(',').sort();
2573
+ while (availcommands.length > 0) {
2574
+ if (f.length > 90) { fin += (f + ',\r\n'); f = ''; }
2575
+ f += (((f != '') ? ', ' : ' ') + availcommands.shift());
2576
+ }
2577
+ if (f != '') { fin += f; }
2578
+ response = "Available commands: \r\n" + fin + ".";
2579
+ break;
2580
+ }
2581
+ case 'agentmsg': {
2582
+ if (args['_'].length == 0) {
2583
+ response = "Proper usage:\r\n agentmsg add \"[message]\" [iconIndex]\r\n agentmsg remove [index]\r\n agentmsg list"; // Display usage
2584
+ } else {
2585
+ if ((args['_'][0] == 'add') && (args['_'].length > 1)) {
2586
+ var msgIndex = 1, iconIndex = 0;
2587
+ while (tunnelUserCount.msg[msgIndex] != null) { msgIndex++; }
2588
+ if (args['_'].length >= 3) { try { iconIndex = parseInt(args['_'][2]); } catch (e) { } }
2589
+ if (typeof iconIndex != 'number') { iconIndex = 0; }
2590
+ tunnelUserCount.msg[msgIndex] = { msg: args['_'][1], icon: iconIndex };
2591
+ response = 'Agent message ' + msgIndex + ' added.';
2592
+ } else if ((args['_'][0] == 'remove') && (args['_'].length > 1)) {
2593
+ var msgIndex = 0;
2594
+ try { msgIndex = parseInt(args['_'][1]); } catch (x) { }
2595
+ if (tunnelUserCount.msg[msgIndex] == null) { response = "Message not found."; } else { delete tunnelUserCount.msg[msgIndex]; response = "Message removed."; }
2596
+ } else if (args['_'][0] == 'list') {
2597
+ response = JSON.stringify(tunnelUserCount.msg, null, 2);
2598
+ }
2599
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2600
+ broadcastSessionsToRegisteredApps();
2601
+ }
2602
+ break;
2603
+ }
2604
+ case 'clearagentmsg': {
2605
+ tunnelUserCount.msg = {};
2606
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (x) { }
2607
+ broadcastSessionsToRegisteredApps();
2608
+ break;
2609
+ }
2610
+ case 'coredump':
2611
+ if (args['_'].length != 1) {
2612
+ response = "Proper usage: coredump on|off|status|clear"; // Display usage
2613
+ } else {
2614
+ switch (args['_'][0].toLowerCase())
2615
+ {
2616
+ case 'on':
2617
+ process.coreDumpLocation = (process.platform == 'win32') ? (process.execPath.replace('.exe', '.dmp')) : (process.execPath + '.dmp');
2618
+ response = 'coredump is now on';
2619
+ break;
2620
+ case 'off':
2621
+ process.coreDumpLocation = null;
2622
+ response = 'coredump is now off';
2623
+ break;
2624
+ case 'status':
2625
+ response = 'coredump is: ' + ((process.coreDumpLocation == null) ? 'off' : 'on');
2626
+ if (process.coreDumpLocation != null) {
2627
+ if (process.platform == 'win32') {
2628
+ if (fs.existsSync(process.coreDumpLocation)) { response += '\r\n CoreDump present at: ' + process.coreDumpLocation; }
2629
+ } else {
2630
+ if ((process.cwd() != '//') && fs.existsSync(process.cwd() + 'core')) { response += '\r\n CoreDump present at: ' + process.cwd() + 'core'; }
2631
+ }
2632
+ }
2633
+ break;
2634
+ case 'clear':
2635
+ db.Put('CoreDumpTime', null);
2636
+ response = 'coredump db cleared';
2637
+ break;
2638
+ default:
2639
+ response = "Proper usage: coredump on|off|status"; // Display usage
2640
+ break;
2641
+ }
2642
+ }
2643
+ break;
2644
+ case 'service':
2645
+ if (args['_'].length != 1)
2646
+ {
2647
+ response = "Proper usage: service status|restart"; // Display usage
2648
+ }
2649
+ else
2650
+ {
2651
+ var s = require('service-manager').manager.getService(process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
2652
+ switch(args['_'][0].toLowerCase())
2653
+ {
2654
+ case 'status':
2655
+ response = 'Service ' + (s.isRunning() ? (s.isMe() ? '[SELF]' : '[RUNNING]') : ('[NOT RUNNING]'));
2656
+ break;
2657
+ case 'restart':
2658
+ if (s.isMe())
2659
+ {
2660
+ s.restart();
2661
+ }
2662
+ else
2663
+ {
2664
+ response = 'Restarting another agent instance is not allowed';
2665
+ }
2666
+ break;
2667
+ default:
2668
+ response = "Proper usage: service status|restart"; // Display usage
2669
+ break;
2670
+ }
2671
+ if (process.platform == 'win32') { s.close(); }
2672
+ }
2673
+ break;
2674
+ case 'zip':
2675
+ if (args['_'].length == 0) {
2676
+ response = "Proper usage: zip (output file name), input1 [, input n]"; // Display usage
2677
+ } else {
2678
+ var p = args['_'].join(' ').split(',');
2679
+ var ofile = p.shift();
2680
+ sendConsoleText('Writing ' + ofile + '...');
2681
+ var out = require('fs').createWriteStream(ofile, { flags: 'wb' });
2682
+ out.fname = ofile;
2683
+ out.sessionid = sessionid;
2684
+ out.on('close', function () { sendConsoleText('DONE writing ' + this.fname, this.sessionid); });
2685
+ var zip = require('zip-writer').write({ files: p });
2686
+ zip.pipe(out);
2687
+ }
2688
+ break;
2689
+ case 'unzip':
2690
+ if (args['_'].length == 0) {
2691
+ response = "Proper usage: unzip input, destination"; // Display usage
2692
+ } else {
2693
+ var p = args['_'].join(' ').split(',');
2694
+ if (p.length != 2) { response = "Proper usage: unzip input, destination"; break; } // Display usage
2695
+ var prom = require('zip-reader').read(p[0]);
2696
+ prom._dest = p[1];
2697
+ prom.self = this;
2698
+ prom.sessionid = sessionid;
2699
+ prom.then(function (zipped) {
2700
+ sendConsoleText('Extracting to ' + this._dest + '...', this.sessionid);
2701
+ zipped.extractAll(this._dest).then(function () { sendConsoleText('finished unzipping', this.sessionid); }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); }).parentPromise.sessionid = this.sessionid;
2702
+ }, function (e) { sendConsoleText('Error unzipping: ' + e, this.sessionid); });
2703
+ }
2704
+ break;
2705
+ case 'setbattery':
2706
+ // require('MeshAgent').SendCommand({ action: 'battery', state: 'dc', level: 55 });
2707
+ if ((args['_'].length > 0) && ((args['_'][0] == 'ac') || (args['_'][0] == 'dc'))) {
2708
+ var b = { action: 'battery', state: args['_'][0] };
2709
+ if (args['_'].length == 2) { b.level = parseInt(args['_'][1]); }
2710
+ require('MeshAgent').SendCommand(b);
2711
+ } else {
2712
+ require('MeshAgent').SendCommand({ action: 'battery' });
2713
+ }
2714
+ break;
2715
+ case 'fdsnapshot':
2716
+ require('ChainViewer').getSnapshot().then(function (c) { sendConsoleText(c, this.sessionid); }).parentPromise.sessionid = sessionid;
2717
+ break;
2718
+ case 'fdcount':
2719
+ require('DescriptorEvents').getDescriptorCount().then(
2720
+ function (c)
2721
+ {
2722
+ sendConsoleText('Descriptor Count: ' + c, this.sessionid);
2723
+ }, function (e)
2724
+ {
2725
+ sendConsoleText('Error fetching descriptor count: ' + e, this.sessionid);
2726
+ }).parentPromise.sessionid = sessionid;
2727
+ break;
2728
+ case 'uac':
2729
+ if (process.platform != 'win32')
2730
+ {
2731
+ response = 'Unknown command "uac", type "help" for list of avaialble commands.';
2732
+ break;
2733
+ }
2734
+ if (args['_'].length != 1)
2735
+ {
2736
+ response = 'Proper usage: uac [get|interactive|secure]';
2737
+ }
2738
+ else
2739
+ {
2740
+ switch(args['_'][0].toUpperCase())
2741
+ {
2742
+ case 'GET':
2743
+ var secd = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop');
2744
+ response = "UAC mode: " + (secd == 0 ? "Interactive Desktop" : "Secure Desktop");
2745
+ break;
2746
+ case 'INTERACTIVE':
2747
+ try
2748
+ {
2749
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 0);
2750
+ response = 'UAC mode changed to: Interactive Desktop';
2751
+ }
2752
+ catch (e)
2753
+ {
2754
+ response = "Unable to change UAC Mode";
2755
+ }
2756
+ break;
2757
+ case 'SECURE':
2758
+ try
2759
+ {
2760
+ require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System', 'PromptOnSecureDesktop', 1);
2761
+ response = 'UAC mode changed to: Secure Desktop';
2762
+ }
2763
+ catch(e)
2764
+ {
2765
+ response = "Unable to change UAC Mode";
2766
+ }
2767
+ break;
2768
+ default:
2769
+ response = 'Proper usage: uac [get|interactive|secure]';
2770
+ break;
2771
+ }
2772
+ }
2773
+ break;
2774
+ case 'vm':
2775
+ response = 'Virtual Machine = ' + require('identifiers').isVM();
2776
+ break;
2777
+ case 'startupoptions':
2778
+ response = JSON.stringify(require('MeshAgent').getStartupOptions());
2779
+ break;
2780
+ case 'kvmmode':
2781
+ if (require('MeshAgent').maxKvmTileSize == null)
2782
+ {
2783
+ response = "Unknown command \"kvmmode\", type \"help\" for list of avaialble commands.";
2784
+ }
2785
+ else
2786
+ {
2787
+ if(require('MeshAgent').maxKvmTileSize == 0)
2788
+ {
2789
+ response = 'KVM Mode: Full JUMBO';
2790
+ }
2791
+ else
2792
+ {
2793
+ response = 'KVM Mode: ' + (require('MeshAgent').maxKvmTileSize <= 65500 ? 'NO JUMBO' : 'Partial JUMBO');
2794
+ response += (', TileLimit: ' + (require('MeshAgent').maxKvmTileSize < 1024 ? (require('MeshAgent').maxKvmTileSize + ' bytes') : (Math.round(require('MeshAgent').maxKvmTileSize/1024) + ' Kbytes')));
2795
+ }
2796
+ }
2797
+ break;
2798
+ case 'alert':
2799
+ if (args['_'].length == 0)
2800
+ {
2801
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2802
+ }
2803
+ else
2804
+ {
2805
+ var p = args['_'].join(' ').split(',');
2806
+ if(p.length<2)
2807
+ {
2808
+ response = "Proper usage: alert TITLE, CAPTION [, TIMEOUT]"; // Display usage
2809
+ }
2810
+ else
2811
+ {
2812
+ this._alert = require('message-box').create(p[0], p[1], p.length==3?parseInt(p[2]):9999,1);
2813
+ }
2814
+ }
2815
+ break;
2816
+ case 'agentsize':
2817
+ var actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
2818
+ if (process.platform == 'win32') {
2819
+ // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
2820
+ var writtenSize = 0;
2821
+ try { writtenSize = require('win-registry').QueryKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize'); } catch (e) { response = e; }
2822
+ if (writtenSize != actualSize) {
2823
+ response = "Size updated from: " + writtenSize + " to: " + actualSize;
2824
+ try { require('win-registry').WriteKey(require('win-registry').HKEY.LocalMachine, 'Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\MeshCentralAgent', 'EstimatedSize', actualSize); } catch (e) { response = e; }
2825
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2826
+ } else { response = "Agent Size: " + actualSize + " kb"; }
2827
+ break;
2828
+ case 'versions':
2829
+ response = JSON.stringify(process.versions, null, ' ');
2830
+ break;
2831
+ case 'wpfhwacceleration':
2832
+ if (process.platform != 'win32') { throw ("wpfhwacceleration setting is only supported on Windows"); }
2833
+ if (args['_'].length != 1) {
2834
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS)"; // Display usage
2835
+ }
2836
+ else {
2837
+ var reg = require('win-registry');
2838
+ var uname = require('user-sessions').getUsername(require('user-sessions').consoleUid());
2839
+ var key = reg.usernameToUserKey(uname);
2840
+
2841
+ switch (args['_'][0].toUpperCase()) {
2842
+ default:
2843
+ response = "Proper usage: wpfhwacceleration (ON|OFF|STATUS|DEFAULT)"; // Display usage
2844
+ break;
2845
+ case 'ON':
2846
+ try {
2847
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 0);
2848
+ response = "OK";
2849
+ } catch (e) { response = "FAILED"; }
2850
+ break;
2851
+ case 'OFF':
2852
+ try {
2853
+ reg.WriteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration', 1);
2854
+ response = 'OK';
2855
+ } catch (e) { response = 'FAILED'; }
2856
+ break;
2857
+ case 'STATUS':
2858
+ var s;
2859
+ try { s = reg.QueryKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration') == 1 ? 'DISABLED' : 'ENABLED'; } catch (e) { s = 'DEFAULT'; }
2860
+ response = "WPF Hardware Acceleration: " + s;
2861
+ break;
2862
+ case 'DEFAULT':
2863
+ try { reg.DeleteKey(reg.HKEY.Users, key + '\\SOFTWARE\\Microsoft\\Avalon.Graphics', 'DisableHWAcceleration'); } catch (e) { }
2864
+ response = 'OK';
2865
+ break;
2866
+ }
2867
+ }
2868
+ break;
2869
+ case 'tsid':
2870
+ if (process.platform == 'win32') {
2871
+ if (args['_'].length != 1) {
2872
+ response = "TSID: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2873
+ } else {
2874
+ var i = parseInt(args['_'][0]);
2875
+ require('MeshAgent')._tsid = (isNaN(i) ? null : i);
2876
+ response = "TSID set to: " + (require('MeshAgent')._tsid == null ? "console" : require('MeshAgent')._tsid);
2877
+ }
2878
+ } else { response = "TSID command only supported on Windows"; }
2879
+ break;
2880
+ case 'activeusers':
2881
+ if (process.platform == 'win32') {
2882
+ var p = require('user-sessions').enumerateUsers();
2883
+ p.sessionid = sessionid;
2884
+ p.then(function (u) {
2885
+ var v = [];
2886
+ for (var i in u) {
2887
+ if (u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
2888
+ }
2889
+ sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
2890
+ });
2891
+ } else { response = "activeusers command only supported on Windows"; }
2892
+ break;
2893
+ case 'wallpaper':
2894
+ if (process.platform != 'win32' && !(process.platform == 'linux' && require('linux-gnome-helpers').available)) {
2895
+ response = "wallpaper command not supported on this platform";
2896
+ }
2897
+ else {
2898
+ if (args['_'].length != 1) {
2899
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2900
+ }
2901
+ else {
2902
+ switch (args['_'][0].toUpperCase()) {
2903
+ default:
2904
+ response = 'Proper usage: wallpaper (GET|TOGGLE)'; // Display usage
2905
+ break;
2906
+ case 'GET':
2907
+ case 'TOGGLE':
2908
+ if (process.platform == 'win32') {
2909
+ var id = require('user-sessions').getProcessOwnerName(process.pid).tsid == 0 ? 1 : 0;
2910
+ var child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0='], { type: id });
2911
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2912
+ child.stderr.on('data', function () { });
2913
+ child.waitExit();
2914
+ var current = child.stdout.str.trim();
2915
+ if (args['_'][0].toUpperCase() == 'GET') {
2916
+ response = current;
2917
+ break;
2918
+ }
2919
+ if (current != '') {
2920
+ require('MeshAgent')._wallpaper = current;
2921
+ response = 'Wallpaper cleared';
2922
+ } else {
2923
+ response = 'Wallpaper restored';
2924
+ }
2925
+ child = require('child_process').execFile(process.execPath, [process.execPath.split('\\').pop(), '-b64exec', 'dmFyIFNQSV9HRVRERVNLV0FMTFBBUEVSID0gMHgwMDczOwp2YXIgU1BJX1NFVERFU0tXQUxMUEFQRVIgPSAweDAwMTQ7CnZhciBHTSA9IHJlcXVpcmUoJ19HZW5lcmljTWFyc2hhbCcpOwp2YXIgdXNlcjMyID0gR00uQ3JlYXRlTmF0aXZlUHJveHkoJ3VzZXIzMi5kbGwnKTsKdXNlcjMyLkNyZWF0ZU1ldGhvZCgnU3lzdGVtUGFyYW1ldGVyc0luZm9BJyk7CgppZiAocHJvY2Vzcy5hcmd2Lmxlbmd0aCA9PSAzKQp7CiAgICB2YXIgdiA9IEdNLkNyZWF0ZVZhcmlhYmxlKDEwMjQpOwogICAgdXNlcjMyLlN5c3RlbVBhcmFtZXRlcnNJbmZvQShTUElfR0VUREVTS1dBTExQQVBFUiwgdi5fc2l6ZSwgdiwgMCk7CiAgICBjb25zb2xlLmxvZyh2LlN0cmluZyk7CiAgICBwcm9jZXNzLmV4aXQoKTsKfQplbHNlCnsKICAgIHZhciBuYiA9IEdNLkNyZWF0ZVZhcmlhYmxlKHByb2Nlc3MuYXJndlszXSk7CiAgICB1c2VyMzIuU3lzdGVtUGFyYW1ldGVyc0luZm9BKFNQSV9TRVRERVNLV0FMTFBBUEVSLCBuYi5fc2l6ZSwgbmIsIDApOwogICAgcHJvY2Vzcy5leGl0KCk7Cn0=', current != '' ? '""' : require('MeshAgent')._wallpaper], { type: id });
2926
+ child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); });
2927
+ child.stderr.on('data', function () { });
2928
+ child.waitExit();
2929
+ }
2930
+ else {
2931
+ var id = require('user-sessions').consoleUid();
2932
+ var current = require('linux-gnome-helpers').getDesktopWallpaper(id);
2933
+ if (args['_'][0].toUpperCase() == 'GET') {
2934
+ response = current;
2935
+ break;
2936
+ }
2937
+ if (current != '/dev/null') {
2938
+ require('MeshAgent')._wallpaper = current;
2939
+ response = 'Wallpaper cleared';
2940
+ } else {
2941
+ response = 'Wallpaper restored';
2942
+ }
2943
+ require('linux-gnome-helpers').setDesktopWallpaper(id, current != '/dev/null' ? undefined : require('MeshAgent')._wallpaper);
2944
+ }
2945
+ break;
2946
+ }
2947
+ }
2948
+ }
2949
+ break;
2950
+ case 'safemode':
2951
+ if (process.platform != 'win32') {
2952
+ response = 'safemode only supported on Windows Platforms'
2953
+ }
2954
+ else {
2955
+ if (args['_'].length != 1) {
2956
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2957
+ }
2958
+ else {
2959
+ switch (args['_'][0].toUpperCase()) {
2960
+ default:
2961
+ response = 'Proper usage: safemode (ON|OFF|STATUS)'; // Display usage
2962
+ break;
2963
+ case 'ON':
2964
+ require('win-bcd').setKey('safeboot', 'Network');
2965
+ require('win-bcd').enableSafeModeService('Mesh Agent');
2966
+ break;
2967
+ case 'OFF':
2968
+ require('win-bcd').deleteKey('safeboot');
2969
+ break;
2970
+ case 'STATUS':
2971
+ var nextboot = require('win-bcd').getKey('safeboot');
2972
+ if (nextboot) {
2973
+ switch (nextboot) {
2974
+ case 'Network':
2975
+ case 'network':
2976
+ nextboot = 'SAFE_MODE_NETWORK';
2977
+ break;
2978
+ default:
2979
+ nextboot = 'SAFE_MODE';
2980
+ break;
2981
+ }
2982
+ }
2983
+ response = 'Current: ' + require('win-bcd').bootMode + ', NextBoot: ' + (nextboot ? nextboot : 'NORMAL');
2984
+ break;
2985
+ }
2986
+ }
2987
+ }
2988
+ break;
2989
+ /*
2990
+ case 'border':
2991
+ {
2992
+ if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
2993
+ if (meshCoreObj.users.length > 0) {
2994
+ obj.borderManager.Start(meshCoreObj.users[0]);
2995
+ response = 'Border blinking is on.';
2996
+ } else {
2997
+ response = 'Cannot turn on border blinking, no logged in users.';
2998
+ }
2999
+ } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
3000
+ obj.borderManager.Stop();
3001
+ response = 'Border blinking is off.';
3002
+ } else {
3003
+ response = 'Proper usage: border "on|off"'; // Display correct command usage
3004
+ }
3005
+ }
3006
+ break;
3007
+ */
3008
+ case 'av':
3009
+ if (process.platform == 'win32') {
3010
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3011
+ response = JSON.stringify(require('win-info').av(), null, 1);
3012
+ } else {
3013
+ response = 'Not supported on the platform';
3014
+ }
3015
+ break;
3016
+ case 'log':
3017
+ if (args['_'].length != 1) { response = 'Proper usage: log "sample text"'; } else { MeshServerLog(args['_'][0]); response = 'ok'; }
3018
+ break;
3019
+ case 'getclip':
3020
+ if (require('MeshAgent').isService) {
3021
+ require('clipboard').dispatchRead().then(function (str) { sendConsoleText(str, sessionid); });
3022
+ } else {
3023
+ require("clipboard").read().then(function (str) { sendConsoleText(str, sessionid); });
3024
+ }
3025
+ break;
3026
+ case 'setclip': {
3027
+ if (args['_'].length != 1) {
3028
+ response = 'Proper usage: setclip "sample text"';
3029
+ } else {
3030
+ if (require('MeshAgent').isService) {
3031
+ require('clipboard').dispatchWrite(args['_'][0]);
3032
+ response = 'Setting clipboard to: "' + args['_'][0] + '"';
3033
+ }
3034
+ else {
3035
+ require("clipboard")(args['_'][0]); response = 'Setting clipboard to: "' + args['_'][0] + '"';
3036
+ }
3037
+ }
3038
+ break;
3039
+ }
3040
+ case 'amtreset': {
3041
+ if (amt != null) { amt.reset(); response = 'Done.'; }
3042
+ break;
3043
+ }
3044
+ case 'amtlmsreset': {
3045
+ if (amt != null) { amt.lmsreset(); response = 'Done.'; }
3046
+ break;
3047
+ }
3048
+ case 'amtccm': {
3049
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
3050
+ if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
3051
+ else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
3052
+ }
3053
+ break;
3054
+ }
3055
+ case 'amtacm': {
3056
+ if (amt == null) { response = 'Intel AMT not supported.'; } else {
3057
+ amt.setPolicy({ type: 0 });
3058
+ amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
3059
+ }
3060
+ break;
3061
+ }
3062
+ case 'amtdeactivate': {
3063
+ if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
3064
+ break;
3065
+ }
3066
+ case 'amtpolicy': {
3067
+ if (amtPolicy == null) {
3068
+ response = 'No Intel(R) AMT policy.';
3069
+ } else {
3070
+ response = JSON.stringify(amtPolicy);
3071
+ }
3072
+ break;
3073
+ }
3074
+ case 'openurl': {
3075
+ if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
3076
+ else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
3077
+ break;
3078
+ }
3079
+ case 'users': {
3080
+ if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
3081
+ require('user-sessions').enumerateUsers().then(function (u) { for (var i in u) { sendConsoleText(u[i]); } });
3082
+ break;
3083
+ }
3084
+ case 'toast': {
3085
+ if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
3086
+ if (require('MeshAgent')._tsid == null) {
3087
+ require('toaster').Toast('MeshCentral', args['_'][0]).then(sendConsoleText, sendConsoleText);
3088
+ }
3089
+ else {
3090
+ require('toaster').Toast('MeshCentral', args['_'][0], require('MeshAgent')._tsid).then(sendConsoleText, sendConsoleText);
3091
+ }
3092
+ }
3093
+ break;
3094
+ }
3095
+ case 'setdebug': {
3096
+ if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
3097
+ else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
3098
+ break;
3099
+ }
3100
+ case 'ps': {
3101
+ processManager.getProcesses(function (plist) {
3102
+ var x = '';
3103
+ for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
3104
+ sendConsoleText(x, sessionid);
3105
+ });
3106
+ break;
3107
+ }
3108
+ case 'kill': {
3109
+ if ((args['_'].length < 1)) {
3110
+ response = 'Proper usage: kill [pid]'; // Display correct command usage
3111
+ } else {
3112
+ process.kill(parseInt(args['_'][0]));
3113
+ response = 'Killed process ' + args['_'][0] + '.';
3114
+ }
3115
+ break;
3116
+ }
3117
+ case 'smbios': {
3118
+ if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
3119
+ break;
3120
+ }
3121
+ case 'rawsmbios': {
3122
+ if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
3123
+ response = '';
3124
+ for (var i in SMBiosTablesRaw) {
3125
+ var header = false;
3126
+ for (var j in SMBiosTablesRaw[i]) {
3127
+ if (SMBiosTablesRaw[i][j].length > 0) {
3128
+ if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
3129
+ response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
3130
+ }
3131
+ }
3132
+ }
3133
+ }
3134
+ break;
3135
+ }
3136
+ case 'eval': { // Eval JavaScript
3137
+ if (args['_'].length < 1) {
3138
+ response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
3139
+ } else {
3140
+ response = JSON.stringify(mesh.eval(args['_'][0])); // This can only be run by trusted administrator.
3141
+ }
3142
+ break;
3143
+ }
3144
+ case 'uninstallagent': // Uninstall this agent
3145
+ var agentName = process.platform == 'win32' ? 'Mesh Agent' : 'meshagent';
3146
+ if (!require('service-manager').manager.getService(agentName).isMe()) {
3147
+ response = 'Uininstall failed, this instance is not the service instance';
3148
+ } else {
3149
+ try { diagnosticAgent_uninstall(); } catch (e) { }
3150
+ var js = "require('service-manager').manager.getService('" + agentName + "').stop(); require('service-manager').manager.uninstallService('" + agentName + "'); process.exit();";
3151
+ this.child = require('child_process').execFile(process.execPath, [process.platform == 'win32' ? (process.execPath.split('\\').pop()) : (process.execPath.split('/').pop()), '-b64exec', Buffer.from(js).toString('base64')], { type: 4, detached: true });
3152
+ }
3153
+ break;
3154
+ case 'notify': { // Send a notification message to the mesh
3155
+ if (args['_'].length != 1) {
3156
+ response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
3157
+ } else {
3158
+ var notification = { action: 'msg', type: 'notify', value: args['_'][0], tag: 'console' };
3159
+ if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
3160
+ mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
3161
+ response = "ok";
3162
+ }
3163
+ break;
3164
+ }
3165
+ case 'cpuinfo': { // Return system information
3166
+ // CPU & memory utilization
3167
+ pr = require('sysinfo').cpuUtilization();
3168
+ pr.sessionid = sessionid;
3169
+ pr.then(function (data) {
3170
+ sendConsoleText(JSON.stringify({ cpu: data, memory: require('sysinfo').memUtilization() }, null, 1), this.sessionid);
3171
+ }, function (e) {
3172
+ sendConsoleText(e);
3173
+ });
3174
+ break;
3175
+ }
3176
+ case 'sysinfo': { // Return system information
3177
+ getSystemInformation(function (results, err) {
3178
+ if (results == null) { sendConsoleText(err, this.sessionid); } else {
3179
+ sendConsoleText(JSON.stringify(results, null, 1), this.sessionid);
3180
+ }
3181
+ });
3182
+ break;
3183
+ }
3184
+ case 'info': { // Return information about the agent and agent core module
3185
+ response = 'Current Core: ' + meshCoreObj.value + '\r\nAgent Time: ' + Date() + '.\r\nUser Rights: 0x' + rights.toString(16) + '.\r\nPlatform: ' + process.platform + '.\r\nCapabilities: ' + meshCoreObj.caps + '.\r\nServer URL: ' + mesh.ServerUrl + '.';
3186
+ if (amt != null) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amt.lmsstate] + '.'; }
3187
+ if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
3188
+ response += '\r\nModules: ' + addedModules.join(', ') + '.';
3189
+ response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
3190
+ response += '\r\lastMeInfo: ' + lastMeInfo + '.';
3191
+ var oldNodeId = db.Get('OldNodeId');
3192
+ if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
3193
+ if (process.platform == 'linux' || process.platform == 'freebsd') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
3194
+ break;
3195
+ }
3196
+ case 'osinfo': { // Return the operating system information
3197
+ var i = 1;
3198
+ if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
3199
+ for (var j = 0; j < i; j++) {
3200
+ var pr = require('os').name();
3201
+ pr.sessionid = sessionid;
3202
+ pr.then(function (v) {
3203
+ sendConsoleText("OS: " + v + (process.platform == 'win32' ? (require('win-virtual-terminal').supported ? ' [ConPTY: YES]' : ' [ConPTY: NO]') : ''), this.sessionid);
3204
+ });
3205
+ }
3206
+ break;
3207
+ }
3208
+ case 'sendcaps': { // Send capability flags to the server
3209
+ if (args['_'].length == 0) {
3210
+ response = 'Proper usage: sendcaps (number)'; // Display correct command usage
3211
+ } else {
3212
+ meshCoreObj.caps = parseInt(args['_'][0]);
3213
+ mesh.SendCommand(meshCoreObj);
3214
+ response = JSON.stringify(meshCoreObj);
3215
+ }
3216
+ break;
3217
+ }
3218
+ case 'sendosdesc': { // Send OS description
3219
+ if (args['_'].length > 0) {
3220
+ meshCoreObj.osdesc = args['_'][0];
3221
+ mesh.SendCommand(meshCoreObj);
3222
+ response = JSON.stringify(meshCoreObj);
3223
+ } else {
3224
+ response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
3225
+ }
3226
+ break;
3227
+ }
3228
+ case 'args': { // Displays parsed command arguments
3229
+ response = 'args ' + objToString(args, 0, ' ', true);
3230
+ break;
3231
+ }
3232
+ case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
3233
+ var r = [];
3234
+ for (var i in args['_']) { r.push(args['_'][i]); }
3235
+ console.log(r.join(' '));
3236
+ response = 'Message printed on agent console.';
3237
+ break;
3238
+ }
3239
+ case 'type': { // Returns the content of a file
3240
+ if (args['_'].length == 0) {
3241
+ response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
3242
+ } else {
3243
+ var max = 4096;
3244
+ if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
3245
+ if (max > 4096) max = 4096;
3246
+ var buf = Buffer.alloc(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
3247
+ response = buf.toString();
3248
+ var i = response.indexOf('\n');
3249
+ if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
3250
+ if (r == max) response += '...';
3251
+ fs.closeSync(fd);
3252
+ }
3253
+ break;
3254
+ }
3255
+ case 'dbkeys': { // Return all data store keys
3256
+ response = JSON.stringify(db.Keys);
3257
+ break;
3258
+ }
3259
+ case 'dbget': { // Return the data store value for a given key
3260
+ if (db == null) { response = 'Database not accessible.'; break; }
3261
+ if (args['_'].length != 1) {
3262
+ response = 'Proper usage: dbget (key)'; // Display the value for a given database key
3263
+ } else {
3264
+ response = db.Get(args['_'][0]);
3265
+ }
3266
+ break;
3267
+ }
3268
+ case 'dbset': { // Set a data store key and value pair
3269
+ if (db == null) { response = 'Database not accessible.'; break; }
3270
+ if (args['_'].length != 2) {
3271
+ response = 'Proper usage: dbset (key) (value)'; // Set a database key
3272
+ } else {
3273
+ var r = db.Put(args['_'][0], args['_'][1]);
3274
+ response = 'Key set: ' + r;
3275
+ }
3276
+ break;
3277
+ }
3278
+ case 'dbcompact': { // Compact the data store
3279
+ if (db == null) { response = 'Database not accessible.'; break; }
3280
+ var r = db.Compact();
3281
+ response = 'Database compacted: ' + r;
3282
+ break;
3283
+ }
3284
+ case 'httpget': {
3285
+ if (consoleHttpRequest != null) {
3286
+ response = 'HTTP operation already in progress.';
3287
+ } else {
3288
+ if (args['_'].length != 1) {
3289
+ response = 'Proper usage: httpget (url)';
3290
+ } else {
3291
+ var options = http.parseUri(args['_'][0]);
3292
+ options.method = 'GET';
3293
+ if (options == null) {
3294
+ response = 'Invalid url.';
3295
+ } else {
3296
+ try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
3297
+ consoleHttpRequest.sessionid = sessionid;
3298
+ if (consoleHttpRequest != null) {
3299
+ consoleHttpRequest.end();
3300
+ response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
3301
+ }
3302
+ }
3303
+ }
3304
+ }
3305
+ break;
3306
+ }
3307
+ case 'wslist': { // List all web sockets
3308
+ response = '';
3309
+ for (var i in consoleWebSockets) {
3310
+ var httprequest = consoleWebSockets[i];
3311
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3312
+ }
3313
+ if (response == '') { response = 'no websocket sessions.'; }
3314
+ break;
3315
+ }
3316
+ case 'wsconnect': { // Setup a web socket
3317
+ if (args['_'].length == 0) {
3318
+ response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
3319
+ } else {
3320
+ var httprequest = null;
3321
+ try {
3322
+ var options = http.parseUri(args['_'][0].split('$').join('%24').split('@').join('%40')); // Escape the $ and @ characters in the URL
3323
+ options.rejectUnauthorized = 0;
3324
+ httprequest = http.request(options);
3325
+ } catch (e) { response = 'Invalid HTTP websocket request'; }
3326
+ if (httprequest != null) {
3327
+ httprequest.upgrade = onWebSocketUpgrade;
3328
+ httprequest.on('error', function (e) { sendConsoleText("ERROR: Unable to connect to: " + this.url + ", " + JSON.stringify(e)); });
3329
+
3330
+ var index = 1;
3331
+ while (consoleWebSockets[index]) { index++; }
3332
+ httprequest.sessionid = sessionid;
3333
+ httprequest.index = index;
3334
+ httprequest.url = args['_'][0];
3335
+ consoleWebSockets[index] = httprequest;
3336
+ response = 'New websocket session #' + index;
3337
+ }
3338
+ }
3339
+ break;
3340
+ }
3341
+ case 'wssend': { // Send data on a web socket
3342
+ if (args['_'].length == 0) {
3343
+ response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
3344
+ for (var i in consoleWebSockets) {
3345
+ var httprequest = consoleWebSockets[i];
3346
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
3347
+ }
3348
+ } else {
3349
+ var i = parseInt(args['_'][0]);
3350
+ var httprequest = consoleWebSockets[i];
3351
+ if (httprequest != undefined) {
3352
+ httprequest.s.write(args['_'][1]);
3353
+ response = 'ok';
3354
+ } else {
3355
+ response = 'Invalid web socket number';
3356
+ }
3357
+ }
3358
+ break;
3359
+ }
3360
+ case 'wsclose': { // Close a websocket
3361
+ if (args['_'].length == 0) {
3362
+ response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
3363
+ } else {
3364
+ var i = parseInt(args['_'][0]);
3365
+ var httprequest = consoleWebSockets[i];
3366
+ if (httprequest != undefined) {
3367
+ if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
3368
+ response = 'ok';
3369
+ } else {
3370
+ response = 'Invalid web socket number';
3371
+ }
3372
+ }
3373
+ break;
3374
+ }
3375
+ case 'tunnels': { // Show the list of current tunnels
3376
+ response = '';
3377
+ for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
3378
+ if (response == '') { response = 'No websocket sessions.'; }
3379
+ break;
3380
+ }
3381
+ case 'ls': { // Show list of files and folders
3382
+ response = '';
3383
+ var xpath = '*';
3384
+ if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
3385
+ response = 'List of ' + xpath + '\r\n';
3386
+ var results = fs.readdirSync(xpath);
3387
+ for (var i = 0; i < results.length; ++i) {
3388
+ var stat = null, p = obj.path.join(args['_'][0], results[i]);
3389
+ try { stat = fs.statSync(p); } catch (e) { }
3390
+ if ((stat == null) || (stat == undefined)) {
3391
+ response += (results[i] + "\r\n");
3392
+ } else {
3393
+ response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
3394
+ }
3395
+ }
3396
+ break;
3397
+ }
3398
+ case 'lsx': { // Show list of files and folders
3399
+ response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
3400
+ break;
3401
+ }
3402
+ case 'lock': { // Lock the current user out of the desktop
3403
+ if (process.platform == 'win32') { var child = require('child_process'); child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 }); response = 'Ok'; }
3404
+ else { response = 'Not supported on the platform'; }
3405
+ break;
3406
+ }
3407
+ case 'amt': { // Show Intel AMT status
3408
+ if (amt != null) {
3409
+ amt.getAmtInfo(function (state) {
3410
+ var resp = 'Intel AMT not detected.';
3411
+ if (state != null) { resp = objToString(state, 0, ' ', true); }
3412
+ sendConsoleText(resp, sessionid);
3413
+ });
3414
+ } else {
3415
+ response = 'Intel AMT not detected.';
3416
+ }
3417
+ break;
3418
+ }
3419
+ case 'netinfo': { // Show network interface information
3420
+ var interfaces = require('os').networkInterfaces();
3421
+ response = objToString(interfaces, 0, ' ', true);
3422
+ break;
3423
+ }
3424
+ case 'wakeonlan': { // Send wake-on-lan
3425
+ if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
3426
+ response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
3427
+ } else {
3428
+ var count = sendWakeOnLan(args['_'][0]);
3429
+ response = 'Sent wake-on-lan on ' + count + ' interface(s).';
3430
+ }
3431
+ break;
3432
+ }
3433
+ case 'sendall': { // Send a message to all consoles on this mesh
3434
+ sendConsoleText(args['_'].join(' '));
3435
+ break;
3436
+ }
3437
+ case 'power': { // Execute a power action on this computer
3438
+ if (mesh.ExecPowerState == undefined) {
3439
+ response = 'Power command not supported on this agent.';
3440
+ } else {
3441
+ if ((args['_'].length == 0) || isNaN(Number(args['_'][0]))) {
3442
+ response = 'Proper usage: power (actionNumber), where actionNumber is:\r\n LOGOFF = 1\r\n SHUTDOWN = 2\r\n REBOOT = 3\r\n SLEEP = 4\r\n HIBERNATE = 5\r\n DISPLAYON = 6\r\n KEEPAWAKE = 7\r\n BEEP = 8\r\n CTRLALTDEL = 9\r\n VIBRATE = 13\r\n FLASH = 14'; // Display correct command usage
3443
+ } else {
3444
+ var r = mesh.ExecPowerState(Number(args['_'][0]), Number(args['_'][1]));
3445
+ response = 'Power action executed with return code: ' + r + '.';
3446
+ }
3447
+ }
3448
+ break;
3449
+ }
3450
+ case 'location': {
3451
+ getIpLocationData(function (location) {
3452
+ sendConsoleText(objToString({ action: 'iplocation', type: 'publicip', value: location }, 0, ' '));
3453
+ });
3454
+ break;
3455
+ }
3456
+ case 'parseuri': {
3457
+ response = JSON.stringify(http.parseUri(args['_'][0]));
3458
+ break;
3459
+ }
3460
+ case 'scanwifi': {
3461
+ if (wifiScanner != null) {
3462
+ var wifiPresent = wifiScanner.hasWireless;
3463
+ if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
3464
+ } else { response = "Wifi module not present."; }
3465
+ break;
3466
+ }
3467
+ case 'scanamt': {
3468
+ if (amtscanner != null) {
3469
+ if (args['_'].length != 1) {
3470
+ response = 'Usage examples:\r\n scanamt 1.2.3.4\r\n scanamt 1.2.3.0-1.2.3.255\r\n scanamt 1.2.3.0/24\r\n'; // Display correct command usage
3471
+ } else {
3472
+ response = 'Scanning: ' + args['_'][0] + '...';
3473
+ amtscanner.scan(args['_'][0], 2000, function (data) {
3474
+ if (data.length > 0) {
3475
+ var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
3476
+ for (var i in data) {
3477
+ var x = data[i];
3478
+ if (r != '') { r += '\r\n'; }
3479
+ r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
3480
+ if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
3481
+ if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
3482
+ r += '.';
3483
+ }
3484
+ } else {
3485
+ r = 'No Intel AMT found.';
3486
+ }
3487
+ sendConsoleText(r);
3488
+ });
3489
+ }
3490
+ } else { response = "Intel AMT scanner module not present."; }
3491
+ break;
3492
+ }
3493
+ case 'modules': {
3494
+ response = JSON.stringify(addedModules);
3495
+ break;
3496
+ }
3497
+ case 'listservices': {
3498
+ var services = require('service-manager').manager.enumerateService();
3499
+ response = JSON.stringify(services, null, 1);
3500
+ break;
3501
+ }
3502
+ case 'getscript': {
3503
+ if (args['_'].length != 1) {
3504
+ response = "Proper usage: getscript [scriptNumber].";
3505
+ } else {
3506
+ mesh.SendCommand({ action: 'getScript', type: args['_'][0] });
3507
+ }
3508
+ break;
3509
+ }
3510
+ case 'diagnostic':
3511
+ {
3512
+ if (!mesh.DAIPC.listening) {
3513
+ response = 'Unable to bind to Diagnostic IPC, most likely because the path (' + process.cwd() + ') is not on a local file system';
3514
+ break;
3515
+ }
3516
+ var diag = diagnosticAgent_installCheck();
3517
+ if (diag) {
3518
+ if (args['_'].length == 1 && args['_'][0] == 'uninstall') {
3519
+ diagnosticAgent_uninstall();
3520
+ response = 'Diagnostic Agent uninstalled';
3521
+ }
3522
+ else {
3523
+ response = 'Diagnostic Agent installed at: ' + diag.appLocation();
3524
+ }
3525
+ }
3526
+ else {
3527
+ if (args['_'].length == 1 && args['_'][0] == 'install') {
3528
+ diag = diagnosticAgent_installCheck(true);
3529
+ if (diag) {
3530
+ response = 'Diagnostic agent was installed at: ' + diag.appLocation();
3531
+ }
3532
+ else {
3533
+ response = 'Diagnostic agent installation failed';
3534
+ }
3535
+ }
3536
+ else {
3537
+ response = 'Diagnostic Agent Not installed. To install: diagnostic install';
3538
+ }
3539
+ }
3540
+ if (diag) { diag.close(); diag = null; }
3541
+ break;
3542
+ }
3543
+ case 'amtconfig': {
3544
+ if (apftunnel != null) { response = "Intel AMT server tunnel already active"; break; }
3545
+ if (amt == null) { response = "No Intel AMT support delected"; break; }
3546
+ getMeiState(15, function (state) {
3547
+ var rx = '';
3548
+ if ((state == null) || (state.ProvisioningState == null)) { rx = "Intel AMT not ready for configuration."; } else {
3549
+ var apfarg = {
3550
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3551
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3552
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3553
+ mpskeepalive: 60000,
3554
+ clientname: state.OsHostname,
3555
+ clientaddress: '127.0.0.1',
3556
+ clientuuid: state.UUID,
3557
+ conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3558
+ meiState: state // MEI state will be passed to MPS server
3559
+ };
3560
+ if ((state.UUID == null) || (state.UUID.length != 36)) {
3561
+ rx = "Unable to get Intel AMT UUID";
3562
+ } else {
3563
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
3564
+ apftunnel.onJsonControl = function (data) {
3565
+ if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message
3566
+ if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3567
+ if (data.action == 'deactivate') { // Request CCM deactivation
3568
+ var amtMeiModule, amtMei;
3569
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
3570
+ amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
3571
+ amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
3572
+ }
3573
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
3574
+ }
3575
+ apftunnel.onChannelClosed = function () { apftunnel = null; }
3576
+ try {
3577
+ apftunnel.connect();
3578
+ rx = "Started Intel AMT configuration";
3579
+ } catch (ex) {
3580
+ rx = JSON.stringify(ex);
3581
+ }
3582
+ }
3583
+ }
3584
+ if (rx != '') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: rx }); }
3585
+ });
3586
+ break;
3587
+ }
3588
+ case 'apf': {
3589
+ if (meshCoreObj.intelamt !== null) {
3590
+ if (args['_'].length == 1) {
3591
+ var connType = -1, connTypeStr = args['_'][0].toLowerCase();
3592
+ if (connTypeStr == 'lms') { connType = 2; }
3593
+ if (connTypeStr == 'relay') { connType = 1; }
3594
+ if (connTypeStr == 'cira') { connType = 0; }
3595
+ if (connTypeStr == 'off') { connType = -2; }
3596
+ if (connType >= 0) { // Connect
3597
+ var apfarg = {
3598
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
3599
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3600
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
3601
+ mpskeepalive: 60000,
3602
+ clientname: require('os').hostname(),
3603
+ clientaddress: '127.0.0.1',
3604
+ clientuuid: meshCoreObj.intelamt.uuid,
3605
+ conntype: connType // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
3606
+ };
3607
+ if ((apfarg.clientuuid == null) || (apfarg.clientuuid.length != 36)) {
3608
+ response = "Unable to get Intel AMT UUID: " + apfarg.clientuuid;
3609
+ } else {
3610
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
3611
+ apftunnel.onJsonControl = function (data) {
3612
+ if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); }
3613
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; }
3614
+ }
3615
+ apftunnel.onChannelClosed = function () { apftunnel = null; }
3616
+ try {
3617
+ apftunnel.connect();
3618
+ response = "Started APF tunnel";
3619
+ } catch (e) {
3620
+ response = JSON.stringify(e);
3621
+ }
3622
+ }
3623
+ } else if (connType == -2) { // Disconnect
3624
+ try {
3625
+ apftunnel.disconnect();
3626
+ response = "Stopped APF tunnel";
3627
+ } catch (e) {
3628
+ response = JSON.stringify(e);
3629
+ }
3630
+ apftunnel = null;
3631
+ } else {
3632
+ response = "Invalid command.\r\nUse: apf lms|relay|cira|off";
3633
+ }
3634
+ } else {
3635
+ response = "APF tunnel is " + (apftunnel == null ? "off" : "on") + "\r\nUse: apf lms|relay|cira|off";
3636
+ }
3637
+ } else {
3638
+ response = "APF tunnel requires Intel AMT";
3639
+ }
3640
+ break;
3641
+ }
3642
+ case 'plugin': {
3643
+ if (typeof args['_'][0] == 'string') {
3644
+ try {
3645
+ // Pass off the action to the plugin
3646
+ // for plugin creators, you'll want to have a plugindir/modules_meshcore/plugin.js
3647
+ // to control the output / actions here.
3648
+ response = require(args['_'][0]).consoleaction(args, rights, sessionid, mesh);
3649
+ } catch (e) {
3650
+ response = "There was an error in the plugin (" + e + ")";
3651
+ }
3652
+ } else {
3653
+ response = "Proper usage: plugin [pluginName] [args].";
3654
+ }
3655
+ break;
3656
+ }
3657
+ default: { // This is an unknown command, return an error message
3658
+ response = "Unknown command \"" + cmd + "\", type \"help\" for list of avaialble commands.";
3659
+ break;
3660
+ }
3661
+ }
3662
+ } catch (e) { response = "Command returned an exception error: " + e; console.log(e); }
3663
+ if (response != null) { sendConsoleText(response, sessionid); }
3664
+ }
3665
+
3666
+ // Send a mesh agent console command
3667
+ function sendConsoleText(text, sessionid) {
3668
+ if (typeof text == 'object') { text = JSON.stringify(text); }
3669
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: text, sessionid: sessionid });
3670
+ }
3671
+
3672
+ // Called before the process exits
3673
+ //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
3674
+
3675
+ // Called when the server connection state changes
3676
+ function handleServerConnection(state) {
3677
+ meshServerConnectionState = state;
3678
+ if (meshServerConnectionState == 0) {
3679
+ // Server disconnected
3680
+ if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
3681
+ lastSelfInfo = null;
3682
+ } else {
3683
+ // Server connected, send mesh core information
3684
+ var oldNodeId = db.Get('OldNodeId');
3685
+ if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
3686
+
3687
+ // Update the server with basic info, logged in users and more.
3688
+ mesh.SendCommand(meshCoreObj);
3689
+
3690
+ // Send SMBios tables if present
3691
+ if (SMBiosTablesRaw != null) { mesh.SendCommand({ action: 'smbios', value: SMBiosTablesRaw }); }
3692
+
3693
+ // Update the server on more advanced stuff, like Intel ME and Network Settings
3694
+ meInfoStr = null;
3695
+ sendPeriodicServerUpdate();
3696
+ if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
3697
+
3698
+ // Send any state messages
3699
+ if (Object.keys(tunnelUserCount.msg).length > 0) {
3700
+ try { mesh.SendCommand({ action: 'sessions', type: 'msg', value: tunnelUserCount.msg }); } catch (e) { }
3701
+ broadcastSessionsToRegisteredApps();
3702
+ }
3703
+
3704
+ // Send update of registered applications to the server
3705
+ updateRegisteredAppsToServer();
3706
+ }
3707
+
3708
+ // Send server state update to registered applications
3709
+ broadcastToRegisteredApps({ cmd: 'serverstate', value: meshServerConnectionState, url: require('MeshAgent').ConnectedServer });
3710
+ }
3711
+
3712
+ // Update the server with the latest network interface information
3713
+ var sendNetworkUpdateNagleTimer = null;
3714
+ function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
3715
+ function sendNetworkUpdate(force) {
3716
+ sendNetworkUpdateNagleTimer = null;
3717
+
3718
+ // Update the network interfaces information data
3719
+ var netInfo = { netif2: require('os').networkInterfaces() };
3720
+ if (netInfo.netif2) {
3721
+ netInfo.action = 'netinfo';
3722
+ var netInfoStr = JSON.stringify(netInfo);
3723
+ if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
3724
+ }
3725
+ }
3726
+
3727
+ // Called periodically to check if we need to send updates to the server
3728
+ function sendPeriodicServerUpdate(flags) {
3729
+ if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
3730
+ if (!flags) { flags = 0xFFFFFFFF; }
3731
+
3732
+ if ((flags & 1) && (amt != null)) {
3733
+ // If we have a connected MEI, get Intel ME information
3734
+ amt.getAmtInfo(function (meinfo) {
3735
+ try {
3736
+ if (meinfo == null) return;
3737
+ var intelamt = {};
3738
+ if (amt != null)
3739
+ {
3740
+ switch(amt.lmsstate)
3741
+ {
3742
+ case 0: intelamt.microlms = 'DISABLED'; break;
3743
+ case 1: intelamt.microlms = 'CONNECTING'; break;
3744
+ case 2: intelamt.microlms = 'CONNECTED'; break;
3745
+ default: intelamt.microlms = 'unknown'; break;
3746
+ }
3747
+ }
3748
+ var p = false;
3749
+ if ((meinfo.Versions != null) && (meinfo.Versions.AMT != null)) { intelamt.ver = meinfo.Versions.AMT; p = true; if (meinfo.Versions.Sku != null) { intelamt.sku = parseInt(meinfo.Versions.Sku); } }
3750
+ if (meinfo.ProvisioningState != null) { intelamt.state = meinfo.ProvisioningState; p = true; }
3751
+ if (meinfo.Flags != null) { intelamt.flags = meinfo.Flags; p = true; }
3752
+ if (meinfo.OsHostname != null) { intelamt.host = meinfo.OsHostname; p = true; }
3753
+ if (meinfo.UUID != null) { intelamt.uuid = meinfo.UUID; p = true; }
3754
+ if ((meinfo.ProvisioningState == 0) && (meinfo.net0 != null) && (meinfo.net0.enabled == 1)) { // If not activated, look to see if we have wired net working.
3755
+ // Not activated and we have wired ethernet, look for the trusted DNS
3756
+ var dns = meinfo.DNS;
3757
+ if (dns == null) {
3758
+ // Trusted DNS not set, let's look for the OS network DNS suffix
3759
+ var interfaces = require('os').networkInterfaces();
3760
+ for (var i in interfaces) {
3761
+ for (var j in interfaces[i]) {
3762
+ if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { dns = interfaces[i][j].fqdn; }
3763
+ }
3764
+ }
3765
+ }
3766
+ if (intelamt.dns != dns) { intelamt.dns = dns; p = true; }
3767
+ } else { if (intelamt.dns != null) { delete intelamt.dns; p = true; } }
3768
+ if (p == true) {
3769
+ var meInfoStr = JSON.stringify(intelamt);
3770
+ if (meInfoStr != lastMeInfo) {
3771
+ meshCoreObj.intelamt = intelamt;
3772
+ mesh.SendCommand(meshCoreObj);
3773
+ lastMeInfo = meInfoStr;
3774
+ }
3775
+ }
3776
+ } catch (e) { }
3777
+ });
3778
+ }
3779
+
3780
+ if (flags & 2) {
3781
+ // Update network information
3782
+ sendNetworkUpdateNagle(false);
3783
+ }
3784
+
3785
+ if ((flags & 4) && (process.platform == 'win32')) {
3786
+ // Update anti-virus information
3787
+ // Windows Command: "wmic /Namespace:\\root\SecurityCenter2 Path AntiVirusProduct get /FORMAT:CSV"
3788
+ var av, pr;
3789
+ try { av = require('win-info').av(); } catch (e) { av = null; } // Antivirus
3790
+ //if (process.platform == 'win32') { try { pr = require('win-info').pendingReboot(); } catch (e) { pr = null; } } // Pending reboot
3791
+ if ((meshCoreObj.av == null) || (JSON.stringify(meshCoreObj.av) != JSON.stringify(av))) { meshCoreObj.av = av; mesh.SendCommand(meshCoreObj); }
3792
+ }
3793
+ }
3794
+
3795
+ // Starting function
3796
+ obj.start = function () {
3797
+ // Setup the mesh agent event handlers
3798
+ mesh.AddCommandHandler(handleServerCommand);
3799
+ mesh.AddConnectHandler(handleServerConnection);
3800
+
3801
+ // Parse input arguments
3802
+ //var args = parseArgs(process.argv);
3803
+ //console.log(args);
3804
+
3805
+ //resetMicroLms();
3806
+
3807
+ // Setup logged in user monitoring (THIS IS BROKEN IN WIN7)
3808
+ try {
3809
+ var userSession = require('user-sessions');
3810
+ userSession.on('changed', function onUserSessionChanged() {
3811
+ userSession.enumerateUsers().then(function (users) {
3812
+ var u = [], a = users.Active;
3813
+ for (var i = 0; i < a.length; i++) {
3814
+ var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
3815
+ if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
3816
+ }
3817
+ meshCoreObj.users = u;
3818
+ mesh.SendCommand(meshCoreObj);
3819
+ });
3820
+ });
3821
+ userSession.emit('changed');
3822
+ //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
3823
+ //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
3824
+ } catch (e) { }
3825
+ }
3826
+
3827
+ obj.stop = function () {
3828
+ mesh.AddCommandHandler(null);
3829
+ mesh.AddConnectHandler(null);
3830
+ }
3831
+
3832
+ function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
3833
+ function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
3834
+ function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
3835
+
3836
+ function onWebSocketUpgrade(response, s, head) {
3837
+ sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
3838
+ this.s = s;
3839
+ s.httprequest = this;
3840
+ s.end = onWebSocketClosed;
3841
+ s.data = onWebSocketData;
3842
+ }
3843
+
3844
+ // Get Intel MEI State in a flexible way
3845
+ // Flags: 1 = Versions, 2 = OsAdmin, 4 = Hashes, 8 = Network
3846
+ function getMeiState(flags, func) {
3847
+ var amtMeiModule, amtMei;
3848
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { func(null); return; }
3849
+ amtMei.on('error', function (e) { func(null); return; });
3850
+ try {
3851
+ var amtMeiTmpState = { OsHostname: require('os').hostname(), Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
3852
+ amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
3853
+ if ((flags & 1) != 0) { amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } }); }
3854
+ amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
3855
+ amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; } }); // 0: "Not Activated (Pre)", 1: "Not Activated (In)", 2: "Activated"
3856
+ amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
3857
+ amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } }); // Flag 2 = CCM, 4 = ACM
3858
+ //amtMei.getMACAddresses(function (result) { if (result) { amtMeiTmpState.mac = result; } });
3859
+ if ((flags & 8) != 0) {
3860
+ amtMei.getLanInterfaceSettings(0, function (result) {
3861
+ if (result) {
3862
+ amtMeiTmpState.net0 = result;
3863
+ var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
3864
+ for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3865
+ }
3866
+ });
3867
+ amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
3868
+ }
3869
+ amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { amtMeiTmpState.UUID = result.uuid; } });
3870
+ if ((flags & 2) != 0) { amtMei.getLocalSystemAccount(function (x) { if ((x != null) && x.user && x.pass) { amtMeiTmpState.OsAdmin = { user: x.user, pass: x.pass }; } }); }
3871
+ amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.DnsSuffix = result; } if ((flags & 4) == 0) { if (func != null) { func(amtMeiTmpState); } } });
3872
+ if ((flags & 4) != 0) {
3873
+ amtMei.getHashHandles(function (handles) {
3874
+ if ((handles != null) && (handles.length > 0)) { amtMeiTmpState.Hashes = []; } else { func(amtMeiTmpState); }
3875
+ var exitOnCount = handles.length;
3876
+ for (var i = 0; i < handles.length; ++i) { this.getCertHashEntry(handles[i], function (hashresult) { amtMeiTmpState.Hashes.push(hashresult); if (--exitOnCount == 0) { if (func != null) { func(amtMeiTmpState); } } }); }
3877
+ });
3878
+ }
3879
+ } catch (e) { if (func != null) { func(null); } return; }
3880
+ }
3881
+
3882
+ return obj;
3883
+}
3884
+
3885
+//
3886
+// Module startup
3887
+//
3888
+
3889
+try {
3890
+ var xexports = null, mainMeshCore = null;
3891
+ try { xexports = module.exports; } catch (e) { }
3892
+
3893
+ if (xexports != null) {
3894
+ // If we are running within NodeJS, export the core
3895
+ module.exports.createMeshCore = createMeshCore;
3896
+ } else {
3897
+ // If we are not running in NodeJS, launch the core
3898
+ mainMeshCore = createMeshCore();
3899
+ mainMeshCore.start(null);
3900
+ }
3901
+} catch (e) {
3902
+ require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: "uncaughtException2: " + ex });
3903
+}
agents/meshcore.js
+36
-44
@@ -400,7 +400,6 @@ function createMeshCore(agent) {
400
var networkMonitor = null;
401
var amtscanner = null;
402
var nextTunnelIndex = 1;
403
- var amtPolicy = null;
403
var apftunnel = null;
404
var tunnelUserCount = { terminal: {}, files: {}, tcp: {}, udp: {}, msg: {} }; // List of userid->count sessions for terminal, files and TCP/UDP routing
405
@@ -538,7 +537,6 @@ function createMeshCore(agent) {
537
mesh.SendCommand(meshCoreObj);
538
});
539
amt.onStateChange = function (state) { if (state == 2) { sendPeriodicServerUpdate(1); } }
541
- if (amtPolicy != null) { amt.setPolicy(amtPolicy); }
540
amt.start();
541
}
542
}
@@ -1075,11 +1073,39 @@ function createMeshCore(agent) {
1073
if (data.url) { mesh.SendCommand({ action: 'openUrl', url: data.url, sessionid: data.sessionid, success: (openUserDesktopUrl(data.url) != null) }); }
1074
break;
1075
}
1078
- case 'amtPolicy': {
1079
- // Store the latest Intel AMT policy
1080
- amtPolicy = data.amtPolicy;
1081
- if (data.amtPolicy != null) { db.Put('amtPolicy', JSON.stringify(data.amtPolicy)); } else { db.Put('amtPolicy', null); }
1082
- if (amt != null) { amt.setPolicy(amtPolicy, true); }
1076
+ case 'amtconfig': {
1077
+ // Perform Intel AMT activation and/or configuration
1078
+ if ((apftunnel != null) || (amt == null)) break;
1079
+ getMeiState(15, function (state) {
1080
+ if ((apftunnel != null) || (amt == null)) return;
1081
+ if ((state == null) || (state.ProvisioningState == null)) return;
1082
+ if ((state.UUID == null) || (state.UUID.length != 36)) return; // Bad UUID
1083
+ var apfarg = {
1084
+ mpsurl: mesh.ServerUrl.replace('agent.ashx', 'apf.ashx'),
1085
+ mpsuser: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16), // TODO: User a server provided encrypted cookie for CIRA-LMS login
1086
+ mpspass: Buffer.from(mesh.ServerInfo.MeshID, 'hex').toString('base64').substring(0, 16),
1087
+ mpskeepalive: 60000,
1088
+ clientname: state.OsHostname,
1089
+ clientaddress: '127.0.0.1',
1090
+ clientuuid: state.UUID,
1091
+ conntype: 2, // 0 = CIRA, 1 = Relay, 2 = LMS. The correct value is 2 since we are performing an LMS relay, other values for testing.
1092
+ meiState: state // MEI state will be passed to MPS server
1093
+ };
1094
+ apftunnel = require('apfclient')({ debug: false }, apfarg);
1095
+ apftunnel.onJsonControl = function (data) {
1096
+ //if (data.action == 'console') { require('MeshAgent').SendCommand({ action: 'msg', type: 'console', value: data.msg }); } // Display a console message (DEBUG)
1097
+ if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
1098
+ if (data.action == 'deactivate') { // Request CCM deactivation
1099
+ var amtMeiModule, amtMei;
1100
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); return; }
1101
+ amtMei.on('error', function (e) { if (apftunnel) apftunnel.sendMeiDeactivationState(1); });
1102
+ amtMei.unprovision(1, function (status) { if (apftunnel) apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
1103
+ }
1104
+ if (data.action == 'close') { try { apftunnel.disconnect(); } catch (e) { } apftunnel = null; } // Close the CIRA-LMS connection
1105
+ }
1106
+ apftunnel.onChannelClosed = function () { apftunnel = null; }
1107
+ try { apftunnel.connect(); } catch (ex) { }
1108
+ });
1109
break;
1110
}
1111
case 'getScript': {
@@ -2563,7 +2589,7 @@ function createMeshCore(agent) {
2589
var response = null;
2590
switch (cmd) {
2591
case 'help': { // Displays available commands
2566
- var fin = '', f = '', availcommands = 'amtconfig,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
2592
+ var fin = '', f = '', availcommands = 'amtconfig,coredump,service,fdsnapshot,fdcount,startupoptions,alert,agentsize,versions,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper,agentmsg';
2593
if (process.platform == 'win32') { availcommands += ',safemode,wpfhwacceleration,uac'; }
2594
if (process.platform != 'freebsd') { availcommands += ',vm';}
2595
if (require('MeshAgent').maxKvmTileSize != null) { availcommands += ',kvmmode'; }
@@ -3037,40 +3063,6 @@ function createMeshCore(agent) {
3063
}
3064
break;
3065
}
3040
- case 'amtreset': {
3041
- if (amt != null) { amt.reset(); response = 'Done.'; }
3042
- break;
3043
- }
3044
- case 'amtlmsreset': {
3045
- if (amt != null) { amt.lmsreset(); response = 'Done.'; }
3046
- break;
3047
- }
3048
- case 'amtccm': {
3049
- if (amt == null) { response = 'Intel AMT not supported.'; } else {
3050
- if (args['_'].length != 1) { response = 'Proper usage: amtccm (adminPassword)'; } // Display usage
3051
- else { amt.setPolicy({ type: 0 }); amt.activeToCCM(args['_'][0]); }
3052
- }
3053
- break;
3054
- }
3055
- case 'amtacm': {
3056
- if (amt == null) { response = 'Intel AMT not supported.'; } else {
3057
- amt.setPolicy({ type: 0 });
3058
- amt.getAmtInfo(function (meinfo) { amt.activeToACM(meinfo); });
3059
- }
3060
- break;
3061
- }
3062
- case 'amtdeactivate': {
3063
- if (amt == null) { response = 'Intel AMT not supported.'; } else { amt.setPolicy({ type: 0 }); amt.deactivateCCM(); }
3064
- break;
3065
- }
3066
- case 'amtpolicy': {
3067
- if (amtPolicy == null) {
3068
- response = 'No Intel(R) AMT policy.';
3069
- } else {
3070
- response = JSON.stringify(amtPolicy);
3071
- }
3072
- break;
3073
- }
3066
case 'openurl': {
3067
if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
3068
else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
@@ -3566,7 +3558,7 @@ function createMeshCore(agent) {
3558
if (data.action == 'mestate') { getMeiState(15, function (state) { apftunnel.updateMeiState(state); }); } // Update the MEI state
3559
if (data.action == 'deactivate') { // Request CCM deactivation
3560
var amtMeiModule, amtMei;
3569
- try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); break; }
3561
+ try { amtMeiModule = require('amt-mei'); amtMei = new amtMeiModule(); } catch (ex) { apftunnel.sendMeiDeactivationState(1); return; }
3562
amtMei.on('error', function (e) { apftunnel.sendMeiDeactivationState(1); });
3563
amtMei.unprovision(1, function (status) { apftunnel.sendMeiDeactivationState(status); }); // 0 = Success
3564
}
@@ -3861,7 +3853,7 @@ function createMeshCore(agent) {
3853
if (result) {
3854
amtMeiTmpState.net0 = result;
3855
var fqdn = null, interfaces = require('os').networkInterfaces(); // Look for the DNS suffix for the Intel AMT Ethernet interface
3864
- for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == mestate.net0.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3856
+ for (var i in interfaces) { for (var j in interfaces[i]) { if ((interfaces[i][j].mac == result.mac) && (interfaces[i][j].fqdn != null) && (interfaces[i][j].fqdn != '')) { amtMeiTmpState.OsDnsSuffix = interfaces[i][j].fqdn; } } }
3857
}
3858
});
3859
amtMei.getLanInterfaceSettings(1, function (result) { if (result) { amtMeiTmpState.net1 = result; } });
amtmanager.js
+42
-24
@@ -259,14 +259,29 @@ module.exports.CreateAmtManager = function(parent) {
259
if (amtPolicy < 2) { ciraPolicy = 0; }
260
dev.policy = { amtPolicy: amtPolicy, ciraPolicy: ciraPolicy, badPass: badPass, password: password };
261
262
- // If there is no Intel AMT policy for this device, stop here.
263
- if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev); return; }
264
-
262
// Setup the monitored device
263
dev.name = node.name;
267
- //if (node.host) { dev.host = node.host.toLowerCase(); }
264
dev.meshid = node.meshid;
265
dev.intelamt = node.intelamt;
266
+
267
+ // Check if the status of Intel AMT sent by the agents matched what we have in the database
268
+ if ((dev.connType == 2) && (dev.mpsConnection != null) && (dev.mpsConnection.tag != null) && (dev.mpsConnection.tag.meiState != null)) {
269
+ dev.aquired = {};
270
+ if (typeof dev.mpsConnection.tag.meiState['ProvisioningState'] == 'number') {
271
+ dev.intelamt.state = dev.aquired.state = dev.mpsConnection.tag.meiState['ProvisioningState'];
272
+ }
273
+ if (typeof dev.mpsConnection.tag.meiState['Flags'] == 'number') {
274
+ const flags = dev.intelamt.flags = dev.mpsConnection.tag.meiState['Flags'];
275
+ if (flags & 2) { dev.aquired.controlMode = 1; } // CCM
276
+ if (flags & 4) { dev.aquired.controlMode = 2; } // ACM
277
+ }
278
+ UpdateDevice(dev);
279
+ }
280
+
281
+ // If there is no Intel AMT policy for this device, stop here.
282
+ if (amtPolicy == 0) { dev.consoleMsg("Done."); removeAmtDevice(dev); return; }
283
+
284
+ // Initiate the communication to Intel AMT
285
dev.consoleMsg("Checking Intel AMT state...");
286
attemptInitialContact(dev);
287
});
@@ -435,8 +450,8 @@ module.exports.CreateAmtManager = function(parent) {
450
var verSplit = stack.wsman.comm.amtVersion.split('.');
451
if (verSplit.length >= 3) { dev.aquired.version = verSplit[0] + '.' + verSplit[1] + '.' + verSplit[2]; dev.aquired.majorver = parseInt(verSplit[0]); dev.aquired.minorver = parseInt(verSplit[1]); }
452
dev.aquired.realm = stack.wsman.comm.digestRealm;
438
- dev.aquired.user = stack.wsman.comm.user;
439
- dev.aquired.pass = stack.wsman.comm.pass;
453
+ dev.aquired.user = dev.intelamt.user = stack.wsman.comm.user;
454
+ dev.aquired.pass = dev.intelamt.pass = stack.wsman.comm.pass;
455
dev.aquired.lastContact = Date.now();
456
if ((dev.connType == 1) || (dev.connType == 3)) { dev.aquired.tls = stack.wsman.comm.xtls; } // Only set the TLS state if in relay or local mode. When using CIRA, this is auto-detected.
457
if (stack.wsman.comm.xtls == 1) { dev.aquired.hash = stack.wsman.comm.xtlsCertificate.fingerprint.split(':').join('').toLowerCase(); } else { delete dev.aquired.hash; }
@@ -512,19 +527,17 @@ module.exports.CreateAmtManager = function(parent) {
527
528
// Change the current core information string and event it
529
function UpdateDevice(dev) {
515
- if (isAmtDeviceValid(dev) == false) return; // Device no longer exists, ignore this request.
516
-
530
// Check that the mesh exists
531
const mesh = parent.webserver.meshes[dev.meshid];
519
- if (mesh == null) { removeAmtDevice(dev); return false; }
532
+ if (mesh == null) { removeAmtDevice(dev); console.log('y3'); return false; }
533
534
// Get the node and change it if needed
535
parent.db.Get(dev.nodeid, function (err, nodes) {
523
- if ((nodes == null) || (nodes.length != 1)) { return false; }
536
+ if ((nodes == null) || (nodes.length != 1)) { console.log('y1'); return false; }
537
const device = nodes[0];
538
var changes = [], change = 0, log = 0;
539
var domain = parent.config.domains[device.domain];
527
- if (domain == null) { return false; }
540
+ if (domain == null) { console.log('y2'); return false; }
541
542
// Check if anything changes
543
if (device.intelamt == null) { device.intelamt = {}; }
@@ -535,7 +548,7 @@ module.exports.CreateAmtManager = function(parent) {
548
if (dev.aquired.realm && (typeof dev.aquired.realm == 'string') && (dev.aquired.realm != device.intelamt.realm)) { change = 1; log = 1; device.intelamt.realm = dev.aquired.realm; changes.push('AMT realm'); }
549
if (dev.aquired.hash && (typeof dev.aquired.hash == 'string') && (dev.aquired.hash != device.intelamt.hash)) { change = 1; log = 1; device.intelamt.hash = dev.aquired.hash; changes.push('AMT hash'); }
550
if (dev.aquired.tls && (typeof dev.aquired.tls == 'number') && (dev.aquired.tls != device.intelamt.tls)) { change = 1; log = 1; device.intelamt.tls = dev.aquired.tls; changes.push('AMT TLS'); }
538
- if (device.intelamt.state != 2) { change = 1; log = 1; device.intelamt.state = 2; changes.push('AMT state'); }
551
+ if ((dev.aquired.state != null) && (typeof dev.aquired.state == 'number') && (dev.aquired.state != device.intelamt.state)) { change = 1; log = 1; device.intelamt.state = dev.aquired.state; changes.push('AMT state'); }
552
553
// Update Intel AMT flags if needed
554
// dev.aquired.controlMode // 1 = CCM, 2 = ACM
@@ -544,7 +557,10 @@ module.exports.CreateAmtManager = function(parent) {
557
if (typeof device.intelamt.flags == 'number') { flags = device.intelamt.flags; }
558
if (dev.aquired.controlMode == 1) { if ((flags & 4) != 0) { flags -= 4; } if ((flags & 2) == 0) { flags += 2; } } // CCM
559
if (dev.aquired.controlMode == 2) { if ((flags & 4) == 0) { flags += 4; } if ((flags & 2) != 0) { flags -= 2; } } // ACM
547
- if (device.intelamt.flags != flags) { change = 1; log = 1; device.intelamt.flags = flags; changes.push('AMT flags'); }
560
+ if (device.intelamt.flags != flags) {
561
+ console.log('ChangeFlags', flags);
562
+ change = 1; log = 1; device.intelamt.flags = flags; changes.push('AMT flags');
563
+ }
564
565
// If there are changes, event the new device
566
if (change == 1) {
@@ -1104,13 +1120,8 @@ module.exports.CreateAmtManager = function(parent) {
1120
// If this device does not have KVM, ignore the response. This can happen for Intel Standard Manageability (Intel(R) SM).
1121
if ((responses['CIM_KVMRedirectionSAP'] == null) || (responses['CIM_KVMRedirectionSAP'].status == 400)) { responses['CIM_KVMRedirectionSAP'] = null; }
1122
1107
- // Check redirection services
1108
- var redir = (responses['AMT_RedirectionService'].response['ListenerEnabled'] == true);
1109
- var sol = ((responses['AMT_RedirectionService'].response['EnabledState'] & 2) != 0);
1110
- var ider = ((responses['AMT_RedirectionService'].response['EnabledState'] & 1) != 0);
1111
-
1123
// Enable SOL & IDER
1113
- if (responses['AMT_RedirectionService'].response['EnabledState'] != 32771) {
1124
+ if ((responses['AMT_RedirectionService'].response['EnabledState'] != 32771) || (responses['AMT_RedirectionService'].response['ListenerEnabled'] == false)) {
1125
dev.redirObj = responses['AMT_RedirectionService'].response;
1126
dev.redirObj['ListenerEnabled'] = true;
1127
dev.redirObj['EnabledState'] = 32771;
@@ -1402,12 +1413,11 @@ module.exports.CreateAmtManager = function(parent) {
1413
dev.aquired.host = dev.mpsConnection.tag.meiState.OsHostname + '.' + dev.mpsConnection.tag.meiState.OsDnsSuffix;
1414
}
1415
dev.aquired.realm = dev.amtstack.wsman.comm.digestRealm;
1405
- dev.aquired.user = 'admin';
1406
- dev.aquired.pass = dev.temp.pass;
1416
+ dev.intelamt.user = dev.aquired.user = 'admin';
1417
+ dev.intelamt.pass = dev.aquired.pass = dev.temp.pass;
1418
+ dev.intelamt.tls = dev.aquired.tls = 0;
1419
dev.aquired.lastContact = Date.now();
1408
- dev.aquired.tls = 0;
1409
- dev.intelamt.user = 'admin';
1410
- dev.intelamt.pass = dev.temp.pass;
1420
+ dev.aquired.state = 2; // Activated
1421
delete dev.acctry;
1422
UpdateDevice(dev);
1423
@@ -1438,6 +1448,14 @@ module.exports.CreateAmtManager = function(parent) {
1448
dev.consoleMsg("Failed to deactivate Intel AMT CCM.");
1449
removeAmtDevice(dev);
1450
} else {
1451
+ // Update the device
1452
+ dev.aquired = {};
1453
+ dev.aquired.controlMode = 0; // 1 = CCM, 2 = ACM
1454
+ dev.aquired.state = 0; // Not activated
1455
+ delete dev.acctry;
1456
+ delete dev.amtstack;
1457
+ UpdateDevice(dev);
1458
+
1459
if (dev.policy.amtPolicy == 1) { // CCM deactivation policy, we are done.
1460
dev.consoleMsg("Deactivation successful.");
1461
dev.consoleMsg("Done.");
meshagent.js
+6
-38
@@ -192,7 +192,6 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
192
obj.sendBinary(common.ShortToStr(10) + common.ShortToStr(0) + argument.hash + argument.core, function () { parent.parent.taskLimiter.completed(taskid); }); // MeshCommand_CoreModule, start core update
193
parent.agentStats.updatingCoreCount++;
194
parent.parent.debug('agent', "Updating core " + argument.name);
195
- agentCoreIsStable();
195
} else {
196
// This agent is probably disconnected, nothing to do.
197
parent.parent.taskLimiter.completed(taskid);
@@ -898,40 +897,11 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
897
}
898
}
899
901
- // Take a basic Intel AMT policy and add all server information to it, making it ready to send to this agent.
902
- /*
903
- function completeIntelAmtPolicy(amtPolicy) {
904
- var r = amtPolicy;
905
- if (amtPolicy == null) return null;
906
- if (amtPolicy.type == 2) {
907
- // CCM - Add server root certificate
908
- if (parent.parent.certificates.rootex == null) { parent.parent.certificates.rootex = parent.parent.certificates.root.cert.split('-----BEGIN CERTIFICATE-----').join('').split('-----END CERTIFICATE-----').join('').split('\r').join('').split('\n').join(''); }
909
- r.rootcert = parent.parent.certificates.rootex;
910
- if ((amtPolicy.cirasetup == 2) && (parent.parent.mpsserver != null) && (parent.parent.certificates.AmtMpsName != null) && (args.lanonly != true) && (args.mpsport != 0)) {
911
- // Add server CIRA settings
912
- r.ciraserver = {
913
- name: parent.parent.certificates.AmtMpsName,
914
- port: (typeof args.mpsaliasport == 'number' ? args.mpsaliasport : args.mpsport),
915
- user: obj.meshid.replace(/\@/g, 'X').replace(/\$/g, 'X').substring(0, 16),
916
- pass: args.mpspass ? args.mpspass : 'A@xew9rt', // If the MPS password is not set, just use anything. TODO: Use the password as an agent identifier?
917
- home: ['sdlwerulis3wpj95dfj'] // Use a random FQDN to not have any home network.
918
- };
919
- if (Array.isArray(args.ciralocalfqdn)) { r.ciraserver.home = args.ciralocalfqdn; }
920
- }
921
- } else if ((amtPolicy.type == 3) && (domain.amtacmactivation.acmmatch)) {
922
- // ACM - In this mode, don't send much to Intel AMT. Just indicate ACM policy and let the agent try activation when possible.
923
- r = { type: 3, match: domain.amtacmactivation.acmmatch };
924
- }
925
- return r;
926
- }
927
- */
928
-
929
- // Send Intel AMT policy
900
+ // Indicate to the agent that we want to reconfigure Intel AMT
901
obj.sendUpdatedIntelAmtPolicy = function (policy) {
902
if (obj.agentExeInfo && (obj.agentExeInfo.amt == true)) { // Only send Intel AMT policy to agents what could have AMT.
932
- // TODO
933
- //if (policy == null) { var mesh = parent.meshes[obj.dbMeshKey]; if (mesh == null) return; policy = mesh.amt; }
934
- //if (policy != null) { try { obj.send(JSON.stringify({ action: 'amtPolicy', amtPolicy: completeIntelAmtPolicy(common.Clone(policy)) })); } catch (ex) { } }
903
+ if (policy == null) { var mesh = parent.meshes[obj.dbMeshKey]; if (mesh == null) return; policy = mesh.amt; }
904
+ if ((policy != null) && (policy.type != 0)) { try { obj.send(JSON.stringify({ action: 'amtconfig' })); } catch (ex) { } }
905
}
906
}
907
@@ -983,12 +953,10 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
953
}
954
});
955
986
- // Send Intel AMT policy
987
- /*
988
- if (obj.agentExeInfo && (obj.agentExeInfo.amt == true) && (mesh.amt != null)) { // Only send Intel AMT policy to agents what could have AMT.
989
- try { obj.send(JSON.stringify({ action: 'amtPolicy', amtPolicy: completeIntelAmtPolicy(common.Clone(mesh.amt)) })); } catch (ex) { }
956
+ // Indicate that we want to check the Intel AMT configuration
957
+ if (obj.agentExeInfo && (obj.agentExeInfo.amt == true) && (mesh.amt != null) && (mesh.amt.type != 0)) { // Only send yo agents what could have AMT and if the policy is not empty.
958
+ try { obj.send(JSON.stringify({ action: 'amtconfig' })); } catch (ex) { }
959
}
991
- */
960
961
// Fetch system information
962
db.GetHash('si' + obj.dbNodeKey, function (err, results) {
translate/translate.json
+739
-746
@@ -224,8 +224,8 @@
224
"zh-chs": " 用户需要先登录到该服务器一次,然后才能将其添加到设备组。",
225
"zh-cht": " 用戶需要先登入到該伺服器一次,然後才能將其新增到裝置群。",
226
"xloc": [
227
- "default.handlebars->29->1435",
228
- "default.handlebars->29->1895"
227
+ "default.handlebars->29->1428",
228
+ "default.handlebars->29->1888"
229
]
230
},
231
{
@@ -458,7 +458,7 @@
458
"zh-chs": "*保留空白可为每个设备分配一个随机密码。",
459
"zh-cht": "*保留空白可為每個裝置分配一個隨機密碼。",
460
"xloc": [
461
- "default.handlebars->29->1405"
461
+ "default.handlebars->29->1398"
462
]
463
},
464
{
@@ -496,7 +496,7 @@
496
"zh-cht": ",",
497
"xloc": [
498
"default-mobile.handlebars->9->461",
499
- "default.handlebars->29->1505"
499
+ "default.handlebars->29->1498"
500
]
501
},
502
{
@@ -768,8 +768,8 @@
768
"xloc": [
769
"default-mobile.handlebars->9->108",
770
"default-mobile.handlebars->9->301",
771
- "default.handlebars->29->1546",
772
- "default.handlebars->29->2050",
771
+ "default.handlebars->29->1539",
772
+ "default.handlebars->29->2043",
773
"default.handlebars->29->894"
774
]
775
},
@@ -826,7 +826,7 @@
826
"zh-chs": "1个活跃时段",
827
"zh-cht": "1個活躍時段",
828
"xloc": [
829
- "default.handlebars->29->1964"
829
+ "default.handlebars->29->1957"
830
]
831
},
832
{
@@ -848,7 +848,7 @@
848
"xloc": [
849
"default-mobile.handlebars->9->118",
850
"default-mobile.handlebars->9->465",
851
- "default.handlebars->29->1570",
851
+ "default.handlebars->29->1563",
852
"download.handlebars->3->1",
853
"download2.handlebars->5->1"
854
]
@@ -913,7 +913,7 @@
913
"zh-chs": "1组",
914
"zh-cht": "1群",
915
"xloc": [
916
- "default.handlebars->29->1929"
916
+ "default.handlebars->29->1922"
917
]
918
},
919
{
@@ -1017,7 +1017,7 @@
1017
"zh-chs": "有1个用户没有显示,请使用搜索框查找用户...",
1018
"zh-cht": "有1個用戶沒有顯示,請使用搜尋框搜尋用戶...",
1019
"xloc": [
1020
- "default.handlebars->29->1719"
1020
+ "default.handlebars->29->1712"
1021
]
1022
},
1023
{
@@ -1083,7 +1083,7 @@
1083
"default-mobile.handlebars->9->172",
1084
"default-mobile.handlebars->9->175",
1085
"default-mobile.handlebars->9->178",
1086
- "default.handlebars->29->1723",
1086
+ "default.handlebars->29->1716",
1087
"default.handlebars->29->250",
1088
"default.handlebars->29->253",
1089
"default.handlebars->29->256",
@@ -1500,7 +1500,7 @@
1500
"zh-chs": "2FA备份代码已清除",
1501
"zh-cht": "2FA備份代碼已清除",
1502
"xloc": [
1503
- "default.handlebars->29->1681"
1503
+ "default.handlebars->29->1674"
1504
]
1505
},
1506
{
@@ -1520,8 +1520,8 @@
1520
"zh-chs": "启用第二因素身份验证",
1521
"zh-cht": "啟用第二因素身份驗證",
1522
"xloc": [
1523
- "default.handlebars->29->1736",
1524
- "default.handlebars->29->1951"
1523
+ "default.handlebars->29->1729",
1524
+ "default.handlebars->29->1944"
1525
]
1526
},
1527
{
@@ -2503,7 +2503,7 @@
2503
"zh-chs": "访问服务器档案",
2504
"zh-cht": "存取伺服器檔案",
2505
"xloc": [
2506
- "default.handlebars->29->1901"
2506
+ "default.handlebars->29->1894"
2507
]
2508
},
2509
{
@@ -2656,7 +2656,7 @@
2656
"zh-chs": "帐户已更改:{0}",
2657
"zh-cht": "帳戶已更改:{0}",
2658
"xloc": [
2659
- "default.handlebars->29->1654"
2659
+ "default.handlebars->29->1647"
2660
]
2661
},
2662
{
@@ -2676,7 +2676,7 @@
2676
"zh-chs": "创建帐户,电子邮件为{0}",
2677
"zh-cht": "創建帳戶,電子郵件為{0}",
2678
"xloc": [
2679
- "default.handlebars->29->1653"
2679
+ "default.handlebars->29->1646"
2680
]
2681
},
2682
{
@@ -2696,7 +2696,7 @@
2696
"zh-chs": "创建帐户,用户名是{0}",
2697
"zh-cht": "帳戶已創建,用戶名是{0}",
2698
"xloc": [
2699
- "default.handlebars->29->1652"
2699
+ "default.handlebars->29->1645"
2700
]
2701
},
2702
{
@@ -2716,8 +2716,8 @@
2716
"zh-chs": "帐户已被锁定",
2717
"zh-cht": "帳戶已被鎖定",
2718
"xloc": [
2719
- "default.handlebars->29->1738",
2720
- "default.handlebars->29->1898"
2719
+ "default.handlebars->29->1731",
2720
+ "default.handlebars->29->1891"
2721
]
2722
},
2723
{
@@ -2781,7 +2781,7 @@
2781
"zh-chs": "帐号登录",
2782
"zh-cht": "帳號登錄",
2783
"xloc": [
2784
- "default.handlebars->29->1589"
2784
+ "default.handlebars->29->1582"
2785
]
2786
},
2787
{
@@ -2801,7 +2801,7 @@
2801
"zh-chs": "帐户登出",
2802
"zh-cht": "帳戶登出",
2803
"xloc": [
2804
- "default.handlebars->29->1590"
2804
+ "default.handlebars->29->1583"
2805
]
2806
},
2807
{
@@ -2843,7 +2843,7 @@
2843
"zh-chs": "帐户密码已更改:{0}",
2844
"zh-cht": "帳戶密碼已更改:{0}",
2845
"xloc": [
2846
- "default.handlebars->29->1662"
2846
+ "default.handlebars->29->1655"
2847
]
2848
},
2849
{
@@ -2863,7 +2863,7 @@
2863
"zh-chs": "帐户已删除",
2864
"zh-cht": "帳戶已刪除",
2865
"xloc": [
2866
- "default.handlebars->29->1651"
2866
+ "default.handlebars->29->1644"
2867
]
2868
},
2869
{
@@ -3121,8 +3121,8 @@
3121
"zh-chs": "添加设备",
3122
"zh-cht": "新增裝置",
3123
"xloc": [
3124
- "default.handlebars->29->1875",
3125
- "default.handlebars->29->1999"
3124
+ "default.handlebars->29->1868",
3125
+ "default.handlebars->29->1992"
3126
]
3127
},
3128
{
@@ -3162,9 +3162,9 @@
3162
"zh-chs": "添加设备组",
3163
"zh-cht": "新增裝置群",
3164
"xloc": [
3165
- "default.handlebars->29->1469",
3166
- "default.handlebars->29->1869",
3167
- "default.handlebars->29->1987",
3165
+ "default.handlebars->29->1462",
3166
+ "default.handlebars->29->1862",
3167
+ "default.handlebars->29->1980",
3168
"default.handlebars->29->237"
3169
]
3170
},
@@ -3185,7 +3185,7 @@
3185
"zh-chs": "添加设备组权限",
3186
"zh-cht": "新增裝置群權限",
3187
"xloc": [
3188
- "default.handlebars->29->1466"
3188
+ "default.handlebars->29->1459"
3189
]
3190
},
3191
{
@@ -3205,8 +3205,8 @@
3205
"zh-chs": "添加设备权限",
3206
"zh-cht": "新增裝置權限",
3207
"xloc": [
3208
- "default.handlebars->29->1471",
3209
- "default.handlebars->29->1473"
3208
+ "default.handlebars->29->1464",
3209
+ "default.handlebars->29->1466"
3210
]
3211
},
3212
{
@@ -3306,7 +3306,7 @@
3306
"zh-chs": "添加成员身份",
3307
"zh-cht": "新增成員身份",
3308
"xloc": [
3309
- "default.handlebars->29->2017"
3309
+ "default.handlebars->29->2010"
3310
]
3311
},
3312
{
@@ -3392,7 +3392,7 @@
3392
"zh-chs": "添加用户设备权限",
3393
"zh-cht": "新增用戶裝置權限",
3394
"xloc": [
3395
- "default.handlebars->29->1476"
3395
+ "default.handlebars->29->1469"
3396
]
3397
},
3398
{
@@ -3413,8 +3413,8 @@
3413
"zh-cht": "新增用戶群",
3414
"xloc": [
3415
"default.handlebars->29->1365",
3416
- "default.handlebars->29->1468",
3417
- "default.handlebars->29->1993",
3416
+ "default.handlebars->29->1461",
3417
+ "default.handlebars->29->1986",
3418
"default.handlebars->29->673"
3419
]
3420
},
@@ -3435,7 +3435,7 @@
3435
"zh-chs": "添加用户组设备权限",
3436
"zh-cht": "新增用戶群裝置權限",
3437
"xloc": [
3438
- "default.handlebars->29->1478"
3438
+ "default.handlebars->29->1471"
3439
]
3440
},
3441
{
@@ -3493,7 +3493,7 @@
3493
"zh-cht": "新增用戶",
3494
"xloc": [
3495
"default.handlebars->29->1364",
3496
- "default.handlebars->29->1864"
3496
+ "default.handlebars->29->1857"
3497
]
3498
},
3499
{
@@ -3513,7 +3513,7 @@
3513
"zh-chs": "将用户添加到设备组",
3514
"zh-cht": "將用戶新增到裝置群",
3515
"xloc": [
3516
- "default.handlebars->29->1465"
3516
+ "default.handlebars->29->1458"
3517
]
3518
},
3519
{
@@ -3533,7 +3533,7 @@
3533
"zh-chs": "将用户添加到用户组",
3534
"zh-cht": "將用戶新增到用戶群",
3535
"xloc": [
3536
- "default.handlebars->29->1897"
3536
+ "default.handlebars->29->1890"
3537
]
3538
},
3539
{
@@ -3696,7 +3696,7 @@
3696
"zh-chs": "添加了身份验证应用程序",
3697
"zh-cht": "添加了身份驗證應用程序",
3698
"xloc": [
3699
- "default.handlebars->29->1678"
3699
+ "default.handlebars->29->1671"
3700
]
3701
},
3702
{
@@ -3716,8 +3716,8 @@
3716
"zh-chs": "已将设备{0}添加到设备组{1}",
3717
"zh-cht": "已將設備{0}添加到設備組{1}",
3718
"xloc": [
3719
- "default.handlebars->29->1645",
3720
- "default.handlebars->29->1672"
3719
+ "default.handlebars->29->1638",
3720
+ "default.handlebars->29->1665"
3721
]
3722
},
3723
{
@@ -3737,7 +3737,7 @@
3737
"zh-chs": "添加了安全密钥",
3738
"zh-cht": "添加了安全密鑰",
3739
"xloc": [
3740
- "default.handlebars->29->1683"
3740
+ "default.handlebars->29->1676"
3741
]
3742
},
3743
{
@@ -3757,7 +3757,7 @@
3757
"zh-chs": "已将用户组{0}添加到设备组{1}",
3758
"zh-cht": "已將用戶組{0}添加到設備組{1}",
3759
"xloc": [
3760
- "default.handlebars->29->1656"
3760
+ "default.handlebars->29->1649"
3761
]
3762
},
3763
{
@@ -3777,8 +3777,8 @@
3777
"zh-chs": "已将用户{0}添加到用户组{1}",
3778
"zh-cht": "已將用戶{0}添加到用戶組{1}",
3779
"xloc": [
3780
- "default.handlebars->29->1659",
3781
- "default.handlebars->29->1668"
3780
+ "default.handlebars->29->1652",
3781
+ "default.handlebars->29->1661"
3782
]
3783
},
3784
{
@@ -3901,7 +3901,7 @@
3901
"zh-chs": "管理领域",
3902
"zh-cht": "管理領域",
3903
"xloc": [
3904
- "default.handlebars->29->1933"
3904
+ "default.handlebars->29->1926"
3905
]
3906
},
3907
{
@@ -3942,7 +3942,7 @@
3942
"zh-chs": "管理领域",
3943
"zh-cht": "管理領域",
3944
"xloc": [
3945
- "default.handlebars->29->1801"
3945
+ "default.handlebars->29->1794"
3946
]
3947
},
3948
{
@@ -3962,7 +3962,7 @@
3962
"zh-chs": "管理员",
3963
"zh-cht": "管理員",
3964
"xloc": [
3965
- "default.handlebars->29->1730"
3965
+ "default.handlebars->29->1723"
3966
]
3967
},
3968
{
@@ -4005,8 +4005,8 @@
4005
"default-mobile.handlebars->9->203",
4006
"default-mobile.handlebars->9->228",
4007
"default-mobile.handlebars->9->244",
4008
- "default.handlebars->29->1532",
4009
- "default.handlebars->29->1540",
4008
+ "default.handlebars->29->1525",
4009
+ "default.handlebars->29->1533",
4010
"default.handlebars->29->213",
4011
"default.handlebars->29->444",
4012
"default.handlebars->container->column_l->p15->consoleTable->1->6->1->1->1->0->p15outputselecttd->p15outputselect->1"
@@ -4029,8 +4029,8 @@
4029
"zh-chs": "代理+英特尔AMT",
4030
"zh-cht": "代理+Intel® AMT",
4031
"xloc": [
4032
- "default.handlebars->29->1534",
4033
- "default.handlebars->29->1542"
4032
+ "default.handlebars->29->1527",
4033
+ "default.handlebars->29->1535"
4034
]
4035
},
4036
{
@@ -4071,7 +4071,7 @@
4071
"zh-cht": "代理控制台",
4072
"xloc": [
4073
"default-mobile.handlebars->9->443",
4074
- "default.handlebars->29->1486"
4074
+ "default.handlebars->29->1479"
4075
]
4076
},
4077
{
@@ -4091,7 +4091,7 @@
4091
"zh-chs": "代理错误计数器",
4092
"zh-cht": "代理錯誤計數器",
4093
"xloc": [
4094
- "default.handlebars->29->2060"
4094
+ "default.handlebars->29->2053"
4095
]
4096
},
4097
{
@@ -4194,7 +4194,7 @@
4194
"zh-chs": "代理时段",
4195
"zh-cht": "代理時段",
4196
"xloc": [
4197
- "default.handlebars->29->2076"
4197
+ "default.handlebars->29->2069"
4198
]
4199
},
4200
{
@@ -4235,7 +4235,7 @@
4235
"zh-chs": "代理类型",
4236
"zh-cht": "代理類型",
4237
"xloc": [
4238
- "default.handlebars->29->1538",
4238
+ "default.handlebars->29->1531",
4239
"default.handlebars->container->column_l->p21->3->1->meshOsChartDiv->1"
4240
]
4241
},
@@ -4256,7 +4256,7 @@
4256
"zh-chs": "代理关闭了与服务器压缩的{0}%代理会话。已发送:{1},已压缩:{2}",
4257
"zh-cht": "代理關閉了與{0}%代理到服務器壓縮的會話。已發送:{1},已壓縮:{2}",
4258
"xloc": [
4259
- "default.handlebars->29->1642"
4259
+ "default.handlebars->29->1635"
4260
]
4261
},
4262
{
@@ -4365,7 +4365,7 @@
4365
"zh-chs": "代理",
4366
"zh-cht": "代理",
4367
"xloc": [
4368
- "default.handlebars->29->2092"
4368
+ "default.handlebars->29->2085"
4369
]
4370
},
4371
{
@@ -4428,7 +4428,7 @@
4428
"zh-chs": "全部可用",
4429
"zh-cht": "全部可用",
4430
"xloc": [
4431
- "default.handlebars->29->1693"
4431
+ "default.handlebars->29->1686"
4432
]
4433
},
4434
{
@@ -4465,7 +4465,7 @@
4465
"zh-chs": "所有事件",
4466
"zh-cht": "所有事件",
4467
"xloc": [
4468
- "default.handlebars->29->1691"
4468
+ "default.handlebars->29->1684"
4469
]
4470
},
4471
{
@@ -4507,8 +4507,8 @@
4507
"zh-chs": "允许用户管理此设备组和该组中的设备。",
4508
"zh-cht": "允許用戶管理此裝置群和該群中的裝置。",
4509
"xloc": [
4510
- "default.handlebars->29->1433",
4511
- "default.handlebars->29->1894"
4510
+ "default.handlebars->29->1426",
4511
+ "default.handlebars->29->1887"
4512
]
4513
},
4514
{
@@ -4528,7 +4528,7 @@
4528
"zh-chs": "允许用户管理此设备。",
4529
"zh-cht": "允許用戶管理此裝置。",
4530
"xloc": [
4531
- "default.handlebars->29->1434"
4531
+ "default.handlebars->29->1427"
4532
]
4533
},
4534
{
@@ -4634,8 +4634,8 @@
4634
"zh-cht": "一直通知",
4635
"xloc": [
4636
"default.handlebars->29->1345",
4637
- "default.handlebars->29->1855",
4638
- "default.handlebars->29->1942",
4637
+ "default.handlebars->29->1848",
4638
+ "default.handlebars->29->1935",
4639
"default.handlebars->29->611"
4640
]
4641
},
@@ -4657,8 +4657,8 @@
4657
"zh-cht": "一直提示",
4658
"xloc": [
4659
"default.handlebars->29->1346",
4660
- "default.handlebars->29->1856",
4661
- "default.handlebars->29->1943",
4660
+ "default.handlebars->29->1849",
4661
+ "default.handlebars->29->1936",
4662
"default.handlebars->29->612"
4663
]
4664
},
@@ -5284,7 +5284,7 @@
5284
"zh-cht": "你確定要刪除群{0}嗎?刪除裝置群還將刪除該群中有關裝置的所有訊息。",
5285
"xloc": [
5286
"default-mobile.handlebars->9->412",
5287
- "default.handlebars->29->1409"
5287
+ "default.handlebars->29->1402"
5288
]
5289
},
5290
{
@@ -5364,7 +5364,7 @@
5364
"zh-chs": "您确定要{0}插件吗:{1}",
5365
"zh-cht": "你確定要{0}外掛嗎:{1}",
5366
"xloc": [
5367
- "default.handlebars->29->2132"
5367
+ "default.handlebars->29->2125"
5368
]
5369
},
5370
{
@@ -5484,7 +5484,7 @@
5484
"zh-chs": "尝试激活英特尔(R)AMT ACM模式",
5485
"zh-cht": "嘗試激活英特爾(R)AMT ACM模式",
5486
"xloc": [
5487
- "default.handlebars->29->1611"
5487
+ "default.handlebars->29->1604"
5488
]
5489
},
5490
{
@@ -5504,7 +5504,7 @@
5504
"zh-chs": "认证软件",
5505
"zh-cht": "認證軟體",
5506
"xloc": [
5507
- "default.handlebars->29->1946"
5507
+ "default.handlebars->29->1939"
5508
]
5509
},
5510
{
@@ -5675,7 +5675,7 @@
5675
"zh-chs": "可用內存",
5676
"zh-cht": "可用內存",
5677
"xloc": [
5678
- "default.handlebars->29->2085"
5678
+ "default.handlebars->29->2078"
5679
]
5680
},
5681
{
@@ -5831,8 +5831,8 @@
5831
"zh-chs": "背景与互动",
5832
"zh-cht": "背景與互動",
5833
"xloc": [
5834
+ "default.handlebars->29->1508",
5835
"default.handlebars->29->1515",
5835
- "default.handlebars->29->1522",
5836
"default.handlebars->29->357",
5837
"default.handlebars->29->371"
5838
]
@@ -5854,8 +5854,8 @@
5854
"zh-chs": "仅背景",
5855
"zh-cht": "僅背景",
5856
"xloc": [
5857
+ "default.handlebars->29->1509",
5858
"default.handlebars->29->1516",
5858
- "default.handlebars->29->1523",
5859
"default.handlebars->29->358",
5860
"default.handlebars->29->372",
5861
"default.handlebars->29->386"
@@ -5898,7 +5898,7 @@
5898
"zh-chs": "备用码",
5899
"zh-cht": "備用碼",
5900
"xloc": [
5901
- "default.handlebars->29->1948"
5901
+ "default.handlebars->29->1941"
5902
]
5903
},
5904
{
@@ -5918,7 +5918,7 @@
5918
"zh-chs": "错误的签名",
5919
"zh-cht": "錯誤的簽名",
5920
"xloc": [
5921
- "default.handlebars->29->2067"
5921
+ "default.handlebars->29->2060"
5922
]
5923
},
5924
{
@@ -5938,7 +5938,7 @@
5938
"zh-chs": "错误的网络证书",
5939
"zh-cht": "錯誤的網絡憑證",
5940
"xloc": [
5941
- "default.handlebars->29->2066"
5941
+ "default.handlebars->29->2059"
5942
]
5943
},
5944
{
@@ -6101,7 +6101,7 @@
6101
"zh-chs": "广播",
6102
"zh-cht": "廣播",
6103
"xloc": [
6104
- "default.handlebars->29->1862",
6104
+ "default.handlebars->29->1855",
6105
"default.handlebars->container->column_l->p4->3->1->0->3->1"
6106
]
6107
},
@@ -6122,7 +6122,7 @@
6122
"zh-chs": "广播消息",
6123
"zh-cht": "廣播消息",
6124
"xloc": [
6125
- "default.handlebars->29->1783"
6125
+ "default.handlebars->29->1776"
6126
]
6127
},
6128
{
@@ -6142,7 +6142,7 @@
6142
"zh-chs": "向所有连接的用户广播消息。",
6143
"zh-cht": "向所有連接的用戶廣播消息。",
6144
"xloc": [
6145
- "default.handlebars->29->1778"
6145
+ "default.handlebars->29->1771"
6146
]
6147
},
6148
{
@@ -6224,8 +6224,7 @@
6224
"zh-cht": "CIRA",
6225
"xloc": [
6226
"default-mobile.handlebars->9->204",
6227
- "default.handlebars->29->1397",
6228
- "default.handlebars->29->1402",
6227
+ "default.handlebars->29->1394",
6228
"default.handlebars->29->215",
6229
"default.handlebars->29->446"
6230
]
@@ -6247,7 +6246,7 @@
6246
"zh-chs": "CIRA服务器",
6247
"zh-cht": "CIRA伺服器",
6248
"xloc": [
6250
- "default.handlebars->29->2120"
6249
+ "default.handlebars->29->2113"
6250
]
6251
},
6252
{
@@ -6267,7 +6266,7 @@
6266
"zh-chs": "CIRA服务器命令",
6267
"zh-cht": "CIRA伺服器指令",
6268
"xloc": [
6270
- "default.handlebars->29->2121"
6269
+ "default.handlebars->29->2114"
6270
]
6271
},
6272
{
@@ -6308,7 +6307,7 @@
6307
"zh-chs": "CPU负载",
6308
"zh-cht": "CPU負載",
6309
"xloc": [
6311
- "default.handlebars->29->2081"
6310
+ "default.handlebars->29->2074"
6311
]
6312
},
6313
{
@@ -6328,7 +6327,7 @@
6327
"zh-chs": "最近15分钟的CPU负载",
6328
"zh-cht": "最近15分鐘的CPU負載",
6329
"xloc": [
6331
- "default.handlebars->29->2084"
6330
+ "default.handlebars->29->2077"
6331
]
6332
},
6333
{
@@ -6348,7 +6347,7 @@
6347
"zh-chs": "最近5分钟的CPU负载",
6348
"zh-cht": "最近5分鐘的CPU負載",
6349
"xloc": [
6351
- "default.handlebars->29->2083"
6350
+ "default.handlebars->29->2076"
6351
]
6352
},
6353
{
@@ -6368,7 +6367,7 @@
6367
"zh-chs": "最近一分钟的CPU负载",
6368
"zh-cht": "最近一分鐘的CPU負載",
6369
"xloc": [
6371
- "default.handlebars->29->2082"
6370
+ "default.handlebars->29->2075"
6371
]
6372
},
6373
{
@@ -6410,7 +6409,7 @@
6409
"zh-chs": "CSV",
6410
"zh-cht": "CSV",
6411
"xloc": [
6413
- "default.handlebars->29->1701"
6412
+ "default.handlebars->29->1694"
6413
]
6414
},
6415
{
@@ -6430,8 +6429,8 @@
6429
"zh-chs": "CSV格式",
6430
"zh-cht": "CSV格式",
6431
"xloc": [
6433
- "default.handlebars->29->1705",
6434
- "default.handlebars->29->1770",
6432
+ "default.handlebars->29->1698",
6433
+ "default.handlebars->29->1763",
6434
"default.handlebars->29->496"
6435
]
6436
},
@@ -6452,7 +6451,7 @@
6451
"zh-chs": "呼叫错误",
6452
"zh-cht": "呼叫錯誤",
6453
"xloc": [
6455
- "default.handlebars->29->2133"
6454
+ "default.handlebars->29->2126"
6455
]
6456
},
6457
{
@@ -6629,7 +6628,7 @@
6628
"zh-chs": "更改{0}的电邮",
6629
"zh-cht": "更改{0}的電郵",
6630
"xloc": [
6632
- "default.handlebars->29->1976"
6631
+ "default.handlebars->29->1969"
6632
]
6633
},
6634
{
@@ -6673,7 +6672,7 @@
6672
"xloc": [
6673
"default-mobile.handlebars->9->90",
6674
"default.handlebars->29->1277",
6676
- "default.handlebars->29->1963"
6675
+ "default.handlebars->29->1956"
6676
]
6677
},
6678
{
@@ -6693,7 +6692,7 @@
6692
"zh-chs": "更改{0}的密码",
6693
"zh-cht": "更改{0}的密碼",
6694
"xloc": [
6696
- "default.handlebars->29->1983"
6695
+ "default.handlebars->29->1976"
6696
]
6697
},
6698
{
@@ -6713,7 +6712,7 @@
6712
"zh-chs": "更改{0}的真实名称",
6713
"zh-cht": "更改{0}的真實名稱",
6714
"xloc": [
6716
- "default.handlebars->29->1971"
6715
+ "default.handlebars->29->1964"
6716
]
6717
},
6718
{
@@ -6795,7 +6794,7 @@
6794
"zh-chs": "更改该用户的密码",
6795
"zh-cht": "更改該用戶的密碼",
6796
"xloc": [
6798
- "default.handlebars->29->1962"
6797
+ "default.handlebars->29->1955"
6798
]
6799
},
6800
{
@@ -6875,7 +6874,7 @@
6874
"zh-chs": "更改帐户凭据",
6875
"zh-cht": "帳戶憑證已更改",
6876
"xloc": [
6878
- "default.handlebars->29->1663"
6877
+ "default.handlebars->29->1656"
6878
]
6879
},
6880
{
@@ -6895,7 +6894,7 @@
6894
"zh-chs": "{1}组中的设备{0}已更改:{2}",
6895
"zh-cht": "{1}組中的設備{0}已更改:{2}",
6896
"xloc": [
6898
- "default.handlebars->29->1647"
6897
+ "default.handlebars->29->1640"
6898
]
6899
},
6900
{
@@ -6915,7 +6914,7 @@
6914
"zh-chs": "语言从{1}更改为{2}",
6915
"zh-cht": "語言從{1}更改為{2}",
6916
"xloc": [
6918
- "default.handlebars->29->1591"
6917
+ "default.handlebars->29->1584"
6918
]
6919
},
6920
{
@@ -6935,8 +6934,8 @@
6934
"zh-chs": "已更改{0}的用户设备权限",
6935
"zh-cht": "已更改{0}的用戶設備權限",
6936
"xloc": [
6938
- "default.handlebars->29->1649",
6939
- "default.handlebars->29->1670"
6937
+ "default.handlebars->29->1642",
6938
+ "default.handlebars->29->1663"
6939
]
6940
},
6941
{
@@ -6976,7 +6975,7 @@
6975
"zh-chs": "聊天",
6976
"zh-cht": "聊天",
6977
"xloc": [
6979
- "default.handlebars->29->1722",
6978
+ "default.handlebars->29->1715",
6979
"default.handlebars->29->693",
6980
"default.handlebars->29->714"
6981
]
@@ -7000,8 +6999,8 @@
6999
"xloc": [
7000
"default-mobile.handlebars->9->433",
7001
"default-mobile.handlebars->9->453",
7003
- "default.handlebars->29->1461",
7004
- "default.handlebars->29->1497"
7002
+ "default.handlebars->29->1454",
7003
+ "default.handlebars->29->1490"
7004
]
7005
},
7006
{
@@ -7142,7 +7141,7 @@
7141
"zh-cht": "檢查...",
7142
"xloc": [
7143
"default.handlebars->29->1049",
7145
- "default.handlebars->29->2127"
7144
+ "default.handlebars->29->2120"
7145
]
7146
},
7147
{
@@ -7369,7 +7368,7 @@
7368
"default-mobile.handlebars->9->324",
7369
"default-mobile.handlebars->9->326",
7370
"default-mobile.handlebars->9->59",
7372
- "default.handlebars->29->1585",
7371
+ "default.handlebars->29->1578",
7372
"default.handlebars->29->914",
7373
"default.handlebars->29->916",
7374
"default.handlebars->29->918",
@@ -7416,7 +7415,7 @@
7415
"zh-chs": "全部清除",
7416
"zh-cht": "全部清除",
7417
"xloc": [
7419
- "default.handlebars->29->2054"
7418
+ "default.handlebars->29->2047"
7419
]
7420
},
7421
{
@@ -7484,7 +7483,7 @@
7483
"zh-chs": "清除此通知",
7484
"zh-cht": "清除此通知",
7485
"xloc": [
7487
- "default.handlebars->29->2053"
7486
+ "default.handlebars->29->2046"
7487
]
7488
},
7489
{
@@ -7545,7 +7544,7 @@
7544
"zh-cht": "單擊此處編輯裝置群名稱",
7545
"xloc": [
7546
"default.handlebars->29->1321",
7548
- "default.handlebars->29->1536"
7547
+ "default.handlebars->29->1529"
7548
]
7549
},
7550
{
@@ -7585,7 +7584,7 @@
7584
"zh-chs": "单击此处编辑用户组名称",
7585
"zh-cht": "單擊此處編輯用戶群名稱",
7586
"xloc": [
7588
- "default.handlebars->29->1839"
7587
+ "default.handlebars->29->1832"
7588
]
7589
},
7590
{
@@ -7727,8 +7726,7 @@
7726
"zh-chs": "客户端启动的远程访问",
7727
"zh-cht": "客戶端啟動的遠程訪問",
7728
"xloc": [
7730
- "default.handlebars->29->1396",
7731
- "default.handlebars->29->1401"
7729
+ "default.handlebars->29->1393"
7730
]
7731
},
7732
{
@@ -7812,7 +7810,7 @@
7810
"zh-chs": "封闭式桌面多路复用会话,{0}秒",
7811
"zh-cht": "封閉式桌面多路復用會話,{0}秒",
7812
"xloc": [
7815
- "default.handlebars->29->1596"
7813
+ "default.handlebars->29->1589"
7814
]
7815
},
7816
{
@@ -7895,7 +7893,7 @@
7893
"nl": "Opdrachten",
7894
"xloc": [
7895
"default-mobile.handlebars->9->455",
7898
- "default.handlebars->29->1499",
7896
+ "default.handlebars->29->1492",
7897
"default.handlebars->29->695",
7898
"default.handlebars->29->716"
7899
]
@@ -7917,8 +7915,8 @@
7915
"zh-chs": "通用设备组",
7916
"zh-cht": "通用裝置群",
7917
"xloc": [
7920
- "default.handlebars->29->1870",
7921
- "default.handlebars->29->1988"
7918
+ "default.handlebars->29->1863",
7919
+ "default.handlebars->29->1981"
7920
]
7921
},
7922
{
@@ -7938,8 +7936,8 @@
7936
"zh-chs": "通用设备",
7937
"zh-cht": "通用裝置",
7938
"xloc": [
7941
- "default.handlebars->29->1876",
7942
- "default.handlebars->29->2000"
7939
+ "default.handlebars->29->1869",
7940
+ "default.handlebars->29->1993"
7941
]
7942
},
7943
{
@@ -7980,7 +7978,7 @@
7978
"zh-cht": "將{1}入口{2}中的{0}限製到此位置?",
7979
"xloc": [
7980
"default-mobile.handlebars->9->127",
7983
- "default.handlebars->29->1580"
7981
+ "default.handlebars->29->1573"
7982
]
7983
},
7984
{
@@ -8002,11 +8000,11 @@
8000
"xloc": [
8001
"default-mobile.handlebars->9->282",
8002
"default-mobile.handlebars->9->413",
8005
- "default.handlebars->29->1410",
8006
- "default.handlebars->29->1750",
8007
- "default.handlebars->29->1829",
8008
- "default.handlebars->29->1890",
8009
- "default.handlebars->29->1986",
8003
+ "default.handlebars->29->1403",
8004
+ "default.handlebars->29->1743",
8005
+ "default.handlebars->29->1822",
8006
+ "default.handlebars->29->1883",
8007
+ "default.handlebars->29->1979",
8008
"default.handlebars->29->472",
8009
"default.handlebars->29->784",
8010
"default.handlebars->29->793"
@@ -8090,7 +8088,7 @@
8088
"zh-chs": "确认删除选定的帐户?",
8089
"zh-cht": "確認刪除所選帳戶?",
8090
"xloc": [
8093
- "default.handlebars->29->1749"
8091
+ "default.handlebars->29->1742"
8092
]
8093
},
8094
{
@@ -8130,7 +8128,7 @@
8128
"zh-chs": "确认删除选定的用户组?",
8129
"zh-cht": "確認刪除所選用戶群?",
8130
"xloc": [
8133
- "default.handlebars->29->1828"
8131
+ "default.handlebars->29->1821"
8132
]
8133
},
8134
{
@@ -8150,7 +8148,7 @@
8148
"zh-chs": "确认删除用户{0}?",
8149
"zh-cht": "確認刪除用戶{0}?",
8150
"xloc": [
8153
- "default.handlebars->29->1985"
8151
+ "default.handlebars->29->1978"
8152
]
8153
},
8154
{
@@ -8170,7 +8168,7 @@
8168
"zh-chs": "确认删除用户“ {0} ”的成员身份?",
8169
"zh-cht": "確認刪除用戶“ {0} ”的成員身份?",
8170
"xloc": [
8173
- "default.handlebars->29->1893"
8171
+ "default.handlebars->29->1886"
8172
]
8173
},
8174
{
@@ -8190,7 +8188,7 @@
8188
"zh-chs": "确认删除用户组“ {0} ”的成员身份?",
8189
"zh-cht": "確認刪除用戶群“ {0} ”的成員身份?",
8190
"xloc": [
8193
- "default.handlebars->29->2015"
8191
+ "default.handlebars->29->2008"
8192
]
8193
},
8194
{
@@ -8271,7 +8269,7 @@
8269
"zh-chs": "确认覆盖?",
8270
"zh-cht": "確認覆蓋?",
8271
"xloc": [
8274
- "default.handlebars->29->1579"
8272
+ "default.handlebars->29->1572"
8273
]
8274
},
8275
{
@@ -8291,8 +8289,8 @@
8289
"zh-chs": "确认删除设备“ {0} ”的访问权限?",
8290
"zh-cht": "確認刪除裝置“ {0} ”的訪問權限?",
8291
"xloc": [
8294
- "default.handlebars->29->1883",
8295
- "default.handlebars->29->2006"
8292
+ "default.handlebars->29->1876",
8293
+ "default.handlebars->29->1999"
8294
]
8295
},
8296
{
@@ -8312,8 +8310,8 @@
8310
"zh-chs": "确认删除设备组“ {0} ”的访问权限?",
8311
"zh-cht": "確認刪除裝置群“ {0} ”的訪問權限?",
8312
"xloc": [
8315
- "default.handlebars->29->1885",
8316
- "default.handlebars->29->2019"
8313
+ "default.handlebars->29->1878",
8314
+ "default.handlebars->29->2012"
8315
]
8316
},
8317
{
@@ -8333,7 +8331,7 @@
8331
"zh-chs": "确认删除用户“ {0} ”的访问权限?",
8332
"zh-cht": "確認刪除用戶“ {0} ”的訪問權限?",
8333
"xloc": [
8336
- "default.handlebars->29->2008"
8334
+ "default.handlebars->29->2001"
8335
]
8336
},
8337
{
@@ -8353,7 +8351,7 @@
8351
"zh-chs": "确认删除用户组“ {0} ”的访问权限?",
8352
"zh-cht": "確認刪除用戶群“ {0} ”的訪問權限?",
8353
"xloc": [
8356
- "default.handlebars->29->2011"
8354
+ "default.handlebars->29->2004"
8355
]
8356
},
8357
{
@@ -8373,8 +8371,8 @@
8371
"zh-chs": "确认删除访问权限?",
8372
"zh-cht": "確認刪除訪問權限?",
8373
"xloc": [
8376
- "default.handlebars->29->2009",
8377
- "default.handlebars->29->2012"
8374
+ "default.handlebars->29->2002",
8375
+ "default.handlebars->29->2005"
8376
]
8377
},
8378
{
@@ -8466,7 +8464,7 @@
8464
"zh-chs": "确认删除用户“ {0} ”的权限?",
8465
"zh-cht": "確認刪除用戶“ {0} ”的權限?",
8466
"xloc": [
8469
- "default.handlebars->29->1508"
8467
+ "default.handlebars->29->1501"
8468
]
8469
},
8470
{
@@ -8486,7 +8484,7 @@
8484
"zh-chs": "确认删除用户组“ {0} ”的权限?",
8485
"zh-cht": "確認刪除用戶群“ {0} ”的權限?",
8486
"xloc": [
8489
- "default.handlebars->29->1510"
8487
+ "default.handlebars->29->1503"
8488
]
8489
},
8490
{
@@ -8594,8 +8592,7 @@
8592
"zh-chs": "连接到服务器",
8593
"zh-cht": "連接到伺服器",
8594
"xloc": [
8597
- "default.handlebars->29->1400",
8598
- "default.handlebars->29->1404"
8595
+ "default.handlebars->29->1397"
8596
]
8597
},
8598
{
@@ -8716,7 +8713,7 @@
8713
"zh-chs": "已连接的英特尔®AMT",
8714
"zh-cht": "已連接的Intel® AMT",
8715
"xloc": [
8719
- "default.handlebars->29->2072"
8716
+ "default.handlebars->29->2065"
8717
]
8718
},
8719
{
@@ -8736,7 +8733,7 @@
8733
"zh-chs": "已连接的用户",
8734
"zh-cht": "已连接的用户",
8735
"xloc": [
8739
- "default.handlebars->29->2077"
8736
+ "default.handlebars->29->2070"
8737
]
8738
},
8739
{
@@ -8826,7 +8823,7 @@
8823
"zh-chs": "连接数量",
8824
"zh-cht": "連接數量",
8825
"xloc": [
8829
- "default.handlebars->29->2091"
8826
+ "default.handlebars->29->2084"
8827
]
8828
},
8829
{
@@ -8846,7 +8843,7 @@
8843
"zh-chs": "连接转发器",
8844
"zh-cht": "連接轉發器",
8845
"xloc": [
8849
- "default.handlebars->29->2119"
8846
+ "default.handlebars->29->2112"
8847
]
8848
},
8849
{
@@ -8907,7 +8904,7 @@
8904
"zh-cht": "連接性",
8905
"xloc": [
8906
"default-mobile.handlebars->9->249",
8910
- "default.handlebars->29->1543",
8907
+ "default.handlebars->29->1536",
8908
"default.handlebars->29->234",
8909
"default.handlebars->29->625",
8910
"default.handlebars->container->column_l->p21->3->1->meshConnChartDiv->1"
@@ -8996,7 +8993,7 @@
8993
"zh-chs": "Cookie编码器",
8994
"zh-cht": "Cookie編碼器",
8995
"xloc": [
8999
- "default.handlebars->29->2105"
8996
+ "default.handlebars->29->2098"
8997
]
8998
},
8999
{
@@ -9153,8 +9150,8 @@
9150
"zh-chs": "复制连结到剪贴板",
9151
"zh-cht": "複製連結到剪貼板",
9152
"xloc": [
9156
- "default.handlebars->29->1548",
9157
- "default.handlebars->29->1567",
9153
+ "default.handlebars->29->1541",
9154
+ "default.handlebars->29->1560",
9155
"default.handlebars->29->195",
9156
"default.handlebars->29->374"
9157
]
@@ -9257,7 +9254,7 @@
9254
"zh-chs": "复制:“{0}”到“{1}”",
9255
"zh-cht": "複製:“{0}”到“{1}”",
9256
"xloc": [
9260
- "default.handlebars->29->1639"
9257
+ "default.handlebars->29->1632"
9258
]
9259
},
9260
{
@@ -9403,7 +9400,7 @@
9400
"zh-chs": "核心服务器",
9401
"zh-cht": "核心伺服器",
9402
"xloc": [
9406
- "default.handlebars->29->2104"
9403
+ "default.handlebars->29->2097"
9404
]
9405
},
9406
{
@@ -9443,7 +9440,7 @@
9440
"zh-chs": "创建帐号",
9441
"zh-cht": "創建帳號",
9442
"xloc": [
9446
- "default.handlebars->29->1797",
9443
+ "default.handlebars->29->1790",
9444
"login-mobile.handlebars->container->page_content->column_l->1->1->0->1->createpanel->1->1->9->1->12->1->1",
9445
"login.handlebars->container->column_l->centralTable->1->0->logincell->createpanel->1->9->1->12->1->1",
9446
"login2.handlebars->centralTable->1->0->logincell->createpanel->1->9->1->12->1->1"
@@ -9486,7 +9483,7 @@
9483
"zh-chs": "创建用户组",
9484
"zh-cht": "創建用戶群",
9485
"xloc": [
9489
- "default.handlebars->29->1836"
9486
+ "default.handlebars->29->1829"
9487
]
9488
},
9489
{
@@ -9566,7 +9563,7 @@
9563
"zh-chs": "创建文件夹:“{0}”",
9564
"zh-cht": "創建文件夾:“{0}”",
9565
"xloc": [
9569
- "default.handlebars->29->1632"
9566
+ "default.handlebars->29->1625"
9567
]
9568
},
9569
{
@@ -9586,7 +9583,7 @@
9583
"zh-chs": "通过导入以下格式的JSON档案一次创建多个帐户:",
9584
"zh-cht": "通過導入以下格式的JSON檔案一次創建多個帳戶:",
9585
"xloc": [
9589
- "default.handlebars->29->1761"
9586
+ "default.handlebars->29->1754"
9587
]
9588
},
9589
{
@@ -9628,7 +9625,7 @@
9625
"zh-chs": "创建的设备组:{0}",
9626
"zh-cht": "創建的設備組:{0}",
9627
"xloc": [
9631
- "default.handlebars->29->1643"
9628
+ "default.handlebars->29->1636"
9629
]
9630
},
9631
{
@@ -9668,7 +9665,7 @@
9665
"zh-chs": "创建",
9666
"zh-cht": "創建",
9667
"xloc": [
9671
- "default.handlebars->29->1922"
9668
+ "default.handlebars->29->1915"
9669
]
9670
},
9671
{
@@ -10080,7 +10077,7 @@
10077
"zh-chs": "停用客户端控制模式(CCM)",
10078
"zh-cht": "停用客戶端控制模式(CCM)",
10079
"xloc": [
10083
- "default.handlebars->29->1388"
10080
+ "default.handlebars->29->1385"
10081
]
10082
},
10083
{
@@ -10121,10 +10118,10 @@
10118
"zh-chs": "默认",
10119
"zh-cht": "默認",
10120
"xloc": [
10124
- "default.handlebars->29->1784",
10125
- "default.handlebars->29->1832",
10126
- "default.handlebars->29->1843",
10127
- "default.handlebars->29->1911"
10121
+ "default.handlebars->29->1777",
10122
+ "default.handlebars->29->1825",
10123
+ "default.handlebars->29->1836",
10124
+ "default.handlebars->29->1904"
10125
]
10126
},
10127
{
@@ -10148,7 +10145,7 @@
10145
"default-mobile.handlebars->9->307",
10146
"default-mobile.handlebars->container->page_content->column_l->p10->p10files->p13toolbar->1->2->1->1",
10147
"default-mobile.handlebars->container->page_content->column_l->p5->p5myfiles->p5toolbar->1->0->1->1",
10151
- "default.handlebars->29->1574",
10148
+ "default.handlebars->29->1567",
10149
"default.handlebars->29->529",
10150
"default.handlebars->29->900",
10151
"default.handlebars->container->column_l->p13->p13toolbar->1->2->1->3",
@@ -10198,7 +10195,7 @@
10195
"zh-chs": "删除帐户",
10196
"zh-cht": "刪除帳戶",
10197
"xloc": [
10201
- "default.handlebars->29->1751"
10198
+ "default.handlebars->29->1744"
10199
]
10200
},
10201
{
@@ -10242,7 +10239,7 @@
10239
"default-mobile.handlebars->9->411",
10240
"default-mobile.handlebars->9->414",
10241
"default.handlebars->29->1381",
10245
- "default.handlebars->29->1411"
10242
+ "default.handlebars->29->1404"
10243
]
10244
},
10245
{
@@ -10303,7 +10300,7 @@
10300
"zh-chs": "删除用户",
10301
"zh-cht": "刪除用戶",
10302
"xloc": [
10306
- "default.handlebars->29->1961"
10303
+ "default.handlebars->29->1954"
10304
]
10305
},
10306
{
@@ -10323,8 +10320,8 @@
10320
"zh-chs": "删除用户群组",
10321
"zh-cht": "刪除用戶群組",
10322
"xloc": [
10326
- "default.handlebars->29->1881",
10327
- "default.handlebars->29->1891"
10323
+ "default.handlebars->29->1874",
10324
+ "default.handlebars->29->1884"
10325
]
10326
},
10327
{
@@ -10344,7 +10341,7 @@
10341
"zh-chs": "删除用户群组",
10342
"zh-cht": "刪除用戶群組",
10343
"xloc": [
10347
- "default.handlebars->29->1830"
10344
+ "default.handlebars->29->1823"
10345
]
10346
},
10347
{
@@ -10364,7 +10361,7 @@
10361
"zh-chs": "删除用户{0}",
10362
"zh-cht": "刪除用戶{0}",
10363
"xloc": [
10367
- "default.handlebars->29->1984"
10364
+ "default.handlebars->29->1977"
10365
]
10366
},
10367
{
@@ -10385,7 +10382,7 @@
10382
"zh-cht": "刪除帳戶",
10383
"xloc": [
10384
"default-mobile.handlebars->container->page_content->column_l->p3->p3info->1->p3AccountActions->p2AccountActions->3->9->0",
10388
- "default.handlebars->29->1747",
10385
+ "default.handlebars->29->1740",
10386
"default.handlebars->container->column_l->p2->p2info->p2AccountActions->3->p2AccountPassActions->7"
10387
]
10388
},
@@ -10426,7 +10423,7 @@
10423
"zh-chs": "删除群组",
10424
"zh-cht": "刪除群組",
10425
"xloc": [
10429
- "default.handlebars->29->1826"
10426
+ "default.handlebars->29->1819"
10427
]
10428
},
10429
{
@@ -10466,7 +10463,7 @@
10463
"zh-chs": "递归删除:“{0}”,{1}个元素已删除",
10464
"zh-cht": "遞歸刪除:“{0}”,{1}個元素已刪除",
10465
"xloc": [
10469
- "default.handlebars->29->1634"
10466
+ "default.handlebars->29->1627"
10467
]
10468
},
10469
{
@@ -10488,7 +10485,7 @@
10485
"xloc": [
10486
"default-mobile.handlebars->9->124",
10487
"default-mobile.handlebars->9->309",
10491
- "default.handlebars->29->1576",
10488
+ "default.handlebars->29->1569",
10489
"default.handlebars->29->902"
10490
]
10491
},
@@ -10509,7 +10506,7 @@
10506
"zh-chs": "删除用户群组{0}?",
10507
"zh-cht": "刪除用戶群組{0}?",
10508
"xloc": [
10512
- "default.handlebars->29->1889"
10509
+ "default.handlebars->29->1882"
10510
]
10511
},
10512
{
@@ -10531,7 +10528,7 @@
10528
"xloc": [
10529
"default-mobile.handlebars->9->123",
10530
"default-mobile.handlebars->9->308",
10534
- "default.handlebars->29->1575",
10531
+ "default.handlebars->29->1568",
10532
"default.handlebars->29->901"
10533
]
10534
},
@@ -10572,7 +10569,7 @@
10569
"zh-chs": "删除:“{0}”",
10570
"zh-cht": "刪除:“{0}”",
10571
"xloc": [
10575
- "default.handlebars->29->1633"
10572
+ "default.handlebars->29->1626"
10573
]
10574
},
10575
{
@@ -10592,7 +10589,7 @@
10589
"zh-chs": "删除:“{0}”,{1}个元素已删除",
10590
"zh-cht": "刪除:“{0}”,已刪除{1}個元素",
10591
"xloc": [
10595
- "default.handlebars->29->1635"
10592
+ "default.handlebars->29->1628"
10593
]
10594
},
10595
{
@@ -10711,11 +10708,11 @@
10708
"default-mobile.handlebars->9->416",
10709
"default.handlebars->29->1289",
10710
"default.handlebars->29->1326",
10714
- "default.handlebars->29->1413",
10715
- "default.handlebars->29->1835",
10716
- "default.handlebars->29->1845",
10717
- "default.handlebars->29->1846",
10718
- "default.handlebars->29->1887",
10711
+ "default.handlebars->29->1406",
10712
+ "default.handlebars->29->1828",
10713
+ "default.handlebars->29->1838",
10714
+ "default.handlebars->29->1839",
10715
+ "default.handlebars->29->1880",
10716
"default.handlebars->29->570",
10717
"default.handlebars->29->571",
10718
"default.handlebars->29->77",
@@ -10760,8 +10757,8 @@
10757
"zh-cht": "桌面",
10758
"xloc": [
10759
"default-mobile.handlebars->9->261",
10763
- "default.handlebars->29->1419",
10764
- "default.handlebars->29->2031",
10760
+ "default.handlebars->29->1412",
10761
+ "default.handlebars->29->2024",
10762
"default.handlebars->29->535",
10763
"default.handlebars->29->872",
10764
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevDesktop",
@@ -10807,8 +10804,8 @@
10804
"zh-cht": "桌面通知",
10805
"xloc": [
10806
"default.handlebars->29->1340",
10810
- "default.handlebars->29->1850",
10811
- "default.handlebars->29->1937",
10807
+ "default.handlebars->29->1843",
10808
+ "default.handlebars->29->1930",
10809
"default.handlebars->29->606"
10810
]
10811
},
@@ -10830,8 +10827,8 @@
10827
"zh-cht": "桌面提示",
10828
"xloc": [
10829
"default.handlebars->29->1339",
10833
- "default.handlebars->29->1849",
10834
- "default.handlebars->29->1936",
10830
+ "default.handlebars->29->1842",
10831
+ "default.handlebars->29->1929",
10832
"default.handlebars->29->605"
10833
]
10834
},
@@ -10853,8 +10850,8 @@
10850
"zh-cht": "桌面提示+工具欄",
10851
"xloc": [
10852
"default.handlebars->29->1337",
10856
- "default.handlebars->29->1847",
10857
- "default.handlebars->29->1934",
10853
+ "default.handlebars->29->1840",
10854
+ "default.handlebars->29->1927",
10855
"default.handlebars->29->603"
10856
]
10857
},
@@ -10897,8 +10894,8 @@
10894
"zh-cht": "桌面工具欄",
10895
"xloc": [
10896
"default.handlebars->29->1338",
10900
- "default.handlebars->29->1848",
10901
- "default.handlebars->29->1935",
10897
+ "default.handlebars->29->1841",
10898
+ "default.handlebars->29->1928",
10899
"default.handlebars->29->604"
10900
]
10901
},
@@ -11002,9 +10999,9 @@
10999
"zh-chs": "设备",
11000
"zh-cht": "裝置",
11001
"xloc": [
11005
- "default.handlebars->29->1442",
11002
+ "default.handlebars->29->1435",
11003
"default.handlebars->29->186",
11007
- "default.handlebars->29->2003",
11004
+ "default.handlebars->29->1996",
11005
"default.handlebars->container->column_l->p1->devListToolbarSpan->1->0->9->devListToolbarSort->sortselect->5"
11006
]
11007
},
@@ -11053,14 +11050,14 @@
11050
"zh-chs": "设备组",
11051
"zh-cht": "裝置群",
11052
"xloc": [
11056
- "default.handlebars->29->1437",
11057
- "default.handlebars->29->1440",
11058
- "default.handlebars->29->1441",
11059
- "default.handlebars->29->1698",
11060
- "default.handlebars->29->1873",
11061
- "default.handlebars->29->1879",
11062
- "default.handlebars->29->1991",
11063
- "default.handlebars->29->2040"
11053
+ "default.handlebars->29->1430",
11054
+ "default.handlebars->29->1433",
11055
+ "default.handlebars->29->1434",
11056
+ "default.handlebars->29->1691",
11057
+ "default.handlebars->29->1866",
11058
+ "default.handlebars->29->1872",
11059
+ "default.handlebars->29->1984",
11060
+ "default.handlebars->29->2033"
11061
]
11062
},
11063
{
@@ -11081,7 +11078,7 @@
11078
"zh-cht": "裝置群用戶",
11079
"xloc": [
11080
"default-mobile.handlebars->9->462",
11084
- "default.handlebars->29->1506"
11081
+ "default.handlebars->29->1499"
11082
]
11083
},
11084
{
@@ -11102,11 +11099,11 @@
11099
"zh-cht": "裝置群",
11100
"xloc": [
11101
"default-mobile.handlebars->container->page_content->column_l->p3->p3info->1->3",
11105
- "default.handlebars->29->1714",
11106
- "default.handlebars->29->1820",
11107
- "default.handlebars->29->1860",
11108
- "default.handlebars->29->1931",
11109
- "default.handlebars->29->2075",
11102
+ "default.handlebars->29->1707",
11103
+ "default.handlebars->29->1813",
11104
+ "default.handlebars->29->1853",
11105
+ "default.handlebars->29->1924",
11106
+ "default.handlebars->29->2068",
11107
"default.handlebars->container->column_l->p2->p2info->7"
11108
]
11109
},
@@ -11188,7 +11185,7 @@
11185
"zh-cht": "裝置名稱",
11186
"xloc": [
11187
"default-mobile.handlebars->9->284",
11191
- "default.handlebars->29->2039",
11188
+ "default.handlebars->29->2032",
11189
"default.handlebars->29->291",
11190
"default.handlebars->29->825",
11191
"player.handlebars->3->9"
@@ -11253,7 +11250,7 @@
11250
"zh-cht": "裝置連接。",
11251
"xloc": [
11252
"default.handlebars->29->1257",
11256
- "default.handlebars->29->1527"
11253
+ "default.handlebars->29->1520"
11254
]
11255
},
11256
{
@@ -11274,7 +11271,7 @@
11271
"zh-cht": "裝置斷開連接。",
11272
"xloc": [
11273
"default.handlebars->29->1258",
11277
- "default.handlebars->29->1528"
11274
+ "default.handlebars->29->1521"
11275
]
11276
},
11277
{
@@ -11294,7 +11291,7 @@
11291
"zh-chs": "创建的设备组:{0}",
11292
"zh-cht": "設備組已創建:{0}",
11293
"xloc": [
11297
- "default.handlebars->29->1664"
11294
+ "default.handlebars->29->1657"
11295
]
11296
},
11297
{
@@ -11314,7 +11311,7 @@
11311
"zh-chs": "设备组已删除:{0}",
11312
"zh-cht": "設備組已刪除:{0}",
11313
"xloc": [
11317
- "default.handlebars->29->1665"
11314
+ "default.handlebars->29->1658"
11315
]
11316
},
11317
{
@@ -11334,7 +11331,7 @@
11331
"zh-chs": "设备组成员身份已更改:{0}",
11332
"zh-cht": "設備組成員身份已更改:{0}",
11333
"xloc": [
11337
- "default.handlebars->29->1666"
11334
+ "default.handlebars->29->1659"
11335
]
11336
},
11337
{
@@ -11374,7 +11371,7 @@
11371
"zh-chs": "设备组通知已更改",
11372
"zh-cht": "設備組通知已更改",
11373
"xloc": [
11377
- "default.handlebars->29->1661"
11374
+ "default.handlebars->29->1654"
11375
]
11376
},
11377
{
@@ -11394,7 +11391,7 @@
11391
"zh-chs": "未删除的设备组:{0}",
11392
"zh-cht": "未刪除的設備組:{0}",
11393
"xloc": [
11397
- "default.handlebars->29->1644"
11394
+ "default.handlebars->29->1637"
11395
]
11396
},
11397
{
@@ -11787,7 +11784,7 @@
11784
"zh-chs": "设备请求激活Intel(R)AMT ACM,FQDN:{0}",
11785
"zh-cht": "設備請求激活Intel(R)AMT ACM,FQDN:{0}",
11786
"xloc": [
11790
- "default.handlebars->29->1646"
11787
+ "default.handlebars->29->1639"
11788
]
11789
},
11790
{
@@ -11824,8 +11821,8 @@
11821
"zh-chs": "设备",
11822
"zh-cht": "裝置",
11823
"xloc": [
11827
- "default.handlebars->29->1821",
11828
- "default.handlebars->29->1861"
11824
+ "default.handlebars->29->1814",
11825
+ "default.handlebars->29->1854"
11826
]
11827
},
11828
{
@@ -11865,7 +11862,7 @@
11862
"zh-chs": "禁用的电子邮件两因素身份验证",
11863
"zh-cht": "禁用的電子郵件兩因素身份驗證",
11864
"xloc": [
11868
- "default.handlebars->29->1677"
11865
+ "default.handlebars->29->1670"
11866
]
11867
},
11868
{
@@ -12065,7 +12062,7 @@
12062
"zh-chs": "显示公共连结",
12063
"zh-cht": "顯示公共鏈結",
12064
"xloc": [
12068
- "default.handlebars->29->1547"
12065
+ "default.handlebars->29->1540"
12066
]
12067
},
12068
{
@@ -12085,7 +12082,7 @@
12082
"zh-chs": "显示消息框,标题= “{0}”,消息= “{1}”",
12083
"zh-cht": "顯示消息框,標題= “{0}”,消息= “{1}”",
12084
"xloc": [
12088
- "default.handlebars->29->1606"
12085
+ "default.handlebars->29->1599"
12086
]
12087
},
12088
{
@@ -12105,7 +12102,7 @@
12102
"zh-chs": "显示吐司消息,标题= “{0}”,消息= “{1}”",
12103
"zh-cht": "顯示吐司消息,標題= “{0}”,消息= “{1}”",
12104
"xloc": [
12108
- "default.handlebars->29->1614"
12105
+ "default.handlebars->29->1607"
12106
]
12107
},
12108
{
@@ -12125,7 +12122,7 @@
12122
"zh-chs": "什么都不做",
12123
"zh-cht": "什麼都不做",
12124
"xloc": [
12128
- "default.handlebars->29->1394"
12125
+ "default.handlebars->29->1391"
12126
]
12127
},
12128
{
@@ -12145,10 +12142,10 @@
12142
"zh-chs": "域",
12143
"zh-cht": "域",
12144
"xloc": [
12148
- "default.handlebars->29->1785",
12149
- "default.handlebars->29->1833",
12150
- "default.handlebars->29->1842",
12151
- "default.handlebars->29->1910",
12145
+ "default.handlebars->29->1778",
12146
+ "default.handlebars->29->1826",
12147
+ "default.handlebars->29->1835",
12148
+ "default.handlebars->29->1903",
12149
"mstsc.handlebars->main->1->3->1->2->1->0",
12150
"mstsc.handlebars->main->1->3->1->2->3"
12151
]
@@ -12170,8 +12167,7 @@
12167
"zh-chs": "不要配置",
12168
"zh-cht": "不要配置",
12169
"xloc": [
12173
- "default.handlebars->29->1398",
12174
- "default.handlebars->29->1403"
12170
+ "default.handlebars->29->1395"
12171
]
12172
},
12173
{
@@ -12191,7 +12187,7 @@
12187
"zh-chs": "不要连接到服务器",
12188
"zh-cht": "不要連接到伺服器",
12189
"xloc": [
12194
- "default.handlebars->29->1399"
12190
+ "default.handlebars->29->1396"
12191
]
12192
},
12193
{
@@ -12411,7 +12407,7 @@
12407
"zh-chs": "下载报告",
12408
"zh-cht": "下載報告",
12409
"xloc": [
12414
- "default.handlebars->29->1703",
12410
+ "default.handlebars->29->1696",
12411
"default.handlebars->container->column_l->p3->3->1->0->3"
12412
]
12413
},
@@ -12612,7 +12608,7 @@
12608
"zh-chs": "使用以下一种档案格式下载事件列表。",
12609
"zh-cht": "使用以下一種檔案格式下載事件列表。",
12610
"xloc": [
12615
- "default.handlebars->29->1704"
12611
+ "default.handlebars->29->1697"
12612
]
12613
},
12614
{
@@ -12632,7 +12628,7 @@
12628
"zh-chs": "使用以下一种档案格式下载用户列表。",
12629
"zh-cht": "使用以下一種檔案格式下載用戶列表。",
12630
"xloc": [
12635
- "default.handlebars->29->1769"
12631
+ "default.handlebars->29->1762"
12632
]
12633
},
12634
{
@@ -12713,7 +12709,7 @@
12709
"zh-chs": "下载:“{0}”",
12710
"zh-cht": "下載:“{0}”",
12711
"xloc": [
12716
- "default.handlebars->29->1637"
12712
+ "default.handlebars->29->1630"
12713
]
12714
},
12715
{
@@ -12753,7 +12749,7 @@
12749
"zh-chs": "代理重复",
12750
"zh-cht": "代理重複",
12751
"xloc": [
12756
- "default.handlebars->29->2071"
12752
+ "default.handlebars->29->2064"
12753
]
12754
},
12755
{
@@ -12793,7 +12789,7 @@
12789
"zh-chs": "复制用户组",
12790
"zh-cht": "複製用戶群",
12791
"xloc": [
12796
- "default.handlebars->29->1837"
12792
+ "default.handlebars->29->1830"
12793
]
12794
},
12795
{
@@ -12830,8 +12826,8 @@
12826
"zh-chs": "持续时间",
12827
"zh-cht": "持續時間",
12828
"xloc": [
12833
- "default.handlebars->29->2025",
12834
- "default.handlebars->29->2045",
12829
+ "default.handlebars->29->2018",
12830
+ "default.handlebars->29->2038",
12831
"player.handlebars->3->2"
12832
]
12833
},
@@ -12852,7 +12848,7 @@
12848
"zh-chs": "在激活期间,代理将有权取得管理员密码信息。",
12849
"zh-cht": "在啟動期間,代理將有權取得管理員密碼訊息。",
12850
"xloc": [
12855
- "default.handlebars->29->1408"
12851
+ "default.handlebars->29->1401"
12852
]
12853
},
12854
{
@@ -13185,10 +13181,10 @@
13181
"default-mobile.handlebars->9->417",
13182
"default-mobile.handlebars->9->419",
13183
"default-mobile.handlebars->9->439",
13188
- "default.handlebars->29->1414",
13189
- "default.handlebars->29->1446",
13190
- "default.handlebars->29->1470",
13191
- "default.handlebars->29->1482"
13184
+ "default.handlebars->29->1407",
13185
+ "default.handlebars->29->1439",
13186
+ "default.handlebars->29->1463",
13187
+ "default.handlebars->29->1475"
13188
]
13189
},
13190
{
@@ -13208,7 +13204,7 @@
13204
"zh-chs": "编辑设备组功能",
13205
"zh-cht": "編輯裝置群功能",
13206
"xloc": [
13211
- "default.handlebars->29->1432"
13207
+ "default.handlebars->29->1425"
13208
]
13209
},
13210
{
@@ -13228,8 +13224,8 @@
13224
"zh-chs": "编辑设备组权限",
13225
"zh-cht": "編輯裝置群權限",
13226
"xloc": [
13231
- "default.handlebars->29->1467",
13232
- "default.handlebars->29->1479"
13227
+ "default.handlebars->29->1460",
13228
+ "default.handlebars->29->1472"
13229
]
13230
},
13231
{
@@ -13249,7 +13245,7 @@
13245
"zh-chs": "编辑设备组用户同意",
13246
"zh-cht": "編輯裝置群用戶同意",
13247
"xloc": [
13252
- "default.handlebars->29->1415"
13248
+ "default.handlebars->29->1408"
13249
]
13250
},
13251
{
@@ -13270,7 +13266,7 @@
13266
"zh-cht": "編輯裝置筆記",
13267
"xloc": [
13268
"default-mobile.handlebars->9->431",
13273
- "default.handlebars->29->1459"
13269
+ "default.handlebars->29->1452"
13270
]
13271
},
13272
{
@@ -13290,8 +13286,8 @@
13286
"zh-chs": "编辑设备权限",
13287
"zh-cht": "編輯裝置權限",
13288
"xloc": [
13293
- "default.handlebars->29->1472",
13294
- "default.handlebars->29->1474"
13289
+ "default.handlebars->29->1465",
13290
+ "default.handlebars->29->1467"
13291
]
13292
},
13293
{
@@ -13331,7 +13327,7 @@
13327
"zh-chs": "编辑设备用户同意",
13328
"zh-cht": "編輯裝置用戶同意",
13329
"xloc": [
13334
- "default.handlebars->29->1417"
13330
+ "default.handlebars->29->1410"
13331
]
13332
},
13333
{
@@ -13395,7 +13391,7 @@
13391
"zh-cht": "編輯筆記",
13392
"xloc": [
13393
"default-mobile.handlebars->9->446",
13398
- "default.handlebars->29->1489"
13394
+ "default.handlebars->29->1482"
13395
]
13396
},
13397
{
@@ -13415,7 +13411,7 @@
13411
"zh-chs": "编辑用户同意",
13412
"zh-cht": "編輯用戶同意",
13413
"xloc": [
13418
- "default.handlebars->29->1416"
13414
+ "default.handlebars->29->1409"
13415
]
13416
},
13417
{
@@ -13435,7 +13431,7 @@
13431
"zh-chs": "编辑用户设备组权限",
13432
"zh-cht": "編輯用戶裝置群權限",
13433
"xloc": [
13438
- "default.handlebars->29->1480"
13434
+ "default.handlebars->29->1473"
13435
]
13436
},
13437
{
@@ -13455,7 +13451,7 @@
13451
"zh-chs": "编辑用户设备权限",
13452
"zh-cht": "編輯用戶裝置權限",
13453
"xloc": [
13458
- "default.handlebars->29->1475"
13454
+ "default.handlebars->29->1468"
13455
]
13456
},
13457
{
@@ -13475,7 +13471,7 @@
13471
"zh-chs": "编辑用户组",
13472
"zh-cht": "編輯用戶群",
13473
"xloc": [
13478
- "default.handlebars->29->1888"
13474
+ "default.handlebars->29->1881"
13475
]
13476
},
13477
{
@@ -13495,14 +13491,14 @@
13491
"zh-chs": "编辑用户组设备权限",
13492
"zh-cht": "編輯用戶群裝置權限",
13493
"xloc": [
13498
- "default.handlebars->29->1477"
13494
+ "default.handlebars->29->1470"
13495
]
13496
},
13497
{
13498
"en": "Edit User Group User Consent",
13499
"nl": "Bewerk groepsgebruikerstoestemming",
13500
"xloc": [
13505
- "default.handlebars->29->1418"
13501
+ "default.handlebars->29->1411"
13502
]
13503
},
13504
{
@@ -13564,11 +13560,11 @@
13560
"zh-cht": "電郵",
13561
"xloc": [
13562
"default-mobile.handlebars->9->78",
13567
- "default.handlebars->29->1787",
13568
- "default.handlebars->29->1914",
13569
- "default.handlebars->29->1916",
13570
- "default.handlebars->29->1956",
13571
- "default.handlebars->29->1972",
13563
+ "default.handlebars->29->1780",
13564
+ "default.handlebars->29->1907",
13565
+ "default.handlebars->29->1909",
13566
+ "default.handlebars->29->1949",
13567
+ "default.handlebars->29->1965",
13568
"default.handlebars->29->341",
13569
"login-mobile.handlebars->5->42",
13570
"login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->3",
@@ -13722,7 +13718,7 @@
13718
"zh-chs": "电邮未验证",
13719
"zh-cht": "電郵未驗證",
13720
"xloc": [
13725
- "default.handlebars->29->1733"
13721
+ "default.handlebars->29->1726"
13722
]
13723
},
13724
{
@@ -13742,8 +13738,8 @@
13738
"zh-chs": "电邮已验证",
13739
"zh-cht": "電郵已驗證",
13740
"xloc": [
13745
- "default.handlebars->29->1734",
13746
- "default.handlebars->29->1908"
13741
+ "default.handlebars->29->1727",
13742
+ "default.handlebars->29->1901"
13743
]
13744
},
13745
{
@@ -13763,7 +13759,7 @@
13759
"zh-chs": "电邮已验证。",
13760
"zh-cht": "電郵已驗證。",
13761
"xloc": [
13766
- "default.handlebars->29->1793"
13762
+ "default.handlebars->29->1786"
13763
]
13764
},
13765
{
@@ -13783,7 +13779,7 @@
13779
"zh-chs": "电邮未验证",
13780
"zh-cht": "電郵未驗證",
13781
"xloc": [
13786
- "default.handlebars->29->1909"
13782
+ "default.handlebars->29->1902"
13783
]
13784
},
13785
{
@@ -13847,7 +13843,7 @@
13843
"zh-chs": "已通过电邮验证,并且需要重置密码。",
13844
"zh-cht": "已通過電郵驗證,並且需要重置密碼。",
13845
"xloc": [
13850
- "default.handlebars->29->1794"
13846
+ "default.handlebars->29->1787"
13847
]
13848
},
13849
{
@@ -13867,7 +13863,7 @@
13863
"zh-chs": "电邮/短信流量",
13864
"zh-cht": "電郵/短信流量",
13865
"xloc": [
13870
- "default.handlebars->29->2113"
13866
+ "default.handlebars->29->2106"
13867
]
13868
},
13869
{
@@ -13913,7 +13909,7 @@
13909
"zh-chs": "启用邀请代码",
13910
"zh-cht": "啟用邀請代碼",
13911
"xloc": [
13916
- "default.handlebars->29->1512"
13912
+ "default.handlebars->29->1505"
13913
]
13914
},
13915
{
@@ -13994,7 +13990,7 @@
13990
"zh-chs": "已启用",
13991
"zh-cht": "已啟用",
13992
"xloc": [
13997
- "default.handlebars->29->2047"
13993
+ "default.handlebars->29->2040"
13994
]
13995
},
13996
{
@@ -14014,7 +14010,7 @@
14010
"zh-chs": "启用电子邮件两因素身份验证",
14011
"zh-cht": "啟用電子郵件兩因素身份驗證",
14012
"xloc": [
14017
- "default.handlebars->29->1676"
14013
+ "default.handlebars->29->1669"
14014
]
14015
},
14016
{
@@ -14054,7 +14050,7 @@
14050
"zh-chs": "时间结束",
14051
"zh-cht": "時間結束",
14052
"xloc": [
14057
- "default.handlebars->29->2044"
14053
+ "default.handlebars->29->2037"
14054
]
14055
},
14056
{
@@ -14074,7 +14070,7 @@
14070
"zh-chs": "从{1}到{2},{3}秒结束了桌面会话“{0}”",
14071
"zh-cht": "從{1}到{2},{3}秒結束了桌面會話“{0}”",
14072
"xloc": [
14077
- "default.handlebars->29->1599"
14073
+ "default.handlebars->29->1592"
14074
]
14075
},
14076
{
@@ -14094,7 +14090,7 @@
14090
"zh-chs": "从{1}到{2},{3}秒结束了文件管理会话“{0}”",
14091
"zh-cht": "從{1}到{2},{3}秒結束了文件管理會話“{0}”",
14092
"xloc": [
14097
- "default.handlebars->29->1600"
14093
+ "default.handlebars->29->1593"
14094
]
14095
},
14096
{
@@ -14114,7 +14110,7 @@
14110
"zh-chs": "从{1}到{2},{3}秒结束了中继会话“{0}”",
14111
"zh-cht": "從{1}到{2},{3}秒結束了中繼會話“{0}”",
14112
"xloc": [
14117
- "default.handlebars->29->1597"
14113
+ "default.handlebars->29->1590"
14114
]
14115
},
14116
{
@@ -14134,7 +14130,7 @@
14130
"zh-chs": "从{1}到{2},{3}秒结束了终端会话“{0}”",
14131
"zh-cht": "從{1}到{2},{3}秒結束了終端會話“{0}”",
14132
"xloc": [
14137
- "default.handlebars->29->1598"
14133
+ "default.handlebars->29->1591"
14134
]
14135
},
14136
{
@@ -14435,7 +14431,7 @@
14431
"zh-chs": "输入管理领域名称的逗号分隔列表。",
14432
"zh-cht": "輸入管理領域名稱的逗號分隔列表。",
14433
"xloc": [
14438
- "default.handlebars->29->1798"
14434
+ "default.handlebars->29->1791"
14435
]
14436
},
14437
{
@@ -14658,7 +14654,7 @@
14654
"zh-chs": "事件列表输出",
14655
"zh-cht": "事件列表輸出",
14656
"xloc": [
14661
- "default.handlebars->29->1709"
14657
+ "default.handlebars->29->1702"
14658
]
14659
},
14660
{
@@ -14850,7 +14846,7 @@
14846
"zh-chs": "外部",
14847
"zh-cht": "外部",
14848
"xloc": [
14853
- "default.handlebars->29->2098"
14849
+ "default.handlebars->29->2091"
14850
]
14851
},
14852
{
@@ -14950,7 +14946,7 @@
14946
"zh-chs": "本地用户拒绝后无法启动远程桌面",
14947
"zh-cht": "本地用戶拒絕後無法啟動遠程桌面",
14948
"xloc": [
14953
- "default.handlebars->29->1622"
14949
+ "default.handlebars->29->1615"
14950
]
14951
},
14952
{
@@ -14970,7 +14966,7 @@
14966
"zh-chs": "本地用户拒绝后无法启动远程文件",
14967
"zh-cht": "本地用戶拒絕後無法啟動遠程文件",
14968
"xloc": [
14973
- "default.handlebars->29->1629"
14969
+ "default.handlebars->29->1622"
14970
]
14971
},
14972
{
@@ -15198,8 +15194,8 @@
15194
"xloc": [
15195
"default-mobile.handlebars->9->171",
15196
"default-mobile.handlebars->9->262",
15201
- "default.handlebars->29->1426",
15202
- "default.handlebars->29->2032",
15197
+ "default.handlebars->29->1419",
15198
+ "default.handlebars->29->2025",
15199
"default.handlebars->29->258",
15200
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevFiles",
15201
"default.handlebars->contextMenu->cxfiles"
@@ -15243,8 +15239,8 @@
15239
"zh-cht": "檔案通知",
15240
"xloc": [
15241
"default.handlebars->29->1344",
15246
- "default.handlebars->29->1854",
15247
- "default.handlebars->29->1941",
15242
+ "default.handlebars->29->1847",
15243
+ "default.handlebars->29->1934",
15244
"default.handlebars->29->610"
15245
]
15246
},
@@ -15266,8 +15262,8 @@
15262
"zh-cht": "檔案提示",
15263
"xloc": [
15264
"default.handlebars->29->1343",
15269
- "default.handlebars->29->1853",
15270
- "default.handlebars->29->1940",
15265
+ "default.handlebars->29->1846",
15266
+ "default.handlebars->29->1933",
15267
"default.handlebars->29->609"
15268
]
15269
},
@@ -15311,7 +15307,7 @@
15307
"zh-chs": "录制会话已完成,{0}秒",
15308
"zh-cht": "錄製會話已完成,{0}秒",
15309
"xloc": [
15314
- "default.handlebars->29->1595"
15310
+ "default.handlebars->29->1588"
15311
]
15312
},
15313
{
@@ -15478,8 +15474,8 @@
15474
"zh-chs": "下次登录时强制重置密码。",
15475
"zh-cht": "下次登入時強制重置密碼。",
15476
"xloc": [
15481
- "default.handlebars->29->1792",
15482
- "default.handlebars->29->1981"
15477
+ "default.handlebars->29->1785",
15478
+ "default.handlebars->29->1974"
15479
]
15480
},
15481
{
@@ -15562,7 +15558,7 @@
15558
"zh-chs": "格式化",
15559
"zh-cht": "格式化",
15560
"xloc": [
15565
- "default.handlebars->29->1700"
15561
+ "default.handlebars->29->1693"
15562
]
15563
},
15564
{
@@ -15603,8 +15599,8 @@
15599
"zh-chs": "自由",
15600
"zh-cht": "自由",
15601
"xloc": [
15606
- "default.handlebars->29->2056",
15607
- "default.handlebars->29->2058"
15602
+ "default.handlebars->29->2049",
15603
+ "default.handlebars->29->2051"
15604
]
15605
},
15606
{
@@ -15830,8 +15826,8 @@
15826
"default-mobile.handlebars->9->418",
15827
"default-mobile.handlebars->9->438",
15828
"default.handlebars->29->1298",
15833
- "default.handlebars->29->1445",
15834
- "default.handlebars->29->1804"
15829
+ "default.handlebars->29->1438",
15830
+ "default.handlebars->29->1797"
15831
]
15832
},
15833
{
@@ -15851,7 +15847,7 @@
15847
"zh-chs": "完整管理员(保留所有权利)",
15848
"zh-cht": "完整管理員(保留所有權利)",
15849
"xloc": [
15854
- "default.handlebars->29->1481"
15850
+ "default.handlebars->29->1474"
15851
]
15852
},
15853
{
@@ -15950,7 +15946,7 @@
15946
"zh-chs": "完整管理员",
15947
"zh-cht": "完整管理員",
15948
"xloc": [
15953
- "default.handlebars->29->1902"
15949
+ "default.handlebars->29->1895"
15950
]
15951
},
15952
{
@@ -16338,7 +16334,7 @@
16334
"zh-chs": "正在获取剪贴板内容,{0}个字节",
16335
"zh-cht": "正在獲取剪貼板內容,{0}個字節",
16336
"xloc": [
16341
- "default.handlebars->29->1609"
16337
+ "default.handlebars->29->1602"
16338
]
16339
},
16340
{
@@ -16569,8 +16565,8 @@
16565
"zh-chs": "集体指令",
16566
"zh-cht": "集體指令",
16567
"xloc": [
16572
- "default.handlebars->29->1748",
16573
- "default.handlebars->29->1827",
16568
+ "default.handlebars->29->1741",
16569
+ "default.handlebars->29->1820",
16570
"default.handlebars->29->470",
16571
"default.handlebars->container->column_l->p1->devListToolbarSpan->1->0->devListToolbar",
16572
"default.handlebars->container->column_l->p4->3->1->0->3->3",
@@ -16594,7 +16590,7 @@
16590
"zh-chs": "通过...分组",
16591
"zh-cht": "通過...分群",
16592
"xloc": [
16597
- "default.handlebars->29->1696"
16593
+ "default.handlebars->29->1689"
16594
]
16595
},
16596
{
@@ -16614,7 +16610,7 @@
16610
"zh-chs": "组标识符",
16611
"zh-cht": "群標識符",
16612
"xloc": [
16617
- "default.handlebars->29->1844"
16613
+ "default.handlebars->29->1837"
16614
]
16615
},
16616
{
@@ -16634,7 +16630,7 @@
16630
"zh-chs": "群组成员",
16631
"zh-cht": "群組成員",
16632
"xloc": [
16637
- "default.handlebars->29->1865"
16633
+ "default.handlebars->29->1858"
16634
]
16635
},
16636
{
@@ -16654,7 +16650,7 @@
16650
"zh-chs": "用户{0}的群组权限。",
16651
"zh-cht": "用戶{0}的群組權限。",
16652
"xloc": [
16657
- "default.handlebars->29->1444"
16653
+ "default.handlebars->29->1437"
16654
]
16655
},
16656
{
@@ -16674,7 +16670,7 @@
16670
"zh-chs": "{0}的群组权限。",
16671
"zh-cht": "{0}的群組權限。",
16672
"xloc": [
16677
- "default.handlebars->29->1443"
16673
+ "default.handlebars->29->1436"
16674
]
16675
},
16676
{
@@ -16865,7 +16861,7 @@
16861
"zh-chs": "堆总数",
16862
"zh-cht": "堆總數",
16863
"xloc": [
16868
- "default.handlebars->29->2100"
16864
+ "default.handlebars->29->2093"
16865
]
16866
},
16867
{
@@ -16885,7 +16881,7 @@
16881
"zh-chs": "堆使用",
16882
"zh-cht": "堆使用",
16883
"xloc": [
16888
- "default.handlebars->29->2099"
16884
+ "default.handlebars->29->2092"
16885
]
16886
},
16887
{
@@ -16940,7 +16936,7 @@
16936
"en": "Help Requested, user: {0}, details: {1}",
16937
"nl": "Hulp verzoek van, user: {0}, details: {1}",
16938
"xloc": [
16943
- "default.handlebars->29->1686"
16939
+ "default.handlebars->29->1679"
16940
]
16941
},
16942
{
@@ -17201,7 +17197,7 @@
17197
"zh-cht": "保存{0}項目{1}給{2}",
17198
"xloc": [
17199
"default-mobile.handlebars->9->129",
17204
- "default.handlebars->29->1582"
17200
+ "default.handlebars->29->1575"
17201
]
17202
},
17203
{
@@ -17918,8 +17914,8 @@
17914
"zh-chs": "安装类型",
17915
"zh-cht": "安裝方式",
17916
"xloc": [
17917
+ "default.handlebars->29->1507",
17918
"default.handlebars->29->1514",
17922
- "default.handlebars->29->1521",
17919
"default.handlebars->29->356",
17920
"default.handlebars->29->370",
17921
"default.handlebars->29->384"
@@ -17963,7 +17959,7 @@
17959
"zh-chs": "英特尔AMT",
17960
"zh-cht": "英特爾AMT",
17961
"xloc": [
17966
- "default.handlebars->29->2096"
17962
+ "default.handlebars->29->2089"
17963
]
17964
},
17965
{
@@ -18008,9 +18004,9 @@
18004
"default-mobile.handlebars->9->246",
18005
"default.handlebars->29->1351",
18006
"default.handlebars->29->1361",
18011
- "default.handlebars->29->1533",
18012
- "default.handlebars->29->1541",
18013
- "default.handlebars->29->2118",
18007
+ "default.handlebars->29->1526",
18008
+ "default.handlebars->29->1534",
18009
+ "default.handlebars->29->2111",
18010
"default.handlebars->29->537",
18011
"default.handlebars->29->592",
18012
"default.handlebars->29->620"
@@ -18159,7 +18155,7 @@
18155
"zh-chs": "英特尔®AMT政策",
18156
"zh-cht": "Intel® AMT政策",
18157
"xloc": [
18162
- "default.handlebars->29->1390"
18158
+ "default.handlebars->29->1387"
18159
]
18160
},
18161
{
@@ -18179,7 +18175,7 @@
18175
"zh-chs": "英特尔®AMT重定向",
18176
"zh-cht": "Intel® AMT重定向",
18177
"xloc": [
18182
- "default.handlebars->29->2034",
18178
+ "default.handlebars->29->2027",
18179
"player.handlebars->3->14"
18180
]
18181
},
@@ -18220,7 +18216,7 @@
18216
"zh-chs": "英特尔®AMT WSMAN",
18217
"zh-cht": "Intle® AMT WSMAN",
18218
"xloc": [
18223
- "default.handlebars->29->2033",
18219
+ "default.handlebars->29->2026",
18220
"player.handlebars->3->13"
18221
]
18222
},
@@ -18285,7 +18281,7 @@
18281
"zh-cht": "Intel® AMT桌面和串行事件。",
18282
"xloc": [
18283
"default.handlebars->29->1259",
18288
- "default.handlebars->29->1529"
18284
+ "default.handlebars->29->1522"
18285
]
18286
},
18287
{
@@ -18757,8 +18753,8 @@
18753
"zh-chs": "仅限互动",
18754
"zh-cht": "僅限互動",
18755
"xloc": [
18756
+ "default.handlebars->29->1510",
18757
"default.handlebars->29->1517",
18761
- "default.handlebars->29->1524",
18758
"default.handlebars->29->359",
18759
"default.handlebars->29->373",
18760
"default.handlebars->29->387"
@@ -18821,7 +18817,7 @@
18817
"zh-chs": "无效的设备组类型",
18818
"zh-cht": "無效的裝置群類型",
18819
"xloc": [
18824
- "default.handlebars->29->2070"
18820
+ "default.handlebars->29->2063"
18821
]
18822
},
18823
{
@@ -18841,7 +18837,7 @@
18837
"zh-chs": "无效的JSON",
18838
"zh-cht": "無效的JSON",
18839
"xloc": [
18844
- "default.handlebars->29->2064"
18840
+ "default.handlebars->29->2057"
18841
]
18842
},
18843
{
@@ -18861,8 +18857,8 @@
18857
"zh-chs": "无效的JSON档案格式。",
18858
"zh-cht": "無效的JSON檔案格式。",
18859
"xloc": [
18864
- "default.handlebars->29->1766",
18865
- "default.handlebars->29->1768"
18860
+ "default.handlebars->29->1759",
18861
+ "default.handlebars->29->1761"
18862
]
18863
},
18864
{
@@ -18882,7 +18878,7 @@
18878
"zh-chs": "无效的JSON档案:{0}。",
18879
"zh-cht": "無效的JSON檔案:{0}。",
18880
"xloc": [
18885
- "default.handlebars->29->1764"
18881
+ "default.handlebars->29->1757"
18882
]
18883
},
18884
{
@@ -18902,7 +18898,7 @@
18898
"zh-chs": "无效的PKCS签名",
18899
"zh-cht": "無效的PKCS簽名",
18900
"xloc": [
18905
- "default.handlebars->29->2062"
18901
+ "default.handlebars->29->2055"
18902
]
18903
},
18904
{
@@ -18922,7 +18918,7 @@
18918
"zh-chs": "無效的RSA密碼",
18919
"zh-cht": "無效的RSA密碼",
18920
"xloc": [
18925
- "default.handlebars->29->2063"
18921
+ "default.handlebars->29->2056"
18922
]
18923
},
18924
{
@@ -19049,7 +19045,7 @@
19045
"zh-chs": "使电邮无效",
19046
"zh-cht": "使電郵無效",
19047
"xloc": [
19052
- "default.handlebars->29->1742"
19048
+ "default.handlebars->29->1735"
19049
]
19050
},
19051
{
@@ -19126,7 +19122,7 @@
19122
"zh-chs": "任何人都可以使用邀请代码通过以下公共连接将设备加入该设备组:",
19123
"zh-cht": "任何人都可以使用邀請代碼通過以下公共鏈結將裝置加入該裝置群:",
19124
"xloc": [
19129
- "default.handlebars->29->1519"
19125
+ "default.handlebars->29->1512"
19126
]
19127
},
19128
{
@@ -19189,10 +19185,10 @@
19185
"zh-cht": "邀請碼",
19186
"xloc": [
19187
"default.handlebars->29->1355",
19188
+ "default.handlebars->29->1506",
19189
+ "default.handlebars->29->1511",
19190
"default.handlebars->29->1513",
19193
- "default.handlebars->29->1518",
19194
- "default.handlebars->29->1520",
19195
- "default.handlebars->29->1525"
19191
+ "default.handlebars->29->1518"
19192
]
19193
},
19194
{
@@ -19333,7 +19329,7 @@
19329
"zh-chs": "JSON",
19330
"zh-cht": "JSON",
19331
"xloc": [
19336
- "default.handlebars->29->1702"
19332
+ "default.handlebars->29->1695"
19333
]
19334
},
19335
{
@@ -19353,8 +19349,8 @@
19349
"zh-chs": "JSON格式",
19350
"zh-cht": "JSON格式",
19351
"xloc": [
19356
- "default.handlebars->29->1707",
19357
- "default.handlebars->29->1772",
19352
+ "default.handlebars->29->1700",
19353
+ "default.handlebars->29->1765",
19354
"default.handlebars->29->498"
19355
]
19356
},
@@ -19395,7 +19391,7 @@
19391
"zh-chs": "已加入桌面Multiplex会话",
19392
"zh-cht": "已加入桌面Multiplex會話",
19393
"xloc": [
19398
- "default.handlebars->29->1592"
19394
+ "default.handlebars->29->1585"
19395
]
19396
},
19397
{
@@ -19576,7 +19572,7 @@
19572
"zh-chs": "杀死进程{0}",
19573
"zh-cht": "殺死進程{0}",
19574
"xloc": [
19579
- "default.handlebars->29->1607"
19575
+ "default.handlebars->29->1600"
19576
]
19577
},
19578
{
@@ -19991,7 +19987,7 @@
19987
"zh-chs": "最后访问",
19988
"zh-cht": "最後訪問",
19989
"xloc": [
19994
- "default.handlebars->29->1715"
19990
+ "default.handlebars->29->1708"
19991
]
19992
},
19993
{
@@ -20011,7 +20007,7 @@
20007
"zh-chs": "上次登录",
20008
"zh-cht": "上次登入",
20009
"xloc": [
20014
- "default.handlebars->29->1923"
20010
+ "default.handlebars->29->1916"
20011
]
20012
},
20013
{
@@ -20083,7 +20079,7 @@
20079
"zh-chs": "上次更改:{0}",
20080
"zh-cht": "上次更改:{0}",
20081
"xloc": [
20086
- "default.handlebars->29->1927"
20082
+ "default.handlebars->29->1920"
20083
]
20084
},
20085
{
@@ -20143,7 +20139,7 @@
20139
"zh-chs": "上次登录:{0}",
20140
"zh-cht": "上次登入:{0}",
20141
"xloc": [
20146
- "default.handlebars->29->1725"
20142
+ "default.handlebars->29->1718"
20143
]
20144
},
20145
{
@@ -20344,7 +20340,7 @@
20340
"zh-chs": "如没有请留空。",
20341
"zh-cht": "如沒有請留空。",
20342
"xloc": [
20347
- "default.handlebars->29->1967"
20343
+ "default.handlebars->29->1960"
20344
]
20345
},
20346
{
@@ -20389,7 +20385,7 @@
20385
"zh-chs": "离开桌面多路复用会话",
20386
"zh-cht": "離開桌面多路復用會話",
20387
"xloc": [
20392
- "default.handlebars->29->1593"
20388
+ "default.handlebars->29->1586"
20389
]
20390
},
20391
{
@@ -20409,7 +20405,7 @@
20405
"zh-chs": "减",
20406
"zh-cht": "減",
20407
"xloc": [
20412
- "default.handlebars->29->2135"
20408
+ "default.handlebars->29->2128"
20409
]
20410
},
20411
{
@@ -20472,7 +20468,7 @@
20468
"zh-cht": "有限輸入",
20469
"xloc": [
20470
"default-mobile.handlebars->9->451",
20475
- "default.handlebars->29->1495",
20471
+ "default.handlebars->29->1488",
20472
"default.handlebars->29->685",
20473
"default.handlebars->29->706"
20474
]
@@ -20495,7 +20491,7 @@
20491
"zh-cht": "僅有限輸入",
20492
"xloc": [
20493
"default-mobile.handlebars->9->424",
20498
- "default.handlebars->29->1451"
20494
+ "default.handlebars->29->1444"
20495
]
20496
},
20497
{
@@ -20968,7 +20964,7 @@
20964
"default.handlebars->29->1037",
20965
"default.handlebars->29->1314",
20966
"default.handlebars->29->1318",
20971
- "default.handlebars->29->2021",
20967
+ "default.handlebars->29->2014",
20968
"default.handlebars->29->797"
20969
]
20970
},
@@ -21051,7 +21047,7 @@
21047
"zh-chs": "本地用户接受的远程终端请求",
21048
"zh-cht": "本地用戶接受的遠程終端請求",
21049
"xloc": [
21054
- "default.handlebars->29->1615"
21050
+ "default.handlebars->29->1608"
21051
]
21052
},
21053
{
@@ -21071,7 +21067,7 @@
21067
"zh-chs": "本地用户拒绝了远程终端请求",
21068
"zh-cht": "本地用戶拒絕了遠程終端請求",
21069
"xloc": [
21074
- "default.handlebars->29->1616"
21070
+ "default.handlebars->29->1609"
21071
]
21072
},
21073
{
@@ -21152,7 +21148,7 @@
21148
"zh-chs": "锁定账户",
21149
"zh-cht": "鎖定賬戶",
21150
"xloc": [
21155
- "default.handlebars->29->1812"
21151
+ "default.handlebars->29->1805"
21152
]
21153
},
21154
{
@@ -21172,7 +21168,7 @@
21168
"zh-chs": "锁定帐户设置",
21169
"zh-cht": "鎖定帳戶設置",
21170
"xloc": [
21175
- "default.handlebars->29->1815"
21171
+ "default.handlebars->29->1808"
21172
]
21173
},
21174
{
@@ -21192,7 +21188,7 @@
21188
"zh-chs": "锁定账户",
21189
"zh-cht": "鎖定賬戶",
21190
"xloc": [
21195
- "default.handlebars->29->1745"
21191
+ "default.handlebars->29->1738"
21192
]
21193
},
21194
{
@@ -21212,7 +21208,7 @@
21208
"zh-chs": "已锁定",
21209
"zh-cht": "已鎖定",
21210
"xloc": [
21215
- "default.handlebars->29->1726"
21211
+ "default.handlebars->29->1719"
21212
]
21213
},
21214
{
@@ -21232,7 +21228,7 @@
21228
"zh-chs": "被锁定账户",
21229
"zh-cht": "被鎖定賬戶",
21230
"xloc": [
21235
- "default.handlebars->29->1899"
21231
+ "default.handlebars->29->1892"
21232
]
21233
},
21234
{
@@ -21252,7 +21248,7 @@
21248
"zh-chs": "将远程用户锁定在桌面之外",
21249
"zh-cht": "將遠程用戶鎖定在桌面之外",
21250
"xloc": [
21255
- "default.handlebars->29->1641"
21251
+ "default.handlebars->29->1634"
21252
]
21253
},
21254
{
@@ -21876,7 +21872,7 @@
21872
"zh-chs": "主服务器信息",
21873
"zh-cht": "主伺服器訊息",
21874
"xloc": [
21879
- "default.handlebars->29->2107"
21875
+ "default.handlebars->29->2100"
21876
]
21877
},
21878
{
@@ -21979,8 +21975,8 @@
21975
"xloc": [
21976
"default-mobile.handlebars->9->421",
21977
"default-mobile.handlebars->9->441",
21982
- "default.handlebars->29->1448",
21983
- "default.handlebars->29->1484"
21978
+ "default.handlebars->29->1441",
21979
+ "default.handlebars->29->1477"
21980
]
21981
},
21982
{
@@ -22002,8 +21998,8 @@
21998
"xloc": [
21999
"default-mobile.handlebars->9->420",
22000
"default-mobile.handlebars->9->440",
22005
- "default.handlebars->29->1447",
22006
- "default.handlebars->29->1483"
22001
+ "default.handlebars->29->1440",
22002
+ "default.handlebars->29->1476"
22003
]
22004
},
22005
{
@@ -22043,7 +22039,7 @@
22039
"zh-chs": "管理录音",
22040
"zh-cht": "管理錄音",
22041
"xloc": [
22046
- "default.handlebars->29->1810"
22042
+ "default.handlebars->29->1803"
22043
]
22044
},
22045
{
@@ -22083,7 +22079,7 @@
22079
"zh-chs": "管理用户组",
22080
"zh-cht": "管理用戶群",
22081
"xloc": [
22086
- "default.handlebars->29->1809"
22082
+ "default.handlebars->29->1802"
22083
]
22084
},
22085
{
@@ -22103,7 +22099,7 @@
22099
"zh-chs": "管理用户",
22100
"zh-cht": "管理用戶",
22101
"xloc": [
22106
- "default.handlebars->29->1808",
22102
+ "default.handlebars->29->1801",
22103
"default.handlebars->29->700"
22104
]
22105
},
@@ -22271,7 +22267,7 @@
22267
"zh-chs": "经理",
22268
"zh-cht": "經理",
22269
"xloc": [
22274
- "default.handlebars->29->1731"
22270
+ "default.handlebars->29->1724"
22271
]
22272
},
22273
{
@@ -22392,7 +22388,7 @@
22388
"zh-chs": "达到连接数量上限",
22389
"zh-cht": "達到連接數量上限",
22390
"xloc": [
22395
- "default.handlebars->29->2068"
22391
+ "default.handlebars->29->2061"
22392
]
22393
},
22394
{
@@ -22457,7 +22453,7 @@
22453
"zh-chs": "Megabyte",
22454
"zh-cht": "Megabyte",
22455
"xloc": [
22460
- "default.handlebars->29->2097"
22456
+ "default.handlebars->29->2090"
22457
]
22458
},
22459
{
@@ -22479,7 +22475,7 @@
22475
"xloc": [
22476
"default-mobile.handlebars->9->392",
22477
"default.handlebars->29->1003",
22482
- "default.handlebars->29->2088",
22478
+ "default.handlebars->29->2081",
22479
"default.handlebars->container->column_l->p40->3->1->p40type->3"
22480
]
22481
},
@@ -22533,7 +22529,7 @@
22529
"zh-cht": "網格代理控制台",
22530
"xloc": [
22531
"default-mobile.handlebars->9->428",
22536
- "default.handlebars->29->1456"
22532
+ "default.handlebars->29->1449"
22533
]
22534
},
22535
{
@@ -22638,7 +22634,7 @@
22634
"zh-chs": "MeshAgent流量",
22635
"zh-cht": "MeshAgent流量",
22636
"xloc": [
22641
- "default.handlebars->29->2109"
22637
+ "default.handlebars->29->2102"
22638
]
22639
},
22640
{
@@ -22658,7 +22654,7 @@
22654
"zh-chs": "MeshAgent更新",
22655
"zh-cht": "MeshAgent更新",
22656
"xloc": [
22661
- "default.handlebars->29->2110"
22657
+ "default.handlebars->29->2103"
22658
]
22659
},
22660
{
@@ -22779,7 +22775,7 @@
22775
"zh-chs": "MeshCentral服务器同级化",
22776
"zh-cht": "MeshCentral伺服器同級化",
22777
"xloc": [
22782
- "default.handlebars->29->2108"
22778
+ "default.handlebars->29->2101"
22779
]
22780
},
22781
{
@@ -23108,7 +23104,7 @@
23104
"zh-chs": "消息调度器",
23105
"zh-cht": "電郵調度器",
23106
"xloc": [
23111
- "default.handlebars->29->2106"
23107
+ "default.handlebars->29->2099"
23108
]
23109
},
23110
{
@@ -23291,7 +23287,7 @@
23287
"zh-chs": "更多",
23288
"zh-cht": "更多",
23289
"xloc": [
23294
- "default.handlebars->29->2134"
23290
+ "default.handlebars->29->2127"
23291
]
23292
},
23293
{
@@ -23372,7 +23368,7 @@
23368
"zh-chs": "移动:“{0}”到“{1}”",
23369
"zh-cht": "移動:“{0}”到“{1}”",
23370
"xloc": [
23375
- "default.handlebars->29->1640"
23371
+ "default.handlebars->29->1633"
23372
]
23373
},
23374
{
@@ -23392,7 +23388,7 @@
23388
"zh-chs": "将设备{0}移动到组{1}",
23389
"zh-cht": "將設備{0}移動到組{1}",
23390
"xloc": [
23395
- "default.handlebars->29->1673"
23391
+ "default.handlebars->29->1666"
23392
]
23393
},
23394
{
@@ -23432,7 +23428,7 @@
23428
"zh-chs": "多路复用器",
23429
"zh-cht": "多工器",
23430
"xloc": [
23435
- "default.handlebars->29->2046"
23431
+ "default.handlebars->29->2039"
23432
]
23433
},
23434
{
@@ -23785,13 +23781,13 @@
23781
"default-mobile.handlebars->container->page_content->column_l->p10->p10desktop->deskarea3->deskarea3x->DeskTools->5->1->1",
23782
"default.handlebars->29->1285",
23783
"default.handlebars->29->1325",
23788
- "default.handlebars->29->1412",
23789
- "default.handlebars->29->1713",
23790
- "default.handlebars->29->1818",
23784
+ "default.handlebars->29->1405",
23785
+ "default.handlebars->29->1706",
23786
+ "default.handlebars->29->1811",
23787
+ "default.handlebars->29->1827",
23788
"default.handlebars->29->1834",
23792
- "default.handlebars->29->1841",
23793
- "default.handlebars->29->1886",
23794
- "default.handlebars->29->1905",
23789
+ "default.handlebars->29->1879",
23790
+ "default.handlebars->29->1898",
23791
"default.handlebars->29->560",
23792
"default.handlebars->29->76",
23793
"default.handlebars->29->856",
@@ -23840,7 +23836,7 @@
23836
"zh-chs": "名称1,名称2,名称3",
23837
"zh-cht": "名稱1,名稱2,名稱3",
23838
"xloc": [
23843
- "default.handlebars->29->1800"
23839
+ "default.handlebars->29->1793"
23840
]
23841
},
23842
{
@@ -24000,7 +23996,7 @@
23996
"zh-chs": "生成新的2FA备份代码",
23997
"zh-cht": "生成新的2FA備份代碼",
23998
"xloc": [
24003
- "default.handlebars->29->1680"
23999
+ "default.handlebars->29->1673"
24000
]
24001
},
24002
{
@@ -24065,7 +24061,7 @@
24061
"xloc": [
24062
"default-mobile.handlebars->9->120",
24063
"default-mobile.handlebars->9->305",
24068
- "default.handlebars->29->1572",
24064
+ "default.handlebars->29->1565",
24065
"default.handlebars->29->898",
24066
"default.handlebars->container->column_l->p13->p13toolbar->1->2->1->3",
24067
"default.handlebars->container->column_l->p5->p5toolbar->1->0->p5filehead->3"
@@ -24233,7 +24229,7 @@
24229
"zh-chs": "没有桌面",
24230
"zh-cht": "沒有桌面",
24231
"xloc": [
24236
- "default.handlebars->29->1491",
24232
+ "default.handlebars->29->1484",
24233
"default.handlebars->29->686",
24234
"default.handlebars->29->707"
24235
]
@@ -24255,7 +24251,7 @@
24251
"zh-chs": "不能访问桌面",
24252
"zh-cht": "不能訪問桌面",
24253
"xloc": [
24258
- "default.handlebars->29->1452"
24254
+ "default.handlebars->29->1445"
24255
]
24256
},
24257
{
@@ -24275,8 +24271,8 @@
24271
"zh-chs": "找不到事件",
24272
"zh-cht": "找不到事件",
24273
"xloc": [
24278
- "default.handlebars->29->1689",
24279
- "default.handlebars->29->2020",
24274
+ "default.handlebars->29->1682",
24275
+ "default.handlebars->29->2013",
24276
"default.handlebars->29->933"
24277
]
24278
},
@@ -24298,7 +24294,7 @@
24294
"zh-cht": "不能存取檔案",
24295
"xloc": [
24296
"default-mobile.handlebars->9->426",
24301
- "default.handlebars->29->1454"
24297
+ "default.handlebars->29->1447"
24298
]
24299
},
24300
{
@@ -24319,7 +24315,7 @@
24315
"zh-cht": "沒有檔案",
24316
"xloc": [
24317
"default-mobile.handlebars->9->449",
24322
- "default.handlebars->29->1493",
24318
+ "default.handlebars->29->1486",
24319
"default.handlebars->29->683",
24320
"default.handlebars->29->704"
24321
]
@@ -24364,8 +24360,8 @@
24360
"xloc": [
24361
"default-mobile.handlebars->9->427",
24362
"default-mobile.handlebars->9->450",
24367
- "default.handlebars->29->1455",
24368
- "default.handlebars->29->1494"
24363
+ "default.handlebars->29->1448",
24364
+ "default.handlebars->29->1487"
24365
]
24366
},
24367
{
@@ -24445,7 +24441,7 @@
24441
"zh-chs": "没有成员",
24442
"zh-cht": "沒有成員",
24443
"xloc": [
24448
- "default.handlebars->29->1868"
24444
+ "default.handlebars->29->1861"
24445
]
24446
},
24447
{
@@ -24465,7 +24461,7 @@
24461
"zh-chs": "没有新的设备组",
24462
"zh-cht": "沒有新的裝置群",
24463
"xloc": [
24468
- "default.handlebars->29->1813"
24464
+ "default.handlebars->29->1806"
24465
]
24466
},
24467
{
@@ -24486,8 +24482,7 @@
24482
"zh-cht": "沒有政策",
24483
"xloc": [
24484
"default.handlebars->29->1356",
24489
- "default.handlebars->29->1384",
24490
- "default.handlebars->29->1387"
24485
+ "default.handlebars->29->1384"
24486
]
24487
},
24488
{
@@ -24511,7 +24506,7 @@
24506
"default-mobile.handlebars->9->410",
24507
"default-mobile.handlebars->9->457",
24508
"default.handlebars->29->1299",
24514
- "default.handlebars->29->1501",
24509
+ "default.handlebars->29->1494",
24510
"default.handlebars->29->697",
24511
"default.handlebars->29->718"
24512
]
@@ -24556,7 +24551,7 @@
24551
"zh-cht": "沒有終端",
24552
"xloc": [
24553
"default-mobile.handlebars->9->448",
24559
- "default.handlebars->29->1492",
24554
+ "default.handlebars->29->1485",
24555
"default.handlebars->29->682",
24556
"default.handlebars->29->703"
24557
]
@@ -24579,7 +24574,7 @@
24574
"zh-cht": "不能訪問終端",
24575
"xloc": [
24576
"default-mobile.handlebars->9->425",
24582
- "default.handlebars->29->1453"
24577
+ "default.handlebars->29->1446"
24578
]
24579
},
24580
{
@@ -24599,7 +24594,7 @@
24594
"zh-chs": "没有工具(MeshCmd /路由器)",
24595
"zh-cht": "沒有工具(MeshCmd /路由器)",
24596
"xloc": [
24602
- "default.handlebars->29->1814"
24597
+ "default.handlebars->29->1807"
24598
]
24599
},
24600
{
@@ -24627,8 +24622,8 @@
24622
"zh-chs": "没有共同的设备组",
24623
"zh-cht": "沒有共同的裝置群",
24624
"xloc": [
24630
- "default.handlebars->29->1874",
24631
- "default.handlebars->29->1992"
24625
+ "default.handlebars->29->1867",
24626
+ "default.handlebars->29->1985"
24627
]
24628
},
24629
{
@@ -24748,8 +24743,8 @@
24743
"zh-chs": "没有共同的设备",
24744
"zh-cht": "沒有共同的裝置",
24745
"xloc": [
24751
- "default.handlebars->29->1880",
24752
- "default.handlebars->29->2004"
24746
+ "default.handlebars->29->1873",
24747
+ "default.handlebars->29->1997"
24748
]
24749
},
24750
{
@@ -24769,7 +24764,7 @@
24764
"zh-chs": "该设备组中没有设备。",
24765
"zh-cht": "該裝置群中沒有裝置。",
24766
"xloc": [
24772
- "default.handlebars->29->1544"
24767
+ "default.handlebars->29->1537"
24768
]
24769
},
24770
{
@@ -24852,7 +24847,7 @@
24847
"zh-chs": "找不到群组。",
24848
"zh-cht": "找不到群組。",
24849
"xloc": [
24855
- "default.handlebars->29->1817"
24850
+ "default.handlebars->29->1810"
24851
]
24852
},
24853
{
@@ -24973,7 +24968,7 @@
24968
"zh-chs": "没有录音。",
24969
"zh-cht": "沒有錄音。",
24970
"xloc": [
24976
- "default.handlebars->29->2022"
24971
+ "default.handlebars->29->2015"
24972
]
24973
},
24974
{
@@ -24993,7 +24988,7 @@
24988
"zh-chs": "没有服务器权限",
24989
"zh-cht": "沒有伺服器權限",
24990
"xloc": [
24996
- "default.handlebars->29->1900"
24991
+ "default.handlebars->29->1893"
24992
]
24993
},
24994
{
@@ -25013,7 +25008,7 @@
25008
"zh-chs": "没有用户组成员身份",
25009
"zh-cht": "沒有用戶群成員身份",
25010
"xloc": [
25016
- "default.handlebars->29->1998"
25011
+ "default.handlebars->29->1991"
25012
]
25013
},
25014
{
@@ -25033,7 +25028,7 @@
25028
"zh-chs": "未找到相应的用户。",
25029
"zh-cht": "未找到相應的用戶。",
25030
"xloc": [
25036
- "default.handlebars->29->1721"
25031
+ "default.handlebars->29->1714"
25032
]
25033
},
25034
{
@@ -25129,19 +25124,19 @@
25124
"default.handlebars->29->1347",
25125
"default.handlebars->29->1352",
25126
"default.handlebars->29->1354",
25132
- "default.handlebars->29->1535",
25133
- "default.handlebars->29->1554",
25134
- "default.handlebars->29->1559",
25135
- "default.handlebars->29->1697",
25127
+ "default.handlebars->29->1528",
25128
+ "default.handlebars->29->1547",
25129
+ "default.handlebars->29->1552",
25130
+ "default.handlebars->29->1690",
25131
"default.handlebars->29->177",
25137
- "default.handlebars->29->1838",
25138
- "default.handlebars->29->1840",
25139
- "default.handlebars->29->1857",
25140
- "default.handlebars->29->1919",
25132
+ "default.handlebars->29->1831",
25133
+ "default.handlebars->29->1833",
25134
+ "default.handlebars->29->1850",
25135
+ "default.handlebars->29->1912",
25136
"default.handlebars->29->192",
25142
- "default.handlebars->29->1928",
25143
- "default.handlebars->29->1932",
25144
- "default.handlebars->29->1944",
25137
+ "default.handlebars->29->1921",
25138
+ "default.handlebars->29->1925",
25139
+ "default.handlebars->29->1937",
25140
"default.handlebars->29->208",
25141
"default.handlebars->29->209",
25142
"default.handlebars->29->557",
@@ -25296,8 +25291,8 @@
25291
"zh-chs": "未连接",
25292
"zh-cht": "未連接",
25293
"xloc": [
25299
- "default.handlebars->29->1531",
25300
- "default.handlebars->29->1539"
25294
+ "default.handlebars->29->1524",
25295
+ "default.handlebars->29->1532"
25296
]
25297
},
25298
{
@@ -25338,7 +25333,7 @@
25333
"zh-chs": "不在服务器上",
25334
"zh-cht": "不在伺服器上",
25335
"xloc": [
25341
- "default.handlebars->29->2038"
25336
+ "default.handlebars->29->2031"
25337
]
25338
},
25339
{
@@ -25358,8 +25353,8 @@
25353
"zh-chs": "没有设置",
25354
"zh-cht": "沒有設置",
25355
"xloc": [
25361
- "default.handlebars->29->1906",
25362
- "default.handlebars->29->1907"
25356
+ "default.handlebars->29->1899",
25357
+ "default.handlebars->29->1900"
25358
]
25359
},
25360
{
@@ -25379,7 +25374,7 @@
25374
"zh-chs": "未经审核的",
25375
"zh-cht": "未經審核的",
25376
"xloc": [
25382
- "default.handlebars->29->1974"
25377
+ "default.handlebars->29->1967"
25378
]
25379
},
25380
{
@@ -25400,7 +25395,7 @@
25395
"zh-cht": "筆記",
25396
"xloc": [
25397
"default.handlebars->29->1362",
25403
- "default.handlebars->29->1952",
25398
+ "default.handlebars->29->1945",
25399
"default.handlebars->29->629",
25400
"default.handlebars->29->691",
25401
"default.handlebars->29->712",
@@ -25445,7 +25440,7 @@
25440
"zh-cht": "通知設定",
25441
"xloc": [
25442
"default.handlebars->29->1260",
25448
- "default.handlebars->29->1530",
25443
+ "default.handlebars->29->1523",
25444
"default.handlebars->container->column_l->p2->p2info->p2AccountActions->3->10"
25445
]
25446
},
@@ -25466,7 +25461,7 @@
25461
"zh-chs": "通知设置还必须在帐户设置中启用。",
25462
"zh-cht": "通知設定還必須在帳戶設定中啟用。",
25463
"xloc": [
25469
- "default.handlebars->29->1526"
25464
+ "default.handlebars->29->1519"
25465
]
25466
},
25467
{
@@ -25527,7 +25522,7 @@
25522
"zh-cht": "通知",
25523
"xloc": [
25524
"default.handlebars->29->189",
25530
- "default.handlebars->29->1958"
25525
+ "default.handlebars->29->1951"
25526
]
25527
},
25528
{
@@ -25567,9 +25562,9 @@
25562
"zh-chs": "通知使用者",
25563
"zh-cht": "通知使用者",
25564
"xloc": [
25570
- "default.handlebars->29->1420",
25571
- "default.handlebars->29->1424",
25572
- "default.handlebars->29->1427"
25565
+ "default.handlebars->29->1413",
25566
+ "default.handlebars->29->1417",
25567
+ "default.handlebars->29->1420"
25568
]
25569
},
25570
{
@@ -25589,7 +25584,7 @@
25584
"zh-chs": "通知{0}",
25585
"zh-cht": "通知{0}",
25586
"xloc": [
25592
- "default.handlebars->29->1760"
25587
+ "default.handlebars->29->1753"
25588
]
25589
},
25590
{
@@ -25681,7 +25676,7 @@
25676
"zh-chs": "发生在{0}",
25677
"zh-cht": "發生在{0}",
25678
"xloc": [
25684
- "default.handlebars->29->2052"
25679
+ "default.handlebars->29->2045"
25680
]
25681
},
25682
{
@@ -25708,7 +25703,7 @@
25703
"zh-chs": "离线用户",
25704
"zh-cht": "離線用戶",
25705
"xloc": [
25711
- "default.handlebars->29->1718"
25706
+ "default.handlebars->29->1711"
25707
]
25708
},
25709
{
@@ -25749,7 +25744,7 @@
25744
"zh-chs": "一天",
25745
"zh-cht": "一天",
25746
"xloc": [
25752
- "default.handlebars->29->1694"
25747
+ "default.handlebars->29->1687"
25748
]
25749
},
25750
{
@@ -25812,7 +25807,7 @@
25807
"zh-chs": "在线用户",
25808
"zh-cht": "在線用戶",
25809
"xloc": [
25815
- "default.handlebars->29->1717"
25810
+ "default.handlebars->29->1710"
25811
]
25812
},
25813
{
@@ -25996,7 +25991,7 @@
25991
"zh-chs": "开头:{0}",
25992
"zh-cht": "開場:{0}",
25993
"xloc": [
25999
- "default.handlebars->29->1608"
25994
+ "default.handlebars->29->1601"
25995
]
25996
},
25997
{
@@ -26042,8 +26037,8 @@
26037
"zh-cht": "操作",
26038
"xloc": [
26039
"default-mobile.handlebars->9->269",
26045
- "default.handlebars->29->1744",
26046
- "default.handlebars->29->1825",
26040
+ "default.handlebars->29->1737",
26041
+ "default.handlebars->29->1818",
26042
"default.handlebars->29->468",
26043
"default.handlebars->29->483",
26044
"default.handlebars->29->757"
@@ -26251,7 +26246,7 @@
26246
"zh-chs": "部分的",
26247
"zh-cht": "部分的",
26248
"xloc": [
26254
- "default.handlebars->29->1732"
26249
+ "default.handlebars->29->1725"
26250
]
26251
},
26252
{
@@ -26327,7 +26322,7 @@
26322
"zh-chs": "部分权限",
26323
"zh-cht": "部分權限",
26324
"xloc": [
26330
- "default.handlebars->29->1903"
26325
+ "default.handlebars->29->1896"
26326
]
26327
},
26328
{
@@ -26368,12 +26363,12 @@
26363
"zh-cht": "密碼",
26364
"xloc": [
26365
"default-mobile.handlebars->9->275",
26371
- "default.handlebars->29->1788",
26372
- "default.handlebars->29->1789",
26373
- "default.handlebars->29->1924",
26374
- "default.handlebars->29->1926",
26375
- "default.handlebars->29->1977",
26376
- "default.handlebars->29->1978",
26366
+ "default.handlebars->29->1781",
26367
+ "default.handlebars->29->1782",
26368
+ "default.handlebars->29->1917",
26369
+ "default.handlebars->29->1919",
26370
+ "default.handlebars->29->1970",
26371
+ "default.handlebars->29->1971",
26372
"default.handlebars->29->296",
26373
"default.handlebars->29->327",
26374
"default.handlebars->29->772",
@@ -26517,7 +26512,7 @@
26512
"zh-chs": "密码提示",
26513
"zh-cht": "密碼提示",
26514
"xloc": [
26520
- "default.handlebars->29->1979"
26515
+ "default.handlebars->29->1972"
26516
]
26517
},
26518
{
@@ -26558,7 +26553,7 @@
26553
"zh-chs": "密码不符",
26554
"zh-cht": "密碼不符",
26555
"xloc": [
26561
- "default.handlebars->29->1393"
26556
+ "default.handlebars->29->1390"
26557
]
26558
},
26559
{
@@ -26600,8 +26595,8 @@
26595
"zh-chs": "密码*",
26596
"zh-cht": "密碼*",
26597
"xloc": [
26603
- "default.handlebars->29->1391",
26604
- "default.handlebars->29->1392"
26598
+ "default.handlebars->29->1388",
26599
+ "default.handlebars->29->1389"
26600
]
26601
},
26602
{
@@ -26663,7 +26658,7 @@
26658
"default-mobile.handlebars->9->318",
26659
"default-mobile.handlebars->container->page_content->column_l->p10->p10files->p13toolbar->1->2->1->3",
26660
"default-mobile.handlebars->container->page_content->column_l->p5->p5myfiles->p5toolbar->1->0->1->3",
26666
- "default.handlebars->29->1581",
26661
+ "default.handlebars->29->1574",
26662
"default.handlebars->29->886",
26663
"default.handlebars->29->912",
26664
"default.handlebars->container->column_l->p12->termTable->1->1->6->1->3",
@@ -26927,7 +26922,7 @@
26922
"zh-chs": "执行电源操作= {0},强制执行= {1}",
26923
"zh-cht": "執行電源操作= {0},強制執行= {1}",
26924
"xloc": [
26930
- "default.handlebars->29->1613"
26925
+ "default.handlebars->29->1606"
26926
]
26927
},
26928
{
@@ -26948,8 +26943,8 @@
26943
"zh-cht": "權限",
26944
"xloc": [
26945
"default-mobile.handlebars->9->460",
26951
- "default.handlebars->29->1504",
26952
- "default.handlebars->29->1716"
26946
+ "default.handlebars->29->1497",
26947
+ "default.handlebars->29->1709"
26948
]
26949
},
26950
{
@@ -26995,7 +26990,7 @@
26990
"default.handlebars->29->1029",
26991
"default.handlebars->29->1032",
26992
"default.handlebars->29->159",
26998
- "default.handlebars->29->1969"
26993
+ "default.handlebars->29->1962"
26994
]
26995
},
26996
{
@@ -27015,7 +27010,7 @@
27010
"zh-chs": "电话号码",
27011
"zh-cht": "電話號碼",
27012
"xloc": [
27018
- "default.handlebars->29->1918"
27013
+ "default.handlebars->29->1911"
27014
]
27015
},
27016
{
@@ -27037,7 +27032,7 @@
27032
"xloc": [
27033
"default-mobile.handlebars->9->66",
27034
"default.handlebars->29->1031",
27040
- "default.handlebars->29->1968"
27035
+ "default.handlebars->29->1961"
27036
]
27037
},
27038
{
@@ -27197,7 +27192,7 @@
27192
"zh-cht": "外掛指令",
27193
"xloc": [
27194
"default.handlebars->29->199",
27200
- "default.handlebars->29->2131"
27195
+ "default.handlebars->29->2124"
27196
]
27197
},
27198
{
@@ -27482,7 +27477,7 @@
27477
"zh-chs": "电源状态",
27478
"zh-cht": "電源狀態",
27479
"xloc": [
27485
- "default.handlebars->29->1537",
27480
+ "default.handlebars->29->1530",
27481
"default.handlebars->container->column_l->p21->3->1->meshPowerChartDiv->1"
27482
]
27483
},
@@ -27592,7 +27587,7 @@
27587
"zh-chs": "存在于服务器上",
27588
"zh-cht": "存在於伺服器上",
27589
"xloc": [
27595
- "default.handlebars->29->2037"
27590
+ "default.handlebars->29->2030"
27591
]
27592
},
27593
{
@@ -27738,7 +27733,7 @@
27733
"zh-chs": "处理控制台命令:“{0}”",
27734
"zh-cht": "處理控制台命令:“{0}”",
27735
"xloc": [
27741
- "default.handlebars->29->1605"
27736
+ "default.handlebars->29->1598"
27737
]
27738
},
27739
{
@@ -27798,9 +27793,9 @@
27793
"zh-chs": "用户同意提示",
27794
"zh-cht": "用戶同意提示",
27795
"xloc": [
27801
- "default.handlebars->29->1421",
27802
- "default.handlebars->29->1425",
27803
- "default.handlebars->29->1428"
27796
+ "default.handlebars->29->1414",
27797
+ "default.handlebars->29->1418",
27798
+ "default.handlebars->29->1421"
27799
]
27800
},
27801
{
@@ -27820,7 +27815,7 @@
27815
"zh-chs": "协议",
27816
"zh-cht": "協議",
27817
"xloc": [
27823
- "default.handlebars->29->2035",
27818
+ "default.handlebars->29->2028",
27819
"player.handlebars->3->16"
27820
]
27821
},
@@ -27880,7 +27875,7 @@
27875
"zh-cht": "公開鏈結",
27876
"xloc": [
27877
"default-mobile.handlebars->9->115",
27883
- "default.handlebars->29->1566"
27878
+ "default.handlebars->29->1559"
27879
]
27880
},
27881
{
@@ -28187,7 +28182,7 @@
28182
"zh-chs": "RSS",
28183
"zh-cht": "RSS",
28184
"xloc": [
28190
- "default.handlebars->29->2101"
28185
+ "default.handlebars->29->2094"
28186
]
28187
},
28188
{
@@ -28207,7 +28202,7 @@
28202
"zh-chs": "随机密码。",
28203
"zh-cht": "隨機密碼。",
28204
"xloc": [
28210
- "default.handlebars->29->1790"
28205
+ "default.handlebars->29->1783"
28206
]
28207
},
28208
{
@@ -28247,7 +28242,7 @@
28242
"zh-chs": "重新激活英特尔®AMT",
28243
"zh-cht": "重新啟動Intel® AMT",
28244
"xloc": [
28250
- "default.handlebars->29->1395"
28245
+ "default.handlebars->29->1392"
28246
]
28247
},
28248
{
@@ -28267,9 +28262,9 @@
28262
"zh-chs": "真正的名字",
28263
"zh-cht": "真正的名字",
28264
"xloc": [
28270
- "default.handlebars->29->1915",
28271
- "default.handlebars->29->1917",
28272
- "default.handlebars->29->1970"
28265
+ "default.handlebars->29->1908",
28266
+ "default.handlebars->29->1910",
28267
+ "default.handlebars->29->1963"
28268
]
28269
},
28270
{
@@ -28289,7 +28284,7 @@
28284
"zh-chs": "境界",
28285
"zh-cht": "境界",
28286
"xloc": [
28292
- "default.handlebars->29->1799"
28287
+ "default.handlebars->29->1792"
28288
]
28289
},
28290
{
@@ -28344,7 +28339,7 @@
28339
"en": "Record sessions",
28340
"nl": "Sessies opnemen",
28341
"xloc": [
28347
- "default.handlebars->29->1431"
28342
+ "default.handlebars->29->1424"
28343
]
28344
},
28345
{
@@ -28364,7 +28359,7 @@
28359
"zh-chs": "记录细节",
28360
"zh-cht": "記錄細節",
28361
"xloc": [
28367
- "default.handlebars->29->2049"
28362
+ "default.handlebars->29->2042"
28363
]
28364
},
28365
{
@@ -28406,7 +28401,7 @@
28401
"xloc": [
28402
"default-mobile.handlebars->9->121",
28403
"default-mobile.handlebars->9->306",
28409
- "default.handlebars->29->1573",
28404
+ "default.handlebars->29->1566",
28405
"default.handlebars->29->899"
28406
]
28407
},
@@ -28525,7 +28520,7 @@
28520
"zh-chs": "中继数量",
28521
"zh-cht": "中繼數量",
28522
"xloc": [
28528
- "default.handlebars->29->2080"
28523
+ "default.handlebars->29->2073"
28524
]
28525
},
28526
{
@@ -28545,7 +28540,7 @@
28540
"zh-chs": "中继错误",
28541
"zh-cht": "中繼錯誤",
28542
"xloc": [
28548
- "default.handlebars->29->2073"
28543
+ "default.handlebars->29->2066"
28544
]
28545
},
28546
{
@@ -28565,8 +28560,8 @@
28560
"zh-chs": "中继连接",
28561
"zh-cht": "中繼連接",
28562
"xloc": [
28568
- "default.handlebars->29->2079",
28569
- "default.handlebars->29->2095"
28563
+ "default.handlebars->29->2072",
28564
+ "default.handlebars->29->2088"
28565
]
28566
},
28567
{
@@ -28711,7 +28706,7 @@
28706
"nl": "Externe opdrachten",
28707
"xloc": [
28708
"default-mobile.handlebars->9->435",
28714
- "default.handlebars->29->1463"
28709
+ "default.handlebars->29->1456"
28710
]
28711
},
28712
{
@@ -28733,8 +28728,8 @@
28728
"xloc": [
28729
"default-mobile.handlebars->9->422",
28730
"default-mobile.handlebars->9->442",
28736
- "default.handlebars->29->1449",
28737
- "default.handlebars->29->1485"
28731
+ "default.handlebars->29->1442",
28732
+ "default.handlebars->29->1478"
28733
]
28734
},
28735
{
@@ -28777,8 +28772,8 @@
28772
"zh-chs": "远程桌面连接栏已激活/更新",
28773
"zh-cht": "遠程桌面連接欄已激活/更新",
28774
"xloc": [
28780
- "default.handlebars->29->1619",
28781
- "default.handlebars->29->1625"
28775
+ "default.handlebars->29->1612",
28776
+ "default.handlebars->29->1618"
28777
]
28778
},
28779
{
@@ -28798,7 +28793,7 @@
28793
"zh-chs": "远程桌面连接栏失败或不受支持",
28794
"zh-cht": "遠程桌面連接欄失敗或不受支持",
28795
"xloc": [
28801
- "default.handlebars->29->1620"
28796
+ "default.handlebars->29->1613"
28797
]
28798
},
28799
{
@@ -28818,7 +28813,7 @@
28813
"zh-chs": "远程桌面连接栏失败或不受支持",
28814
"zh-cht": "遠程桌面連接欄失敗或不受支持",
28815
"xloc": [
28821
- "default.handlebars->29->1626"
28816
+ "default.handlebars->29->1619"
28817
]
28818
},
28819
{
@@ -28838,9 +28833,9 @@
28833
"zh-chs": "本地用户强行关闭了远程桌面连接",
28834
"zh-cht": "本地用戶強行關閉了遠程桌面連接",
28835
"xloc": [
28841
- "default.handlebars->29->1617",
28842
- "default.handlebars->29->1621",
28843
- "default.handlebars->29->1627"
28836
+ "default.handlebars->29->1610",
28837
+ "default.handlebars->29->1614",
28838
+ "default.handlebars->29->1620"
28839
]
28840
},
28841
{
@@ -28963,8 +28958,8 @@
28958
"xloc": [
28959
"default-mobile.handlebars->9->423",
28960
"default-mobile.handlebars->9->447",
28966
- "default.handlebars->29->1450",
28967
- "default.handlebars->29->1490"
28961
+ "default.handlebars->29->1443",
28962
+ "default.handlebars->29->1483"
28963
]
28964
},
28965
{
@@ -29099,8 +29094,8 @@
29094
"zh-chs": "删除设备组权限",
29095
"zh-cht": "刪除裝置群權限",
29096
"xloc": [
29102
- "default.handlebars->29->1884",
29103
- "default.handlebars->29->2018"
29097
+ "default.handlebars->29->1877",
29098
+ "default.handlebars->29->2011"
29099
]
29100
},
29101
{
@@ -29120,8 +29115,8 @@
29115
"zh-chs": "删除设备权限",
29116
"zh-cht": "刪除裝置權限",
29117
"xloc": [
29123
- "default.handlebars->29->1882",
29124
- "default.handlebars->29->2005"
29118
+ "default.handlebars->29->1875",
29119
+ "default.handlebars->29->1998"
29120
]
29121
},
29122
{
@@ -29158,7 +29153,7 @@
29153
"zh-chs": "删除用户组成员身份",
29154
"zh-cht": "刪除用戶群成員身份",
29155
"xloc": [
29161
- "default.handlebars->29->2014"
29156
+ "default.handlebars->29->2007"
29157
]
29158
},
29159
{
@@ -29178,8 +29173,8 @@
29173
"zh-chs": "删除用户组权限",
29174
"zh-cht": "刪除用戶群權限",
29175
"xloc": [
29181
- "default.handlebars->29->1509",
29182
- "default.handlebars->29->2010"
29176
+ "default.handlebars->29->1502",
29177
+ "default.handlebars->29->2003"
29178
]
29179
},
29180
{
@@ -29199,7 +29194,7 @@
29194
"zh-chs": "删除用户成员资格",
29195
"zh-cht": "刪除用戶成員資格",
29196
"xloc": [
29202
- "default.handlebars->29->1892"
29197
+ "default.handlebars->29->1885"
29198
]
29199
},
29200
{
@@ -29219,8 +29214,8 @@
29214
"zh-chs": "删除用户权限",
29215
"zh-cht": "刪除用戶權限",
29216
"xloc": [
29222
- "default.handlebars->29->1507",
29223
- "default.handlebars->29->2007"
29217
+ "default.handlebars->29->1500",
29218
+ "default.handlebars->29->2000"
29219
]
29220
},
29221
{
@@ -29240,7 +29235,7 @@
29235
"zh-chs": "删除所有两因素认证。",
29236
"zh-cht": "刪除所有二因子鑑別。",
29237
"xloc": [
29243
- "default.handlebars->29->1982"
29238
+ "default.handlebars->29->1975"
29239
]
29240
},
29241
{
@@ -29260,7 +29255,7 @@
29255
"zh-chs": "删除此用户标识的所有先前事件。",
29256
"zh-cht": "刪除此用戶標識的所有先前事件。",
29257
"xloc": [
29263
- "default.handlebars->29->1791"
29258
+ "default.handlebars->29->1784"
29259
]
29260
},
29261
{
@@ -29280,7 +29275,7 @@
29275
"zh-chs": "断开连接后移除设备",
29276
"zh-cht": "斷開連接後删除裝置",
29277
"xloc": [
29283
- "default.handlebars->29->1429"
29278
+ "default.handlebars->29->1422"
29279
]
29280
},
29281
{
@@ -29381,7 +29376,7 @@
29376
"zh-chs": "删除此用户",
29377
"zh-cht": "刪除此用戶",
29378
"xloc": [
29384
- "default.handlebars->29->1960"
29379
+ "default.handlebars->29->1953"
29380
]
29381
},
29382
{
@@ -29401,7 +29396,7 @@
29396
"zh-chs": "删除用户组成员身份",
29397
"zh-cht": "刪除用戶群成員身份",
29398
"xloc": [
29404
- "default.handlebars->29->1996"
29399
+ "default.handlebars->29->1989"
29400
]
29401
},
29402
{
@@ -29421,7 +29416,7 @@
29416
"zh-chs": "删除此设备的用户组权限",
29417
"zh-cht": "刪除此裝置的用戶群權限",
29418
"xloc": [
29424
- "default.handlebars->29->1878"
29419
+ "default.handlebars->29->1871"
29420
]
29421
},
29422
{
@@ -29441,7 +29436,7 @@
29436
"zh-chs": "删除此设备组的用户组权限",
29437
"zh-cht": "刪除此裝置群的用戶群權限",
29438
"xloc": [
29444
- "default.handlebars->29->1872",
29439
+ "default.handlebars->29->1865",
29440
"default.handlebars->29->675"
29441
]
29442
},
@@ -29463,9 +29458,9 @@
29458
"zh-cht": "刪除此裝置群的用戶權限",
29459
"xloc": [
29460
"default.handlebars->29->1379",
29466
- "default.handlebars->29->1866",
29467
- "default.handlebars->29->1990",
29468
- "default.handlebars->29->2002",
29461
+ "default.handlebars->29->1859",
29462
+ "default.handlebars->29->1983",
29463
+ "default.handlebars->29->1995",
29464
"default.handlebars->29->676"
29465
]
29466
},
@@ -29486,7 +29481,7 @@
29481
"zh-chs": "删除身份验证应用程序",
29482
"zh-cht": "刪除身份驗證應用程序",
29483
"xloc": [
29489
- "default.handlebars->29->1679"
29484
+ "default.handlebars->29->1672"
29485
]
29486
},
29487
{
@@ -29506,7 +29501,7 @@
29501
"zh-chs": "从设备组{1}中删除了设备{0}",
29502
"zh-cht": "從設備組{1}中刪除了設備{0}",
29503
"xloc": [
29509
- "default.handlebars->29->1675"
29504
+ "default.handlebars->29->1668"
29505
]
29506
},
29507
{
@@ -29526,7 +29521,7 @@
29521
"zh-chs": "已删除用户{0}的电话号码",
29522
"zh-cht": "已刪除用戶{0}的電話號碼",
29523
"xloc": [
29529
- "default.handlebars->29->1685"
29524
+ "default.handlebars->29->1678"
29525
]
29526
},
29527
{
@@ -29546,7 +29541,7 @@
29541
"zh-chs": "移除安全密钥",
29542
"zh-cht": "移除安全密鑰",
29543
"xloc": [
29549
- "default.handlebars->29->1682"
29544
+ "default.handlebars->29->1675"
29545
]
29546
},
29547
{
@@ -29566,9 +29561,9 @@
29561
"zh-chs": "删除了{0}的用户设备权限",
29562
"zh-cht": "刪除了{0}的用戶設備權限",
29563
"xloc": [
29569
- "default.handlebars->29->1648",
29570
- "default.handlebars->29->1669",
29571
- "default.handlebars->29->1674"
29564
+ "default.handlebars->29->1641",
29565
+ "default.handlebars->29->1662",
29566
+ "default.handlebars->29->1667"
29567
]
29568
},
29569
{
@@ -29588,7 +29583,7 @@
29583
"zh-chs": "从设备组{1}中删除了用户组{0}",
29584
"zh-cht": "從設備組{1}中刪除了用戶組{0}",
29585
"xloc": [
29591
- "default.handlebars->29->1658"
29586
+ "default.handlebars->29->1651"
29587
]
29588
},
29589
{
@@ -29608,7 +29603,7 @@
29603
"zh-chs": "从设备组{1}中删除了用户{0}",
29604
"zh-cht": "已從設備組{1}中刪除用戶{0}",
29605
"xloc": [
29611
- "default.handlebars->29->1671"
29606
+ "default.handlebars->29->1664"
29607
]
29608
},
29609
{
@@ -29628,8 +29623,8 @@
29623
"zh-chs": "从用户组{1}中删除了用户{0}",
29624
"zh-cht": "從用戶組{1}中刪除了用戶{0}",
29625
"xloc": [
29631
- "default.handlebars->29->1650",
29632
- "default.handlebars->29->1660"
29626
+ "default.handlebars->29->1643",
29627
+ "default.handlebars->29->1653"
29628
]
29629
},
29630
{
@@ -29653,7 +29648,7 @@
29648
"default-mobile.handlebars->9->310",
29649
"default-mobile.handlebars->container->page_content->column_l->p10->p10files->p13toolbar->1->2->1->1",
29650
"default-mobile.handlebars->container->page_content->column_l->p5->p5myfiles->p5toolbar->1->0->1->1",
29656
- "default.handlebars->29->1577",
29651
+ "default.handlebars->29->1570",
29652
"default.handlebars->29->526",
29653
"default.handlebars->29->903",
29654
"default.handlebars->container->column_l->p13->p13toolbar->1->2->1->3",
@@ -29678,7 +29673,7 @@
29673
"zh-chs": "重命名:“{0}”为“{1}”",
29674
"zh-cht": "重命名:“{0}”為“{1}”",
29675
"xloc": [
29681
- "default.handlebars->29->1636"
29676
+ "default.handlebars->29->1629"
29677
]
29678
},
29679
{
@@ -29698,7 +29693,7 @@
29693
"zh-chs": "报告日",
29694
"zh-cht": "報告日",
29695
"xloc": [
29701
- "default.handlebars->29->1695"
29696
+ "default.handlebars->29->1688"
29697
]
29698
},
29699
{
@@ -29718,7 +29713,7 @@
29713
"zh-chs": "报告类型",
29714
"zh-cht": "報告類型",
29715
"xloc": [
29721
- "default.handlebars->29->1690"
29716
+ "default.handlebars->29->1683"
29717
]
29718
},
29719
{
@@ -29759,8 +29754,8 @@
29754
"zh-cht": "要求:{0}。",
29755
"xloc": [
29756
"default-mobile.handlebars->9->89",
29762
- "default.handlebars->29->1796",
29763
- "default.handlebars->29->1980"
29757
+ "default.handlebars->29->1789",
29758
+ "default.handlebars->29->1973"
29759
]
29760
},
29761
{
@@ -29853,7 +29848,7 @@
29848
"nl": "Reset / uitschakelen",
29849
"xloc": [
29850
"default-mobile.handlebars->9->436",
29856
- "default.handlebars->29->1464"
29851
+ "default.handlebars->29->1457"
29852
]
29853
},
29854
{
@@ -29967,7 +29962,7 @@
29962
"nl": "Reset/Uit",
29963
"xloc": [
29964
"default-mobile.handlebars->9->456",
29970
- "default.handlebars->29->1500",
29965
+ "default.handlebars->29->1493",
29966
"default.handlebars->29->696",
29967
"default.handlebars->29->717"
29968
]
@@ -30078,7 +30073,7 @@
30073
"zh-chs": "限制条件",
30074
"zh-cht": "限制條件",
30075
"xloc": [
30081
- "default.handlebars->29->1904"
30076
+ "default.handlebars->29->1897"
30077
]
30078
},
30079
{
@@ -30160,7 +30155,7 @@
30155
"xloc": [
30156
"default-mobile.handlebars->9->107",
30157
"default-mobile.handlebars->9->300",
30163
- "default.handlebars->29->1545",
30158
+ "default.handlebars->29->1538",
30159
"default.handlebars->29->893"
30160
]
30161
},
@@ -30422,19 +30417,19 @@
30417
"zh-chs": "运行命令",
30418
"zh-cht": "運行命令",
30419
"xloc": [
30425
- "default.handlebars->29->1612"
30420
+ "default.handlebars->29->1605"
30421
]
30422
},
30423
{
30424
"en": "Running commands as user",
30425
"xloc": [
30431
- "default.handlebars->29->1687"
30426
+ "default.handlebars->29->1680"
30427
]
30428
},
30429
{
30430
"en": "Running commands as user if possible",
30431
"xloc": [
30437
- "default.handlebars->29->1688"
30432
+ "default.handlebars->29->1681"
30433
]
30434
},
30435
{
@@ -30494,8 +30489,8 @@
30489
"zh-chs": "短信",
30490
"zh-cht": "短信",
30491
"xloc": [
30497
- "default.handlebars->29->1949",
30498
- "default.handlebars->29->1954",
30492
+ "default.handlebars->29->1942",
30493
+ "default.handlebars->29->1947",
30494
"login-mobile.handlebars->container->page_content->column_l->1->1->0->1->tokenpanel->1->7->1->4->1->3",
30495
"login.handlebars->container->column_l->centralTable->1->0->logincell->resettokenpanel->1->5->1->2->1->3",
30496
"login.handlebars->container->column_l->centralTable->1->0->logincell->tokenpanel->1->7->1->4->1->3",
@@ -30520,7 +30515,7 @@
30515
"zh-chs": "此用户的短信功能电话号码。",
30516
"zh-cht": "此用戶的短信功能電話號碼。",
30517
"xloc": [
30523
- "default.handlebars->29->1966"
30518
+ "default.handlebars->29->1959"
30519
]
30520
},
30521
{
@@ -31070,7 +31065,7 @@
31065
"xloc": [
31066
"default-mobile.handlebars->9->276",
31067
"default-mobile.handlebars->9->369",
31073
- "default.handlebars->29->1950",
31068
+ "default.handlebars->29->1943",
31069
"default.handlebars->29->297",
31070
"default.handlebars->29->773",
31071
"default.handlebars->29->980"
@@ -31093,7 +31088,7 @@
31088
"zh-chs": "安全密钥",
31089
"zh-cht": "安全密鑰",
31090
"xloc": [
31096
- "default.handlebars->29->1947"
31091
+ "default.handlebars->29->1940"
31092
]
31093
},
31094
{
@@ -31133,9 +31128,9 @@
31128
"zh-chs": "全选",
31129
"zh-cht": "全選",
31130
"xloc": [
31136
- "default.handlebars->29->1569",
31137
- "default.handlebars->29->1740",
31138
- "default.handlebars->29->1823",
31131
+ "default.handlebars->29->1562",
31132
+ "default.handlebars->29->1733",
31133
+ "default.handlebars->29->1816",
31134
"default.handlebars->29->455",
31135
"default.handlebars->29->895",
31136
"default.handlebars->29->897",
@@ -31164,9 +31159,9 @@
31159
"zh-chs": "选择无",
31160
"zh-cht": "選擇無",
31161
"xloc": [
31167
- "default.handlebars->29->1568",
31168
- "default.handlebars->29->1739",
31169
- "default.handlebars->29->1822",
31162
+ "default.handlebars->29->1561",
31163
+ "default.handlebars->29->1732",
31164
+ "default.handlebars->29->1815",
31165
"default.handlebars->29->454",
31166
"default.handlebars->29->896",
31167
"default.handlebars->meshContextMenu->cxselectnone"
@@ -31269,8 +31264,8 @@
31264
"zh-chs": "选择要对所有选定用户执行的操作。",
31265
"zh-cht": "選擇要對所有選定用戶執行的操作。",
31266
"xloc": [
31272
- "default.handlebars->29->1743",
31273
- "default.handlebars->29->1824"
31267
+ "default.handlebars->29->1736",
31268
+ "default.handlebars->29->1817"
31269
]
31270
},
31271
{
@@ -31333,7 +31328,7 @@
31328
"zh-cht": "僅自我事件",
31329
"xloc": [
31330
"default-mobile.handlebars->9->452",
31336
- "default.handlebars->29->1496"
31331
+ "default.handlebars->29->1489"
31332
]
31333
},
31334
{
@@ -31376,7 +31371,7 @@
31371
"zh-chs": "发电邮",
31372
"zh-cht": "發電郵",
31373
"xloc": [
31379
- "default.handlebars->29->1754"
31374
+ "default.handlebars->29->1747"
31375
]
31376
},
31377
{
@@ -31437,7 +31432,7 @@
31432
"zh-chs": "发送短信",
31433
"zh-cht": "發送簡訊",
31434
"xloc": [
31440
- "default.handlebars->29->1752"
31435
+ "default.handlebars->29->1745"
31436
]
31437
},
31438
{
@@ -31457,7 +31452,7 @@
31452
"zh-chs": "发送短信给该用户",
31453
"zh-cht": "發送短信給該用戶",
31454
"xloc": [
31460
- "default.handlebars->29->1955"
31455
+ "default.handlebars->29->1948"
31456
]
31457
},
31458
{
@@ -31477,7 +31472,7 @@
31472
"zh-chs": "发送电邮给该用户",
31473
"zh-cht": "發送電郵給該用戶",
31474
"xloc": [
31480
- "default.handlebars->29->1957"
31475
+ "default.handlebars->29->1950"
31476
]
31477
},
31478
{
@@ -31497,7 +31492,7 @@
31492
"zh-chs": "向该组中的所有用户发送通知。",
31493
"zh-cht": "向該群中的所有用戶發送通知。",
31494
"xloc": [
31500
- "default.handlebars->29->1863"
31495
+ "default.handlebars->29->1856"
31496
]
31497
},
31498
{
@@ -31517,7 +31512,7 @@
31512
"zh-chs": "向该用户发送文本通知。",
31513
"zh-cht": "向該用戶發送文本通知。",
31514
"xloc": [
31520
- "default.handlebars->29->1755"
31515
+ "default.handlebars->29->1748"
31516
]
31517
},
31518
{
@@ -31537,7 +31532,7 @@
31532
"zh-chs": "发送电邮给用户",
31533
"zh-cht": "發送電郵給用戶",
31534
"xloc": [
31540
- "default.handlebars->29->1735"
31535
+ "default.handlebars->29->1728"
31536
]
31537
},
31538
{
@@ -31577,7 +31572,7 @@
31572
"zh-chs": "发送邀请电邮。",
31573
"zh-cht": "發送邀請電郵。",
31574
"xloc": [
31580
- "default.handlebars->29->1795"
31575
+ "default.handlebars->29->1788"
31576
]
31577
},
31578
{
@@ -31699,7 +31694,7 @@
31694
"zh-chs": "发送用户通知",
31695
"zh-cht": "發送用戶通知",
31696
"xloc": [
31702
- "default.handlebars->29->1959"
31697
+ "default.handlebars->29->1952"
31698
]
31699
},
31700
{
@@ -31760,7 +31755,7 @@
31755
"zh-chs": "服务器备份",
31756
"zh-cht": "伺服器備份",
31757
"xloc": [
31763
- "default.handlebars->29->1805"
31758
+ "default.handlebars->29->1798"
31759
]
31760
},
31761
{
@@ -31780,7 +31775,7 @@
31775
"zh-chs": "服务器证书",
31776
"zh-cht": "伺服器憑證",
31777
"xloc": [
31783
- "default.handlebars->29->2111"
31778
+ "default.handlebars->29->2104"
31779
]
31780
},
31781
{
@@ -31800,7 +31795,7 @@
31795
"zh-chs": "服务器数据库",
31796
"zh-cht": "伺服器數據庫",
31797
"xloc": [
31803
- "default.handlebars->29->2112"
31798
+ "default.handlebars->29->2105"
31799
]
31800
},
31801
{
@@ -31822,9 +31817,9 @@
31817
"xloc": [
31818
"default-mobile.handlebars->9->429",
31819
"default-mobile.handlebars->9->444",
31825
- "default.handlebars->29->1457",
31826
- "default.handlebars->29->1487",
31827
- "default.handlebars->29->1802",
31820
+ "default.handlebars->29->1450",
31821
+ "default.handlebars->29->1480",
31822
+ "default.handlebars->29->1795",
31823
"default.handlebars->29->689",
31824
"default.handlebars->29->710"
31825
]
@@ -31846,8 +31841,8 @@
31841
"zh-chs": "服务器权限",
31842
"zh-cht": "伺服器權限",
31843
"xloc": [
31849
- "default.handlebars->29->1727",
31850
- "default.handlebars->29->1816"
31844
+ "default.handlebars->29->1720",
31845
+ "default.handlebars->29->1809"
31846
]
31847
},
31848
{
@@ -31867,7 +31862,7 @@
31862
"zh-chs": "服务器配额",
31863
"zh-cht": "伺服器配額",
31864
"xloc": [
31870
- "default.handlebars->29->1921"
31865
+ "default.handlebars->29->1914"
31866
]
31867
},
31868
{
@@ -31887,7 +31882,7 @@
31882
"zh-chs": "服务器还原",
31883
"zh-cht": "伺服器還原",
31884
"xloc": [
31890
- "default.handlebars->29->1806"
31885
+ "default.handlebars->29->1799"
31886
]
31887
},
31888
{
@@ -31907,7 +31902,7 @@
31902
"zh-chs": "服务器权限",
31903
"zh-cht": "伺服器權限",
31904
"xloc": [
31910
- "default.handlebars->29->1920"
31905
+ "default.handlebars->29->1913"
31906
]
31907
},
31908
{
@@ -31927,7 +31922,7 @@
31922
"zh-chs": "服务器状态",
31923
"zh-cht": "伺服器狀態",
31924
"xloc": [
31930
- "default.handlebars->29->2059"
31925
+ "default.handlebars->29->2052"
31926
]
31927
},
31928
{
@@ -31967,7 +31962,7 @@
31962
"zh-chs": "服务器跟踪",
31963
"zh-cht": "伺服器追蹤",
31964
"xloc": [
31970
- "default.handlebars->29->2122"
31965
+ "default.handlebars->29->2115"
31966
]
31967
},
31968
{
@@ -31987,7 +31982,7 @@
31982
"zh-chs": "服务器更新",
31983
"zh-cht": "伺服器更新",
31984
"xloc": [
31990
- "default.handlebars->29->1807"
31985
+ "default.handlebars->29->1800"
31986
]
31987
},
31988
{
@@ -32130,7 +32125,7 @@
32125
"zh-chs": "ServerStats.csv",
32126
"zh-cht": "ServerStats.csv",
32127
"xloc": [
32133
- "default.handlebars->29->2103"
32128
+ "default.handlebars->29->2096"
32129
]
32130
},
32131
{
@@ -32190,7 +32185,7 @@
32185
"zh-chs": "会话",
32186
"zh-cht": "節",
32187
"xloc": [
32193
- "default.handlebars->29->2023"
32188
+ "default.handlebars->29->2016"
32189
]
32190
},
32191
{
@@ -32315,7 +32310,7 @@
32310
"zh-chs": "设置剪贴板内容,{0}个字节",
32311
"zh-cht": "設置剪貼板內容,{0}個字節",
32312
"xloc": [
32318
- "default.handlebars->29->1610"
32313
+ "default.handlebars->29->1603"
32314
]
32315
},
32316
{
@@ -32696,7 +32691,7 @@
32691
"zh-cht": "只顯示自己的事件",
32692
"xloc": [
32693
"default-mobile.handlebars->9->432",
32699
- "default.handlebars->29->1460"
32694
+ "default.handlebars->29->1453"
32695
]
32696
},
32697
{
@@ -32716,7 +32711,7 @@
32711
"zh-chs": "显示连接工具栏",
32712
"zh-cht": "顯示連接工具欄",
32713
"xloc": [
32719
- "default.handlebars->29->1422"
32714
+ "default.handlebars->29->1415"
32715
]
32716
},
32717
{
@@ -32796,8 +32791,8 @@
32791
"zh-chs": "显示1分钟",
32792
"zh-cht": "顯示1分鐘",
32793
"xloc": [
32799
- "default.handlebars->29->1758",
32800
- "default.handlebars->29->1781"
32794
+ "default.handlebars->29->1751",
32795
+ "default.handlebars->29->1774"
32796
]
32797
},
32798
{
@@ -32817,8 +32812,8 @@
32812
"zh-chs": "显示10秒",
32813
"zh-cht": "顯示10秒",
32814
"xloc": [
32820
- "default.handlebars->29->1757",
32821
- "default.handlebars->29->1780"
32815
+ "default.handlebars->29->1750",
32816
+ "default.handlebars->29->1773"
32817
]
32818
},
32819
{
@@ -32838,8 +32833,8 @@
32833
"zh-chs": "显示5分钟",
32834
"zh-cht": "顯示5分鐘",
32835
"xloc": [
32841
- "default.handlebars->29->1759",
32842
- "default.handlebars->29->1782"
32836
+ "default.handlebars->29->1752",
32837
+ "default.handlebars->29->1775"
32838
]
32839
},
32840
{
@@ -32859,8 +32854,8 @@
32854
"zh-chs": "显示消息,直到被用户拒绝",
32855
"zh-cht": "顯示消息,直到被用戶拒絕",
32856
"xloc": [
32862
- "default.handlebars->29->1756",
32863
- "default.handlebars->29->1779"
32857
+ "default.handlebars->29->1749",
32858
+ "default.handlebars->29->1772"
32859
]
32860
},
32861
{
@@ -33076,8 +33071,7 @@
33071
"zh-cht": "簡單客戶端控制模式(CCM)",
33072
"xloc": [
33073
"default.handlebars->29->1357",
33079
- "default.handlebars->29->1385",
33080
- "default.handlebars->29->1389"
33074
+ "default.handlebars->29->1386"
33075
]
33076
},
33077
{
@@ -33159,8 +33153,8 @@
33153
"zh-chs": "尺寸",
33154
"zh-cht": "尺寸",
33155
"xloc": [
33162
- "default.handlebars->29->2026",
33163
- "default.handlebars->29->2041",
33156
+ "default.handlebars->29->2019",
33157
+ "default.handlebars->29->2034",
33158
"default.handlebars->container->column_l->p1->devListToolbarSpan->1->0->9->devListToolbarSize"
33159
]
33160
},
@@ -34150,8 +34144,8 @@
34144
"zh-chs": "开始时间",
34145
"zh-cht": "開始時間",
34146
"xloc": [
34153
- "default.handlebars->29->2024",
34154
- "default.handlebars->29->2043",
34147
+ "default.handlebars->29->2017",
34148
+ "default.handlebars->29->2036",
34149
"default.handlebars->29->839",
34150
"desktop.handlebars->3->15"
34151
]
@@ -34173,7 +34167,7 @@
34167
"zh-chs": "开始桌面多重会话",
34168
"zh-cht": "啟動桌面多路復用會話",
34169
"xloc": [
34176
- "default.handlebars->29->1594"
34170
+ "default.handlebars->29->1587"
34171
]
34172
},
34173
{
@@ -34193,7 +34187,7 @@
34187
"zh-chs": "从{1}到{2}开始了桌面会话“{0}”",
34188
"zh-cht": "從{1}到{2}開始了桌面會話“{0}”",
34189
"xloc": [
34196
- "default.handlebars->29->1603"
34190
+ "default.handlebars->29->1596"
34191
]
34192
},
34193
{
@@ -34213,7 +34207,7 @@
34207
"zh-chs": "从{1}到{2}开始文件管理会话“{0}”",
34208
"zh-cht": "從{1}到{2}開始文件管理會話“{0}”",
34209
"xloc": [
34216
- "default.handlebars->29->1604"
34210
+ "default.handlebars->29->1597"
34211
]
34212
},
34213
{
@@ -34233,7 +34227,7 @@
34227
"zh-chs": "从{1}到{2}开始中继会话“{0}”",
34228
"zh-cht": "從{1}到{2}開始中繼會話“{0}”",
34229
"xloc": [
34236
- "default.handlebars->29->1601"
34230
+ "default.handlebars->29->1594"
34231
]
34232
},
34233
{
@@ -34253,7 +34247,7 @@
34247
"zh-chs": "使用Toast通知启动远程桌面",
34248
"zh-cht": "使用Toast通知啟動遠程桌面",
34249
"xloc": [
34256
- "default.handlebars->29->1623"
34250
+ "default.handlebars->29->1616"
34251
]
34252
},
34253
{
@@ -34273,7 +34267,7 @@
34267
"zh-chs": "启动远程桌面,而无需通知",
34268
"zh-cht": "啟動遠程桌面,而無需通知",
34269
"xloc": [
34276
- "default.handlebars->29->1624"
34270
+ "default.handlebars->29->1617"
34271
]
34272
},
34273
{
@@ -34293,7 +34287,7 @@
34287
"zh-chs": "启动带有Toast通知的远程文件",
34288
"zh-cht": "啟動帶有Toast通知的遠程文件",
34289
"xloc": [
34296
- "default.handlebars->29->1630"
34290
+ "default.handlebars->29->1623"
34291
]
34292
},
34293
{
@@ -34313,7 +34307,7 @@
34307
"zh-chs": "已启动的远程文件,恕不另行通知",
34308
"zh-cht": "已啟動的遠程文件,恕不另行通知",
34309
"xloc": [
34316
- "default.handlebars->29->1631"
34310
+ "default.handlebars->29->1624"
34311
]
34312
},
34313
{
@@ -34333,7 +34327,7 @@
34327
"zh-chs": "从{1}到{2}开始了终端会话“{0}”",
34328
"zh-cht": "從{1}到{2}開始了終端會話“{0}”",
34329
"xloc": [
34336
- "default.handlebars->29->1602"
34330
+ "default.handlebars->29->1595"
34331
]
34332
},
34333
{
@@ -34353,7 +34347,7 @@
34347
"zh-chs": "接受本地用户后启动远程桌面",
34348
"zh-cht": "接受本地用戶後啟動遠程桌面",
34349
"xloc": [
34356
- "default.handlebars->29->1618"
34350
+ "default.handlebars->29->1611"
34351
]
34352
},
34353
{
@@ -34373,7 +34367,7 @@
34367
"zh-chs": "本地用户接受后启动远程文件",
34368
"zh-cht": "本地用戶接受後啟動遠程文件",
34369
"xloc": [
34376
- "default.handlebars->29->1628"
34370
+ "default.handlebars->29->1621"
34371
]
34372
},
34373
{
@@ -34434,8 +34428,8 @@
34428
"zh-chs": "状况",
34429
"zh-cht": "狀態",
34430
"xloc": [
34437
- "default.handlebars->29->1973",
34438
- "default.handlebars->29->2036",
34431
+ "default.handlebars->29->1966",
34432
+ "default.handlebars->29->2029",
34433
"default.handlebars->container->column_l->p42->p42tbl->1->0->7"
34434
]
34435
},
@@ -34563,7 +34557,7 @@
34557
"zh-chs": "超出储存空间",
34558
"zh-cht": "超出儲存空間",
34559
"xloc": [
34566
- "default.handlebars->29->1549"
34560
+ "default.handlebars->29->1542"
34561
]
34562
},
34563
{
@@ -34628,7 +34622,7 @@
34622
"zh-chs": "主题",
34623
"zh-cht": "主題",
34624
"xloc": [
34631
- "default.handlebars->29->1753"
34625
+ "default.handlebars->29->1746"
34626
]
34627
},
34628
{
@@ -34835,7 +34829,7 @@
34829
"zh-chs": "将服务器设备名称同步到主机名称",
34830
"zh-cht": "將伺服器裝置名稱同步到主機名稱",
34831
"xloc": [
34838
- "default.handlebars->29->1430"
34832
+ "default.handlebars->29->1423"
34833
]
34834
},
34835
{
@@ -35154,8 +35148,8 @@
35148
"zh-cht": "終端機",
35149
"xloc": [
35150
"default-mobile.handlebars->9->168",
35157
- "default.handlebars->29->1423",
35158
- "default.handlebars->29->2030",
35151
+ "default.handlebars->29->1416",
35152
+ "default.handlebars->29->2023",
35153
"default.handlebars->29->255",
35154
"default.handlebars->29->536",
35155
"default.handlebars->container->topbar->1->1->MainSubMenuSpan->MainSubMenu->1->0->MainDevTerminal",
@@ -35200,8 +35194,8 @@
35194
"zh-cht": "終端機通知",
35195
"xloc": [
35196
"default.handlebars->29->1342",
35203
- "default.handlebars->29->1852",
35204
- "default.handlebars->29->1939",
35197
+ "default.handlebars->29->1845",
35198
+ "default.handlebars->29->1932",
35199
"default.handlebars->29->608"
35200
]
35201
},
@@ -35223,8 +35217,8 @@
35217
"zh-cht": "終端機提示",
35218
"xloc": [
35219
"default.handlebars->29->1341",
35226
- "default.handlebars->29->1851",
35227
- "default.handlebars->29->1938",
35220
+ "default.handlebars->29->1844",
35221
+ "default.handlebars->29->1931",
35222
"default.handlebars->29->607"
35223
]
35224
},
@@ -35395,7 +35389,7 @@
35389
"zh-chs": "目前没有任何通知",
35390
"zh-cht": "目前沒有任何通知",
35391
"xloc": [
35398
- "default.handlebars->29->2051"
35392
+ "default.handlebars->29->2044"
35393
]
35394
},
35395
{
@@ -35533,7 +35527,7 @@
35527
"zh-chs": "这是不安全政策,因为将用代理执行激活。",
35528
"zh-cht": "這是不安全政策,因為將用代理執行啟動。",
35529
"xloc": [
35536
- "default.handlebars->29->1407"
35530
+ "default.handlebars->29->1400"
35531
]
35532
},
35533
{
@@ -35574,7 +35568,7 @@
35568
"zh-chs": "此政策不会影响采用ACM模式的英特尔®AMT的设备。",
35569
"zh-cht": "此政策不會影響採用ACM模式的Intel® AMT的裝置。",
35570
"xloc": [
35577
- "default.handlebars->29->1406"
35571
+ "default.handlebars->29->1399"
35572
]
35573
},
35574
{
@@ -35774,7 +35768,7 @@
35768
"zh-chs": "时间跨度",
35769
"zh-cht": "時間跨度",
35770
"xloc": [
35777
- "default.handlebars->29->1692"
35771
+ "default.handlebars->29->1685"
35772
]
35773
},
35774
{
@@ -36636,7 +36630,6 @@
36630
"default.handlebars->29->1286",
36631
"default.handlebars->29->1328",
36632
"default.handlebars->29->1383",
36639
- "default.handlebars->29->1386",
36633
"default.handlebars->29->865",
36634
"default.handlebars->container->column_l->p11->deskarea0->deskarea4->3",
36635
"desktop.handlebars->p11->deskarea0->deskarea4->3"
@@ -36951,7 +36944,7 @@
36944
"zh-cht": "卸載",
36945
"xloc": [
36946
"default-mobile.handlebars->9->454",
36954
- "default.handlebars->29->1498",
36947
+ "default.handlebars->29->1491",
36948
"default.handlebars->29->694",
36949
"default.handlebars->29->715"
36950
]
@@ -36995,7 +36988,7 @@
36988
"zh-chs": "卸载代理/删除设备",
36989
"zh-cht": "卸載代理/刪除設備",
36990
"xloc": [
36998
- "default.handlebars->29->1462"
36991
+ "default.handlebars->29->1455"
36992
]
36993
},
36994
{
@@ -37048,9 +37041,9 @@
37041
"default.handlebars->29->13",
37042
"default.handlebars->29->1315",
37043
"default.handlebars->29->1316",
37051
- "default.handlebars->29->2013",
37052
- "default.handlebars->29->2028",
37053
- "default.handlebars->29->2029",
37044
+ "default.handlebars->29->2006",
37045
+ "default.handlebars->29->2021",
37046
+ "default.handlebars->29->2022",
37047
"default.handlebars->29->42",
37048
"default.handlebars->29->453",
37049
"default.handlebars->29->972",
@@ -37095,7 +37088,7 @@
37088
"zh-chs": "未知动作",
37089
"zh-cht": "未知動作",
37090
"xloc": [
37098
- "default.handlebars->29->2065"
37091
+ "default.handlebars->29->2058"
37092
]
37093
},
37094
{
@@ -37115,8 +37108,8 @@
37108
"zh-chs": "未知设备",
37109
"zh-cht": "未知裝置",
37110
"xloc": [
37118
- "default.handlebars->29->1877",
37119
- "default.handlebars->29->2001"
37111
+ "default.handlebars->29->1870",
37112
+ "default.handlebars->29->1994"
37113
]
37114
},
37115
{
@@ -37136,9 +37129,9 @@
37129
"zh-chs": "未知设备组",
37130
"zh-cht": "未知裝置群",
37131
"xloc": [
37139
- "default.handlebars->29->1871",
37140
- "default.handlebars->29->1989",
37141
- "default.handlebars->29->2069"
37132
+ "default.handlebars->29->1864",
37133
+ "default.handlebars->29->1982",
37134
+ "default.handlebars->29->2062"
37135
]
37136
},
37137
{
@@ -37158,7 +37151,7 @@
37151
"zh-chs": "未知群组",
37152
"zh-cht": "未知群組",
37153
"xloc": [
37161
- "default.handlebars->29->2061"
37154
+ "default.handlebars->29->2054"
37155
]
37156
},
37157
{
@@ -37199,7 +37192,7 @@
37192
"zh-chs": "未知用户组",
37193
"zh-cht": "未知用戶群",
37194
"xloc": [
37202
- "default.handlebars->29->1995"
37195
+ "default.handlebars->29->1988"
37196
]
37197
},
37198
{
@@ -37262,7 +37255,7 @@
37255
"zh-chs": "解锁帐户",
37256
"zh-cht": "解鎖帳戶",
37257
"xloc": [
37265
- "default.handlebars->29->1746"
37258
+ "default.handlebars->29->1739"
37259
]
37260
},
37261
{
@@ -37306,7 +37299,7 @@
37299
"zh-chs": "最新",
37300
"zh-cht": "最新",
37301
"xloc": [
37309
- "default.handlebars->29->2129"
37302
+ "default.handlebars->29->2122"
37303
]
37304
},
37305
{
@@ -37354,8 +37347,8 @@
37347
"default-mobile.handlebars->9->311",
37348
"default-mobile.handlebars->9->329",
37349
"default-mobile.handlebars->9->332",
37357
- "default.handlebars->29->1578",
37358
- "default.handlebars->29->1586",
37350
+ "default.handlebars->29->1571",
37351
+ "default.handlebars->29->1579",
37352
"default.handlebars->29->904",
37353
"default.handlebars->29->928",
37354
"default.handlebars->29->931",
@@ -37480,7 +37473,7 @@
37473
"zh-cht": "上傳將覆蓋1個檔案。繼續?",
37474
"xloc": [
37475
"default-mobile.handlebars->9->330",
37483
- "default.handlebars->29->1587",
37476
+ "default.handlebars->29->1580",
37477
"default.handlebars->29->929"
37478
]
37479
},
@@ -37502,7 +37495,7 @@
37495
"zh-cht": "上傳將覆蓋{0}個檔案。繼續?",
37496
"xloc": [
37497
"default-mobile.handlebars->9->331",
37505
- "default.handlebars->29->1588",
37498
+ "default.handlebars->29->1581",
37499
"default.handlebars->29->930"
37500
]
37501
},
@@ -37540,7 +37533,7 @@
37533
"zh-chs": "上传:“{0}”",
37534
"zh-cht": "上傳:“{0}”",
37535
"xloc": [
37543
- "default.handlebars->29->1638"
37536
+ "default.handlebars->29->1631"
37537
]
37538
},
37539
{
@@ -37644,8 +37637,8 @@
37637
"zh-chs": "用过的",
37638
"zh-cht": "用過的",
37639
"xloc": [
37647
- "default.handlebars->29->2055",
37648
- "default.handlebars->29->2057"
37640
+ "default.handlebars->29->2048",
37641
+ "default.handlebars->29->2050"
37642
]
37643
},
37644
{
@@ -37666,10 +37659,10 @@
37659
"zh-cht": "用戶",
37660
"xloc": [
37661
"default.handlebars->29->1380",
37669
- "default.handlebars->29->1699",
37670
- "default.handlebars->29->1728",
37671
- "default.handlebars->29->1867",
37672
- "default.handlebars->29->2048",
37662
+ "default.handlebars->29->1692",
37663
+ "default.handlebars->29->1721",
37664
+ "default.handlebars->29->1860",
37665
+ "default.handlebars->29->2041",
37666
"default.handlebars->29->232",
37667
"default.handlebars->29->678"
37668
]
@@ -37691,7 +37684,7 @@
37684
"zh-chs": "用户+档案",
37685
"zh-cht": "用戶+檔案",
37686
"xloc": [
37694
- "default.handlebars->29->1729"
37687
+ "default.handlebars->29->1722"
37688
]
37689
},
37690
{
@@ -37711,10 +37704,10 @@
37704
"zh-chs": "用户帐户导入",
37705
"zh-cht": "用戶帳戶導入",
37706
"xloc": [
37714
- "default.handlebars->29->1762",
37715
- "default.handlebars->29->1763",
37716
- "default.handlebars->29->1765",
37717
- "default.handlebars->29->1767"
37707
+ "default.handlebars->29->1755",
37708
+ "default.handlebars->29->1756",
37709
+ "default.handlebars->29->1758",
37710
+ "default.handlebars->29->1760"
37711
]
37712
},
37713
{
@@ -37734,7 +37727,7 @@
37727
"zh-chs": "用户帐户",
37728
"zh-cht": "用戶帳戶",
37729
"xloc": [
37737
- "default.handlebars->29->2074"
37730
+ "default.handlebars->29->2067"
37731
]
37732
},
37733
{
@@ -37777,9 +37770,9 @@
37770
"zh-cht": "用戶同意",
37771
"xloc": [
37772
"default.handlebars->29->1348",
37780
- "default.handlebars->29->1858",
37773
+ "default.handlebars->29->1851",
37774
"default.handlebars->29->193",
37782
- "default.handlebars->29->1945",
37775
+ "default.handlebars->29->1938",
37776
"default.handlebars->29->614",
37777
"default.handlebars->29->745"
37778
]
@@ -37801,11 +37794,11 @@
37794
"zh-chs": "用户组",
37795
"zh-cht": "用戶群",
37796
"xloc": [
37804
- "default.handlebars->29->1438",
37805
- "default.handlebars->29->1439",
37806
- "default.handlebars->29->1831",
37807
- "default.handlebars->29->1997",
37808
- "default.handlebars->29->2016",
37797
+ "default.handlebars->29->1431",
37798
+ "default.handlebars->29->1432",
37799
+ "default.handlebars->29->1824",
37800
+ "default.handlebars->29->1990",
37801
+ "default.handlebars->29->2009",
37802
"default.handlebars->29->677"
37803
]
37804
},
@@ -37846,7 +37839,7 @@
37839
"zh-chs": "用户组成员",
37840
"zh-cht": "用戶群成員",
37841
"xloc": [
37849
- "default.handlebars->29->1994"
37842
+ "default.handlebars->29->1987"
37843
]
37844
},
37845
{
@@ -37886,9 +37879,9 @@
37879
"zh-chs": "用户识别码",
37880
"zh-cht": "用戶識別碼",
37881
"xloc": [
37889
- "default.handlebars->29->1503",
37890
- "default.handlebars->29->1912",
37891
- "default.handlebars->29->1913"
37882
+ "default.handlebars->29->1496",
37883
+ "default.handlebars->29->1905",
37884
+ "default.handlebars->29->1906"
37885
]
37886
},
37887
{
@@ -37908,8 +37901,8 @@
37901
"zh-chs": "用户标识符",
37902
"zh-cht": "用戶標識符",
37903
"xloc": [
37911
- "default.handlebars->29->1436",
37912
- "default.handlebars->29->1896"
37904
+ "default.handlebars->29->1429",
37905
+ "default.handlebars->29->1889"
37906
]
37907
},
37908
{
@@ -37929,7 +37922,7 @@
37922
"zh-chs": "用户列表输出",
37923
"zh-cht": "用戶列表輸出",
37924
"xloc": [
37932
- "default.handlebars->29->1774"
37925
+ "default.handlebars->29->1767"
37926
]
37927
},
37928
{
@@ -37950,7 +37943,7 @@
37943
"zh-cht": "用戶名",
37944
"xloc": [
37945
"default-mobile.handlebars->9->458",
37953
- "default.handlebars->29->1502"
37946
+ "default.handlebars->29->1495"
37947
]
37948
},
37949
{
@@ -38025,7 +38018,7 @@
38018
"zh-chs": "用户节",
38019
"zh-cht": "用戶節",
38020
"xloc": [
38028
- "default.handlebars->29->2094"
38021
+ "default.handlebars->29->2087"
38022
]
38023
},
38024
{
@@ -38150,7 +38143,7 @@
38143
"zh-chs": "用户组已更改:{0}",
38144
"zh-cht": "用戶組已更改:{0}",
38145
"xloc": [
38153
- "default.handlebars->29->1667"
38146
+ "default.handlebars->29->1660"
38147
]
38148
},
38149
{
@@ -38170,7 +38163,7 @@
38163
"zh-chs": "已创建用户组:{0}",
38164
"zh-cht": "已創建用戶組:{0}",
38165
"xloc": [
38173
- "default.handlebars->29->1657"
38166
+ "default.handlebars->29->1650"
38167
]
38168
},
38169
{
@@ -38190,7 +38183,7 @@
38183
"zh-chs": "用户组成员身份已更改:{0}",
38184
"zh-cht": "用戶組成員身份已更改:{0}",
38185
"xloc": [
38193
- "default.handlebars->29->1655"
38186
+ "default.handlebars->29->1648"
38187
]
38188
},
38189
{
@@ -38256,7 +38249,7 @@
38249
"zh-cht": "用戶名",
38250
"xloc": [
38251
"default-mobile.handlebars->9->274",
38259
- "default.handlebars->29->1786",
38252
+ "default.handlebars->29->1779",
38253
"default.handlebars->29->294",
38254
"default.handlebars->29->326",
38255
"default.handlebars->29->771",
@@ -38331,9 +38324,9 @@
38324
"zh-chs": "用户",
38325
"zh-cht": "用戶",
38326
"xloc": [
38334
- "default.handlebars->29->1819",
38335
- "default.handlebars->29->1859",
38336
- "default.handlebars->29->2093",
38327
+ "default.handlebars->29->1812",
38328
+ "default.handlebars->29->1852",
38329
+ "default.handlebars->29->2086",
38330
"default.handlebars->container->topbar->1->1->UsersSubMenuSpan->UsersSubMenu->1->0->UsersGeneral"
38331
]
38332
},
@@ -38354,7 +38347,7 @@
38347
"zh-chs": "用户会话",
38348
"zh-cht": "用戶節",
38349
"xloc": [
38357
- "default.handlebars->29->2078"
38350
+ "default.handlebars->29->2071"
38351
]
38352
},
38353
{
@@ -38394,7 +38387,7 @@
38387
"zh-chs": "验证电邮",
38388
"zh-cht": "驗證電郵",
38389
"xloc": [
38397
- "default.handlebars->29->1741"
38390
+ "default.handlebars->29->1734"
38391
]
38392
},
38393
{
@@ -38478,7 +38471,7 @@
38471
"zh-chs": "已验证",
38472
"zh-cht": "已驗證",
38473
"xloc": [
38481
- "default.handlebars->29->1975"
38474
+ "default.handlebars->29->1968"
38475
]
38476
},
38477
{
@@ -38521,7 +38514,7 @@
38514
"xloc": [
38515
"default-mobile.handlebars->9->63",
38516
"default.handlebars->29->1027",
38524
- "default.handlebars->29->1737"
38517
+ "default.handlebars->29->1730"
38518
]
38519
},
38520
{
@@ -38541,7 +38534,7 @@
38534
"zh-chs": "用户{0}的已验证电话号码",
38535
"zh-cht": "用戶{0}的已驗證電話號碼",
38536
"xloc": [
38544
- "default.handlebars->29->1684"
38537
+ "default.handlebars->29->1677"
38538
]
38539
},
38540
{
@@ -38630,7 +38623,7 @@
38623
"zh-chs": "版本不兼容,请先升级您的MeshCentral",
38624
"zh-cht": "版本不兼容,請先升級你的MeshCentral",
38625
"xloc": [
38633
- "default.handlebars->29->2125"
38626
+ "default.handlebars->29->2118"
38627
]
38628
},
38629
{
@@ -38712,7 +38705,7 @@
38705
"zh-chs": "查看所有事件",
38706
"zh-cht": "查看所有事件",
38707
"xloc": [
38715
- "default.handlebars->29->1811"
38708
+ "default.handlebars->29->1804"
38709
]
38710
},
38711
{
@@ -38732,8 +38725,8 @@
38725
"zh-chs": "查看变更日志",
38726
"zh-cht": "查看變更日誌",
38727
"xloc": [
38735
- "default.handlebars->29->2128",
38736
- "default.handlebars->29->2130"
38728
+ "default.handlebars->29->2121",
38729
+ "default.handlebars->29->2123"
38730
]
38731
},
38732
{
@@ -38793,7 +38786,7 @@
38786
"zh-chs": "查看有关此用户的注释",
38787
"zh-cht": "查看有關此用戶的註釋",
38788
"xloc": [
38796
- "default.handlebars->29->1953"
38789
+ "default.handlebars->29->1946"
38790
]
38791
},
38792
{
@@ -38917,8 +38910,8 @@
38910
"xloc": [
38911
"default-mobile.handlebars->9->430",
38912
"default-mobile.handlebars->9->445",
38920
- "default.handlebars->29->1458",
38921
- "default.handlebars->29->1488"
38913
+ "default.handlebars->29->1451",
38914
+ "default.handlebars->29->1481"
38915
]
38916
},
38917
{
@@ -39044,8 +39037,8 @@
39037
"zh-chs": "网络服务器",
39038
"zh-cht": "網絡伺服器",
39039
"xloc": [
39047
- "default.handlebars->29->2114",
39048
- "default.handlebars->29->2115"
39040
+ "default.handlebars->29->2107",
39041
+ "default.handlebars->29->2108"
39042
]
39043
},
39044
{
@@ -39065,7 +39058,7 @@
39058
"zh-chs": "Web服务器请求",
39059
"zh-cht": "Web伺服器請求",
39060
"xloc": [
This file is too large to show in full.
views/default.handlebars
+1
-1
@@ -5674,8 +5674,8 @@
5674
str += (', v' + EscapeHtml(node.intelamt.ver));
5675
}
5676
5677
- if (node.intelamt.tls == 1) { str += ', <span title="' + "Intel® AMT is setup with TLS network security" + '">' + "TLS" + '</span>'; }
5677
if (node.intelamt.state == 2) {
5678
+ if (node.intelamt.tls == 1) { str += ', <span title="' + "Intel® AMT is setup with TLS network security" + '">' + "TLS" + '</span>'; }
5679
if (node.intelamt.user == null || node.intelamt.user == '') {
5680
if ((meshrights & 4) != 0) {
5681
str += ', <i style=color:#FF0000;cursor:pointer title="' + "Edit Intel® AMT credentials" + '" onclick=editDeviceAmtSettings("' + node._id + '")>' + "No Credentials" + '</i>';