New toast notifications

Ylian Saint-Hilaire committed Apr 19, 2018 at 18:19 UTC bd82453c3bde56ee2a7366beb14ac95ca29303ee
30 files changed +725 -33
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/meshagent_arm
Binary files a/agents/meshagent_arm and b/agents/meshagent_arm differ
agents/meshagent_pi
Binary files a/agents/meshagent_pi and b/agents/meshagent_pi differ
agents/meshagent_pogo
Binary files a/agents/meshagent_pogo and b/agents/meshagent_pogo differ
agents/meshagent_poky
Binary files a/agents/meshagent_poky and b/agents/meshagent_poky differ
agents/meshagent_poky64
Binary files a/agents/meshagent_poky64 and b/agents/meshagent_poky64 differ
agents/meshagent_x86
Binary files a/agents/meshagent_x86 and b/agents/meshagent_x86 differ
agents/meshagent_x86-64
Binary files a/agents/meshagent_x86-64 and b/agents/meshagent_x86-64 differ
agents/meshagent_x86-64_nokvm
Binary files a/agents/meshagent_x86-64_nokvm and b/agents/meshagent_x86-64_nokvm differ
agents/meshagent_x86_nokvm
Binary files a/agents/meshagent_x86_nokvm and b/agents/meshagent_x86_nokvm differ
agents/meshcore.js
+24 -6
@@ -18,7 +18,7 @@ function createMeshCore(agent) {
18 var obj = {};
19
20 // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
21 - obj.meshCoreInfo = "MeshCore v4";
21 + obj.meshCoreInfo = "MeshCore v5";
22 obj.meshCoreCapabilities = 14; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
23 var meshServerConnectionState = 0;
24 var tunnels = {};
@@ -402,6 +402,11 @@ function createMeshCore(agent) {
402 getIpLocationData(function (location) { mesh.SendCommand({ "action": "iplocation", "type": "publicip", "value": location }); });
403 break;
404 }
405 + case 'toast': {
406 + // Display a toast message
407 + if (data.title && data.msg) { require('toaster').Toast(data.title, data.msg); }
408 + break;
409 + }
410 }
411 }
412 }
@@ -521,7 +526,6 @@ function createMeshCore(agent) {
526
527 // Clean up WebSocket
528 this.removeAllListeners('data');
524 - delete this;
529 }
530 function onTunnelSendOk() { sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid); }
531 function onTunnelData(data) {
@@ -571,19 +575,28 @@ function createMeshCore(agent) {
575 this.on('data', onTunnelControlData);
576 //this.write('MeshCore Terminal Hello');
577 if (process.platform != 'win32') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nclear\n"); }
574 - } else if (this.httprequest.protocol == 2) {
578 + } else if (this.httprequest.protocol == 2)
579 + {
580 // Remote desktop using native pipes
581 this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
582 this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
583 this.desktop = this.httprequest.desktop;
584 +
585 + // Display a toast message
586 + //require('toaster').Toast('MeshCentral', 'Remote Desktop Control Started.');
587 +
588 this.end = function () {
589 --this.desktop.kvm.connectionCount;
590 this.unpipe(this.httprequest.desktop.kvm);
591 this.httprequest.desktop.kvm.unpipe(this);
583 - if (this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); }
592 + if (this.desktop.kvm.connectionCount == 0) {
593 + // Display a toast message
594 + //require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.');
595 + this.httprequest.desktop.kvm.end();
596 + }
597 };
598 if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
586 - this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. (****************)
599 + this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
600 this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
601 this.removeAllListeners('data');
602 this.on('data', onTunnelControlData);
@@ -839,7 +852,12 @@ function createMeshCore(agent) {
852 var response = null;
853 switch (cmd) {
854 case 'help': { // Displays available commands
842 - response = 'Available commands: help, info, 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.';
855 + response = 'aaaAvailable commands: help, info, 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.';
856 + break;
857 + }
858 + case 'toast': {
859 + require('toaster').Toast('MeshCentral', args['_'][0]);
860 + response = 'ok';
861 break;
862 }
863 case 'setdebug': {
agents/modules_meshcore/process-manager.js
+17 -13
@@ -5,15 +5,16 @@ 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
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.
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 +// JavaScript source code
18 var GM = require('_GenericMarshal');
19
20 function processManager() {
@@ -34,12 +35,16 @@ function processManager() {
35 }
36 this.getProcesses = function getProcesses(callback) {
37 switch (process.platform) {
38 + default:
39 + throw ('Enumerating processes on ' + process.platform + ' not supported');
40 case 'win32':
38 - var h = this._kernel32.CreateToolhelp32Snapshot(2, 0), info = GM.CreateVariable(304), retVal = {};
41 + var retVal = [];
42 + var h = this._kernel32.CreateToolhelp32Snapshot(2, 0);
43 + var info = GM.CreateVariable(304);
44 info.toBuffer().writeUInt32LE(304, 0);
45 var nextProcess = this._kernel32.Process32First(h, info);
46 while (nextProcess.Val) {
42 - retVal[info.Deref(8, 4).toBuffer().readUInt32LE(0)] = { cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String };
47 + retVal.push({ pid: info.Deref(8, 4).toBuffer().readUInt32LE(0), cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String });
48 nextProcess = this._kernel32.Process32Next(h, info);
49 }
50 if (callback) { callback.apply(this, [retVal]); }
@@ -55,15 +60,16 @@ function processManager() {
60 for (var i = 1; i < arguments.length; ++i) { p.args.push(arguments[i]); }
61 p.on('exit', function onGetProcesses() {
62 delete this.Parent._psp[this.pid];
58 - var retVal = {}, lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0;
63 + var retVal = [], lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0;
64 for (var i in lines) {
60 - var tokens = lines[i].split(' '), tokenList = [];
65 + var tokens = lines[i].split(' ');
66 + var tokenList = [];
67 for (var x in tokens) {
68 if (i == 0 && tokens[x]) { key[tokens[x]] = keyi++; }
69 if (i > 0 && tokens[x]) { tokenList.push(tokens[x]); }
70 }
71 if ((i > 0) && (tokenList[key.PID])) {
66 - retVal[tokenList[key.PID]] = { user: tokenList[key.USER], cmd: tokenList[key.COMMAND] };
72 + retVal.push({ pid: tokenList[key.PID], user: tokenList[key.USER], cmd: tokenList[key.COMMAND] });
73 }
74 }
75 if (this.callback) {
@@ -73,22 +79,20 @@ function processManager() {
79 });
80 p.stdout.on('data', function (chunk) { this.parent.ps += chunk.toString(); });
81 break;
76 - default:
77 - throw ('Enumerating processes on ' + process.platform + ' not supported');
82 }
83 };
84 this.getProcessInfo = function getProcessInfo(pid) {
85 switch (process.platform) {
86 + default:
87 + throw ('getProcessInfo() not supported for ' + process.platform);
88 case 'linux':
83 - var status = require('fs').readFileSync('/proc/' + pid + '/status'), lines = status.toString().split('\n'), info = {};
89 + var status = require('fs').readFileSync('/proc/' + pid + '/status'), info = {}, lines = status.toString().split('\n');
90 for (var i in lines) {
91 var tokens = lines[i].split(':');
92 if (tokens.length > 1) { tokens[1] = tokens[1].trim(); }
93 info[tokens[0]] = tokens[1];
94 }
89 - return info;
90 - default:
91 - throw ('getProcessInfo() not supported for ' + process.platform);
95 + return (info);
96 }
97 };
98 }
agents/modules_meshcore/toaster.js new
+72
@@ -0,0 +1,72 @@
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 toasters = {};
18 +
19 +function Toaster()
20 +{
21 + this._ObjectID = 'Toaster';
22 + this.Toast = function Toast(title, caption)
23 + {
24 + if (process.platform != 'win32') return;
25 +
26 + var retVal = {};
27 + var emitter = require('events').inherits(retVal);
28 + emitter.createEvent('Clicked');
29 + emitter.createEvent('Dismissed');
30 +
31 + var session = require('user-sessions').Current();
32 + for (var i in session)
33 + {
34 + console.log(session[i]);
35 + }
36 + try
37 + {
38 + console.log('Attempting Toast Mechanism 1');
39 + retVal._child = require('ScriptContainer').Create({ processIsolation: true, sessionId: session.connected[0].SessionId });
40 + }
41 + catch (e) {
42 + console.log(e);
43 + console.log('Attempting Toast Mechanism 2');
44 + retVal._child = require('ScriptContainer').Create({ processIsolation: true });
45 + }
46 + retVal._child.parent = retVal;
47 +
48 + retVal._child.on('exit', function (code) { this.parent.emit('Dismissed'); delete this.parent._child; });
49 + retVal._child.addModule('win-console', getJSModule('win-console'));
50 + retVal._child.addModule('win-messagepump', getJSModule('win-messagepump'));
51 +
52 + var str = "\
53 + try{\
54 + var toast = require('win-console');\
55 + var balloon = toast.SetTrayIcon({ szInfo: '" + caption + "', szInfoTitle: '" + title + "', balloonOnly: true });\
56 + balloon.on('ToastDismissed', function(){process.exit();});\
57 + }\
58 + catch(e)\
59 + {\
60 + require('ScriptContainer').send(e);\
61 + }\
62 + require('ScriptContainer').send('done');\
63 + ";
64 + retVal._child.ExecuteString(str);
65 + toasters[retVal._hashCode()] = retVal;
66 + retVal.on('Dismissed', function () { delete toasters[this._hashCode()]; });
67 + console.log('Returning');
68 + return (retVal);
69 + };
70 +}
71 +
72 +module.exports = new Toaster();
\ No newline at end of file
agents/modules_meshcore/user-sessions.js renamed
+66 -1
@@ -1,4 +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 UserSessions()
18 {
@@ -87,6 +101,8 @@ function UserSessions()
101 }
102
103 this._wts.WTSFreeMemory(pinfo.Deref());
104 +
105 + Object.defineProperty(retVal, 'connected', { value: showActiveOnly(retVal) });
106 return (retVal);
107 };
108 }
@@ -94,9 +110,58 @@ function UserSessions()
110 {
111 this.Current = function Current()
112 {
97 - return ({});
113 + var retVal = {};
114 + var emitterUtils = require('events').inherits(retVal);
115 + emitterUtils.createEvent('logon');
116 +
117 + retVal._child = require('child_process').execFile('/usr/bin/last', ['last', '-f', '/var/run/utmp']);
118 + retVal._child.Parent = retVal;
119 + retVal._child._txt = '';
120 + retVal._child.on('exit', function (code)
121 + {
122 + var lines = this._txt.split('\n');
123 + var sessions = [];
124 + for(var i in lines)
125 + {
126 + if (lines[i])
127 + {
128 + console.log(getTokens(lines[i]));
129 + var user = lines[i].substring(0, lines[i].indexOf(' '));
130 + sessions.push(user);
131 + }
132 + }
133 + sessions.pop();
134 + console.log(sessions);
135 + });
136 + retVal._child.stdout.Parent = retVal._child;
137 + retVal._child.stdout.on('data', function (chunk) { this.Parent._txt += chunk.toString(); });
138 +
139 + return (retVal);
140 + }
141 + }
142 +}
143 +function showActiveOnly(source)
144 +{
145 + var retVal = [];
146 + for (var i in source)
147 + {
148 + if (source[i].State == 'Active' || source[i].State == 'Connected')
149 + {
150 + retVal.push(source[i]);
151 }
152 }
153 + return (retVal);
154 +}
155 +function getTokens(str)
156 +{
157 + var columns = [];
158 + var i;
159 +
160 + columns.push(str.substring(0, (i=str.indexOf(' '))));
161 + while (str[++i] == ' ');
162 + columns.push(str.substring(i, str.substring(i).indexOf(' ') + i));
163 +
164 + return (columns);
165 }
166
167 module.exports = new UserSessions();
\ No newline at end of file
agents/modules_meshcore/wifi-scanner-windows.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
18 function _Scan()
19 {
agents/modules_meshcore/wifi-scanner.js
+16 -1
@@ -1,7 +1,22 @@
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
4 -
20 function AccessPoint(_ssid, _bssid, _lq)
21 {
22 this.ssid = _ssid;
agents/modules_meshcore/win-console.js new
+164
@@ -0,0 +1,164 @@
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 TrayIconFlags =
18 + {
19 + NIF_MESSAGE: 0x00000001,
20 + NIF_ICON: 0x00000002,
21 + NIF_TIP: 0x00000004,
22 + NIF_STATE: 0x00000008,
23 + NIF_INFO: 0x00000010,
24 + NIF_GUID: 0x00000020,
25 + NIF_REALTIME: 0x00000040,
26 + NIF_SHOWTIP: 0x00000080,
27 +
28 + NIM_ADD: 0x00000000,
29 + NIM_MODIFY: 0x00000001,
30 + NIM_DELETE: 0x00000002,
31 + NIM_SETFOCUS: 0x00000003,
32 + NIM_SETVERSION: 0x00000004
33 + };
34 +var NOTIFYICON_VERSION_4 = 4;
35 +var MessageTypes = { WM_APP: 0x8000, WM_USER: 0x0400 };
36 +function WindowsConsole()
37 +{
38 + if (process.platform == 'win32')
39 + {
40 + this._ObjectID = 'WindowsConsole';
41 + this._Marshal = require('_GenericMarshal');
42 + this._kernel32 = this._Marshal.CreateNativeProxy("kernel32.dll");
43 + this._user32 = this._Marshal.CreateNativeProxy("user32.dll");
44 + this._kernel32.CreateMethod("GetConsoleWindow");
45 + this._kernel32.CreateMethod('GetCurrentThread');
46 + this._user32.CreateMethod("ShowWindow");
47 + this._user32.CreateMethod("LoadImageA");
48 + this._user32.CreateMethod({ method: 'GetMessageA', threadDispatch: 1 });
49 + this._shell32 = this._Marshal.CreateNativeProxy('Shell32.dll');
50 + this._shell32.CreateMethod('Shell_NotifyIconA');
51 +
52 + this._handle = this._kernel32.GetConsoleWindow();
53 + this.minimize = function () {
54 + this._user32.ShowWindow(this._handle, 6);
55 + };
56 + this.restore = function () {
57 + this._user32.ShowWindow(this._handle, 9);
58 + };
59 + this.hide = function () {
60 + this._user32.ShowWindow(this._handle, 0);
61 + };
62 + this.show = function () {
63 + this._user32.ShowWindow(this._handle, 5);
64 + };
65 +
66 +
67 + this._loadicon = function (imagePath) {
68 + var h = this._user32.LoadImageA(0, this._Marshal.CreateVariable(imagePath), 1, 0, 0, 0x00000010 | 0x00008000 | 0x00000040); // LR_LOADFROMFILE | LR_SHARED | LR_DEFAULTSIZE
69 + return (h);
70 + };
71 +
72 + this.SetTrayIcon = function SetTrayIcon(options)
73 + {
74 + var data = this._Marshal.CreateVariable(this._Marshal.PointerSize == 4 ? 508 : 528);
75 + //console.log('struct size = ' + data._size);
76 + //console.log('TryIcon, WM_MESSAGE filter = ' + options.filter);
77 + data.toBuffer().writeUInt32LE(data._size, 0);
78 +
79 + var trayType = TrayIconFlags.NIF_TIP | TrayIconFlags.NIF_MESSAGE
80 + options.filter = MessageTypes.WM_APP + 1;
81 + data.Deref(this._Marshal.PointerSize == 4 ? 16 : 24, 4).toBuffer().writeUInt32LE(options.filter);
82 +
83 + if (!options.noBalloon) { trayType |= TrayIconFlags.NIF_INFO; }
84 +
85 + if (options.icon)
86 + {
87 + trayType |= TrayIconFlags.NIF_ICON;
88 + var hIcon = data.Deref(this._Marshal.PointerSize == 4 ? 20 : 32, this._Marshal.PointerSize);
89 + options.icon.pointerBuffer().copy(hIcon.toBuffer());
90 + }
91 +
92 + data.Deref(this._Marshal.PointerSize * 2, 4).toBuffer().writeUInt32LE(1);
93 + data.Deref(this._Marshal.PointerSize == 4 ? 12 : 20, 4).toBuffer().writeUInt32LE(trayType);
94 + data.Deref(this._Marshal.PointerSize == 4 ? 416 : 432, 4).toBuffer().writeUInt32LE(NOTIFYICON_VERSION_4);
95 +
96 + var szTip = data.Deref(this._Marshal.PointerSize == 4 ? 24 : 40, 128);
97 + var szInfo = data.Deref(this._Marshal.PointerSize == 4 ? 160 : 176, 256);
98 + var szInfoTitle = data.Deref(this._Marshal.PointerSize == 4 ? 420 : 436, 64);
99 +
100 + if (options.szTip) { Buffer.from(options.szTip).copy(szTip.toBuffer()); }
101 + if (options.szInfo) { Buffer.from(options.szInfo).copy(szInfo.toBuffer()); }
102 + if (options.szInfoTitle) { Buffer.from(options.szInfoTitle).copy(szInfoTitle.toBuffer()); }
103 +
104 +
105 + var MessagePump = require('win-messagepump');
106 + retVal = { _ObjectID: 'WindowsConsole.TrayIcon', MessagePump: new MessagePump(options) };
107 + var retValEvents = require('events').inherits(retVal);
108 + retValEvents.createEvent('ToastClicked');
109 + retValEvents.createEvent('IconHover');
110 + retValEvents.createEvent('ToastDismissed');
111 + retVal.Options = options;
112 + retVal.MessagePump.TrayIcon = retVal;
113 + retVal.MessagePump.NotifyData = data;
114 + retVal.MessagePump.WindowsConsole = this;
115 + retVal.MessagePump.on('exit', function onExit(code) { console.log('Pump Exited'); if (this.TrayIcon) { this.TrayIcon.remove(); } });
116 + retVal.MessagePump.on('hwnd', function onHwnd(h)
117 + {
118 + //console.log('Got HWND');
119 + options.hwnd = h;
120 + h.pointerBuffer().copy(this.NotifyData.Deref(this.WindowsConsole._Marshal.PointerSize, this.WindowsConsole._Marshal.PointerSize).toBuffer());
121 +
122 + if(this.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_ADD, this.NotifyData).Val == 0)
123 + {
124 + // Something went wrong
125 + }
126 + });
127 + retVal.MessagePump.on('message', function onWindowsMessage(msg)
128 + {
129 + if(msg.message == this.TrayIcon.Options.filter)
130 + {
131 + var handled = false;
132 + if (msg.wparam == 1 && msg.lparam == 1029)
133 + {
134 + this.TrayIcon.emit('ToastClicked');
135 + handled = true;
136 + }
137 + if (msg.wparam == 1 && msg.lparam == 512)
138 + {
139 + this.TrayIcon.emit('IconHover');
140 + handled = true;
141 + }
142 + if (this.TrayIcon.Options.balloonOnly && msg.wparam == 1 && (msg.lparam == 1028 || msg.lparam == 1029))
143 + {
144 + this.TrayIcon.emit('ToastDismissed');
145 + this.TrayIcon.remove();
146 + handled = true;
147 + }
148 + if (!handled) { console.log(msg); }
149 + }
150 + });
151 + retVal.remove = function remove()
152 + {
153 + this.MessagePump.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_DELETE, this.MessagePump.NotifyData);
154 + this.MessagePump.stop();
155 + delete this.MessagePump.TrayIcon;
156 + delete this.MessagePump;
157 + };
158 + return (retVal);
159 +
160 + };
161 + }
162 +}
163 +
164 +module.exports = new WindowsConsole();
\ No newline at end of file
agents/modules_meshcore/win-messagepump.js new
+122
@@ -0,0 +1,122 @@
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 WH_CALLWNDPROC = 4;
18 +var WM_QUIT = 0x0012;
19 +
20 +function WindowsMessagePump(options)
21 +{
22 + this._ObjectID = 'WindowsMessagePump';
23 + this._options = options;
24 + var emitterUtils = require('events').inherits(this);
25 + emitterUtils.createEvent('hwnd');
26 + emitterUtils.createEvent('error');
27 + emitterUtils.createEvent('message');
28 + emitterUtils.createEvent('exit');
29 +
30 + this._child = require('ScriptContainer').Create({ processIsolation: 0 });
31 + this._child.MessagePump = this;
32 + this._child.prependListener('~', function _childFinalizer() { this.MessagePump.emit('exit', 0); console.log('calling stop'); 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 + console.log('child ready');
37 + var execString =
38 + "var m = require('_GenericMarshal');\
39 + var h = null;\
40 + var k = m.CreateNativeProxy('Kernel32.dll');\
41 + k.CreateMethod('GetLastError');\
42 + k.CreateMethod('GetModuleHandleA');\
43 + var u = m.CreateNativeProxy('User32.dll');\
44 + u.CreateMethod('GetMessageA');\
45 + u.CreateMethod('CreateWindowExA');\
46 + u.CreateMethod('TranslateMessage');\
47 + u.CreateMethod('DispatchMessageA');\
48 + u.CreateMethod('RegisterClassExA');\
49 + u.CreateMethod('DefWindowProcA');\
50 + var wndclass = m.CreateVariable(m.PointerSize == 4 ? 48 : 80);\
51 + wndclass.hinstance = k.GetModuleHandleA(0);\
52 + wndclass.cname = m.CreateVariable('MainWWWClass');\
53 + wndclass.wndproc = m.GetGenericGlobalCallback(4);\
54 + wndclass.toBuffer().writeUInt32LE(wndclass._size);\
55 + wndclass.cname.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 40 : 64, m.PointerSize).toBuffer());\
56 + wndclass.wndproc.pointerBuffer().copy(wndclass.Deref(8, m.PointerSize).toBuffer());\
57 + wndclass.hinstance.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 20 : 24, m.PointerSize).toBuffer());\
58 + wndclass.wndproc.on('GlobalCallback', function onWndProc(xhwnd, xmsg, wparam, lparam)\
59 + {\
60 + if(h==null || h.Val == xhwnd.Val)\
61 + {\
62 + require('ScriptContainer').send({message: xmsg.Val, wparam: wparam.Val, lparam: lparam.Val});\
63 + var retVal = u.DefWindowProcA(xhwnd, xmsg, wparam, lparam);\
64 + return(retVal);\
65 + }\
66 + });\
67 + u.RegisterClassExA(wndclass);\
68 + h = u.CreateWindowExA(0x00000088, wndclass.cname, 0, 0x00800000, 0, 0, 100, 100, 0, 0, 0, 0);\
69 + if(h.Val == 0)\
70 + {\
71 + require('ScriptContainer').send({error: 'Error Creating Hidden Window'});\
72 + process.exit();\
73 + }\
74 + require('ScriptContainer').send({hwnd: h.pointerBuffer().toString('hex')});\
75 + require('ScriptContainer').on('data', function onData(jmsg)\
76 + {\
77 + if(jmsg.listen)\
78 + {\
79 + var msg = m.CreateVariable(m.PointerSize == 4 ? 28 : 48);\
80 + while(u.GetMessageA(msg, h, 0, 0).Val>0)\
81 + {\
82 + u.TranslateMessage(msg);\
83 + u.DispatchMessageA(msg);\
84 + }\
85 + process.exit();\
86 + }\
87 + });";
88 +
89 + this.ExecuteString(execString);
90 + });
91 + this._child.on('data', function onChildData(msg)
92 + {
93 + if (msg.hwnd)
94 + {
95 + var m = require('_GenericMarshal');
96 + this._hwnd = m.CreatePointer(Buffer.from(msg.hwnd, 'hex'));
97 + this.MessagePump.emit('hwnd', this._hwnd);
98 + this.send({ listen: this.MessagePump._options.filter });
99 + }
100 + else if(msg.message)
101 + {
102 + this.MessagePump.emit('message', msg);
103 + }
104 + else
105 + {
106 + console.log('Received: ', msg);
107 + }
108 + });
109 + this.stop = function stop()
110 + {
111 + if(this._child && this._child._hwnd)
112 + {
113 + console.log('posting WM_QUIT');
114 + var marshal = require('_GenericMarshal');
115 + var User32 = marshal.CreateNativeProxy('User32.dll');
116 + User32.CreateMethod('PostMessageA');
117 + User32.PostMessageA(this._child._hwnd, WM_QUIT, 0, 0);
118 + }
119 + };
120 +}
121 +
122 +module.exports = WindowsMessagePump;
agents/modules_meshcore/win-registry.js new
+167
@@ -0,0 +1,167 @@
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 KEY_QUERY_VALUE = 0x0001;
18 +var KEY_WRITE = 0x20006;
19 +
20 +var KEY_DATA_TYPES =
21 + {
22 + REG_NONE: 0,
23 + REG_SZ: 1,
24 + REG_EXPAND_SZ: 2,
25 + REG_BINARY: 3,
26 + REG_DWORD: 4,
27 + REG_DWORD_BIG_ENDIAN: 5,
28 + REG_LINK: 6,
29 + REG_MULTI_SZ: 7,
30 + REG_RESOURCE_LIST: 8,
31 + REG_FULL_RESOURCE_DESCRIPTOR: 9,
32 + REG_RESOURCE_REQUIREMENTS_LIST: 10,
33 + REG_QWORD: 11
34 + };
35 +
36 +function windows_registry()
37 +{
38 + this._ObjectId = 'windows_registry';
39 + this._marshal = require('_GenericMarshal');
40 + this._AdvApi = this._marshal.CreateNativeProxy('Advapi32.dll');
41 + this._AdvApi.CreateMethod('RegCreateKeyExA');
42 + this._AdvApi.CreateMethod('RegOpenKeyExA');
43 + this._AdvApi.CreateMethod('RegQueryValueExA');
44 + this._AdvApi.CreateMethod('RegCloseKey');
45 + this._AdvApi.CreateMethod('RegDeleteKeyA');
46 + this._AdvApi.CreateMethod('RegDeleteValueA');
47 + this._AdvApi.CreateMethod('RegSetValueExA');
48 + this.HKEY = { Root: Buffer.from('80000000', 'hex').swap32(), CurrentUser: Buffer.from('80000001', 'hex').swap32(), LocalMachine: Buffer.from('80000002', 'hex').swap32(), Users: Buffer.from('80000003', 'hex').swap32() };
49 +
50 + this.QueryKey = function QueryKey(hkey, path, key)
51 + {
52 + var h = this._marshal.CreatePointer();
53 + var len = this._marshal.CreateVariable(4);
54 + var valType = this._marshal.CreateVariable(4);
55 + key = this._marshal.CreateVariable(key);
56 + var HK = this._marshal.CreatePointer(hkey);
57 + var retVal = null;
58 +
59 + if (this._AdvApi.RegOpenKeyExA(HK, this._marshal.CreateVariable(path), 0, KEY_QUERY_VALUE, h).Val != 0)
60 + {
61 + throw ('Error Opening Registry Key: ' + path);
62 + }
63 +
64 + if(this._AdvApi.RegQueryValueExA(h.Deref(), key, 0, 0, 0, len).Val == 0)
65 + {
66 + var data = this._marshal.CreateVariable(len.toBuffer().readUInt32LE());
67 + if (this._AdvApi.RegQueryValueExA(h.Deref(), key, 0, valType, data, len).Val == 0)
68 + {
69 + switch(valType.toBuffer().readUInt32LE())
70 + {
71 + case KEY_DATA_TYPES.REG_DWORD:
72 + retVal = data.toBuffer().readUInt32LE();
73 + break;
74 + case KEY_DATA_TYPES.REG_DWORD_BIG_ENDIAN:
75 + retVal = data.toBuffer().readUInt32BE();
76 + break;
77 + case KEY_DATA_TYPES.REG_SZ:
78 + retVal = data.String;
79 + break;
80 + case KEY_DATA_TYPES.REG_BINARY:
81 + default:
82 + retVal = data.toBuffer();
83 + retVal._data = data;
84 + break;
85 + }
86 + }
87 + }
88 + else
89 + {
90 + this._AdvApi.RegCloseKey(h.Deref());
91 + throw ('Not Found');
92 + }
93 + this._AdvApi.RegCloseKey(h.Deref());
94 + return (retVal);
95 + };
96 + this.WriteKey = function WriteKey(hkey, path, key, value)
97 + {
98 + var result;
99 + var h = this._marshal.CreatePointer();
100 +
101 + if (this._AdvApi.RegCreateKeyExA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path), 0, 0, 0, KEY_WRITE, 0, h, 0).Val != 0)
102 + {
103 + throw ('Error Opening Registry Key: ' + path);
104 + }
105 +
106 + var data;
107 + var dataType;
108 +
109 + switch(typeof(value))
110 + {
111 + case 'boolean':
112 + dataType = KEY_DATA_TYPES.REG_DWORD;
113 + data = this._marshal.CreateVariable(4);
114 + data.toBuffer().writeUInt32LE(value ? 1 : 0);
115 + break;
116 + case 'number':
117 + dataType = KEY_DATA_TYPES.REG_DWORD;
118 + data = this._marshal.CreateVariable(4);
119 + data.toBuffer().writeUInt32LE(value);
120 + break;
121 + case 'string':
122 + dataType = KEY_DATA_TYPES.REG_SZ;
123 + data = this._marshal.CreateVariable(value);
124 + break;
125 + default:
126 + dataType = KEY_DATA_TYPES.REG_BINARY;
127 + data = this._marshal.CreateVariable(value.length);
128 + value.copy(data.toBuffer());
129 + break;
130 + }
131 +
132 + if(this._AdvApi.RegSetValueExA(h.Deref(), this._marshal.CreateVariable(key), 0, dataType, data, data._size).Val != 0)
133 + {
134 + this._AdvApi.RegCloseKey(h.Deref());
135 + throw ('Error writing reg key: ' + key);
136 + }
137 + this._AdvApi.RegCloseKey(h.Deref());
138 + };
139 + this.DeleteKey = function DeleteKey(hkey, path, key)
140 + {
141 + if(!key)
142 + {
143 + if(this._AdvApi.RegDeleteKeyA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path)).Val != 0)
144 + {
145 + throw ('Error Deleting Key: ' + path);
146 + }
147 + }
148 + else
149 + {
150 + var h = this._marshal.CreatePointer();
151 + var result;
152 + if (this._AdvApi.RegOpenKeyExA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path), 0, KEY_QUERY_VALUE | KEY_WRITE, h).Val != 0)
153 + {
154 + throw ('Error Opening Registry Key: ' + path);
155 + }
156 + if ((result = this._AdvApi.RegDeleteValueA(h.Deref(), this._marshal.CreateVariable(key)).Val) != 0)
157 + {
158 + this._AdvApi.RegCloseKey(h.Deref());
159 + throw ('Error[' + result + '] Deleting Key: ' + path + '.' + key);
160 + }
161 + this._AdvApi.RegCloseKey(h.Deref());
162 + }
163 + };
164 +}
165 +
166 +module.exports = new windows_registry();
167 +
letsEncrypt.js
+2 -2
@@ -75,7 +75,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
75 func(certs);
76
77 // Check if the Let's Encrypt certificate needs to be renewed.
78 - setTimeout(obj.checkRenewCertificate, 300000); // Check in 5 minutes.
78 + setTimeout(obj.checkRenewCertificate, 60000); // Check in 1 minute.
79 setInterval(obj.checkRenewCertificate, 86400000); // Check again in 24 hours and every 24 hours.
80 return;
81 } else {
@@ -111,7 +111,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
111 if (obj.leResults == null) { return; }
112 // TODO: Only renew on one of the peers if multi-peers are active.
113 // Check if we need to renew the certificate
114 - obj.le.renew({ duplicate: false }, obj.leResults).then(function (xresults) {
114 + obj.le.renew({ duplicate: false, domains: obj.leDomains, email: obj.parent.config.letsencrypt.email }, obj.leResults).then(function (xresults) {
115 obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers
116 }, function (err) { }); // If we can't renew, ignore.
117 }
meshuser.js
+34
@@ -873,6 +873,40 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
873 }
874 break;
875 }
876 + case 'toast':
877 + {
878 + if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
879 + if (obj.common.validateString(command.title, 1, 512) == false) break; // Check title
880 + if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message
881 + for (var i in command.nodeids) {
882 + var nodeid = command.nodeids[i], powerActions = 0;
883 + if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
884 + if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
885 + // Get the device
886 + obj.db.Get(nodeid, function (err, nodes) {
887 + if (nodes.length != 1) return;
888 + var node = nodes[0];
889 +
890 + // Get the mesh for this device
891 + var mesh = obj.parent.meshes[node.meshid];
892 + if (mesh) {
893 +
894 + // Check if this user has rights to do this
895 + if (mesh.links[user._id] != null && ((mesh.links[user._id].rights & 8) != 0)) { // "Remote Control permission"
896 +
897 + // Get this device
898 + var agent = obj.parent.wsagents[node._id];
899 + if (agent != null) {
900 + // Send the power command
901 + agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg }));
902 + }
903 + }
904 + }
905 + });
906 + }
907 + }
908 + break;
909 + }
910 case 'getnetworkinfo':
911 {
912 // Argument validation
package.json
+1 -1
@@ -1,6 +1,6 @@
1 {
2 "name": "meshcentral",
3 - "version": "0.1.6-q",
3 + "version": "0.1.6-s",
4 "keywords": [
5 "Remote Management",
6 "Intel AMT",
public/images/mesh-200.png
Binary files /dev/null and b/public/images/mesh-200.png differ
public/scripts/agent-redir-ws-0.1.0.js
+11 -8
@@ -81,9 +81,6 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
81 }
82 }
83
84 - // Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
85 - obj.xxCloseWebRTC = function () { obj.Stop(); }
86 -
84 obj.xxOnMessage = function (e) {
85 //if (obj.debugmode == 1) { console.log('Recv', e.data); }
86 //console.log('Recv', e.data, obj.State);
@@ -111,7 +108,8 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
108 }
109 obj.webrtc.oniceconnectionstatechange = function () {
110 if (obj.webrtc != null) {
114 - if ((obj.webrtc.iceConnectionState == 'disconnected') || (obj.webrtc.iceConnectionState == 'failed')) { obj.xxCloseWebRTC(); }
111 + if (obj.webrtc.iceConnectionState == 'disconnected') { obj.Stop(); }
112 + else if (obj.webrtc.iceConnectionState == 'failed') { obj.xxCloseWebRTC(); }
113 }
114 }
115 obj.webrtc.createOffer(function (offer) {
@@ -206,13 +204,18 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
204 if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
205 }
206
209 - obj.Stop = function (x) {
210 - if (obj.debugmode == 1) { console.log('stop', x); }
211 -
212 - // Close WebRTC
207 + // Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
208 + obj.xxCloseWebRTC = function () {
209 if (obj.webchannel != null) { try { obj.webchannel.close(); } catch (e) { } obj.webchannel = null; }
210 if (obj.webrtc != null) { try { obj.webrtc.close(); } catch (e) { } obj.webrtc = null; }
211 obj.webRtcActive = false;
212 + }
213 +
214 + obj.Stop = function (x) {
215 + if (obj.debugmode == 1) { console.log('stop', x); }
216 +
217 + // Clean up WebRTC
218 + obj.xxCloseWebRTC();
219
220 //obj.debug("Agent Redir Socket Stopped");
221 obj.connectstate = -1;
views/default.handlebars
+13 -1
@@ -572,6 +572,7 @@
572 <div id=p16events style="max-height:600px;overflow-y:scroll"></div>
573 </div>
574 <div id=p20 style=display:none>
575 + <img id=MainMeshImage src="images/mesh-200.png" style=border-width:0px;height:200px;width:200px;float:right>
576 <h1><span id=p20meshName></span> - General</h1>
577 <p id=p20info></p>
578 </div>
@@ -2848,6 +2849,7 @@
2849 // Show action button, only show if we have permissions 4, 8, 64
2850 if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
2851 x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
2852 + if ((connectivity & 1) && (meshrights & 8) && (node.agent.id < 5)) { x += '<input type=button value=Toast title="Display a text message of the remote device" onclick=deviceToastFunction() />'; }
2853 QH('p10html', x);
2854
2855 // Show node last 7 days timeline
@@ -2939,7 +2941,17 @@
2941
2942 function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
2943
2944 + function deviceToastFunction() {
2945 + if (xxdialogMode) return;
2946 + setDialogMode(2, "Device Toast", 3, deviceToastFunctionEx, '<textarea id=d2devToast style=width:100%;height:80px;resize:none;overflow-y:scroll></textarea>');
2947 + }
2948 +
2949 + function deviceToastFunctionEx() {
2950 + meshserver.send({ action: 'toast', nodeids: [ currentNode._id ], title: 'MeshCentral', msg: Q('d2devToast').value });
2951 + }
2952 +
2953 function deviceActionFunction() {
2954 + if (xxdialogMode) return;
2955 var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
2956 var x = "Select an operation to perform on this device.<br /><br />";
2957 var y = '<select id=d2deviceop style=float:right;width:250px>';
@@ -4542,7 +4554,7 @@
4554
4555 x += '<br><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
4556
4545 - x += '<br><br>';
4557 + x += '<br style=clear:both><br>';
4558 var currentMeshLinks = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
4559 if (currentMeshLinks && ((currentMeshLinks.rights & 2) != 0)) { x += '<a onclick=p20showAddMeshUserDialog() style=cursor:pointer;margin-right:10px><img src=images/icon-addnew.png border=0 height=12 width=12> Add User</a>'; }
4560