Improved Windows Terminal
Ylian Saint-Hilaire committed
Dec 12, 2018 at 15:34 UTC
210f867960fb5551c4223c22331c33d1c66d0651
17 files changed
+2733
-302
agents/MeshCmd-signed.exe
Binary files a/agents/MeshCmd-signed.exe and b/agents/MeshCmd-signed.exe differ
agents/MeshCmd64-signed.exe
Binary files a/agents/MeshCmd64-signed.exe and b/agents/MeshCmd64-signed.exe differ
agents/MeshService-signed.exe
Binary files a/agents/MeshService-signed.exe and b/agents/MeshService-signed.exe differ
agents/MeshService.exe
Binary files a/agents/MeshService.exe and b/agents/MeshService.exe differ
agents/MeshService64-signed.exe
Binary files a/agents/MeshService64-signed.exe and b/agents/MeshService64-signed.exe differ
agents/MeshService64.exe
Binary files a/agents/MeshService64.exe and b/agents/MeshService64.exe differ
agents/meshcore-01.js
new
+1892
@@ -0,0 +1,1892 @@
1
+/*
2
+Copyright 2018 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
+
18
+process.on('uncaughtException', function (ex) {
19
+ require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException1: " + ex });
20
+});
21
+
22
+// NOTE: This seems to cause big problems, don't enable the debugger in the server's meshcore.
23
+//attachDebugger({ webport: 9999, wait: 1 }).then(function (prt) { console.log('Point Browser for Debug to port: ' + prt); });
24
+
25
+// Mesh Rights
26
+const MESHRIGHT_EDITMESH = 1;
27
+const MESHRIGHT_MANAGEUSERS = 2;
28
+const MESHRIGHT_MANAGECOMPUTERS = 4;
29
+const MESHRIGHT_REMOTECONTROL = 8;
30
+const MESHRIGHT_AGENTCONSOLE = 16;
31
+const MESHRIGHT_SERVERFILES = 32;
32
+const MESHRIGHT_WAKEDEVICE = 64;
33
+const MESHRIGHT_SETNOTES = 128;
34
+const MESHRIGHT_REMOTEVIEW = 256;
35
+
36
+function createMeshCore(agent) {
37
+ var obj = {};
38
+
39
+ /*
40
+ function borderController() {
41
+ this.container = null;
42
+ this.Start = function Start(user) {
43
+ if (this.container == null) {
44
+ if (process.platform == 'win32') {
45
+ try {
46
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
47
+ } catch (ex) {
48
+ this.container = require('ScriptContainer').Create({ processIsolation: 1 });
49
+ }
50
+ } else {
51
+ this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
52
+ }
53
+ this.container.parent = this;
54
+ this.container.addModule('monitor-info', getJSModule('monitor-info'));
55
+ this.container.addModule('monitor-border', getJSModule('monitor-border'));
56
+ this.container.addModule('promise', getJSModule('promise'));
57
+ this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
58
+ this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
59
+ }
60
+ }
61
+ this.Stop = function Stop() {
62
+ if (this.container != null) {
63
+ this._container = this.container;
64
+ this._container.parent = this;
65
+ this.container = null;
66
+ this._container.exit();
67
+ }
68
+ }
69
+ }
70
+ obj.borderManager = new borderController();
71
+ */
72
+
73
+ // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
74
+ var meshCoreObj = { "action": "coreinfo", "value": "MeshCore v6", "caps": 14 }; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
75
+
76
+ // Get the operating system description string
77
+ try { require('os').name().then(function (v) { meshCoreObj.osdesc = v; }); } catch (ex) { }
78
+
79
+ var meshServerConnectionState = 0;
80
+ var tunnels = {};
81
+ var lastMeInfo = null;
82
+ var lastNetworkInfo = null;
83
+ var lastPublicLocationInfo = null;
84
+ var selfInfoUpdateTimer = null;
85
+ var http = require('http');
86
+ var net = require('net');
87
+ var fs = require('fs');
88
+ var rtc = require('ILibWebRTC');
89
+ var processManager = require('process-manager');
90
+ var amtMei = null, amtLms = null, amtLmsState = 0;
91
+ var amtMeiConnected = 0, amtMeiTmpState = null;
92
+ var wifiScannerLib = null;
93
+ var wifiScanner = null;
94
+ var networkMonitor = null;
95
+ var amtscanner = null;
96
+ var nextTunnelIndex = 1;
97
+
98
+ // If we are running in Duktape, agent will be null
99
+ if (agent == null) {
100
+ // Running in native agent, Import libraries
101
+ db = require('SimpleDataStore').Shared();
102
+ sha = require('SHA256Stream');
103
+ mesh = require('MeshAgent');
104
+ childProcess = require('child_process');
105
+ if (mesh.hasKVM == 1) { // if the agent is compiled with KVM support
106
+ // Check if this computer supports a desktop
107
+ try { if ((process.platform == 'win32') || (process.platform == 'darwin') || (require('monitor-info').kvm_x11_support)) { meshCoreObj.caps |= 1; } } catch (ex) { }
108
+ }
109
+ } else {
110
+ // Running in nodejs
111
+ meshCoreObj.value += '-NodeJS';
112
+ meshCoreObj.caps = 8;
113
+ mesh = agent.getMeshApi();
114
+ }
115
+
116
+ /*
117
+ var AMTScanner = require("AMTScanner");
118
+ var scan = new AMTScanner();
119
+
120
+ scan.on("found", function (data) {
121
+ if (typeof data === 'string') {
122
+ console.log(data);
123
+ } else {
124
+ console.log(JSON.stringify(data, null, " "));
125
+ }
126
+ });
127
+ scan.scan("10.2.55.140", 1000);
128
+ scan.scan("10.2.55.139-10.2.55.145", 1000);
129
+ scan.scan("10.2.55.128/25", 2000);
130
+ */
131
+
132
+ /*
133
+ // Try to load up the network monitor
134
+ try {
135
+ networkMonitor = require('NetworkMonitor');
136
+ networkMonitor.on('change', function () { sendNetworkUpdateNagle(); });
137
+ networkMonitor.on('add', function (addr) { sendNetworkUpdateNagle(); });
138
+ networkMonitor.on('remove', function (addr) { sendNetworkUpdateNagle(); });
139
+ } catch (e) { networkMonitor = null; }
140
+ */
141
+
142
+ // Try to load up the Intel AMT scanner
143
+ try {
144
+ var AMTScannerModule = require('amt-scanner');
145
+ amtscanner = new AMTScannerModule();
146
+ //amtscanner.on('found', function (data) { if (typeof data != 'string') { data = JSON.stringify(data, null, " "); } sendConsoleText(data); });
147
+ } catch (ex) { amtscanner = null; }
148
+
149
+ // Fetch the SMBios Tables
150
+ var SMBiosTables = null;
151
+ var SMBiosTablesRaw = null;
152
+ try {
153
+ require('smbios').get(function (data) {
154
+ if (data != null) {
155
+ SMBiosTablesRaw = data;
156
+ SMBiosTables = require('smbios').parse(data)
157
+ if (mesh.isControlChannelConnected) { mesh.SendCommand({ "action": "smbios", "value": SMBiosTablesRaw }); }
158
+
159
+ // If SMBios tables say that AMT is present, try to connect MEI
160
+ if (SMBiosTables.amtInfo && (SMBiosTables.amtInfo.AMT == true)) {
161
+ // Try to load up the MEI module
162
+ try {
163
+ var amtMeiLib = require('amt-mei');
164
+ amtMei = new amtMeiLib();
165
+ amtMei.on('error', function (e) { amtMeiLib = null; amtMei = null; amtMeiConnected = -1; });
166
+ amtMeiConnected = 2;
167
+ sendPeriodicServerUpdate(1);
168
+ } catch (ex) { amtMeiLib = null; amtMei = null; amtMeiConnected = -1; }
169
+ }
170
+ }
171
+ });
172
+ } catch (ex) { sendConsoleText(ex); }
173
+
174
+ // Try to load up the WIFI scanner
175
+ try {
176
+ var wifiScannerLib = require('wifi-scanner');
177
+ wifiScanner = new wifiScannerLib();
178
+ wifiScanner.on('accessPoint', function (data) { sendConsoleText(data); });
179
+ } catch (ex) { wifiScannerLib = null; wifiScanner = null; }
180
+
181
+ // Get our location (lat/long) using our public IP address
182
+ var getIpLocationDataExInProgress = false;
183
+ var getIpLocationDataExCounts = [0, 0];
184
+ function getIpLocationDataEx(func) {
185
+ if (getIpLocationDataExInProgress == true) { return false; }
186
+ try {
187
+ getIpLocationDataExInProgress = true;
188
+ getIpLocationDataExCounts[0]++;
189
+ var options = http.parseUri("http://ipinfo.io/json");
190
+ options.method = 'GET';
191
+ http.request(options, function (resp) {
192
+ if (resp.statusCode == 200) {
193
+ var geoData = '';
194
+ resp.data = function (geoipdata) { geoData += geoipdata; };
195
+ resp.end = function () {
196
+ var location = null;
197
+ try {
198
+ if (typeof geoData == 'string') {
199
+ var result = JSON.parse(geoData);
200
+ if (result.ip && result.loc) { location = result; }
201
+ }
202
+ } catch (e) { }
203
+ if (func) { getIpLocationDataExCounts[1]++; func(location); }
204
+ }
205
+ } else { func(null); }
206
+ getIpLocationDataExInProgress = false;
207
+ }).end();
208
+ return true;
209
+ }
210
+ catch (e) { return false; }
211
+ }
212
+
213
+ // Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
214
+ function clearGatewayMac(str) {
215
+ if (str == null) return null;
216
+ var x = JSON.parse(str);
217
+ for (var i in x.netif) { if (x.netif[i].gatewaymac) { delete x.netif[i].gatewaymac } }
218
+ return JSON.stringify(x);
219
+ }
220
+
221
+ function getIpLocationData(func) {
222
+ // Get the location information for the cache if possible
223
+ var publicLocationInfo = db.Get('publicLocationInfo');
224
+ if (publicLocationInfo != null) { publicLocationInfo = JSON.parse(publicLocationInfo); }
225
+ if (publicLocationInfo == null) {
226
+ // Nothing in the cache, fetch the data
227
+ getIpLocationDataEx(function (locationData) {
228
+ if (locationData != null) {
229
+ publicLocationInfo = {};
230
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
231
+ publicLocationInfo.locationData = locationData;
232
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
233
+ if (func) func(locationData); // Report the new location
234
+ } else {
235
+ if (func) func(null); // Report no location
236
+ }
237
+ });
238
+ } else {
239
+ // Check the cache
240
+ if (clearGatewayMac(publicLocationInfo.netInfoStr) == clearGatewayMac(lastNetworkInfo)) {
241
+ // Cache match
242
+ if (func) func(publicLocationInfo.locationData);
243
+ } else {
244
+ // Cache mismatch
245
+ getIpLocationDataEx(function (locationData) {
246
+ if (locationData != null) {
247
+ publicLocationInfo = {};
248
+ publicLocationInfo.netInfoStr = lastNetworkInfo;
249
+ publicLocationInfo.locationData = locationData;
250
+ var x = db.Put('publicLocationInfo', JSON.stringify(publicLocationInfo)); // Save to database
251
+ if (func) func(locationData); // Report the new location
252
+ } else {
253
+ if (func) func(publicLocationInfo.locationData); // Can't get new location, report the old location
254
+ }
255
+ });
256
+ }
257
+ }
258
+ }
259
+
260
+ // Polyfill String.endsWith
261
+ if (!String.prototype.endsWith) {
262
+ String.prototype.endsWith = function (searchString, position) {
263
+ var subjectString = this.toString();
264
+ if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { position = subjectString.length; }
265
+ position -= searchString.length;
266
+ var lastIndex = subjectString.lastIndexOf(searchString, position);
267
+ return lastIndex !== -1 && lastIndex === position;
268
+ };
269
+ }
270
+
271
+ // Polyfill path.join
272
+ obj.path = {
273
+ join: function () {
274
+ var x = [];
275
+ for (var i in arguments) {
276
+ var w = arguments[i];
277
+ if (w != null) {
278
+ while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
279
+ if (i != 0) {
280
+ while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); }
281
+ }
282
+ x.push(w);
283
+ }
284
+ }
285
+ if (x.length == 0) return '/';
286
+ return x.join('/');
287
+ }
288
+ };
289
+
290
+ // Replace a string with a number if the string is an exact number
291
+ function toNumberIfNumber(x) { if ((typeof x == 'string') && (+parseInt(x) === x)) { x = parseInt(x); } return x; }
292
+
293
+ // Convert decimal to hex
294
+ function char2hex(i) { return (i + 0x100).toString(16).substr(-2).toUpperCase(); }
295
+
296
+ // Convert a raw string to a hex string
297
+ function rstr2hex(input) { var r = '', i; for (i = 0; i < input.length; i++) { r += char2hex(input.charCodeAt(i)); } return r; }
298
+
299
+ // Convert a buffer into a string
300
+ function buf2rstr(buf) { var r = ''; for (var i = 0; i < buf.length; i++) { r += String.fromCharCode(buf[i]); } return r; }
301
+
302
+ // Convert a hex string to a raw string // TODO: Do this using Buffer(), will be MUCH faster
303
+ function hex2rstr(d) {
304
+ if (typeof d != "string" || d.length == 0) return '';
305
+ var r = '', m = ('' + d).match(/../g), t;
306
+ while (t = m.shift()) r += String.fromCharCode('0x' + t);
307
+ return r
308
+ }
309
+
310
+ // Convert an object to string with all functions
311
+ function objToString(x, p, pad, ret) {
312
+ if (ret == undefined) ret = '';
313
+ if (p == undefined) p = 0;
314
+ if (x == null) { return '[null]'; }
315
+ if (p > 8) { return '[...]'; }
316
+ if (x == undefined) { return '[undefined]'; }
317
+ if (typeof x == 'string') { if (p == 0) return x; return '"' + x + '"'; }
318
+ if (typeof x == 'buffer') { return '[buffer]'; }
319
+ if (typeof x != 'object') { return x; }
320
+ var r = '{' + (ret ? '\r\n' : ' ');
321
+ for (var i in x) { if (i != '_ObjectID') { r += (addPad(p + 2, pad) + i + ': ' + objToString(x[i], p + 2, pad, ret) + (ret ? '\r\n' : ' ')); } }
322
+ return r + addPad(p, pad) + '}';
323
+ }
324
+
325
+ // Return p number of spaces
326
+ function addPad(p, ret) { var r = ''; for (var i = 0; i < p; i++) { r += ret; } return r; }
327
+
328
+ // Split a string taking into account the quoats. Used for command line parsing
329
+ function splitArgs(str) {
330
+ var myArray = [], myRegexp = /[^\s"]+|"([^"]*)"/gi;
331
+ do { var match = myRegexp.exec(str); if (match != null) { myArray.push(match[1] ? match[1] : match[0]); } } while (match != null);
332
+ return myArray;
333
+ }
334
+
335
+ // Parse arguments string array into an object
336
+ function parseArgs(argv) {
337
+ var results = { '_': [] }, current = null;
338
+ for (var i = 1, len = argv.length; i < len; i++) {
339
+ var x = argv[i];
340
+ if (x.length > 2 && x[0] == '-' && x[1] == '-') {
341
+ if (current != null) { results[current] = true; }
342
+ current = x.substring(2);
343
+ } else {
344
+ if (current != null) { results[current] = toNumberIfNumber(x); current = null; } else { results['_'].push(toNumberIfNumber(x)); }
345
+ }
346
+ }
347
+ if (current != null) { results[current] = true; }
348
+ return results;
349
+ }
350
+
351
+ // Get server target url with a custom path
352
+ function getServerTargetUrl(path) {
353
+ var x = mesh.ServerUrl;
354
+ //sendConsoleText("mesh.ServerUrl: " + mesh.ServerUrl);
355
+ if (x == null) { return null; }
356
+ if (path == null) { path = ''; }
357
+ x = http.parseUri(x);
358
+ if (x == null) return null;
359
+ return x.protocol + '//' + x.host + ':' + x.port + '/' + path;
360
+ }
361
+
362
+ // Get server url. If the url starts with "*/..." change it, it not use the url as is.
363
+ function getServerTargetUrlEx(url) {
364
+ if (url.substring(0, 2) == '*/') { return getServerTargetUrl(url.substring(2)); }
365
+ return url;
366
+ }
367
+
368
+ // Send a wake-on-lan packet
369
+ function sendWakeOnLan(hexMac) {
370
+ var count = 0;
371
+ try {
372
+ var interfaces = require('os').networkInterfaces();
373
+ var magic = 'FFFFFFFFFFFF';
374
+ for (var x = 1; x <= 16; ++x) { magic += hexMac; }
375
+ var magicbin = Buffer.from(magic, 'hex');
376
+
377
+ for (var adapter in interfaces) {
378
+ if (interfaces.hasOwnProperty(adapter)) {
379
+ for (var i = 0; i < interfaces[adapter].length; ++i) {
380
+ var addr = interfaces[adapter][i];
381
+ if ((addr.family == 'IPv4') && (addr.mac != '00:00:00:00:00:00')) {
382
+ var socket = require('dgram').createSocket({ type: "udp4" });
383
+ socket.bind({ address: addr.address });
384
+ socket.setBroadcast(true);
385
+ socket.send(magicbin, 7, "255.255.255.255");
386
+ count++;
387
+ }
388
+ }
389
+ }
390
+ }
391
+ } catch (e) { }
392
+ return count;
393
+ }
394
+
395
+ // Handle a mesh agent command
396
+ function handleServerCommand(data) {
397
+ if (typeof data == 'object') {
398
+ // If this is a console command, parse it and call the console handler
399
+ switch (data.action) {
400
+ case 'msg': {
401
+ switch (data.type) {
402
+ case 'console': { // Process a console command
403
+ if (data.value && data.sessionid) {
404
+ var args = splitArgs(data.value);
405
+ processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
406
+ }
407
+ break;
408
+ }
409
+ case 'tunnel': {
410
+ if (data.value != null) { // Process a new tunnel connection request
411
+ // Create a new tunnel object
412
+ var xurl = getServerTargetUrlEx(data.value);
413
+ if (xurl != null) {
414
+ var woptions = http.parseUri(xurl);
415
+ woptions.rejectUnauthorized = 0;
416
+ //sendConsoleText(JSON.stringify(woptions));
417
+ var tunnel = http.request(woptions);
418
+ tunnel.upgrade = onTunnelUpgrade;
419
+ tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
420
+ tunnel.sessionid = data.sessionid;
421
+ tunnel.rights = data.rights;
422
+ tunnel.state = 0;
423
+ tunnel.url = xurl;
424
+ tunnel.protocol = 0;
425
+ tunnel.tcpaddr = data.tcpaddr;
426
+ tunnel.tcpport = data.tcpport;
427
+ tunnel.end();
428
+ // Put the tunnel in the tunnels list
429
+ var index = nextTunnelIndex++;
430
+ tunnel.index = index;
431
+ tunnels[index] = tunnel;
432
+
433
+ //sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
434
+ }
435
+ }
436
+ break;
437
+ }
438
+ case 'ps': {
439
+ // Return the list of running processes
440
+ if (data.sessionid) {
441
+ processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
442
+ }
443
+ break;
444
+ }
445
+ case 'pskill': {
446
+ // Kill a process
447
+ if (data.value) {
448
+ try { process.kill(data.value); } catch (e) { sendConsoleText(JSON.stringify(e)); }
449
+ }
450
+ break;
451
+ }
452
+ case 'openUrl': {
453
+ // Open a local web browser and return success/fail
454
+ sendConsoleText('OpenURL: ' + data.url);
455
+ if (data.url) { mesh.SendCommand({ "action": "msg", "type":"openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
456
+ break;
457
+ }
458
+ }
459
+ break;
460
+ }
461
+ case 'wakeonlan': {
462
+ // Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
463
+ sendConsoleText('Server requesting wake-on-lan for: ' + data.macs.join(', '));
464
+ for (var i in data.macs) { sendWakeOnLan(data.macs[i]); }
465
+ break;
466
+ }
467
+ case 'poweraction': {
468
+ // Server telling us to execute a power action
469
+ if ((mesh.ExecPowerState != undefined) && (data.actiontype)) {
470
+ var forced = 0;
471
+ if (data.forced == 1) { forced = 1; }
472
+ data.actiontype = parseInt(data.actiontype);
473
+ sendConsoleText('Performing power action=' + data.actiontype + ', forced=' + forced + '.');
474
+ var r = mesh.ExecPowerState(data.actiontype, forced);
475
+ sendConsoleText('ExecPowerState returned code: ' + r);
476
+ }
477
+ break;
478
+ }
479
+ case 'iplocation': {
480
+ // 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
481
+ getIpLocationData(function (location) { mesh.SendCommand({ "action": "iplocation", "type": "publicip", "value": location }); });
482
+ break;
483
+ }
484
+ case 'toast': {
485
+ // Display a toast message
486
+ if (data.title && data.msg) { require('toaster').Toast(data.title, data.msg); }
487
+ break;
488
+ }
489
+ case 'openUrl': {
490
+ // Open a local web browser and return success/fail
491
+ sendConsoleText('OpenURL: ' + data.url);
492
+ if (data.url) { mesh.SendCommand({ "action": "openUrl", "url": data.url, "sessionid": data.sessionid, "success": (openUserDesktopUrl(data.url) != null) }); }
493
+ break;
494
+ }
495
+ }
496
+ }
497
+ }
498
+
499
+ // Called when a file changed in the file system
500
+ /*
501
+ function onFileWatcher(a, b) {
502
+ console.log('onFileWatcher', a, b, this.path);
503
+ var response = getDirectoryInfo(this.path);
504
+ if ((response != undefined) && (response != null)) { this.tunnel.s.write(JSON.stringify(response)); }
505
+ }
506
+ */
507
+
508
+ // Get a formated response for a given directory path
509
+ function getDirectoryInfo(reqpath) {
510
+ var response = { path: reqpath, dir: [] };
511
+ if (((reqpath == undefined) || (reqpath == '')) && (process.platform == 'win32')) {
512
+ // List all the drives in the root, or the root itself
513
+ var results = null;
514
+ try { results = fs.readDrivesSync(); } catch (e) { } // TODO: Anyway to get drive total size and free space? Could draw a progress bar.
515
+ if (results != null) {
516
+ for (var i = 0; i < results.length; ++i) {
517
+ var drive = { n: results[i].name, t: 1 };
518
+ if (results[i].type == 'REMOVABLE') { drive.dt = 'removable'; } // TODO: See if this is USB/CDROM or something else, we can draw icons.
519
+ response.dir.push(drive);
520
+ }
521
+ }
522
+ } else {
523
+ // List all the files and folders in this path
524
+ if (reqpath == '') { reqpath = '/'; }
525
+ var results = null, xpath = obj.path.join(reqpath, '*');
526
+ //if (process.platform == "win32") { xpath = xpath.split('/').join('\\'); }
527
+ try { results = fs.readdirSync(xpath); } catch (e) { }
528
+ if (results != null) {
529
+ for (var i = 0; i < results.length; ++i) {
530
+ if ((results[i] != '.') && (results[i] != '..')) {
531
+ var stat = null, p = obj.path.join(reqpath, results[i]);
532
+ //if (process.platform == "win32") { p = p.split('/').join('\\'); }
533
+ try { stat = fs.statSync(p); } catch (e) { } // TODO: Get file size/date
534
+ if ((stat != null) && (stat != undefined)) {
535
+ if (stat.isDirectory() == true) {
536
+ response.dir.push({ n: results[i], t: 2, d: stat.mtime });
537
+ } else {
538
+ response.dir.push({ n: results[i], t: 3, s: stat.size, d: stat.mtime });
539
+ }
540
+ }
541
+ }
542
+ }
543
+ }
544
+ }
545
+ return response;
546
+ }
547
+
548
+ // Tunnel callback operations
549
+ function onTunnelUpgrade(response, s, head) {
550
+ this.s = s;
551
+ s.httprequest = this;
552
+ s.end = onTunnelClosed;
553
+ s.tunnel = this;
554
+
555
+ if (this.tcpport != null) {
556
+ // This is a TCP relay connection, pause now and try to connect to the target.
557
+ s.pause();
558
+ s.data = onTcpRelayServerTunnelData;
559
+ var connectionOptions = { port: parseInt(this.tcpport) };
560
+ if (this.tcpaddr != null) { connectionOptions.host = this.tcpaddr; } else { connectionOptions.host = '127.0.0.1'; }
561
+ s.tcprelay = net.createConnection(connectionOptions, onTcpRelayTargetTunnelConnect);
562
+ s.tcprelay.peerindex = this.index;
563
+ } else {
564
+ // This is a normal connect for KVM/Terminal/Files
565
+ s.data = onTunnelData;
566
+ }
567
+ }
568
+
569
+ // Called when the TCP relay target is connected
570
+ function onTcpRelayTargetTunnelConnect() {
571
+ var peerTunnel = tunnels[this.peerindex];
572
+ this.pipe(peerTunnel.s); // Pipe Target --> Server
573
+ peerTunnel.s.first = true;
574
+ peerTunnel.s.resume();
575
+ }
576
+
577
+ // Called when we get data from the server for a TCP relay (We have to skip the first received 'c' and pipe the rest)
578
+ function onTcpRelayServerTunnelData(data) {
579
+ if (this.first == true) { this.first = false; this.pipe(this.tcprelay); } // Pipe Server --> Target
580
+ }
581
+
582
+ function onTunnelClosed() {
583
+ if (tunnels[this.httprequest.index] == null) return; // Stop duplicate calls.
584
+ //sendConsoleText("Tunnel #" + this.httprequest.index + " closed.", this.httprequest.sessionid);
585
+ delete tunnels[this.httprequest.index];
586
+
587
+ /*
588
+ // Close the watcher if required
589
+ if (this.httprequest.watcher != undefined) {
590
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
591
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
592
+ delete this.httprequest.watcher;
593
+ }
594
+ */
595
+
596
+ // If there is a upload or download active on this connection, close the file
597
+ if (this.httprequest.uploadFile) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
598
+ if (this.httprequest.downloadFile) { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; }
599
+
600
+ // Clean up WebRTC
601
+ if (this.webrtc != null) {
602
+ 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; }
603
+ if (this.webrtc.websocket) { delete this.webrtc.websocket; }
604
+ try { this.webrtc.close(); } catch (e) { }
605
+ this.webrtc.removeAllListeners('connected');
606
+ this.webrtc.removeAllListeners('disconnected');
607
+ this.webrtc.removeAllListeners('dataChannel');
608
+ delete this.webrtc;
609
+ }
610
+
611
+ // Clean up WebSocket
612
+ this.removeAllListeners('data');
613
+ }
614
+ function onTunnelSendOk() { /*sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid);*/ }
615
+ function onTunnelData(data) {
616
+ //console.log("OnTunnelData");
617
+ //sendConsoleText('OnTunnelData, ' + data.length + ', ' + typeof data + ', ' + data);
618
+
619
+ // If this is upload data, save it to file
620
+ if (this.httprequest.uploadFile) {
621
+ try { fs.writeSync(this.httprequest.uploadFile, data); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror' }))); return; } // Write to the file, if there is a problem, error out.
622
+ this.write(new Buffer(JSON.stringify({ action: 'uploadack', reqid: this.httprequest.uploadFileid }))); // Ask for more data
623
+ return;
624
+ }
625
+ /*
626
+ // If this is a download, send more of the file
627
+ if (this.httprequest.downloadFile) {
628
+ var buf = new Buffer(4096);
629
+ var len = fs.readSync(this.httprequest.downloadFile, buf, 0, 4096, null);
630
+ this.httprequest.downloadFilePtr += len;
631
+ if (len > 0) { this.write(buf.slice(0, len)); } else { fs.closeSync(this.httprequest.downloadFile); this.httprequest.downloadFile = undefined; this.end(); }
632
+ return;
633
+ }
634
+ */
635
+
636
+ if (this.httprequest.state == 0) {
637
+ // Check if this is a relay connection
638
+ if (data == 'c') { this.httprequest.state = 1; /*sendConsoleText("Tunnel #" + this.httprequest.index + " now active", this.httprequest.sessionid);*/ }
639
+ } else {
640
+ // Handle tunnel data
641
+ if (this.httprequest.protocol == 0) { // 1 = SOL, 2 = KVM, 3 = IDER, 4 = Files, 5 = FileTransfer
642
+ // Take a look at the protocol
643
+ this.httprequest.protocol = parseInt(data);
644
+ if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
645
+ if (this.httprequest.protocol == 1) {
646
+ // Check user access rights
647
+ if ((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) {
648
+ // Disengage this tunnel, user does not have the rights to do this!!
649
+ this.httprequest.protocol = 999999;
650
+ sendConsoleText('Error: No Remote Control Rights.');
651
+ return;
652
+ }
653
+
654
+ // Remote terminal using native pipes
655
+ if (process.platform == "win32") {
656
+ this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
657
+ } else {
658
+ this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
659
+ }
660
+
661
+ this.httprequest.process.tunnel = this;
662
+ this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
663
+ this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
664
+ this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
665
+ this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
666
+ this.prependListener('end', function () { this.httprequest.process.kill(); });
667
+ this.removeAllListeners('data');
668
+ this.on('data', onTunnelControlData);
669
+ //this.write('MeshCore Terminal Hello');
670
+ if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nclear\n"); }
671
+ } else if (this.httprequest.protocol == 2)
672
+ {
673
+ // Check user access rights
674
+ if (((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) && ((this.httprequest.rights & MESHRIGHT_REMOTEVIEW) == 0)) {
675
+ // Disengage this tunnel, user does not have the rights to do this!!
676
+ this.httprequest.protocol = 999999;
677
+ sendConsoleText('Error: No Remote Control Rights.');
678
+ return;
679
+ }
680
+
681
+ // Remote desktop using native pipes
682
+ this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
683
+ this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
684
+ this.desktop = this.httprequest.desktop;
685
+
686
+ // Display a toast message
687
+ //require('toaster').Toast('MeshCentral', 'Remote Desktop Control Started.');
688
+
689
+ this.end = function () {
690
+ --this.desktop.kvm.connectionCount;
691
+ this.unpipe(this.httprequest.desktop.kvm);
692
+ this.httprequest.desktop.kvm.unpipe(this);
693
+ if (this.desktop.kvm.connectionCount == 0) {
694
+ // Display a toast message
695
+ //require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.');
696
+ this.httprequest.desktop.kvm.end();
697
+ }
698
+ };
699
+ if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
700
+
701
+ //sendConsoleText('KVM Rights: ' + this.httprequest.rights);
702
+ if ((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) != 0) {
703
+ // If we have remote control rights, pipe the KVM input
704
+ this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. Pipe the Browser --> KVM input.
705
+ } else {
706
+ // We need to only pipe non-mouse & non-keyboard inputs.
707
+ // TODO!!!
708
+ }
709
+
710
+ this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. Pipe the KVM --> Browser images.
711
+ this.removeAllListeners('data');
712
+ this.on('data', onTunnelControlData);
713
+ //this.write('MeshCore KVM Hello!1');
714
+ } else if (this.httprequest.protocol == 5) {
715
+ // Check user access rights
716
+ if ((this.httprequest.rights & MESHRIGHT_REMOTECONTROL) == 0) {
717
+ // Disengage this tunnel, user does not have the rights to do this!!
718
+ this.httprequest.protocol = 999999;
719
+ sendConsoleText('Error: No Remote Control Rights.');
720
+ return;
721
+ }
722
+
723
+ // Setup files
724
+ // NOP
725
+ }
726
+ } else if (this.httprequest.protocol == 1) {
727
+ // Send data into terminal stdin
728
+ //this.write(data); // Echo back the keys (Does not seem to be a good idea)
729
+ this.httprequest.process.write(data);
730
+ } else if (this.httprequest.protocol == 2) {
731
+ // Send data into remote desktop
732
+ if (this.httprequest.desktop.state == 0) {
733
+ this.write(new Buffer(String.fromCharCode(0x11, 0xFE, 0x00, 0x00, 0x4D, 0x45, 0x53, 0x48, 0x00, 0x00, 0x00, 0x00, 0x02)));
734
+ this.httprequest.desktop.state = 1;
735
+ } else {
736
+ this.httprequest.desktop.write(data);
737
+ }
738
+ } else if (this.httprequest.protocol == 5) {
739
+ // Process files commands
740
+ var cmd = null;
741
+ try { cmd = JSON.parse(data); } catch (e) { };
742
+ if (cmd == null) { return; }
743
+ if ((cmd.ctrlChannel == '102938') || ((cmd.type == 'offer') && (cmd.sdp != null))) { onTunnelControlData(cmd, this); return; } // If this is control data, handle it now.
744
+ if (cmd.action == undefined) { return; }
745
+ //sendConsoleText('CMD: ' + JSON.stringify(cmd));
746
+
747
+ if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
748
+ //console.log(objToString(cmd, 0, ' '));
749
+ switch (cmd.action) {
750
+ case 'ls': {
751
+ /*
752
+ // Close the watcher if required
753
+ var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
754
+ if ((this.httprequest.watcher != undefined) && (samepath == false)) {
755
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
756
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
757
+ delete this.httprequest.watcher;
758
+ }
759
+ */
760
+
761
+ // Send the folder content to the browser
762
+ var response = getDirectoryInfo(cmd.path);
763
+ if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
764
+ this.write(new Buffer(JSON.stringify(response)));
765
+
766
+ /*
767
+ // Start the directory watcher
768
+ if ((cmd.path != '') && (samepath == false)) {
769
+ var watcher = fs.watch(cmd.path, onFileWatcher);
770
+ watcher.tunnel = this.httprequest;
771
+ watcher.path = cmd.path;
772
+ this.httprequest.watcher = watcher;
773
+ //console.log('Starting watcher: ' + this.httprequest.watcher.path);
774
+ }
775
+ */
776
+ break;
777
+ }
778
+ case 'mkdir': {
779
+ // Create a new empty folder
780
+ fs.mkdirSync(cmd.path);
781
+ break;
782
+ }
783
+ case 'rm': {
784
+ // Delete, possibly recursive delete
785
+ for (var i in cmd.delfiles) {
786
+ try { deleteFolderRecursive(obj.path.join(cmd.path, cmd.delfiles[i]), cmd.rec); } catch (e) { }
787
+ }
788
+ break;
789
+ }
790
+ case 'rename': {
791
+ // Rename a file or folder
792
+ var oldfullpath = obj.path.join(cmd.path, cmd.oldname);
793
+ var newfullpath = obj.path.join(cmd.path, cmd.newname);
794
+ try { fs.renameSync(oldfullpath, newfullpath); } catch (e) { console.log(e); }
795
+ break;
796
+ }
797
+ case 'download': {
798
+ // Download a file
799
+ var sendNextBlock = 0;
800
+ if (cmd.sub == 'start') { // Setup the download
801
+ if (this.filedownload != null) { this.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
802
+ this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
803
+ 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; }
804
+ if (this.filedownload) { this.write({ action: 'download', sub: 'start', id: cmd.id }); }
805
+ } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
806
+ if (cmd.sub == 'startack') { sendNextBlock = 8; } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
807
+ }
808
+ // Send the next download block(s)
809
+ while (sendNextBlock > 0) {
810
+ sendNextBlock--;
811
+ var buf = new Buffer(4096);
812
+ var len = fs.readSync(this.filedownload.f, buf, 4, 4092, null);
813
+ this.filedownload.ptr += len;
814
+ if (len < 4092) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
815
+ this.write(buf.slice(0, len + 4)); // Write as binary
816
+ }
817
+ break;
818
+ }
819
+ /*
820
+ case 'download': {
821
+ // Packet download of a file, agent to browser
822
+ if (cmd.path == undefined) break;
823
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
824
+ //console.log('Download: ' + filepath);
825
+ try { this.httprequest.downloadFile = fs.openSync(filepath, 'rbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'downloaderror', reqid: cmd.reqid }))); break; }
826
+ this.httprequest.downloadFileId = cmd.reqid;
827
+ this.httprequest.downloadFilePtr = 0;
828
+ if (this.httprequest.downloadFile) { this.write(new Buffer(JSON.stringify({ action: 'downloadstart', reqid: this.httprequest.downloadFileId }))); }
829
+ break;
830
+ }
831
+ case 'download2': {
832
+ // Stream download of a file, agent to browser
833
+ if (cmd.path == undefined) break;
834
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
835
+ try { this.httprequest.downloadFile = fs.createReadStream(filepath, { flags: 'rbN' }); } catch (e) { console.log(e); }
836
+ this.httprequest.downloadFile.pipe(this);
837
+ this.httprequest.downloadFile.end = function () { }
838
+ break;
839
+ }
840
+ */
841
+ case 'upload': {
842
+ // Upload a file, browser to agent
843
+ if (this.httprequest.uploadFile != undefined) { fs.closeSync(this.httprequest.uploadFile); this.httprequest.uploadFile = undefined; }
844
+ if (cmd.path == undefined) break;
845
+ var filepath = cmd.name ? obj.path.join(cmd.path, cmd.name) : cmd.path;
846
+ try { this.httprequest.uploadFile = fs.openSync(filepath, 'wbN'); } catch (e) { this.write(new Buffer(JSON.stringify({ action: 'uploaderror', reqid: cmd.reqid }))); break; }
847
+ this.httprequest.uploadFileid = cmd.reqid;
848
+ if (this.httprequest.uploadFile) { this.write(new Buffer(JSON.stringify({ action: 'uploadstart', reqid: this.httprequest.uploadFileid }))); }
849
+ break;
850
+ }
851
+ case 'copy': {
852
+ // Copy a bunch of files from scpath to dspath
853
+ for (var i in cmd.names) {
854
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
855
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
856
+ }
857
+ break;
858
+ }
859
+ case 'move': {
860
+ // Move a bunch of files from scpath to dspath
861
+ for (var i in cmd.names) {
862
+ var sc = obj.path.join(cmd.scpath, cmd.names[i]), ds = obj.path.join(cmd.dspath, cmd.names[i]);
863
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
864
+ }
865
+ break;
866
+ }
867
+ }
868
+ }
869
+ //sendConsoleText("Got tunnel #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid);
870
+ }
871
+ }
872
+
873
+ // Called when receiving control data on WebRTC
874
+ function onTunnelWebRTCControlData(data) {
875
+ if (typeof data != 'string') return;
876
+ var obj;
877
+ try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON on WebRTC: ' + data); return; }
878
+ if (obj.type == 'close') {
879
+ //sendConsoleText('Tunnel #' + this.xrtc.websocket.tunnel.index + ' WebRTC control close');
880
+ try { this.close(); } catch (e) { }
881
+ try { this.xrtc.close(); } catch (e) { }
882
+ }
883
+ }
884
+
885
+ // Called when receiving control data on websocket
886
+ function onTunnelControlData(data, ws) {
887
+ var obj;
888
+ if (ws == null) { ws = this; }
889
+ if (typeof data == 'string') { try { obj = JSON.parse(data); } catch (e) { sendConsoleText('Invalid control JSON: ' + data); return; } }
890
+ else if (typeof data == 'object') { obj = data; } else { return; }
891
+ //sendConsoleText('onTunnelControlData(' + ws.httprequest.protocol + '): ' + JSON.stringify(data));
892
+ //console.log('onTunnelControlData: ' + JSON.stringify(data));
893
+
894
+ if (obj.action) {
895
+ switch (obj.action) {
896
+ case 'lock': {
897
+ // Lock the current user out of the desktop
898
+ try {
899
+ if (process.platform == 'win32') {
900
+ var child = require('child_process');
901
+ child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
902
+ }
903
+ } catch (e) { }
904
+ break;
905
+ }
906
+ }
907
+ return;
908
+ }
909
+
910
+ if (obj.type == 'close') {
911
+ // We received the close on the websocket
912
+ //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
913
+ try { ws.close(); } catch (e) { }
914
+ } else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
915
+ if (ws.httprequest.protocol == 1) { // Terminal
916
+ // 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
917
+ ws.httprequest.process.stdout.unpipe(ws);
918
+ ws.httprequest.process.stderr.unpipe(ws);
919
+ } else if (ws.httprequest.protocol == 2) { // Desktop
920
+ // 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
921
+ ws.httprequest.desktop.kvm.unpipe(ws);
922
+ } else {
923
+ // Switch things around so all WebRTC data goes to onTunnelData().
924
+ ws.rtcchannel.httprequest = ws.httprequest;
925
+ ws.rtcchannel.removeAllListeners('data');
926
+ ws.rtcchannel.on('data', onTunnelData);
927
+ }
928
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc1\"}"); // End of data marker
929
+ } else if (obj.type == 'webrtc1') {
930
+ if (ws.httprequest.protocol == 1) { // Terminal
931
+ // Switch the user input from websocket to webrtc at this point.
932
+ ws.unpipe(ws.httprequest.process.stdin);
933
+ ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
934
+ ws.resume(); // Resume the websocket to keep receiving control data
935
+ } else if (ws.httprequest.protocol == 2) { // Desktop
936
+ // Switch the user input from websocket to webrtc at this point.
937
+ ws.unpipe(ws.httprequest.desktop.kvm);
938
+ try { ws.webrtc.rtcchannel.pipe(ws.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); } catch (e) { sendConsoleText('EX2'); } // 0 = Binary, 1 = Text.
939
+ ws.resume(); // Resume the websocket to keep receiving control data
940
+ }
941
+ ws.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc2\"}"); // Indicates we will no longer get any data on websocket, switching to WebRTC at this point.
942
+ } else if (obj.type == 'webrtc2') {
943
+ // Other side received websocket end of data marker, start sending data on WebRTC channel
944
+ if (ws.httprequest.protocol == 1) { // Terminal
945
+ ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
946
+ ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
947
+ } else if (ws.httprequest.protocol == 2) { // Desktop
948
+ ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
949
+ }
950
+ } else if (obj.type == 'offer') {
951
+ // This is a WebRTC offer.
952
+ ws.webrtc = rtc.createConnection();
953
+ ws.webrtc.websocket = ws;
954
+ ws.webrtc.on('connected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC connected');*/ });
955
+ ws.webrtc.on('disconnected', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC disconnected');*/ });
956
+ ws.webrtc.on('dataChannel', function (rtcchannel) {
957
+ //sendConsoleText('WebRTC Datachannel open, protocol: ' + this.websocket.httprequest.protocol);
958
+ rtcchannel.xrtc = this;
959
+ rtcchannel.websocket = this.websocket;
960
+ this.rtcchannel = rtcchannel;
961
+ this.websocket.rtcchannel = rtcchannel;
962
+ this.websocket.rtcchannel.on('data', onTunnelWebRTCControlData);
963
+ this.websocket.rtcchannel.on('end', function () { /*sendConsoleText('Tunnel #' + this.websocket.tunnel.index + ' WebRTC data channel closed');*/ });
964
+ this.websocket.write("{\"ctrlChannel\":\"102938\",\"type\":\"webrtc0\"}"); // Indicate we are ready for WebRTC switch-over.
965
+ });
966
+ var sdp = null;
967
+ try { sdp = ws.webrtc.setOffer(obj.sdp); } catch (ex) { }
968
+ if (sdp != null) { ws.write({ type: 'answer', ctrlChannel: '102938', sdp: sdp }); }
969
+ }
970
+ }
971
+
972
+ // Console state
973
+ var consoleWebSockets = {};
974
+ var consoleHttpRequest = null;
975
+
976
+ // Console HTTP response
977
+ function consoleHttpResponse(response) {
978
+ response.data = function (data) { sendConsoleText(rstr2hex(buf2rstr(data)), this.sessionid); consoleHttpRequest = null; }
979
+ response.close = function () { sendConsoleText('httprequest.response.close', this.sessionid); consoleHttpRequest = null; }
980
+ };
981
+
982
+ // Open a web browser to a specified URL on current user's desktop
983
+ function openUserDesktopUrl(url) {
984
+ var child = null;
985
+ try {
986
+ switch (process.platform) {
987
+ case 'win32':
988
+ child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe', ["/c", "start", url], { type: childProcess.SpawnTypes.USER });
989
+ break;
990
+ case 'linux':
991
+ child = require('child_process').execFile('/usr/bin/xdg-open', ['xdg-open', url], { type: require('child_process').SpawnTypes.DETACHED, uid: require('user-sessions').consoleUid() });
992
+ break;
993
+ case 'darwin':
994
+ child = require('child_process').execFile('/usr/bin/open', ['open', url], { uid: require('user-sessions').consoleUid() });
995
+ break;
996
+ }
997
+ } catch (ex) { }
998
+ return child;
999
+ }
1000
+
1001
+ // Process a mesh agent console command
1002
+ function processConsoleCommand(cmd, args, rights, sessionid) {
1003
+ try {
1004
+ var response = null;
1005
+ switch (cmd) {
1006
+ case 'help': { // Displays available commands
1007
+ response = 'Available commands: help, info, osinfo,args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios, toast, lock, users, sendcaps, openurl.';
1008
+ break;
1009
+ }
1010
+ /*
1011
+ case 'border':
1012
+ {
1013
+ if ((args['_'].length == 1) && (args['_'][0] == 'on')) {
1014
+ if (meshCoreObj.users.length > 0) {
1015
+ obj.borderManager.Start(meshCoreObj.users[0]);
1016
+ response = 'Border blinking is on.';
1017
+ } else {
1018
+ response = 'Cannot turn on border blinking, no logged in users.';
1019
+ }
1020
+ } else if ((args['_'].length == 1) && (args['_'][0] == 'off')) {
1021
+ obj.borderManager.Stop();
1022
+ response = 'Border blinking is off.';
1023
+ } else {
1024
+ response = 'Proper usage: border "on|off"'; // Display correct command usage
1025
+ }
1026
+ }
1027
+ break;
1028
+ */
1029
+ case 'openurl': {
1030
+ if (args['_'].length != 1) { response = 'Proper usage: openurl (url)'; } // Display usage
1031
+ else { if (openUserDesktopUrl(args['_'][0]) == null) { response = 'Failed.'; } else { response = 'Success.'; } }
1032
+ break;
1033
+ }
1034
+ case 'users': {
1035
+ if (meshCoreObj.users == null) { response = 'Active users are unknown.'; } else { response = 'Active Users: ' + meshCoreObj.users.join(', ') + '.'; }
1036
+ break;
1037
+ }
1038
+ case 'toast': {
1039
+ if (process.platform == 'win32') {
1040
+ if (args['_'].length < 1) { response = 'Proper usage: toast "message"'; } else {
1041
+ require('toaster').Toast('MeshCentral', args['_'][0]);
1042
+ response = 'ok';
1043
+ }
1044
+ } else {
1045
+ response = 'Only supported on Windows.';
1046
+ }
1047
+ break;
1048
+ }
1049
+ case 'setdebug': {
1050
+ if (args['_'].length < 1) { response = 'Proper usage: setdebug (target), 0 = Disabled, 1 = StdOut, 2 = This Console, * = All Consoles, 4 = WebLog, 8 = Logfile'; } // Display usage
1051
+ else { if (args['_'][0] == '*') { console.setDestination(2); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
1052
+ break;
1053
+ }
1054
+ case 'ps': {
1055
+ processManager.getProcesses(function (plist) {
1056
+ var x = '';
1057
+ for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
1058
+ sendConsoleText(x, sessionid);
1059
+ });
1060
+ break;
1061
+ }
1062
+ case 'kill': {
1063
+ if ((args['_'].length < 1)) {
1064
+ response = 'Proper usage: kill [pid]'; // Display correct command usage
1065
+ } else {
1066
+ process.kill(parseInt(args['_'][0]));
1067
+ response = 'Killed process ' + args['_'][0] + '.';
1068
+ }
1069
+ break;
1070
+ }
1071
+ case 'smbios': {
1072
+ if (SMBiosTables == null) { response = 'SMBios tables not available.'; } else { response = objToString(SMBiosTables, 0, ' ', true); }
1073
+ break;
1074
+ }
1075
+ case 'rawsmbios': {
1076
+ if (SMBiosTablesRaw == null) { response = 'SMBios tables not available.'; } else {
1077
+ response = '';
1078
+ for (var i in SMBiosTablesRaw) {
1079
+ var header = false;
1080
+ for (var j in SMBiosTablesRaw[i]) {
1081
+ if (SMBiosTablesRaw[i][j].length > 0) {
1082
+ if (header == false) { response += ('Table type #' + i + ((require('smbios').smTableTypes[i] == null) ? '' : (', ' + require('smbios').smTableTypes[i]))) + '\r\n'; header = true; }
1083
+ response += (' ' + SMBiosTablesRaw[i][j].toString('hex')) + '\r\n';
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ break;
1089
+ }
1090
+ case 'eval': { // Eval JavaScript
1091
+ if (args['_'].length < 1) {
1092
+ response = 'Proper usage: eval "JavaScript code"'; // Display correct command usage
1093
+ } else {
1094
+ response = JSON.stringify(mesh.eval(args['_'][0]));
1095
+ }
1096
+ break;
1097
+ }
1098
+ case 'notify': { // Send a notification message to the mesh
1099
+ if (args['_'].length != 1) {
1100
+ response = 'Proper usage: notify "message" [--session]'; // Display correct command usage
1101
+ } else {
1102
+ var notification = { "action": "msg", "type": "notify", "value": args['_'][0], "tag": "console" };
1103
+ if (args.session) { notification.sessionid = sessionid; } // If "--session" is specified, notify only this session, if not, the server will notify the mesh
1104
+ mesh.SendCommand(notification); // no sessionid or userid specified, notification will go to the entire mesh
1105
+ response = 'ok';
1106
+ }
1107
+ break;
1108
+ }
1109
+ case 'info': { // Return information about the agent and agent core module
1110
+ 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 + '.';
1111
+ if (amtLmsState >= 0) { response += '\r\nBuilt-in LMS: ' + ['Disabled', 'Connecting..', 'Connected'][amtLmsState] + '.'; }
1112
+ if (meshCoreObj.osdesc) { response += '\r\nOS: ' + meshCoreObj.osdesc + '.'; }
1113
+ response += '\r\nModules: ' + addedModules.join(', ') + '.';
1114
+ response += '\r\nServer Connection: ' + mesh.isControlChannelConnected + ', State: ' + meshServerConnectionState + '.';
1115
+ response += '\r\lastMeInfo: ' + lastMeInfo + '.';
1116
+ var oldNodeId = db.Get('OldNodeId');
1117
+ if (oldNodeId != null) { response += '\r\nOldNodeID: ' + oldNodeId + '.'; }
1118
+ if (process.platform != 'win32') { response += '\r\nX11 support: ' + require('monitor-info').kvm_x11_support + '.'; }
1119
+ break;
1120
+ }
1121
+ case 'osinfo': { // Return the operating system information
1122
+ var i = 1;
1123
+ if (args['_'].length > 0) { i = parseInt(args['_'][0]); if (i > 8) { i = 8; } response = 'Calling ' + i + ' times.'; }
1124
+ for (var j = 0; j < i; j++) {
1125
+ var pr = require('os').name();
1126
+ pr.sessionid = sessionid;
1127
+ pr.then(function (v) { sendConsoleText("OS: " + v, this.sessionid); });
1128
+ }
1129
+ break;
1130
+ }
1131
+ case 'sendcaps': { // Send capability flags to the server
1132
+ if (args['_'].length == 0) {
1133
+ response = 'Proper usage: sendcaps (number)'; // Display correct command usage
1134
+ } else {
1135
+ meshCoreObj.caps = parseInt(args['_'][0]);
1136
+ mesh.SendCommand(meshCoreObj);
1137
+ response = JSON.stringify(meshCoreObj);
1138
+ }
1139
+ break;
1140
+ }
1141
+ case 'sendosdesc': { // Send OS description
1142
+ if (args['_'].length > 0) {
1143
+ meshCoreObj.osdesc = args['_'][0];
1144
+ mesh.SendCommand(meshCoreObj);
1145
+ response = JSON.stringify(meshCoreObj);
1146
+ } else {
1147
+ response = 'Proper usage: sendosdesc [os description]'; // Display correct command usage
1148
+ }
1149
+ break;
1150
+ }
1151
+ case 'args': { // Displays parsed command arguments
1152
+ response = 'args ' + objToString(args, 0, ' ', true);
1153
+ break;
1154
+ }
1155
+ case 'print': { // Print a message on the mesh agent console, does nothing when running in the background
1156
+ var r = [];
1157
+ for (var i in args['_']) { r.push(args['_'][i]); }
1158
+ console.log(r.join(' '));
1159
+ response = 'Message printed on agent console.';
1160
+ break;
1161
+ }
1162
+ case 'type': { // Returns the content of a file
1163
+ if (args['_'].length == 0) {
1164
+ response = 'Proper usage: type (filepath) [maxlength]'; // Display correct command usage
1165
+ } else {
1166
+ var max = 4096;
1167
+ if ((args['_'].length > 1) && (typeof args['_'][1] == 'number')) { max = args['_'][1]; }
1168
+ if (max > 4096) max = 4096;
1169
+ var buf = new Buffer(max), fd = fs.openSync(args['_'][0], "r"), r = fs.readSync(fd, buf, 0, max); // Read the file content
1170
+ response = buf.toString();
1171
+ var i = response.indexOf('\n');
1172
+ if ((i > 0) && (response[i - 1] != '\r')) { response = response.split('\n').join('\r\n'); }
1173
+ if (r == max) response += '...';
1174
+ fs.closeSync(fd);
1175
+ }
1176
+ break;
1177
+ }
1178
+ case 'dbkeys': { // Return all data store keys
1179
+ response = JSON.stringify(db.Keys);
1180
+ break;
1181
+ }
1182
+ case 'dbget': { // Return the data store value for a given key
1183
+ if (db == null) { response = 'Database not accessible.'; break; }
1184
+ if (args['_'].length != 1) {
1185
+ response = 'Proper usage: dbget (key)'; // Display the value for a given database key
1186
+ } else {
1187
+ response = db.Get(args['_'][0]);
1188
+ }
1189
+ break;
1190
+ }
1191
+ case 'dbset': { // Set a data store key and value pair
1192
+ if (db == null) { response = 'Database not accessible.'; break; }
1193
+ if (args['_'].length != 2) {
1194
+ response = 'Proper usage: dbset (key) (value)'; // Set a database key
1195
+ } else {
1196
+ var r = db.Put(args['_'][0], args['_'][1]);
1197
+ response = 'Key set: ' + r;
1198
+ }
1199
+ break;
1200
+ }
1201
+ case 'dbcompact': { // Compact the data store
1202
+ if (db == null) { response = 'Database not accessible.'; break; }
1203
+ var r = db.Compact();
1204
+ response = 'Database compacted: ' + r;
1205
+ break;
1206
+ }
1207
+ case 'httpget': {
1208
+ if (consoleHttpRequest != null) {
1209
+ response = 'HTTP operation already in progress.';
1210
+ } else {
1211
+ if (args['_'].length != 1) {
1212
+ response = 'Proper usage: httpget (url)';
1213
+ } else {
1214
+ var options = http.parseUri(args['_'][0]);
1215
+ options.method = 'GET';
1216
+ if (options == null) {
1217
+ response = 'Invalid url.';
1218
+ } else {
1219
+ try { consoleHttpRequest = http.request(options, consoleHttpResponse); } catch (e) { response = 'Invalid HTTP GET request'; }
1220
+ consoleHttpRequest.sessionid = sessionid;
1221
+ if (consoleHttpRequest != null) {
1222
+ consoleHttpRequest.end();
1223
+ response = 'HTTPGET ' + options.protocol + '//' + options.host + ':' + options.port + options.path;
1224
+ }
1225
+ }
1226
+ }
1227
+ }
1228
+ break;
1229
+ }
1230
+ case 'wslist': { // List all web sockets
1231
+ response = '';
1232
+ for (var i in consoleWebSockets) {
1233
+ var httprequest = consoleWebSockets[i];
1234
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
1235
+ }
1236
+ if (response == '') { response = 'no websocket sessions.'; }
1237
+ break;
1238
+ }
1239
+ case 'wsconnect': { // Setup a web socket
1240
+ if (args['_'].length == 0) {
1241
+ response = 'Proper usage: wsconnect (url)\r\nFor example: wsconnect wss://localhost:443/meshrelay.ashx?id=abc'; // Display correct command usage
1242
+ } else {
1243
+ var httprequest = null;
1244
+ try {
1245
+ var options = http.parseUri(args['_'][0]);
1246
+ options.rejectUnauthorized = 0;
1247
+ httprequest = http.request(options);
1248
+ } catch (e) { response = 'Invalid HTTP websocket request'; }
1249
+ if (httprequest != null) {
1250
+ httprequest.upgrade = onWebSocketUpgrade;
1251
+ httprequest.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
1252
+
1253
+ var index = 1;
1254
+ while (consoleWebSockets[index]) { index++; }
1255
+ httprequest.sessionid = sessionid;
1256
+ httprequest.index = index;
1257
+ httprequest.url = args['_'][0];
1258
+ consoleWebSockets[index] = httprequest;
1259
+ response = 'New websocket session #' + index;
1260
+ }
1261
+ }
1262
+ break;
1263
+ }
1264
+ case 'wssend': { // Send data on a web socket
1265
+ if (args['_'].length == 0) {
1266
+ response = 'Proper usage: wssend (socketnumber)\r\n'; // Display correct command usage
1267
+ for (var i in consoleWebSockets) {
1268
+ var httprequest = consoleWebSockets[i];
1269
+ response += 'Websocket #' + i + ', ' + httprequest.url + '\r\n';
1270
+ }
1271
+ } else {
1272
+ var i = parseInt(args['_'][0]);
1273
+ var httprequest = consoleWebSockets[i];
1274
+ if (httprequest != undefined) {
1275
+ httprequest.s.write(args['_'][1]);
1276
+ response = 'ok';
1277
+ } else {
1278
+ response = 'Invalid web socket number';
1279
+ }
1280
+ }
1281
+ break;
1282
+ }
1283
+ case 'wsclose': { // Close a websocket
1284
+ if (args['_'].length == 0) {
1285
+ response = 'Proper usage: wsclose (socketnumber)'; // Display correct command usage
1286
+ } else {
1287
+ var i = parseInt(args['_'][0]);
1288
+ var httprequest = consoleWebSockets[i];
1289
+ if (httprequest != undefined) {
1290
+ if (httprequest.s != null) { httprequest.s.end(); } else { httprequest.end(); }
1291
+ response = 'ok';
1292
+ } else {
1293
+ response = 'Invalid web socket number';
1294
+ }
1295
+ }
1296
+ break;
1297
+ }
1298
+ case 'tunnels': { // Show the list of current tunnels
1299
+ response = '';
1300
+ for (var i in tunnels) { response += 'Tunnel #' + i + ', ' + tunnels[i].url + '\r\n'; }
1301
+ if (response == '') { response = 'No websocket sessions.'; }
1302
+ break;
1303
+ }
1304
+ case 'ls': { // Show list of files and folders
1305
+ response = '';
1306
+ var xpath = '*';
1307
+ if (args['_'].length > 0) { xpath = obj.path.join(args['_'][0], '*'); }
1308
+ response = 'List of ' + xpath + '\r\n';
1309
+ var results = fs.readdirSync(xpath);
1310
+ for (var i = 0; i < results.length; ++i) {
1311
+ var stat = null, p = obj.path.join(args['_'][0], results[i]);
1312
+ try { stat = fs.statSync(p); } catch (e) { }
1313
+ if ((stat == null) || (stat == undefined)) {
1314
+ response += (results[i] + "\r\n");
1315
+ } else {
1316
+ response += (results[i] + " " + ((stat.isDirectory()) ? "(Folder)" : "(File)") + "\r\n");
1317
+ }
1318
+ }
1319
+ break;
1320
+ }
1321
+ case 'lsx': { // Show list of files and folders
1322
+ response = objToString(getDirectoryInfo(args['_'][0]), 0, ' ', true);
1323
+ break;
1324
+ }
1325
+ case 'lock': { // Lock the current user out of the desktop
1326
+ 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'; }
1327
+ else { response = 'Not supported on the platform'; }
1328
+ break;
1329
+ }
1330
+ case 'amt': { // Show Intel AMT status
1331
+ getAmtInfo(function (state) {
1332
+ var resp = 'Intel AMT not detected.';
1333
+ if (state != null) { resp = objToString(state, 0, ' ', true); }
1334
+ sendConsoleText(resp, sessionid);
1335
+ });
1336
+ break;
1337
+ }
1338
+ case 'netinfo': { // Show network interface information
1339
+ //response = objToString(mesh.NetInfo, 0, ' ');
1340
+ var interfaces = require('os').networkInterfaces();
1341
+ response = objToString(interfaces, 0, ' ', true);
1342
+ break;
1343
+ }
1344
+ case 'netinfo2': { // Show network interface information
1345
+ response = objToString(mesh.NetInfo, 0, ' ', true);
1346
+ break;
1347
+ }
1348
+ case 'wakeonlan': { // Send wake-on-lan
1349
+ if ((args['_'].length != 1) || (args['_'][0].length != 12)) {
1350
+ response = 'Proper usage: wakeonlan [mac], for example "wakeonlan 010203040506".';
1351
+ } else {
1352
+ var count = sendWakeOnLan(args['_'][0]);
1353
+ response = 'Sent wake-on-lan on ' + count + ' interface(s).';
1354
+ }
1355
+ break;
1356
+ }
1357
+ case 'sendall': { // Send a message to all consoles on this mesh
1358
+ sendConsoleText(args['_'].join(' '));
1359
+ break;
1360
+ }
1361
+ case 'power': { // Execute a power action on this computer
1362
+ if (mesh.ExecPowerState == undefined) {
1363
+ response = 'Power command not supported on this agent.';
1364
+ } else {
1365
+ if ((args['_'].length == 0) || (typeof args['_'][0] != 'number')) {
1366
+ 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
1367
+ } else {
1368
+ var r = mesh.ExecPowerState(args['_'][0], args['_'][1]);
1369
+ response = 'Power action executed with return code: ' + r + '.';
1370
+ }
1371
+ }
1372
+ break;
1373
+ }
1374
+ case 'location': {
1375
+ getIpLocationData(function (location) {
1376
+ sendConsoleText(objToString({ "action": "iplocation", "type": "publicip", "value": location }, 0, ' '));
1377
+ });
1378
+ break;
1379
+ }
1380
+ case 'parseuri': {
1381
+ response = JSON.stringify(http.parseUri(args['_'][0]));
1382
+ break;
1383
+ }
1384
+ case 'scanwifi': {
1385
+ if (wifiScanner != null) {
1386
+ var wifiPresent = wifiScanner.hasWireless;
1387
+ if (wifiPresent) { response = "Perfoming Wifi scan..."; wifiScanner.Scan(); } else { response = "Wifi absent."; }
1388
+ } else { response = "Wifi module not present."; }
1389
+ break;
1390
+ }
1391
+ case 'scanamt': {
1392
+ if (amtscanner != null) {
1393
+ if (args['_'].length != 1) {
1394
+ 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
1395
+ } else {
1396
+ response = 'Scanning: ' + args['_'][0] + '...';
1397
+ amtscanner.scan(args['_'][0], 2000, function (data) {
1398
+ if (data.length > 0) {
1399
+ var r = '', pstates = ['NotActivated', 'InActivation', 'Activated'];
1400
+ for (var i in data) {
1401
+ var x = data[i];
1402
+ if (r != '') { r += '\r\n'; }
1403
+ r += x.address + ' - Intel AMT v' + x.majorVersion + '.' + x.minorVersion;
1404
+ if (x.provisioningState < 3) { r += (', ' + pstates[x.provisioningState]); }
1405
+ if (x.provisioningState == 2) { r += (', ' + x.openPorts.join(', ')); }
1406
+ r += '.';
1407
+ }
1408
+ } else {
1409
+ r = 'No Intel AMT found.';
1410
+ }
1411
+ sendConsoleText(r);
1412
+ });
1413
+ }
1414
+ } else { response = "Intel AMT scanner module not present."; }
1415
+ break;
1416
+ }
1417
+ case 'modules': {
1418
+ response = JSON.stringify(addedModules);
1419
+ break;
1420
+ }
1421
+ default: { // This is an unknown command, return an error message
1422
+ response = 'Unknown command \"' + cmd + '\", type \"help\" for list of avaialble commands.';
1423
+ break;
1424
+ }
1425
+ }
1426
+ } catch (e) { response = 'Command returned an exception error: ' + e; console.log(e); }
1427
+ if (response != null) { sendConsoleText(response, sessionid); }
1428
+ }
1429
+
1430
+ // Send a mesh agent console command
1431
+ function sendConsoleText(text, sessionid) {
1432
+ if (typeof text == 'object') { text = JSON.stringify(text); }
1433
+ mesh.SendCommand({ "action": "msg", "type": "console", "value": text, "sessionid": sessionid });
1434
+ }
1435
+
1436
+ // Called before the process exits
1437
+ //process.exit = function (code) { console.log("Exit with code: " + code.toString()); }
1438
+
1439
+ // Called when the server connection state changes
1440
+ function handleServerConnection(state) {
1441
+ meshServerConnectionState = state;
1442
+ if (meshServerConnectionState == 0) {
1443
+ // Server disconnected
1444
+ if (selfInfoUpdateTimer != null) { clearInterval(selfInfoUpdateTimer); selfInfoUpdateTimer = null; }
1445
+ lastSelfInfo = null;
1446
+ } else {
1447
+ // Server connected, send mesh core information
1448
+ var oldNodeId = db.Get('OldNodeId');
1449
+ if (oldNodeId != null) { mesh.SendCommand({ action: 'mc1migration', oldnodeid: oldNodeId }); }
1450
+
1451
+ // Update the server with basic info, logged in users and more.
1452
+ mesh.SendCommand(meshCoreObj);
1453
+
1454
+ // Send SMBios tables if present
1455
+ if (SMBiosTablesRaw != null) { mesh.SendCommand({ "action": "smbios", "value": SMBiosTablesRaw }); }
1456
+
1457
+ // Update the server on more advanced stuff, like Intel ME and Network Settings
1458
+ meInfoStr = null;
1459
+ sendPeriodicServerUpdate();
1460
+ //if (selfInfoUpdateTimer == null) { selfInfoUpdateTimer = setInterval(sendPeriodicServerUpdate, 1200000); } // 20 minutes
1461
+ }
1462
+ }
1463
+
1464
+ // Update the server with the latest network interface information
1465
+ var sendNetworkUpdateNagleTimer = null;
1466
+ function sendNetworkUpdateNagle() { if (sendNetworkUpdateNagleTimer != null) { clearTimeout(sendNetworkUpdateNagleTimer); sendNetworkUpdateNagleTimer = null; } sendNetworkUpdateNagleTimer = setTimeout(sendNetworkUpdate, 5000); }
1467
+ function sendNetworkUpdate(force) {
1468
+ sendNetworkUpdateNagleTimer = null;
1469
+
1470
+ // Update the network interfaces information data
1471
+ var netInfo = mesh.NetInfo;
1472
+ netInfo.action = 'netinfo';
1473
+ var netInfoStr = JSON.stringify(netInfo);
1474
+ if ((force == true) || (clearGatewayMac(netInfoStr) != clearGatewayMac(lastNetworkInfo))) { mesh.SendCommand(netInfo); lastNetworkInfo = netInfoStr; }
1475
+ }
1476
+
1477
+ // Called periodically to check if we need to send updates to the server
1478
+ function sendPeriodicServerUpdate(flags) {
1479
+ if (meshServerConnectionState == 0) return; // Not connected to server, do nothing.
1480
+ if (!flags) { flags = 0xFFFFFFFF; }
1481
+
1482
+ if (flags & 1) {
1483
+ // If we have a connected MEI, get Intel ME information
1484
+ getAmtInfo(function (meinfo) {
1485
+ try {
1486
+ if (meinfo == null) return;
1487
+ var intelamt = {}, p = false;
1488
+ if (meinfo.Versions && meinfo.Versions.AMT) { intelamt.ver = meinfo.Versions.AMT; p = true; }
1489
+ if (meinfo.ProvisioningState) { intelamt.state = meinfo.ProvisioningState; p = true; }
1490
+ if (meinfo.Flags) { intelamt.flags = meinfo.Flags; p = true; }
1491
+ if (meinfo.OsHostname) { intelamt.host = meinfo.OsHostname; p = true; }
1492
+ if (meinfo.UUID) { intelamt.uuid = meinfo.UUID; p = true; }
1493
+ if (p == true) {
1494
+ var meInfoStr = JSON.stringify(intelamt);
1495
+ if (meInfoStr != lastMeInfo) {
1496
+ meshCoreObj.intelamt = intelamt;
1497
+ mesh.SendCommand(meshCoreObj);
1498
+ lastMeInfo = meInfoStr;
1499
+ }
1500
+ }
1501
+ } catch (ex) { }
1502
+ });
1503
+ }
1504
+
1505
+ if (flags & 2) {
1506
+ // Update network information
1507
+ sendNetworkUpdateNagle(false);
1508
+ }
1509
+ }
1510
+
1511
+ // Get Intel AMT information using MEI
1512
+ function getAmtInfo(func) {
1513
+ if (amtMei == null || amtMeiConnected != 2) { if (func != null) { func(null); } return; }
1514
+ try {
1515
+ amtMeiTmpState = { Flags: 0 }; // Flags: 1=EHBC, 2=CCM, 4=ACM
1516
+ amtMei.getProtocolVersion(function (result) { if (result != null) { amtMeiTmpState.MeiVersion = result; } });
1517
+ amtMei.getVersion(function (result) { if (result) { amtMeiTmpState.Versions = {}; for (var version in result.Versions) { amtMeiTmpState.Versions[result.Versions[version].Description] = result.Versions[version].Version; } } });
1518
+ amtMei.getProvisioningMode(function (result) { if (result) { amtMeiTmpState.ProvisioningMode = result.mode; } });
1519
+ amtMei.getProvisioningState(function (result) { if (result) { amtMeiTmpState.ProvisioningState = result.state; } });
1520
+ amtMei.getEHBCState(function (result) { if ((result != null) && (result.EHBC == true)) { amtMeiTmpState.Flags += 1; } });
1521
+ amtMei.getControlMode(function (result) { if (result != null) { if (result.controlMode == 1) { amtMeiTmpState.Flags += 2; } if (result.controlMode == 2) { amtMeiTmpState.Flags += 4; } } });
1522
+ amtMei.getUuid(function (result) { if ((result != null) && (result.uuid != null)) { amtMeiTmpState.UUID = result.uuid; } });
1523
+ //amtMei.getMACAddresses(function (result) { amtMeiTmpState.mac = result; });
1524
+ amtMei.getDnsSuffix(function (result) { if (result != null) { amtMeiTmpState.dns = result; } if (func != null) { func(amtMeiTmpState); } });
1525
+ } catch (e) { if (func != null) { func(null); } return; }
1526
+ }
1527
+
1528
+ // Called on MicroLMS Intel AMT user notification
1529
+ function handleAmtNotification(notifyMsg) {
1530
+ if ((notifyMsg == null) || (notifyMsg.Body == null) || (notifyMsg.Body.MessageID == null) || (notifyMsg.Body.MessageArguments == null)) return null;
1531
+ var amtMessage = notifyMsg.Body.MessageID, amtMessageArg = notifyMsg.Body.MessageArguments[0], notify = null;
1532
+
1533
+ switch (amtMessage) {
1534
+ case 'iAMT0050': { if (amtMessageArg == '48') { notify = 'Intel® AMT Serial-over-LAN connected'; } else if (amtMessageArg == '49') { notify = 'Intel® AMT Serial-over-LAN disconnected'; } break; } // SOL
1535
+ case 'iAMT0052': { if (amtMessageArg == '1') { notify = 'Intel® AMT KVM connected'; } else if (amtMessageArg == '2') { notify = 'Intel® AMT KVM disconnected'; } break; } // KVM
1536
+ }
1537
+
1538
+ // Send to the entire mesh, no sessionid or userid specified.
1539
+ if (notify != null) { mesh.SendCommand({ "action": "msg", "type": "notify", "value": notify, "tag": "general" }); }
1540
+ }
1541
+
1542
+ // Starting function
1543
+ obj.start = function () {
1544
+ // Setup the mesh agent event handlers
1545
+ mesh.AddCommandHandler(handleServerCommand);
1546
+ mesh.AddConnectHandler(handleServerConnection);
1547
+
1548
+ // Parse input arguments
1549
+ //var args = parseArgs(process.argv);
1550
+ //console.log(args);
1551
+
1552
+ // Launch LMS
1553
+ try {
1554
+ var lme_heci = require('amt-lme');
1555
+ amtLmsState = 1;
1556
+ amtLms = new lme_heci();
1557
+ amtLms.on('error', function (e) { amtLmsState = 0; amtLms = null; obj.setupMeiOsAdmin(null, 1); });
1558
+ amtLms.on('connect', function () { amtLmsState = 2; obj.setupMeiOsAdmin(null, 2); });
1559
+ //amtLms.on('bind', function (map) { });
1560
+ amtLms.on('notify', function (data, options, str, code) {
1561
+ if (code == 'iAMT0052-3') {
1562
+ obj.kvmGetData();
1563
+ } else {
1564
+ //if (str != null) { sendConsoleText('Intel AMT LMS: ' + str); }
1565
+ handleAmtNotification(data);
1566
+ }
1567
+ });
1568
+ } catch (e) { amtLmsState = -1; amtLms = null; }
1569
+
1570
+ // Setup logged in user monitoring
1571
+ try {
1572
+ var userSession = require('user-sessions');
1573
+ userSession.on('changed', function onUserSessionChanged() {
1574
+ userSession.enumerateUsers().then(function (users) {
1575
+ var u = [], a = users.Active;
1576
+ for (var i = 0; i < a.length; i++) {
1577
+ var un = a[i].Domain ? (a[i].Domain + '\\' + a[i].Username) : (a[i].Username);
1578
+ if (u.indexOf(un) == -1) { u.push(un); } // Only push users in the list once.
1579
+ }
1580
+ meshCoreObj.users = u;
1581
+ mesh.SendCommand(meshCoreObj);
1582
+ });
1583
+ });
1584
+ userSession.emit('changed');
1585
+ //userSession.on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
1586
+ //userSession.on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
1587
+ } catch (ex) { }
1588
+ }
1589
+
1590
+ obj.stop = function () {
1591
+ mesh.AddCommandHandler(null);
1592
+ mesh.AddConnectHandler(null);
1593
+ }
1594
+
1595
+ function onWebSocketClosed() { sendConsoleText("WebSocket #" + this.httprequest.index + " closed.", this.httprequest.sessionid); delete consoleWebSockets[this.httprequest.index]; }
1596
+ function onWebSocketData(data) { sendConsoleText("Got WebSocket #" + this.httprequest.index + " data: " + data, this.httprequest.sessionid); }
1597
+ function onWebSocketSendOk() { sendConsoleText("WebSocket #" + this.index + " SendOK.", this.sessionid); }
1598
+
1599
+ function onWebSocketUpgrade(response, s, head) {
1600
+ sendConsoleText("WebSocket #" + this.index + " connected.", this.sessionid);
1601
+ this.s = s;
1602
+ s.httprequest = this;
1603
+ s.end = onWebSocketClosed;
1604
+ s.data = onWebSocketData;
1605
+ }
1606
+
1607
+
1608
+ //
1609
+ // KVM Data Channel
1610
+ //
1611
+
1612
+ obj.setupMeiOsAdmin = function (func, state) {
1613
+ if ((amtMei == null) || (amtMeiConnected != 2)) { return; } // If there is no MEI, don't bother with this.
1614
+ amtMei.getLocalSystemAccount(function (x) {
1615
+ if (x == null) return;
1616
+ var transport = require('amt-wsman-duk');
1617
+ var wsman = require('amt-wsman');
1618
+ var amt = require('amt');
1619
+ oswsstack = new wsman(transport, '127.0.0.1', 16992, x.user, x.pass, false);
1620
+ obj.osamtstack = new amt(oswsstack);
1621
+ if (func) { func(state); }
1622
+ //var AllWsman = "CIM_SoftwareIdentity,IPS_SecIOService,IPS_ScreenSettingData,IPS_ProvisioningRecordLog,IPS_HostBasedSetupService,IPS_HostIPSettings,IPS_IPv6PortSettings".split(',');
1623
+ //obj.osamtstack.BatchEnum(null, AllWsman, startLmsWsmanResponse, null, true);
1624
+ //*************************************
1625
+ // Setup KVM data channel if this is Intel AMT 12 or above
1626
+ amtMei.getVersion(function (x) {
1627
+ if (x == null) return;
1628
+ var amtver = null;
1629
+ try { for (var i in x.Versions) { if (x.Versions[i].Description == 'AMT') amtver = parseInt(x.Versions[i].Version.split('.')[0]); } } catch (e) { }
1630
+ if ((amtver != null) && (amtver >= 12)) {
1631
+ obj.kvmGetData('skip'); // Clear any previous data, this is a dummy read to about handling old data.
1632
+ obj.kvmTempTimer = setInterval(function () { obj.kvmGetData(); }, 2000); // Start polling for KVM data.
1633
+ obj.kvmSetData(JSON.stringify({ action: 'restart', ver: 1 })); // Send a restart command to advise the console if present that MicroLMS just started.
1634
+ }
1635
+ });
1636
+ });
1637
+ }
1638
+
1639
+ obj.kvmGetData = function (tag) {
1640
+ obj.osamtstack.IPS_KVMRedirectionSettingData_DataChannelRead(obj.kvmDataGetResponse, tag);
1641
+ }
1642
+
1643
+ obj.kvmDataGetResponse = function (stack, name, response, status, tag) {
1644
+ if ((tag != 'skip') && (status == 200) && (response.Body.ReturnValue == 0)) {
1645
+ var val = null;
1646
+ try { val = Buffer.from(response.Body.DataMessage, 'base64').toString(); } catch (e) { return }
1647
+ if (val != null) { obj.kvmProcessData(response.Body.RealmsBitmap, response.Body.MessageId, val); }
1648
+ }
1649
+ }
1650
+
1651
+ var webRtcDesktop = null;
1652
+ obj.kvmProcessData = function (realms, messageId, val) {
1653
+ var data = null;
1654
+ try { data = JSON.parse(val) } catch (e) { }
1655
+ if ((data != null) && (data.action)) {
1656
+ if (data.action == 'present') { obj.kvmSetData(JSON.stringify({ action: 'present', ver: 1, platform: process.platform })); }
1657
+ if (data.action == 'offer') {
1658
+ webRtcDesktop = {};
1659
+ var rtc = require('ILibWebRTC');
1660
+ webRtcDesktop.webrtc = rtc.createConnection();
1661
+ webRtcDesktop.webrtc.on('connected', function () { });
1662
+ webRtcDesktop.webrtc.on('disconnected', function () { webRtcCleanUp(); });
1663
+ webRtcDesktop.webrtc.on('dataChannel', function (rtcchannel) {
1664
+ webRtcDesktop.rtcchannel = rtcchannel;
1665
+ webRtcDesktop.kvm = mesh.getRemoteDesktopStream();
1666
+ webRtcDesktop.kvm.pipe(webRtcDesktop.rtcchannel, { dataTypeSkip: 1, end: false });
1667
+ webRtcDesktop.rtcchannel.on('end', function () { obj.webRtcCleanUp(); });
1668
+ webRtcDesktop.rtcchannel.on('data', function (x) { obj.kvmCtrlData(this, x); });
1669
+ webRtcDesktop.rtcchannel.pipe(webRtcDesktop.kvm, { dataTypeSkip: 1, end: false });
1670
+ //webRtcDesktop.kvm.on('end', function () { console.log('WebRTC DataChannel closed2'); webRtcCleanUp(); });
1671
+ //webRtcDesktop.rtcchannel.on('data', function (data) { console.log('WebRTC data: ' + data); });
1672
+ });
1673
+ obj.kvmSetData(JSON.stringify({ action: 'answer', ver: 1, sdp: webRtcDesktop.webrtc.setOffer(data.sdp) }));
1674
+ }
1675
+ }
1676
+ }
1677
+
1678
+ // Polyfill path.join
1679
+ var path = {
1680
+ join: function () {
1681
+ var x = [];
1682
+ for (var i in arguments) {
1683
+ var w = arguments[i];
1684
+ if (w != null) {
1685
+ while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); }
1686
+ if (i != 0) { while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } }
1687
+ x.push(w);
1688
+ }
1689
+ }
1690
+ if (x.length == 0) return '/';
1691
+ return x.join('/');
1692
+ }
1693
+ };
1694
+
1695
+ // Process KVM control channel data
1696
+ obj.kvmCtrlData = function(channel, cmd) {
1697
+ if (cmd.length > 0 && cmd.charCodeAt(0) != 123) {
1698
+ // This is upload data
1699
+ if (this.fileupload != null) {
1700
+ cmd = Buffer.from(cmd, 'base64');
1701
+ var header = cmd.readUInt32BE(0);
1702
+ if ((header == 0x01000000) || (header == 0x01000001)) {
1703
+ fs.writeSync(this.fileupload.fp, cmd.slice(4));
1704
+ channel.write({ action: 'upload', sub: 'ack', reqid: this.fileupload.reqid });
1705
+ if (header == 0x01000001) { fs.closeSync(this.fileupload.fp); this.fileupload = null; } // Close the file
1706
+ }
1707
+ }
1708
+ return;
1709
+ }
1710
+ //console.log('KVM Ctrl Data', cmd);
1711
+ //sendConsoleText('KVM Ctrl Data: ' + cmd);
1712
+
1713
+ try { cmd = JSON.parse(cmd); } catch (ex) { console.error('Invalid JSON: ' + cmd); return; }
1714
+ if ((cmd.path != null) && (process.platform != 'win32') && (cmd.path[0] != '/')) { cmd.path = '/' + cmd.path; } // Add '/' to paths on non-windows
1715
+ switch (cmd.action) {
1716
+ case 'ping': {
1717
+ // This is a keep alive
1718
+ channel.write({ action: 'pong' });
1719
+ break;
1720
+ }
1721
+ case 'lock': {
1722
+ // Lock the current user out of the desktop
1723
+ 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 }); }
1724
+ break;
1725
+ }
1726
+ case 'ls': {
1727
+ /*
1728
+ // Close the watcher if required
1729
+ var samepath = ((this.httprequest.watcher != undefined) && (cmd.path == this.httprequest.watcher.path));
1730
+ if ((this.httprequest.watcher != undefined) && (samepath == false)) {
1731
+ //console.log('Closing watcher: ' + this.httprequest.watcher.path);
1732
+ //this.httprequest.watcher.close(); // TODO: This line causes the agent to crash!!!!
1733
+ delete this.httprequest.watcher;
1734
+ }
1735
+ */
1736
+
1737
+ // Send the folder content to the browser
1738
+ var response = getDirectoryInfo(cmd.path);
1739
+ if (cmd.reqid != undefined) { response.reqid = cmd.reqid; }
1740
+ channel.write(response);
1741
+
1742
+ /*
1743
+ // Start the directory watcher
1744
+ if ((cmd.path != '') && (samepath == false)) {
1745
+ var watcher = fs.watch(cmd.path, onFileWatcher);
1746
+ watcher.tunnel = this.httprequest;
1747
+ watcher.path = cmd.path;
1748
+ this.httprequest.watcher = watcher;
1749
+ //console.log('Starting watcher: ' + this.httprequest.watcher.path);
1750
+ }
1751
+ */
1752
+ break;
1753
+ }
1754
+ case 'mkdir': {
1755
+ // Create a new empty folder
1756
+ fs.mkdirSync(cmd.path);
1757
+ break;
1758
+ }
1759
+ case 'rm': {
1760
+ // Remove many files or folders
1761
+ for (var i in cmd.delfiles) {
1762
+ var fullpath = path.join(cmd.path, cmd.delfiles[i]);
1763
+ try { fs.unlinkSync(fullpath); } catch (e) { console.log(e); }
1764
+ }
1765
+ break;
1766
+ }
1767
+ case 'rename': {
1768
+ // Rename a file or folder
1769
+ try { fs.renameSync(path.join(cmd.path, cmd.oldname), path.join(cmd.path, cmd.newname)); } catch (e) { console.log(e); }
1770
+ break;
1771
+ }
1772
+ case 'download': {
1773
+ // Download a file, to browser
1774
+ var sendNextBlock = 0;
1775
+ if (cmd.sub == 'start') { // Setup the download
1776
+ if (this.filedownload != null) { channel.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
1777
+ this.filedownload = { id: cmd.id, path: cmd.path, ptr: 0 }
1778
+ try { this.filedownload.f = fs.openSync(this.filedownload.path, 'rbN'); } catch (e) { channel.write({ action: 'download', sub: 'cancel', id: this.filedownload.id }); delete this.filedownload; }
1779
+ if (this.filedownload) { channel.write({ action: 'download', sub: 'start', id: cmd.id }); }
1780
+ } else if ((this.filedownload != null) && (cmd.id == this.filedownload.id)) { // Download commands
1781
+ if (cmd.sub == 'startack') { sendNextBlock = 8; } else if (cmd.sub == 'stop') { delete this.filedownload; } else if (cmd.sub == 'ack') { sendNextBlock = 1; }
1782
+ }
1783
+ // Send the next download block(s)
1784
+ while (sendNextBlock > 0) {
1785
+ sendNextBlock--;
1786
+ var buf = new Buffer(4096);
1787
+ var len = fs.readSync(this.filedownload.f, buf, 4, 4092, null);
1788
+ this.filedownload.ptr += len;
1789
+ if (len < 4092) { buf.writeInt32BE(0x01000001, 0); fs.closeSync(this.filedownload.f); delete this.filedownload; sendNextBlock = 0; } else { buf.writeInt32BE(0x01000000, 0); }
1790
+ channel.write(buf.slice(0, len + 4).toString('base64')); // Write as Base64
1791
+ }
1792
+ break;
1793
+ }
1794
+ case 'upload': {
1795
+ // Upload a file, from browser
1796
+ if (cmd.sub == 'start') { // Start the upload
1797
+ if (this.fileupload != null) { fs.closeSync(this.fileupload.fp); }
1798
+ if (!cmd.path || !cmd.name) break;
1799
+ this.fileupload = { reqid: cmd.reqid };
1800
+ var filepath = path.join(cmd.path, cmd.name);
1801
+ try { this.fileupload.fp = fs.openSync(filepath, 'wbN'); } catch (e) { }
1802
+ if (this.fileupload.fp) { channel.write({ action: 'upload', sub: 'start', reqid: this.fileupload.reqid }); } else { this.fileupload = null; channel.write({ action: 'upload', sub: 'error', reqid: this.fileupload.reqid }); }
1803
+ }
1804
+ else if (cmd.sub == 'cancel') { // Stop the upload
1805
+ if (this.fileupload != null) { fs.closeSync(this.fileupload.fp); this.fileupload = null; }
1806
+ }
1807
+ break;
1808
+ }
1809
+ case 'copy': {
1810
+ // Copy a bunch of files from scpath to dspath
1811
+ for (var i in cmd.names) {
1812
+ var sc = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
1813
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); } catch (e) { } }
1814
+ }
1815
+ break;
1816
+ }
1817
+ case 'move': {
1818
+ // Move a bunch of files from scpath to dspath
1819
+ for (var i in cmd.names) {
1820
+ var sc = path.join(cmd.scpath, cmd.names[i]), ds = path.join(cmd.dspath, cmd.names[i]);
1821
+ if (sc != ds) { try { fs.copyFileSync(sc, ds); fs.unlinkSync(sc); } catch (e) { } }
1822
+ }
1823
+ break;
1824
+ }
1825
+ }
1826
+ }
1827
+
1828
+ obj.webRtcCleanUp = function() {
1829
+ if (webRtcDesktop == null) return;
1830
+ if (webRtcDesktop.rtcchannel) {
1831
+ try { webRtcDesktop.rtcchannel.close(); } catch (e) { }
1832
+ try { webRtcDesktop.rtcchannel.removeAllListeners('data'); } catch (e) { }
1833
+ try { webRtcDesktop.rtcchannel.removeAllListeners('end'); } catch (e) { }
1834
+ delete webRtcDesktop.rtcchannel;
1835
+ }
1836
+ if (webRtcDesktop.webrtc) {
1837
+ try { webRtcDesktop.webrtc.close(); } catch (e) { }
1838
+ try { webRtcDesktop.webrtc.removeAllListeners('connected'); } catch (e) { }
1839
+ try { webRtcDesktop.webrtc.removeAllListeners('disconnected'); } catch (e) { }
1840
+ try { webRtcDesktop.webrtc.removeAllListeners('dataChannel'); } catch (e) { }
1841
+ delete webRtcDesktop.webrtc;
1842
+ }
1843
+ if (webRtcDesktop.kvm) {
1844
+ try { webRtcDesktop.kvm.end(); } catch (e) { }
1845
+ delete webRtcDesktop.kvm;
1846
+ }
1847
+ webRtcDesktop = null;
1848
+ }
1849
+
1850
+ obj.kvmSetData = function(x) {
1851
+ obj.osamtstack.IPS_KVMRedirectionSettingData_DataChannelWrite(Buffer.from(x).toString('base64'), function () { });
1852
+ }
1853
+
1854
+ // Delete a directory with a files and directories within it
1855
+ function deleteFolderRecursive(path, rec) {
1856
+ if (fs.existsSync(path)) {
1857
+ if (rec == true) {
1858
+ fs.readdirSync(obj.path.join(path, '*')).forEach(function (file, index) {
1859
+ var curPath = obj.path.join(path, file);
1860
+ if (fs.statSync(curPath).isDirectory()) { // recurse
1861
+ deleteFolderRecursive(curPath, true);
1862
+ } else { // delete file
1863
+ fs.unlinkSync(curPath);
1864
+ }
1865
+ });
1866
+ }
1867
+ fs.unlinkSync(path);
1868
+ }
1869
+ };
1870
+
1871
+ return obj;
1872
+}
1873
+
1874
+//
1875
+// Module startup
1876
+//
1877
+
1878
+try {
1879
+ var xexports = null, mainMeshCore = null;
1880
+ try { xexports = module.exports; } catch (e) { }
1881
+
1882
+ if (xexports != null) {
1883
+ // If we are running within NodeJS, export the core
1884
+ module.exports.createMeshCore = createMeshCore;
1885
+ } else {
1886
+ // If we are not running in NodeJS, launch the core
1887
+ mainMeshCore = createMeshCore();
1888
+ mainMeshCore.start(null);
1889
+ }
1890
+} catch (ex) {
1891
+ require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException2: " + ex });
1892
+}
\ No newline at end of file
agents/meshcore.js
+43
-16
@@ -14,7 +14,6 @@ See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16
17
-
17
process.on('uncaughtException', function (ex) {
18
require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException1: " + ex });
19
});
@@ -652,18 +651,24 @@ function createMeshCore(agent) {
651
}
652
653
// Remote terminal using native pipes
655
- if (process.platform == "win32") {
656
- this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
657
- } else {
654
+ if (process.platform == "win32")
655
+ {
656
+ this.httprequest._term = require('win-terminal').Start(80, 25);
657
+ this.httprequest._term.pipe(this, { dataTypeSkip: 1 });
658
+ this.pipe(this.httprequest._term, { dataTypeSkip: 1, end: false });
659
+ this.prependListener('end', function () { this.httprequest._term.end(function () { console.log('Terminal was closed');}); });
660
+ //this.httprequest.process = childProcess.execFile("%windir%\\system32\\cmd.exe");
661
+ } else
662
+ {
663
this.httprequest.process = childProcess.execFile("/bin/sh", ["sh"], { type: childProcess.SpawnTypes.TERM });
664
+ this.httprequest.process.tunnel = this;
665
+ this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
666
+ this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
667
+ this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
668
+ this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
669
+ this.prependListener('end', function () { this.httprequest.process.kill(); });
670
}
671
661
- this.httprequest.process.tunnel = this;
662
- this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
663
- this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
664
- this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
665
- this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
666
- this.prependListener('end', function () { this.httprequest.process.kill(); });
672
this.removeAllListeners('data');
673
this.on('data', onTunnelControlData);
674
//this.write('MeshCore Terminal Hello');
@@ -914,8 +919,15 @@ function createMeshCore(agent) {
919
} else if (obj.type == 'webrtc0') { // Browser indicates we can start WebRTC switch-over.
920
if (ws.httprequest.protocol == 1) { // Terminal
921
// 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
917
- ws.httprequest.process.stdout.unpipe(ws);
918
- ws.httprequest.process.stderr.unpipe(ws);
922
+ if (process.platform == 'win32')
923
+ {
924
+ ws.httprequest._term.unpipe(ws);
925
+ }
926
+ else
927
+ {
928
+ ws.httprequest.process.stdout.unpipe(ws);
929
+ ws.httprequest.process.stderr.unpipe(ws);
930
+ }
931
} else if (ws.httprequest.protocol == 2) { // Desktop
932
// 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
933
ws.httprequest.desktop.kvm.unpipe(ws);
@@ -929,8 +941,16 @@ function createMeshCore(agent) {
941
} else if (obj.type == 'webrtc1') {
942
if (ws.httprequest.protocol == 1) { // Terminal
943
// Switch the user input from websocket to webrtc at this point.
932
- ws.unpipe(ws.httprequest.process.stdin);
933
- ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
944
+ if (process.platform == 'win32')
945
+ {
946
+ ws.unpipe(ws.httprequest._term);
947
+ ws.rtcchannel.pipe(ws.httprequest._term, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
948
+ }
949
+ else
950
+ {
951
+ ws.unpipe(ws.httprequest.process.stdin);
952
+ ws.rtcchannel.pipe(ws.httprequest.process.stdin, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
953
+ }
954
ws.resume(); // Resume the websocket to keep receiving control data
955
} else if (ws.httprequest.protocol == 2) { // Desktop
956
// Switch the user input from websocket to webrtc at this point.
@@ -942,8 +962,15 @@ function createMeshCore(agent) {
962
} else if (obj.type == 'webrtc2') {
963
// Other side received websocket end of data marker, start sending data on WebRTC channel
964
if (ws.httprequest.protocol == 1) { // Terminal
945
- ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
946
- ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
965
+ if (process.platform == 'win32')
966
+ {
967
+ ws.httprequest._term.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
968
+ }
969
+ else
970
+ {
971
+ ws.httprequest.process.stdout.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
972
+ ws.httprequest.process.stderr.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
973
+ }
974
} else if (ws.httprequest.protocol == 2) { // Desktop
975
ws.httprequest.desktop.kvm.pipe(ws.webrtc.rtcchannel, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
976
}
agents/modules_meshcore/monitor-border.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 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
var red = 0xFF;
18
var yellow = 0xFFFF;
19
var GXxor = 0x6; // src XOR dst
agents/modules_meshcore/monitor-info.js
+15
@@ -1,3 +1,18 @@
1
+/*
2
+Copyright 2018 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
var promise = require('promise');
18
var PPosition = 4;
agents/modules_meshcore/promise.js
deleted
-207
@@ -1,207 +0,0 @@
1
-/*
2
-Copyright 2018 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
-var refTable = {};
18
-
19
-function event_switcher_helper(desired_callee, target)
20
-{
21
- this._ObjectID = 'event_switcher';
22
- this.func = function func()
23
- {
24
- var args = [];
25
- for(var i in arguments)
26
- {
27
- args.push(arguments[i]);
28
- }
29
- return (func.target.apply(func.desired, args));
30
- };
31
- this.func.desired = desired_callee;
32
- this.func.target = target;
33
- this.func.self = this;
34
-}
35
-function event_switcher(desired_callee, target)
36
-{
37
- return (new event_switcher_helper(desired_callee, target));
38
-}
39
-
40
-function Promise(promiseFunc)
41
-{
42
- this._ObjectID = 'promise';
43
- this.promise = this;
44
- this._internal = { _ObjectID: 'promise.internal', promise: this, func: promiseFunc, completed: false, errors: false, completedArgs: [] };
45
- require('events').EventEmitter.call(this._internal);
46
- this._internal.on('_eventHook', function (eventName, eventCallback)
47
- {
48
- //console.log('hook', eventName, 'errors/' + this.errors + ' completed/' + this.completed);
49
- var r = null;
50
-
51
- if (eventName == 'resolved' && !this.errors && this.completed)
52
- {
53
- r = eventCallback.apply(this, this.completedArgs);
54
- if(r!=null)
55
- {
56
- this.emit_returnValue('resolved', r);
57
- }
58
- }
59
- if (eventName == 'rejected' && this.errors && this.completed)
60
- {
61
- eventCallback.apply(this, this.completedArgs);
62
- }
63
- if (eventName == 'settled' && this.completed)
64
- {
65
- eventCallback.apply(this, []);
66
- }
67
- });
68
- this._internal.resolver = function _resolver()
69
- {
70
- _resolver._self.errors = false;
71
- _resolver._self.completed = true;
72
- _resolver._self.completedArgs = [];
73
- var args = ['resolved'];
74
- if (this.emit_returnValue && this.emit_returnValue('resolved') != null)
75
- {
76
- _resolver._self.completedArgs.push(this.emit_returnValue('resolved'));
77
- args.push(this.emit_returnValue('resolved'));
78
- }
79
- else
80
- {
81
- for (var a in arguments)
82
- {
83
- _resolver._self.completedArgs.push(arguments[a]);
84
- args.push(arguments[a]);
85
- }
86
- }
87
- _resolver._self.emit.apply(_resolver._self, args);
88
- _resolver._self.emit('settled');
89
- };
90
- this._internal.rejector = function _rejector()
91
- {
92
- _rejector._self.errors = true;
93
- _rejector._self.completed = true;
94
- _rejector._self.completedArgs = [];
95
- var args = ['rejected'];
96
- for (var a in arguments)
97
- {
98
- _rejector._self.completedArgs.push(arguments[a]);
99
- args.push(arguments[a]);
100
- }
101
-
102
- _rejector._self.emit.apply(_rejector._self, args);
103
- _rejector._self.emit('settled');
104
- };
105
- this.catch = function(func)
106
- {
107
- this._internal.once('rejected', event_switcher(this, func).func);
108
- }
109
- this.finally = function (func)
110
- {
111
- this._internal.once('settled', event_switcher(this, func).func);
112
- };
113
- this.then = function (resolved, rejected)
114
- {
115
- if (resolved) { this._internal.once('resolved', event_switcher(this, resolved).func); }
116
- if (rejected) { this._internal.once('rejected', event_switcher(this, rejected).func); }
117
-
118
- var retVal = new Promise(function (r, j) { });
119
- this._internal.once('resolved', retVal._internal.resolver);
120
- this._internal.once('rejected', retVal._internal.rejector);
121
- retVal.parentPromise = this;
122
- return (retVal);
123
- };
124
-
125
- this._internal.resolver._self = this._internal;
126
- this._internal.rejector._self = this._internal;;
127
-
128
- try
129
- {
130
- promiseFunc.call(this, this._internal.resolver, this._internal.rejector);
131
- }
132
- catch(e)
133
- {
134
- this._internal.errors = true;
135
- this._internal.completed = true;
136
- this._internal.completedArgs = [e];
137
- this._internal.emit('rejected', e);
138
- this._internal.emit('settled');
139
- }
140
-
141
- if(!this._internal.completed)
142
- {
143
- // Save reference of this object
144
- refTable[this._internal._hashCode()] = this._internal;
145
- this._internal.once('settled', function () { refTable[this._hashCode()] = null; });
146
- }
147
-}
148
-
149
-Promise.resolve = function resolve()
150
-{
151
- var retVal = new Promise(function (r, j) { });
152
- var args = [];
153
- for (var i in arguments)
154
- {
155
- args.push(arguments[i]);
156
- }
157
- retVal._internal.resolver.apply(retVal._internal, args);
158
- return (retVal);
159
-};
160
-Promise.reject = function reject() {
161
- var retVal = new Promise(function (r, j) { });
162
- var args = [];
163
- for (var i in arguments) {
164
- args.push(arguments[i]);
165
- }
166
- retVal._internal.rejector.apply(retVal._internal, args);
167
- return (retVal);
168
-};
169
-Promise.all = function all(promiseList)
170
-{
171
- var ret = new Promise(function (res, rej)
172
- {
173
- this.__rejector = rej;
174
- this.__resolver = res;
175
- this.__promiseList = promiseList;
176
- this.__done = false;
177
- this.__count = 0;
178
- });
179
-
180
- for (var i in promiseList)
181
- {
182
- promiseList[i].then(function ()
183
- {
184
- // Success
185
- if(++ret.__count == ret.__promiseList.length)
186
- {
187
- ret.__done = true;
188
- ret.__resolver(ret.__promiseList);
189
- }
190
- }, function (arg)
191
- {
192
- // Failure
193
- if(!ret.__done)
194
- {
195
- ret.__done = true;
196
- ret.__rejector(arg);
197
- }
198
- });
199
- }
200
- if (promiseList.length == 0)
201
- {
202
- ret.__resolver(promiseList);
203
- }
204
- return (ret);
205
-};
206
-
207
-module.exports = Promise;
\ No newline at end of file
agents/modules_meshcore/service-manager.js
+100
-2
@@ -214,6 +214,13 @@ function serviceManager()
214
throw ('could not find service: ' + name);
215
}
216
}
217
+ else
218
+ {
219
+ this.isAdmin = function isAdmin()
220
+ {
221
+ return (require('user-sessions').isRoot());
222
+ }
223
+ }
224
this.installService = function installService(options)
225
{
226
if (process.platform == 'win32')
@@ -273,6 +280,8 @@ function serviceManager()
280
}
281
if(process.platform == 'linux')
282
{
283
+ if (!this.isAdmin()) { throw ('Installing as Service, requires root'); }
284
+
285
switch (this.getServiceType())
286
{
287
case 'init':
@@ -311,14 +320,70 @@ function serviceManager()
320
break;
321
}
322
}
323
+ if(process.platform == 'darwin')
324
+ {
325
+ if (!this.isAdmin()) { throw ('Installing as Service, requires root'); }
326
+
327
+ // Mac OS
328
+ var stdoutpath = (options.stdout ? ('<key>StandardOutPath</key>\n<string>' + options.stdout + '</string>') : '');
329
+ var autoStart = (options.startType == 'AUTO_START' ? '<true/>' : '<false/>');
330
+ var params = ' <key>ProgramArguments</key>\n';
331
+ params += ' <array>\n';
332
+ params += (' <string>/usr/local/mesh_services/' + options.name + '/' + options.name + '</string>\n');
333
+ if(options.parameters)
334
+ {
335
+ for(var itm in options.parameters)
336
+ {
337
+ params += (' <string>' + options.parameters[itm] + '</string>\n');
338
+ }
339
+ }
340
+ params += ' </array>\n';
341
+
342
+ var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
343
+ plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
344
+ plist += '<plist version="1.0">\n';
345
+ plist += ' <dict>\n';
346
+ plist += ' <key>Label</key>\n';
347
+ plist += (' <string>' + options.name + '</string>\n');
348
+ plist += (params + '\n');
349
+ plist += ' <key>WorkingDirectory</key>\n';
350
+ plist += (' <string>/usr/local/mesh_services/' + options.name + '</string>\n');
351
+ plist += (stdoutpath + '\n');
352
+ plist += ' <key>RunAtLoad</key>\n';
353
+ plist += (autoStart + '\n');
354
+ plist += ' </dict>\n';
355
+ plist += '</plist>';
356
+
357
+ if (!require('fs').existsSync('/usr/local/mesh_services')) { require('fs').mkdirSync('/usr/local/mesh_services'); }
358
+ if (!require('fs').existsSync('/Library/LaunchDaemons/' + options.name + '.plist'))
359
+ {
360
+ if (!require('fs').existsSync('/usr/local/mesh_services/' + options.name)) { require('fs').mkdirSync('/usr/local/mesh_services/' + options.name); }
361
+ if (options.binary)
362
+ {
363
+ require('fs').writeFileSync('/usr/local/mesh_services/' + options.name + '/' + options.name, options.binary);
364
+ }
365
+ else
366
+ {
367
+ require('fs').copyFileSync(options.servicePath, '/usr/local/mesh_services/' + options.name + '/' + options.name);
368
+ }
369
+ require('fs').writeFileSync('/Library/LaunchDaemons/' + options.name + '.plist', plist);
370
+ var m = require('fs').statSync('/usr/local/mesh_services/' + options.name + '/' + options.name).mode;
371
+ m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
372
+ require('fs').chmodSync('/usr/local/mesh_services/' + options.name + '/' + options.name, m);
373
+ }
374
+ else
375
+ {
376
+ throw ('Service: ' + options.name + ' already exists');
377
+ }
378
+ }
379
}
380
this.uninstallService = function uninstallService(name)
381
{
382
+ if (!this.isAdmin()) { throw ('Uninstalling a service, requires admin'); }
383
+
384
if (typeof (name) == 'object') { name = name.name; }
385
if (process.platform == 'win32')
386
{
320
- if (!this.isAdmin()) { throw ('Uninstalling a service, requires admin'); }
321
-
387
var service = this.getService(name);
388
if (service.status.state == undefined || service.status.state == 'STOPPED')
389
{
@@ -388,6 +453,39 @@ function serviceManager()
453
break;
454
}
455
}
456
+ else if(process.platform == 'darwin')
457
+ {
458
+ if (require('fs').existsSync('/Library/LaunchDaemons/' + name + '.plist'))
459
+ {
460
+ var child = require('child_process').execFile('/bin/sh', ['sh']);
461
+ child.stdout.on('data', function (chunk) { });
462
+ child.stdin.write('launchctl stop ' + name + '\n');
463
+ child.stdin.write('launchctl unload /Library/LaunchDaemons/' + name + '.plist\n');
464
+ child.stdin.write('exit\n');
465
+ child.waitExit();
466
+
467
+ try
468
+ {
469
+ require('fs').unlinkSync('/usr/local/mesh_services/' + name + '/' + name);
470
+ require('fs').unlinkSync('/Library/LaunchDaemons/' + name + '.plist');
471
+ }
472
+ catch(e)
473
+ {
474
+ throw ('Error uninstalling service: ' + name + ' => ' + e);
475
+ }
476
+
477
+ try
478
+ {
479
+ require('fs').rmdirSync('/usr/local/mesh_services/' + name);
480
+ }
481
+ catch(e)
482
+ {}
483
+ }
484
+ else
485
+ {
486
+ throw ('Service: ' + name + ' does not exist');
487
+ }
488
+ }
489
}
490
if(process.platform == 'linux')
491
{
agents/modules_meshcore/wifi-scanner-windows.js
+15
@@ -1,3 +1,18 @@
1
+/*
2
+Copyright 2018 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
function _Scan()
18
{
agents/modules_meshcore/wifi-scanner.js
+16
@@ -1,3 +1,19 @@
1
+/*
2
+Copyright 2018 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
var MemoryStream = require('MemoryStream');
18
var WindowsChildScript = 'var parent = require("ScriptContainer");var Wireless = require("wifi-scanner-windows");Wireless.on("Scan", function (ap) { parent.send(ap); });Wireless.Scan();';
19
agents/modules_meshcore/win-message-pump.js
+80
-76
@@ -17,6 +17,8 @@ limitations under the License.
17
var WH_CALLWNDPROC = 4;
18
var WM_QUIT = 0x0012;
19
20
+var GM = require('_GenericMarshal');
21
+
22
function WindowsMessagePump(options)
23
{
24
this._ObjectID = 'win-message-pump';
@@ -27,92 +29,94 @@ function WindowsMessagePump(options)
29
emitterUtils.createEvent('message');
30
emitterUtils.createEvent('exit');
31
30
- this._child = require('ScriptContainer').Create({ processIsolation: 0 });
31
- this._child.MessagePump = this;
32
- this._child.prependListener('~', function _childFinalizer() { this.MessagePump.emit('exit', 0); this.MessagePump.stop(); });
33
- this._child.once('exit', function onExit(code) { this.MessagePump.emit('exit', code); });
34
- this._child.once('ready', function onReady()
35
- {
36
- var execString =
37
- "var m = require('_GenericMarshal');\
38
- var h = null;\
39
- var k = m.CreateNativeProxy('Kernel32.dll');\
40
- k.CreateMethod('GetLastError');\
41
- k.CreateMethod('GetModuleHandleA');\
42
- var u = m.CreateNativeProxy('User32.dll');\
43
- u.CreateMethod('GetMessageA');\
44
- u.CreateMethod('CreateWindowExA');\
45
- u.CreateMethod('TranslateMessage');\
46
- u.CreateMethod('DispatchMessageA');\
47
- u.CreateMethod('RegisterClassExA');\
48
- u.CreateMethod('DefWindowProcA');\
49
- var wndclass = m.CreateVariable(m.PointerSize == 4 ? 48 : 80);\
50
- wndclass.hinstance = k.GetModuleHandleA(0);\
51
- wndclass.cname = m.CreateVariable('MainWWWClass');\
52
- wndclass.wndproc = m.GetGenericGlobalCallback(4);\
53
- wndclass.toBuffer().writeUInt32LE(wndclass._size);\
54
- wndclass.cname.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 40 : 64, m.PointerSize).toBuffer());\
55
- wndclass.wndproc.pointerBuffer().copy(wndclass.Deref(8, m.PointerSize).toBuffer());\
56
- wndclass.hinstance.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 20 : 24, m.PointerSize).toBuffer());\
57
- wndclass.wndproc.on('GlobalCallback', function onWndProc(xhwnd, xmsg, wparam, lparam)\
58
- {\
59
- if(h==null || h.Val == xhwnd.Val)\
60
- {\
61
- require('ScriptContainer').send({message: xmsg.Val, wparam: wparam.Val, lparam: lparam.Val, lparam_hex: lparam.pointerBuffer().toString('hex')});\
62
- var retVal = u.DefWindowProcA(xhwnd, xmsg, wparam, lparam);\
63
- return(retVal);\
64
- }\
65
- });\
66
- u.RegisterClassExA(wndclass);\
67
- h = u.CreateWindowExA(0x00000088, wndclass.cname, 0, 0x00800000, 0, 0, 100, 100, 0, 0, 0, 0);\
68
- if(h.Val == 0)\
69
- {\
70
- require('ScriptContainer').send({error: 'Error Creating Hidden Window'});\
71
- process.exit();\
72
- }\
73
- require('ScriptContainer').send({hwnd: h.pointerBuffer().toString('hex')});\
74
- require('ScriptContainer').on('data', function onData(jmsg)\
75
- {\
76
- if(jmsg.listen)\
77
- {\
78
- var msg = m.CreateVariable(m.PointerSize == 4 ? 28 : 48);\
79
- while(u.GetMessageA(msg, h, 0, 0).Val>0)\
80
- {\
81
- u.TranslateMessage(msg);\
82
- u.DispatchMessageA(msg);\
83
- }\
84
- process.exit();\
85
- }\
86
- });";
32
+ this._msg = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
33
+ this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
34
+ this._kernel32.mp = this;
35
+ this._kernel32.CreateMethod('GetLastError');
36
+ this._kernel32.CreateMethod('GetModuleHandleA');
37
88
- this.ExecuteString(execString);
89
- });
90
- this._child.on('data', function onChildData(msg)
38
+ this._user32 = GM.CreateNativeProxy('User32.dll');
39
+ this._user32.mp = this;
40
+ this._user32.CreateMethod('GetMessageA');
41
+ this._user32.CreateMethod('CreateWindowExA');
42
+ this._user32.CreateMethod('TranslateMessage');
43
+ this._user32.CreateMethod('DispatchMessageA');
44
+ this._user32.CreateMethod('RegisterClassExA');
45
+ this._user32.CreateMethod('DefWindowProcA');
46
+ this._user32.CreateMethod('PostMessageA');
47
+
48
+
49
+ this.wndclass = GM.CreateVariable(GM.PointerSize == 4 ? 48 : 80);
50
+ this.wndclass.mp = this;
51
+ this.wndclass.hinstance = this._kernel32.GetModuleHandleA(0);
52
+ this.wndclass.cname = GM.CreateVariable('MainWWWClass');
53
+ this.wndclass.wndproc = GM.GetGenericGlobalCallback(4);
54
+ this.wndclass.wndproc.mp = this;
55
+ this.wndclass.toBuffer().writeUInt32LE(this.wndclass._size);
56
+ this.wndclass.cname.pointerBuffer().copy(this.wndclass.Deref(GM.PointerSize == 4 ? 40 : 64, GM.PointerSize).toBuffer());
57
+ this.wndclass.wndproc.pointerBuffer().copy(this.wndclass.Deref(8, GM.PointerSize).toBuffer());
58
+ this.wndclass.hinstance.pointerBuffer().copy(this.wndclass.Deref(GM.PointerSize == 4 ? 20 : 24, GM.PointerSize).toBuffer());
59
+ this.wndclass.wndproc.on('GlobalCallback', function onWndProc(xhwnd, xmsg, wparam, lparam)
60
{
92
- if (msg.hwnd)
93
- {
94
- var m = require('_GenericMarshal');
95
- this._hwnd = m.CreatePointer(Buffer.from(msg.hwnd, 'hex'));
96
- this.MessagePump.emit('hwnd', this._hwnd);
97
- this.send({ listen: this.MessagePump._options.filter });
98
- }
99
- else if(msg.message)
61
+ if (this.mp._hwnd != null && this.mp._hwnd.Val == xhwnd.Val)
62
{
101
- this.MessagePump.emit('message', msg);
63
+ // This is for us
64
+ this.mp.emit('message', { message: xmsg.Val, wparam: wparam.Val, lparam: lparam.Val, lparam_hex: lparam.pointerBuffer().toString('hex') });
65
+ return (this.mp._user32.DefWindowProcA(xhwnd, xmsg, wparam, lparam));
66
}
103
- else
67
+ else if(this.mp._hwnd == null && this.CallingThread() == this.mp._user32.RegisterClassExA.async.threadId())
68
{
105
- console.log('Received: ', msg);
69
+ // This message was generated from our CreateWindowExA method
70
+ return (this.mp._user32.DefWindowProcA(xhwnd, xmsg, wparam, lparam));
71
}
72
});
73
+
74
+ this._user32.RegisterClassExA.async(this.wndclass).then(function ()
75
+ {
76
+ this.nativeProxy.CreateWindowExA.async(this.nativeProxy.RegisterClassExA.async, 0x00000088, this.nativeProxy.mp.wndclass.cname, 0, 0x00800000, 0, 0, 100, 100, 0, 0, 0, 0)
77
+ .then(function(h)
78
+ {
79
+ if (h.Val == 0)
80
+ {
81
+ // Error creating hidden window
82
+ this.nativeProxy.mp.emit('error', 'Error creating hidden window');
83
+ }
84
+ else
85
+ {
86
+ this.nativeProxy.mp._hwnd = h;
87
+ this.nativeProxy.mp.emit('hwnd', h);
88
+ this.nativeProxy.mp._startPump();
89
+ }
90
+ });
91
+ });
92
+ this._startPump = function _startPump()
93
+ {
94
+ this._user32.GetMessageA.async(this._user32.RegisterClassExA.async, this._msg, this._hwnd, 0, 0).then(function (r)
95
+ {
96
+ if(r.Val > 0)
97
+ {
98
+ this.nativeProxy.TranslateMessage.async(this.nativeProxy.RegisterClassExA.async, this.nativeProxy.mp._msg).then(function ()
99
+ {
100
+ this.nativeProxy.DispatchMessageA.async(this.nativeProxy.RegisterClassExA.async, this.nativeProxy.mp._msg).then(function ()
101
+ {
102
+ this.nativeProxy.mp._startPump();
103
+ });
104
+ });
105
+ }
106
+ else
107
+ {
108
+ // We got a 'QUIT' message
109
+ delete this.nativeProxy.mp._hwnd;
110
+ this.nativeProxy.mp.emit('exit', 0);
111
+ }
112
+ }, function (err) { this.nativeProxy.mp.stop(); });
113
+ }
114
+
115
this.stop = function stop()
116
{
110
- if(this._child && this._child._hwnd)
117
+ if (this._hwnd)
118
{
112
- var marshal = require('_GenericMarshal');
113
- var User32 = marshal.CreateNativeProxy('User32.dll');
114
- User32.CreateMethod('PostMessageA');
115
- User32.PostMessageA(this._child._hwnd, WM_QUIT, 0, 0);
119
+ this._user32.PostMessageA(this._hwnd, WM_QUIT, 0, 0);
120
}
121
};
122
}
agents/modules_meshcore/win-terminal.js
new
+555
@@ -0,0 +1,555 @@
1
+/*
2
+Copyright 2018 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
+var promise = require('promise');
18
+var duplex = require('stream').Duplex;
19
+
20
+var SW_HIDE = 0;
21
+var SW_MINIMIZE = 6;
22
+var STARTF_USESHOWWINDOW = 0x1;
23
+var STD_INPUT_HANDLE = -10;
24
+var STD_OUTPUT_HANDLE = -11;
25
+var EVENT_CONSOLE_CARET = 0x4001;
26
+var EVENT_CONSOLE_END_APPLICATION = 0x4007;
27
+var WINEVENT_OUTOFCONTEXT = 0x000;
28
+var WINEVENT_SKIPOWNPROCESS = 0x0002;
29
+var CREATE_NEW_PROCESS_GROUP = 0x200;
30
+var EVENT_CONSOLE_UPDATE_REGION = 0x4002;
31
+var EVENT_CONSOLE_UPDATE_SIMPLE = 0x4003;
32
+var EVENT_CONSOLE_UPDATE_SCROLL = 0x4004;
33
+var EVENT_CONSOLE_LAYOUT = 0x4005;
34
+var EVENT_CONSOLE_START_APPLICATION = 0x4006;
35
+var KEY_EVENT = 0x1;
36
+var MAPVK_VK_TO_VSC = 0;
37
+var WM_QUIT = 0x12;
38
+
39
+var GM = require('_GenericMarshal');
40
+var si = GM.CreateVariable(GM.PointerSize == 4 ? 68 : 104);
41
+var pi = GM.CreateVariable(GM.PointerSize == 4 ? 16 : 24);
42
+
43
+si.Deref(0, 4).toBuffer().writeUInt32LE(GM.PointerSize == 4 ? 68 : 104); // si.cb
44
+si.Deref(GM.PointerSize == 4 ? 48 : 64, 2).toBuffer().writeUInt16LE(SW_HIDE | SW_MINIMIZE); // si.wShowWindow
45
+si.Deref(GM.PointerSize == 4 ? 44 : 60, 4).toBuffer().writeUInt32LE(STARTF_USESHOWWINDOW); // si.dwFlags;
46
+
47
+var MSG = GM.CreateVariable(GM.PointerSize == 4 ? 28 : 48);
48
+
49
+function windows_terminal()
50
+{
51
+ this._ObjectID = 'windows_terminal';
52
+ this._user32 = GM.CreateNativeProxy('User32.dll');
53
+ this._user32.CreateMethod('DispatchMessageA');
54
+ this._user32.CreateMethod('GetMessageA');
55
+ this._user32.CreateMethod('MapVirtualKeyA');
56
+ this._user32.CreateMethod('PostThreadMessageA');
57
+ this._user32.CreateMethod('SetWinEventHook');
58
+ this._user32.CreateMethod('ShowWindow');
59
+ this._user32.CreateMethod('TranslateMessage');
60
+ this._user32.CreateMethod('UnhookWinEvent');
61
+ this._user32.CreateMethod('VkKeyScanA');
62
+ this._user32.terminal = this;
63
+
64
+ this._kernel32 = GM.CreateNativeProxy('Kernel32.dll');
65
+ this._kernel32.CreateMethod('AllocConsole');
66
+ this._kernel32.CreateMethod('CreateProcessA');
67
+ this._kernel32.CreateMethod('CloseHandle');
68
+ this._kernel32.CreateMethod('FillConsoleOutputAttribute');
69
+ this._kernel32.CreateMethod('FillConsoleOutputCharacterA');
70
+ this._kernel32.CreateMethod('GetConsoleScreenBufferInfo');
71
+ this._kernel32.CreateMethod('GetConsoleWindow');
72
+ this._kernel32.CreateMethod('GetLastError');
73
+ this._kernel32.CreateMethod('GetStdHandle');
74
+ this._kernel32.CreateMethod('GetThreadId');
75
+ this._kernel32.CreateMethod('ReadConsoleOutputA');
76
+ this._kernel32.CreateMethod('SetConsoleCursorPosition');
77
+ this._kernel32.CreateMethod('SetConsoleScreenBufferSize');
78
+ this._kernel32.CreateMethod('SetConsoleWindowInfo');
79
+ this._kernel32.CreateMethod('TerminateProcess');
80
+ this._kernel32.CreateMethod('WaitForSingleObject');
81
+ this._kernel32.CreateMethod('WriteConsoleInputA');
82
+
83
+ var currentX = 0;
84
+ var currentY = 0;
85
+
86
+ this._scrx = 0;
87
+ this._scry = 0;
88
+
89
+ this.SendCursorUpdate = function()
90
+ {
91
+ var newCsbi = GM.CreateVariable(22);
92
+
93
+ if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, newCsbi).Val == 0) { return; }
94
+ if (newCsbi.Deref(4,2).toBuffer().readUInt16LE() != this.currentX || newCsbi.Deref(6,2).toBuffer().readUInt16LE() != this.currentY)
95
+ {
96
+ //wchar_t mywbuf[512];
97
+ //swprintf(mywbuf, 512, TEXT("csbi.dwCursorPosition.X = %d, csbi.dwCursorPosition.Y = %d, newCsbi.dwCursorPosition.X = %d, newCsbi.dwCursorPosition.Y = %d\r\n"), csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, newCsbi.dwCursorPosition.X, newCsbi.dwCursorPosition.Y);
98
+ //OutputDebugString(mywbuf);
99
+
100
+ //m_viewOffset = newCsbi.srWindow.Top;
101
+ //WriteMoveCursor((SerialAgent *)this->sa, (char)(newCsbi.dwCursorPosition.Y - m_viewOffset), (char)(newCsbi.dwCursorPosition.X - m_viewOffset));
102
+ //LowStackSendData((SerialAgent *)(this->sa), "", 0);
103
+
104
+ this.currentX = newCsbi.Deref(4,2).toBuffer().readUInt16LE();
105
+ this.currentY = newCsbi.Deref(6,2).toBuffer().readUInt16LE();
106
+ }
107
+ }
108
+ this.ClearScreen = function()
109
+ {
110
+ var CONSOLE_SCREEN_BUFFER_INFO = GM.CreateVariable(22);
111
+ if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
112
+
113
+ var coordScreen = GM.CreateVariable(4);
114
+ var dwConSize = CONSOLE_SCREEN_BUFFER_INFO.Deref(0,2).toBuffer().readUInt16LE(0) * CONSOLE_SCREEN_BUFFER_INFO.Deref(2,2).toBuffer().readUInt16LE(0);
115
+ var cCharsWritten = GM.CreateVariable(4);
116
+
117
+ // Fill the entire screen with blanks.
118
+ if (this._kernel32.FillConsoleOutputCharacterA(this._stdoutput, 32, dwConSize, coordScreen.Deref(0,4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
119
+
120
+ // Get the current text attribute.
121
+ if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO).Val == 0) { return; }
122
+
123
+ // Set the buffer's attributes accordingly.
124
+ if (this._kernel32.FillConsoleOutputAttribute(this._stdoutput, CONSOLE_SCREEN_BUFFER_INFO.Deref(8, 2).toBuffer().readUInt16LE(0), dwConSize, coordScreen.Deref(0, 4).toBuffer().readUInt32LE(), cCharsWritten).Val == 0) { return; }
125
+
126
+ // Put the cursor at its home coordinates.
127
+ this._kernel32.SetConsoleCursorPosition(this._stdoutput, coordScreen.Deref(0, 4).toBuffer().readUInt32LE());
128
+
129
+ // Put the window to top-left.
130
+ var rect = GM.CreateVariable(8);
131
+ var srWindow = CONSOLE_SCREEN_BUFFER_INFO.Deref(10,8).toBuffer();
132
+ rect.Deref(4,2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(4) - srWindow.readUInt16LE(0));
133
+ rect.Deref(6,2).toBuffer().writeUInt16LE(srWindow.readUInt16LE(6) - srWindow.readUInt16LE(2));
134
+
135
+ this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect);
136
+ }
137
+
138
+ this.Start = function Start(CONSOLE_SCREEN_WIDTH, CONSOLE_SCREEN_HEIGHT)
139
+ {
140
+ if(this._kernel32.GetConsoleWindow().Val == 0)
141
+ {
142
+ if(this._kernel32.AllocConsole().Val == 0)
143
+ {
144
+ throw ('AllocConsole failed with: ' + this._kernel32.GetLastError().Val);
145
+ }
146
+ }
147
+
148
+ this._stdinput = this._kernel32.GetStdHandle(STD_INPUT_HANDLE);
149
+ this._stdoutput = this._kernel32.GetStdHandle(STD_OUTPUT_HANDLE);
150
+ this._connected = false;
151
+ var coordScreen = GM.CreateVariable(4);
152
+ coordScreen.Deref(0, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH);
153
+ coordScreen.Deref(2, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT);
154
+
155
+ var rect = GM.CreateVariable(8);
156
+ rect.Deref(4, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_WIDTH - 1);
157
+ rect.Deref(6, 2).toBuffer().writeUInt16LE(CONSOLE_SCREEN_HEIGHT - 1);
158
+
159
+ if(this._kernel32.SetConsoleWindowInfo(this._stdoutput, 1, rect).Val == 0)
160
+ {
161
+ throw ('Failed to set Console Screen Size');
162
+ }
163
+ if(this._kernel32.SetConsoleScreenBufferSize(this._stdoutput, coordScreen.Deref(0,4).toBuffer().readUInt32LE()).Val == 0)
164
+ {
165
+ throw ('Failed to set Console Buffer Size');
166
+ }
167
+ this.ClearScreen();
168
+ this._hookThread().then(function ()
169
+ {
170
+ // Hook Ready
171
+ this.terminal.StartCommand();
172
+ }, console.log);
173
+ this._stream = new duplex({
174
+ 'write': function (chunk, flush)
175
+ {
176
+ if (!this.terminal.connected)
177
+ {
178
+ //console.log('_write: ' + chunk);
179
+ if (!this._promise.chunk)
180
+ {
181
+ this._promise.chunk = [];
182
+ }
183
+ if (typeof (chunk) == 'string')
184
+ {
185
+ this._promise.chunk.push(chunk);
186
+ }
187
+ else
188
+ {
189
+ this._promise.chunk.push(Buffer.alloc(chunk.length));
190
+ chunk.copy(this._promise.chunk.peek());
191
+ }
192
+ this._promise.chunk.peek().flush = flush;
193
+ this._promise.then(function ()
194
+ {
195
+ var buf;
196
+ while(this.chunk.length > 0)
197
+ {
198
+ buf = this.chunk.shift();
199
+ this.terminal._WriteBuffer(buf);
200
+ buf.flush();
201
+ }
202
+ });
203
+ }
204
+ else
205
+ {
206
+ //console.log('writeNOW: ' + chunk);
207
+ this.terminal._WriteBuffer(chunk);
208
+ flush();
209
+ }
210
+ },
211
+ 'final': function (flush)
212
+ {
213
+ var p = this.terminal._stop();
214
+ p.__flush = flush;
215
+ p.then(function () { this.__flush(); });
216
+ }
217
+ });
218
+ this._stream.terminal = this;
219
+ this._stream._promise = new promise(function (res, rej) { this._res = res; this._rej = rej; });
220
+ this._stream._promise.terminal = this;
221
+ return (this._stream);
222
+ };
223
+ this._stop = function()
224
+ {
225
+ if (this.stopping) { return (this.stopping); }
226
+ console.log('Stopping Terminal...');
227
+ this.stopping = new promise(function (res, rej) { this._res = res; this._rej = rej; });
228
+
229
+ var threadID = this._kernel32.GetThreadId(this._user32.SetWinEventHook.async.thread()).Val;
230
+ this._user32.PostThreadMessageA(threadID, WM_QUIT, 0, 0);
231
+ return (this.stopping);
232
+ }
233
+
234
+ this._hookThread = function ()
235
+ {
236
+ var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; });
237
+ ret.terminal = this;
238
+ this._ConsoleWinEventProc = GM.GetGenericGlobalCallback(7);
239
+ this._ConsoleWinEventProc.terminal = this;
240
+ var p = this._user32.SetWinEventHook.async(EVENT_CONSOLE_CARET, EVENT_CONSOLE_END_APPLICATION, 0, this._ConsoleWinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
241
+ p.ready = ret;
242
+ p.terminal = this;
243
+ p.then(function (hwinEventHook)
244
+ {
245
+ if (hwinEventHook.Val == 0)
246
+ {
247
+ this.ready._rej('Error calling SetWinEventHook');
248
+ }
249
+ else
250
+ {
251
+ this.terminal.hwinEventHook = hwinEventHook;
252
+ this.ready._res();
253
+ this.terminal._GetMessage();
254
+ }
255
+ });
256
+ this._ConsoleWinEventProc.on('GlobalCallback', function (hhook, dwEvent, hwnd, idObject, idChild, idEventThread, swmsEventTime)
257
+ {
258
+ if (!this.terminal.hwinEventHook || this.terminal.hwinEventHook.Val != hhook.Val) { return; }
259
+ var buffer = null;
260
+
261
+ switch (dwEvent.Val)
262
+ {
263
+ case EVENT_CONSOLE_CARET:
264
+ break;
265
+ case EVENT_CONSOLE_UPDATE_REGION:
266
+ if (!this.terminal.connected)
267
+ {
268
+ this.terminal.connected = true; this.terminal._stream._promise._res();
269
+ }
270
+ if (this.terminal._scrollTimer == null)
271
+ {
272
+ buffer = this.terminal._GetScreenBuffer(LOWORD(idObject.Val), HIWORD(idObject.Val), LOWORD(idChild.Val), HIWORD(idChild.Val));
273
+ //console.log('UPDATE REGION: [Left: ' + LOWORD(idObject.Val) + ' Top: ' + HIWORD(idObject.Val) + ' Right: ' + LOWORD(idChild.Val) + ' Bottom: ' + HIWORD(idChild.Val) + ']');
274
+
275
+ this.terminal._SendDataBuffer(buffer);
276
+ }
277
+ break;
278
+ case EVENT_CONSOLE_UPDATE_SIMPLE:
279
+ //console.log('UPDATE SIMPLE: [X: ' + LOWORD(idObject.Val) + ' Y: ' + HIWORD(idObject.Val) + ' Char: ' + LOWORD(idChild.Val) + ' Attr: ' + HIWORD(idChild.Val) + ']');
280
+ var simplebuffer = { data: [Buffer.alloc(1, LOWORD(idChild.Val))], attributes: [HIWORD(idChild.Val)], width: 1, height: 1, x: LOWORD(idObject.Val)+1, y: HIWORD(idObject.Val) };
281
+ this.terminal._SendDataBuffer(simplebuffer);
282
+ break;
283
+ case EVENT_CONSOLE_UPDATE_SCROLL:
284
+ //console.log('UPDATE SCROLL: [dx: ' + idObject.Val + ' dy: ' + idChild.Val + ']');
285
+ this.terminal._SendScroll(idObject.Val, idChild.Val);
286
+ break;
287
+ case EVENT_CONSOLE_LAYOUT:
288
+ //console.log('CONSOLE_LAYOUT');
289
+ //snprintf( Buf, 512, "Event Console LAYOUT!\r\n");
290
+ //SendLayout();
291
+ break;
292
+ case EVENT_CONSOLE_START_APPLICATION:
293
+ //console.log('START APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
294
+ //snprintf( Buf, 512, "Event Console START APPLICATION!\r\nProcess ID: %d - Child ID: %d\r\n\r\n", (int)idObject, (int)idChild);
295
+ //SendConsoleEvent(dwEvent, idObject, idChild);
296
+ break;
297
+ case EVENT_CONSOLE_END_APPLICATION:
298
+ if(idObject.Val == this.terminal._hProcessID)
299
+ {
300
+ //console.log('END APPLICATION: [PID: ' + idObject.Val + ' CID: ' + idChild.Val + ']');
301
+ this.terminal._stop().then(function () { console.log('STOPPED'); });
302
+ }
303
+ break;
304
+ default:
305
+ //snprintf(Buf, 512, "unknown console event.\r\n");
306
+ console.log('Unknown event: ' + dwEvent.Val);
307
+ break;
308
+ }
309
+
310
+ //mbstowcs_s(&l, wBuf, Buf, 512);
311
+ //OutputDebugString(wBuf);
312
+
313
+ });
314
+ return (ret);
315
+ }
316
+
317
+ this._GetMessage = function()
318
+ {
319
+ if (this._user32.abort) { console.log('aborting loop'); return; }
320
+ this._user32.GetMessageA.async(this._user32.SetWinEventHook.async, MSG, 0, 0, 0).then(function (ret)
321
+ {
322
+ //console.log('GetMessage Response');
323
+ if(ret.Val != 0)
324
+ {
325
+ if (ret.Val == -1)
326
+ {
327
+ // handle the error and possibly exit
328
+ }
329
+ else
330
+ {
331
+ //console.log('TranslateMessage');
332
+ this.nativeProxy._user32.TranslateMessage.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
333
+ {
334
+ //console.log('DispatchMessage');
335
+ this.nativeProxy._user32.DispatchMessageA.async(this.nativeProxy.user32.SetWinEventHook.async, MSG).then(function ()
336
+ {
337
+ this.nativeProxy.terminal._GetMessage();
338
+ }, console.log);
339
+ }, console.log);
340
+ }
341
+ }
342
+ else
343
+ {
344
+ this.nativeProxy.UnhookWinEvent.async(this.nativeProxy.terminal._user32.SetWinEventHook.async, this.nativeProxy.terminal.hwinEventHook)
345
+ .then(function ()
346
+ {
347
+ this.nativeProxy.terminal.stopping._res();
348
+ if(this.nativeProxy.terminal._kernel32.TerminateProcess(this.nativeProxy.terminal._hProcess, 1067).Val == 0)
349
+ {
350
+ var e = this.nativeProxy.terminal._kernel32.GetLastError().Val;
351
+ console.log('Unable to kill Terminal Process, error: ' + e);
352
+ }
353
+ this.nativeProxy.terminal.stopping = null;
354
+ }, function (err)
355
+ {
356
+ console.log('REJECTED_UnhookWinEvent: ' + err);
357
+ });
358
+ }
359
+ }, function (err)
360
+ {
361
+ // Get Message Failed
362
+ console.log('REJECTED_GETMessage: ' + err);
363
+ });
364
+ }
365
+ this._WriteBuffer = function(buf)
366
+ {
367
+ for (var i = 0; i < buf.length; ++i)
368
+ {
369
+ if (typeof (buf) == 'string')
370
+ {
371
+ this._WriteCharacter(buf.charCodeAt(i), false);
372
+ }
373
+ else
374
+ {
375
+ this._WriteCharacter(buf[i], false);
376
+ }
377
+ }
378
+ }
379
+ this._WriteCharacter = function(key, bControlKey)
380
+ {
381
+ var rec = GM.CreateVariable(20);
382
+ rec.Deref(0,2).toBuffer().writeUInt16LE(KEY_EVENT); // rec.EventType
383
+ rec.Deref(4,4).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.bKeyDown
384
+ rec.Deref(16, 4).toBuffer().writeUInt32LE(bControlKey); // rec.Event.KeyEvent.dwControlKeyState
385
+ rec.Deref(14, 1).toBuffer()[0] = key; // rec.Event.KeyEvent.uChar.AsciiChar
386
+ rec.Deref(8, 2).toBuffer().writeUInt16LE(1); // rec.Event.KeyEvent.wRepeatCount
387
+ rec.Deref(10, 2).toBuffer().writeUInt16LE(this._user32.VkKeyScanA(key).Val); // rec.Event.KeyEvent.wVirtualKeyCode
388
+ rec.Deref(12, 2).toBuffer().writeUInt16LE(this._user32.MapVirtualKeyA(this._user32.VkKeyScanA(key).Val, MAPVK_VK_TO_VSC).Val);
389
+
390
+ var dwWritten = GM.CreateVariable(4);
391
+ if(this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val == 0) { return(false); }
392
+
393
+ rec.Deref(4,4).toBuffer().writeUInt16LE(0); // rec.Event.KeyEvent.bKeyDown
394
+ return(this._kernel32.WriteConsoleInputA(this._stdinput, rec, 1, dwWritten).Val != 0);
395
+ }
396
+
397
+ this._GetScreenBuffer = function(sx, sy, ex, ey)
398
+ {
399
+ // get the current visible screen buffer
400
+
401
+ var info = GM.CreateVariable(22);
402
+ if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw('Error getting screen buffer info'); }
403
+
404
+ var nWidth = info.Deref(14,2).toBuffer().readUInt16LE() - info.Deref(10,2).toBuffer().readUInt16LE() + 1;
405
+ var nHeight = info.Deref(16,2).toBuffer().readUInt16LE() - info.Deref(12,2).toBuffer().readUInt16LE() + 1;
406
+
407
+ if (arguments[3] == null)
408
+ {
409
+ // Use Default Parameters
410
+ sx = 0;
411
+ sy = 0;
412
+ ex = nWidth-1;
413
+ ey = nHeight-1;
414
+ }
415
+ else
416
+ {
417
+ if(this._scrx != 0)
418
+ {
419
+ sx += this._scrx;
420
+ ex += this._scrx;
421
+ }
422
+ if(this._scry != 0)
423
+ {
424
+ sy += this._scry;
425
+ ey += this._scry;
426
+ }
427
+ this._scrx = this._scry = 0;
428
+ }
429
+
430
+
431
+ var nBuffer = GM.CreateVariable((ex-sx+1) * (ey-sy+1) * 4);
432
+ var size = GM.CreateVariable(4);
433
+ size.Deref(0,2).toBuffer().writeUInt16LE(ex-sx+1, 0);
434
+ size.Deref(2, 2).toBuffer().writeUInt16LE(ey-sy+1, 0);
435
+
436
+ var startCoord = GM.CreateVariable(4);
437
+ startCoord.Deref(0, 2).toBuffer().writeUInt16LE(0, 0);
438
+ startCoord.Deref(2, 2).toBuffer().writeUInt16LE(0, 0);
439
+
440
+ var region = GM.CreateVariable(8);
441
+ region.buffer = region.toBuffer();
442
+ region.buffer.writeUInt16LE(sx, 0);
443
+ region.buffer.writeUInt16LE(sy, 2);
444
+ region.buffer.writeUInt16LE(ex, 4);
445
+ region.buffer.writeUInt16LE(ey, 6);
446
+
447
+ if (this._kernel32.ReadConsoleOutputA(this._stdoutput, nBuffer, size.Deref(0, 4).toBuffer().readUInt32LE(), startCoord.Deref(0, 4).toBuffer().readUInt32LE(), region).Val == 0)
448
+ {
449
+ throw('Unable to read Console Output');
450
+ }
451
+
452
+ // Lets convert the buffer into something simpler
453
+ //var retVal = { data: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), attributes: Buffer.alloc((dw - dx + 1) * (dh - dy + 1)), width: dw - dx + 1, height: dh - dy + 1, x: dx, y: dy };
454
+
455
+ var retVal = { data: [], attributes: [], width: ex - sx + 1, height: ey - sy + 1, x: sx, y: sy };
456
+ var x, y, line, ifo;
457
+ var tmp;
458
+ var lineWidth = ex - sx + 1;
459
+
460
+ for (y = 0; y <= (ey - sy) ; ++y)
461
+ {
462
+ retVal.data.push(Buffer.alloc(lineWidth));
463
+ retVal.attributes.push(Buffer.alloc(lineWidth));
464
+
465
+ line = nBuffer.Deref(y * lineWidth * 4, lineWidth * 4).toBuffer();
466
+ for(x = 0; x < lineWidth; ++x)
467
+ {
468
+ retVal.data.peek()[x] = line[x * 4];
469
+ retVal.attributes.peek()[x] = line[2 + (x * 4)];
470
+ }
471
+ }
472
+
473
+ return (retVal);
474
+ }
475
+
476
+ this._SendDataBuffer = function(data)
477
+ {
478
+ // { data, attributes, width, height, x, y }
479
+
480
+ var dy, line, attr;
481
+ for(dy = 0; dy < data.height; ++dy)
482
+ {
483
+ line = data.data[dy];
484
+ attr = data.attributes[dy];
485
+ line.s = line.toString();
486
+ //line = data.data.slice(data.width * dy, (data.width * dy) + data.width);
487
+ //attr = data.attributes.slice(data.width * dy, (data.width * dy) + data.width);
488
+ this._stream.push(TranslateLine(data.x, data.y + dy, line, attr));
489
+ }
490
+ }
491
+ this._SendScroll = function _SendScroll(dx, dy)
492
+ {
493
+ if (this._scrollTimer)
494
+ {
495
+ return;
496
+ }
497
+
498
+ var info = GM.CreateVariable(22);
499
+ if (this._kernel32.GetConsoleScreenBufferInfo(this._stdoutput, info).Val == 0) { throw ('Error getting screen buffer info'); }
500
+
501
+ var nWidth = info.Deref(14, 2).toBuffer().readUInt16LE() - info.Deref(10, 2).toBuffer().readUInt16LE() + 1;
502
+ var nHeight = info.Deref(16, 2).toBuffer().readUInt16LE() - info.Deref(12, 2).toBuffer().readUInt16LE() + 1;
503
+
504
+ this._stream.push(GetEsc('H', nHeight-1, 0));
505
+ for (var i = 0; i > nHeight; ++i)
506
+ {
507
+ this._stream.push(Buffer.from('\r\n'));
508
+ }
509
+
510
+ var buffer = this._GetScreenBuffer(0, 0, nWidth - 1, nHeight - 1);
511
+ this._SendDataBuffer(buffer);
512
+
513
+ this._scrollTimer = setTimeout(function (self, nw, nh)
514
+ {
515
+ var buffer = self._GetScreenBuffer(0, 0, nw - 1, nh - 1);
516
+ self._SendDataBuffer(buffer);
517
+ self._scrollTimer = null;
518
+ }, 250, this, nWidth, nHeight);
519
+ }
520
+
521
+ this.StartCommand = function StartCommand()
522
+ {
523
+ if(this._kernel32.CreateProcessA(GM.CreateVariable(process.env['windir'] + '\\system32\\cmd.exe'), 0, 0, 0, 1, CREATE_NEW_PROCESS_GROUP, 0, 0, si, pi).Val == 0)
524
+ {
525
+ console.log('Error Spawning CMD');
526
+ return;
527
+ }
528
+
529
+ this._kernel32.CloseHandle(pi.Deref(GM.PointerSize, GM.PointerSize).Deref()); // pi.hThread
530
+ this._hProcess = pi.Deref(0, GM.PointerSize).Deref(); // pi.hProcess
531
+ this._hProcessID = pi.Deref(GM.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE(); // pi.dwProcessId
532
+ //console.log('Ready => hProcess: ' + this._hProcess._ptr + ' PID: ' + this._hProcessID);
533
+ }
534
+}
535
+
536
+function LOWORD(val)
537
+{
538
+ return (val & 0xFFFF);
539
+}
540
+function HIWORD(val)
541
+{
542
+ return ((val >> 16) & 0xFFFF);
543
+}
544
+
545
+function GetEsc(CodeCharStr, arg1, arg2)
546
+{
547
+ return (Buffer.from('\x1B[' + arg1 + ';' + arg2 + CodeCharStr));
548
+ //return (Buffer.from('*[' + arg1 + ';' + arg2 + CodeCharStr));
549
+}
550
+
551
+function TranslateLine(x, y, data, attributes)
552
+{
553
+ return (Buffer.concat([GetEsc('H', y, x), data]));
554
+}
555
+module.exports = new windows_terminal();
\ No newline at end of file
package.json
+1
-1
@@ -1,6 +1,6 @@
1
{
2
"name": "meshcentral",
3
- "version": "0.2.4-g",
3
+ "version": "0.2.4-h",
4
"keywords": [
5
"Remote Management",
6
"Intel AMT",