New toast notifications

This commit is contained in:
Ylian Saint-Hilaire 2018-04-19 18:19:15 -07:00
parent b6b2706d19
commit bd82453c3b
30 changed files with 725 additions and 33 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -18,7 +18,7 @@ function createMeshCore(agent) {
var obj = {}; var obj = {};
// MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent. // MeshAgent JavaScript Core Module. This code is sent to and running on the mesh agent.
obj.meshCoreInfo = "MeshCore v4"; obj.meshCoreInfo = "MeshCore v5";
obj.meshCoreCapabilities = 14; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript obj.meshCoreCapabilities = 14; // Capability bitmask: 1 = Desktop, 2 = Terminal, 4 = Files, 8 = Console, 16 = JavaScript
var meshServerConnectionState = 0; var meshServerConnectionState = 0;
var tunnels = {}; var tunnels = {};
@ -402,6 +402,11 @@ function createMeshCore(agent) {
getIpLocationData(function (location) { mesh.SendCommand({ "action": "iplocation", "type": "publicip", "value": location }); }); getIpLocationData(function (location) { mesh.SendCommand({ "action": "iplocation", "type": "publicip", "value": location }); });
break; break;
} }
case 'toast': {
// Display a toast message
if (data.title && data.msg) { require('toaster').Toast(data.title, data.msg); }
break;
}
} }
} }
} }
@ -521,7 +526,6 @@ function createMeshCore(agent) {
// Clean up WebSocket // Clean up WebSocket
this.removeAllListeners('data'); this.removeAllListeners('data');
delete this;
} }
function onTunnelSendOk() { sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid); } function onTunnelSendOk() { sendConsoleText("Tunnel #" + this.index + " SendOK.", this.sessionid); }
function onTunnelData(data) { function onTunnelData(data) {
@ -571,19 +575,28 @@ function createMeshCore(agent) {
this.on('data', onTunnelControlData); this.on('data', onTunnelControlData);
//this.write('MeshCore Terminal Hello'); //this.write('MeshCore Terminal Hello');
if (process.platform != 'win32') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nclear\n"); } if (process.platform != 'win32') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nclear\n"); }
} else if (this.httprequest.protocol == 2) { } else if (this.httprequest.protocol == 2)
{
// Remote desktop using native pipes // Remote desktop using native pipes
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this }; this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(), tunnel: this };
this.httprequest.desktop.kvm.parent = this.httprequest.desktop; this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
this.desktop = this.httprequest.desktop; this.desktop = this.httprequest.desktop;
// Display a toast message
//require('toaster').Toast('MeshCentral', 'Remote Desktop Control Started.');
this.end = function () { this.end = function () {
--this.desktop.kvm.connectionCount; --this.desktop.kvm.connectionCount;
this.unpipe(this.httprequest.desktop.kvm); this.unpipe(this.httprequest.desktop.kvm);
this.httprequest.desktop.kvm.unpipe(this); this.httprequest.desktop.kvm.unpipe(this);
if (this.desktop.kvm.connectionCount == 0) { this.httprequest.desktop.kvm.end(); } if (this.desktop.kvm.connectionCount == 0) {
// Display a toast message
//require('toaster').Toast('MeshCentral', 'Remote Desktop Control Ended.');
this.httprequest.desktop.kvm.end();
}
}; };
if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; } if (this.httprequest.desktop.kvm.hasOwnProperty("connectionCount")) { this.httprequest.desktop.kvm.connectionCount++; } else { this.httprequest.desktop.kvm.connectionCount = 1; }
this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. (****************) this.pipe(this.httprequest.desktop.kvm, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. this.httprequest.desktop.kvm.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
this.removeAllListeners('data'); this.removeAllListeners('data');
this.on('data', onTunnelControlData); this.on('data', onTunnelControlData);
@ -839,7 +852,12 @@ function createMeshCore(agent) {
var response = null; var response = null;
switch (cmd) { switch (cmd) {
case 'help': { // Displays available commands case 'help': { // Displays available commands
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.'; 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.';
break;
}
case 'toast': {
require('toaster').Toast('MeshCentral', args['_'][0]);
response = 'ok';
break; break;
} }
case 'setdebug': { case 'setdebug': {

View File

@ -5,15 +5,16 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
You may obtain a copy of the License at You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0 http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// JavaScript source code
var GM = require('_GenericMarshal'); var GM = require('_GenericMarshal');
function processManager() { function processManager() {
@ -34,12 +35,16 @@ function processManager() {
} }
this.getProcesses = function getProcesses(callback) { this.getProcesses = function getProcesses(callback) {
switch (process.platform) { switch (process.platform) {
default:
throw ('Enumerating processes on ' + process.platform + ' not supported');
case 'win32': case 'win32':
var h = this._kernel32.CreateToolhelp32Snapshot(2, 0), info = GM.CreateVariable(304), retVal = {}; var retVal = [];
var h = this._kernel32.CreateToolhelp32Snapshot(2, 0);
var info = GM.CreateVariable(304);
info.toBuffer().writeUInt32LE(304, 0); info.toBuffer().writeUInt32LE(304, 0);
var nextProcess = this._kernel32.Process32First(h, info); var nextProcess = this._kernel32.Process32First(h, info);
while (nextProcess.Val) { while (nextProcess.Val) {
retVal[info.Deref(8, 4).toBuffer().readUInt32LE(0)] = { cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String }; retVal.push({ pid: info.Deref(8, 4).toBuffer().readUInt32LE(0), cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String });
nextProcess = this._kernel32.Process32Next(h, info); nextProcess = this._kernel32.Process32Next(h, info);
} }
if (callback) { callback.apply(this, [retVal]); } if (callback) { callback.apply(this, [retVal]); }
@ -55,15 +60,16 @@ function processManager() {
for (var i = 1; i < arguments.length; ++i) { p.args.push(arguments[i]); } for (var i = 1; i < arguments.length; ++i) { p.args.push(arguments[i]); }
p.on('exit', function onGetProcesses() { p.on('exit', function onGetProcesses() {
delete this.Parent._psp[this.pid]; delete this.Parent._psp[this.pid];
var retVal = {}, lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0; var retVal = [], lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0;
for (var i in lines) { for (var i in lines) {
var tokens = lines[i].split(' '), tokenList = []; var tokens = lines[i].split(' ');
var tokenList = [];
for (var x in tokens) { for (var x in tokens) {
if (i == 0 && tokens[x]) { key[tokens[x]] = keyi++; } if (i == 0 && tokens[x]) { key[tokens[x]] = keyi++; }
if (i > 0 && tokens[x]) { tokenList.push(tokens[x]); } if (i > 0 && tokens[x]) { tokenList.push(tokens[x]); }
} }
if ((i > 0) && (tokenList[key.PID])) { if ((i > 0) && (tokenList[key.PID])) {
retVal[tokenList[key.PID]] = { user: tokenList[key.USER], cmd: tokenList[key.COMMAND] }; retVal.push({ pid: tokenList[key.PID], user: tokenList[key.USER], cmd: tokenList[key.COMMAND] });
} }
} }
if (this.callback) { if (this.callback) {
@ -73,22 +79,20 @@ function processManager() {
}); });
p.stdout.on('data', function (chunk) { this.parent.ps += chunk.toString(); }); p.stdout.on('data', function (chunk) { this.parent.ps += chunk.toString(); });
break; break;
default:
throw ('Enumerating processes on ' + process.platform + ' not supported');
} }
}; };
this.getProcessInfo = function getProcessInfo(pid) { this.getProcessInfo = function getProcessInfo(pid) {
switch (process.platform) { switch (process.platform) {
default:
throw ('getProcessInfo() not supported for ' + process.platform);
case 'linux': case 'linux':
var status = require('fs').readFileSync('/proc/' + pid + '/status'), lines = status.toString().split('\n'), info = {}; var status = require('fs').readFileSync('/proc/' + pid + '/status'), info = {}, lines = status.toString().split('\n');
for (var i in lines) { for (var i in lines) {
var tokens = lines[i].split(':'); var tokens = lines[i].split(':');
if (tokens.length > 1) { tokens[1] = tokens[1].trim(); } if (tokens.length > 1) { tokens[1] = tokens[1].trim(); }
info[tokens[0]] = tokens[1]; info[tokens[0]] = tokens[1];
} }
return info; return (info);
default:
throw ('getProcessInfo() not supported for ' + process.platform);
} }
}; };
} }

View File

@ -0,0 +1,72 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var toasters = {};
function Toaster()
{
this._ObjectID = 'Toaster';
this.Toast = function Toast(title, caption)
{
if (process.platform != 'win32') return;
var retVal = {};
var emitter = require('events').inherits(retVal);
emitter.createEvent('Clicked');
emitter.createEvent('Dismissed');
var session = require('user-sessions').Current();
for (var i in session)
{
console.log(session[i]);
}
try
{
console.log('Attempting Toast Mechanism 1');
retVal._child = require('ScriptContainer').Create({ processIsolation: true, sessionId: session.connected[0].SessionId });
}
catch (e) {
console.log(e);
console.log('Attempting Toast Mechanism 2');
retVal._child = require('ScriptContainer').Create({ processIsolation: true });
}
retVal._child.parent = retVal;
retVal._child.on('exit', function (code) { this.parent.emit('Dismissed'); delete this.parent._child; });
retVal._child.addModule('win-console', getJSModule('win-console'));
retVal._child.addModule('win-messagepump', getJSModule('win-messagepump'));
var str = "\
try{\
var toast = require('win-console');\
var balloon = toast.SetTrayIcon({ szInfo: '" + caption + "', szInfoTitle: '" + title + "', balloonOnly: true });\
balloon.on('ToastDismissed', function(){process.exit();});\
}\
catch(e)\
{\
require('ScriptContainer').send(e);\
}\
require('ScriptContainer').send('done');\
";
retVal._child.ExecuteString(str);
toasters[retVal._hashCode()] = retVal;
retVal.on('Dismissed', function () { delete toasters[this._hashCode()]; });
console.log('Returning');
return (retVal);
};
}
module.exports = new Toaster();

View File

@ -1,4 +1,18 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
function UserSessions() function UserSessions()
{ {
@ -87,6 +101,8 @@ function UserSessions()
} }
this._wts.WTSFreeMemory(pinfo.Deref()); this._wts.WTSFreeMemory(pinfo.Deref());
Object.defineProperty(retVal, 'connected', { value: showActiveOnly(retVal) });
return (retVal); return (retVal);
}; };
} }
@ -94,9 +110,58 @@ function UserSessions()
{ {
this.Current = function Current() this.Current = function Current()
{ {
return ({}); var retVal = {};
var emitterUtils = require('events').inherits(retVal);
emitterUtils.createEvent('logon');
retVal._child = require('child_process').execFile('/usr/bin/last', ['last', '-f', '/var/run/utmp']);
retVal._child.Parent = retVal;
retVal._child._txt = '';
retVal._child.on('exit', function (code)
{
var lines = this._txt.split('\n');
var sessions = [];
for(var i in lines)
{
if (lines[i])
{
console.log(getTokens(lines[i]));
var user = lines[i].substring(0, lines[i].indexOf(' '));
sessions.push(user);
}
}
sessions.pop();
console.log(sessions);
});
retVal._child.stdout.Parent = retVal._child;
retVal._child.stdout.on('data', function (chunk) { this.Parent._txt += chunk.toString(); });
return (retVal);
} }
} }
} }
function showActiveOnly(source)
{
var retVal = [];
for (var i in source)
{
if (source[i].State == 'Active' || source[i].State == 'Connected')
{
retVal.push(source[i]);
}
}
return (retVal);
}
function getTokens(str)
{
var columns = [];
var i;
columns.push(str.substring(0, (i=str.indexOf(' '))));
while (str[++i] == ' ');
columns.push(str.substring(i, str.substring(i).indexOf(' ') + i));
return (columns);
}
module.exports = new UserSessions(); module.exports = new UserSessions();

View File

@ -1,3 +1,19 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
function _Scan() function _Scan()
{ {

View File

@ -1,7 +1,22 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var MemoryStream = require('MemoryStream'); var MemoryStream = require('MemoryStream');
var WindowsChildScript = 'var parent = require("ScriptContainer");var Wireless = require("wifi-scanner-windows");Wireless.on("Scan", function (ap) { parent.send(ap); });Wireless.Scan();'; var WindowsChildScript = 'var parent = require("ScriptContainer");var Wireless = require("wifi-scanner-windows");Wireless.on("Scan", function (ap) { parent.send(ap); });Wireless.Scan();';
function AccessPoint(_ssid, _bssid, _lq) function AccessPoint(_ssid, _bssid, _lq)
{ {
this.ssid = _ssid; this.ssid = _ssid;

View File

@ -0,0 +1,164 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var TrayIconFlags =
{
NIF_MESSAGE: 0x00000001,
NIF_ICON: 0x00000002,
NIF_TIP: 0x00000004,
NIF_STATE: 0x00000008,
NIF_INFO: 0x00000010,
NIF_GUID: 0x00000020,
NIF_REALTIME: 0x00000040,
NIF_SHOWTIP: 0x00000080,
NIM_ADD: 0x00000000,
NIM_MODIFY: 0x00000001,
NIM_DELETE: 0x00000002,
NIM_SETFOCUS: 0x00000003,
NIM_SETVERSION: 0x00000004
};
var NOTIFYICON_VERSION_4 = 4;
var MessageTypes = { WM_APP: 0x8000, WM_USER: 0x0400 };
function WindowsConsole()
{
if (process.platform == 'win32')
{
this._ObjectID = 'WindowsConsole';
this._Marshal = require('_GenericMarshal');
this._kernel32 = this._Marshal.CreateNativeProxy("kernel32.dll");
this._user32 = this._Marshal.CreateNativeProxy("user32.dll");
this._kernel32.CreateMethod("GetConsoleWindow");
this._kernel32.CreateMethod('GetCurrentThread');
this._user32.CreateMethod("ShowWindow");
this._user32.CreateMethod("LoadImageA");
this._user32.CreateMethod({ method: 'GetMessageA', threadDispatch: 1 });
this._shell32 = this._Marshal.CreateNativeProxy('Shell32.dll');
this._shell32.CreateMethod('Shell_NotifyIconA');
this._handle = this._kernel32.GetConsoleWindow();
this.minimize = function () {
this._user32.ShowWindow(this._handle, 6);
};
this.restore = function () {
this._user32.ShowWindow(this._handle, 9);
};
this.hide = function () {
this._user32.ShowWindow(this._handle, 0);
};
this.show = function () {
this._user32.ShowWindow(this._handle, 5);
};
this._loadicon = function (imagePath) {
var h = this._user32.LoadImageA(0, this._Marshal.CreateVariable(imagePath), 1, 0, 0, 0x00000010 | 0x00008000 | 0x00000040); // LR_LOADFROMFILE | LR_SHARED | LR_DEFAULTSIZE
return (h);
};
this.SetTrayIcon = function SetTrayIcon(options)
{
var data = this._Marshal.CreateVariable(this._Marshal.PointerSize == 4 ? 508 : 528);
//console.log('struct size = ' + data._size);
//console.log('TryIcon, WM_MESSAGE filter = ' + options.filter);
data.toBuffer().writeUInt32LE(data._size, 0);
var trayType = TrayIconFlags.NIF_TIP | TrayIconFlags.NIF_MESSAGE
options.filter = MessageTypes.WM_APP + 1;
data.Deref(this._Marshal.PointerSize == 4 ? 16 : 24, 4).toBuffer().writeUInt32LE(options.filter);
if (!options.noBalloon) { trayType |= TrayIconFlags.NIF_INFO; }
if (options.icon)
{
trayType |= TrayIconFlags.NIF_ICON;
var hIcon = data.Deref(this._Marshal.PointerSize == 4 ? 20 : 32, this._Marshal.PointerSize);
options.icon.pointerBuffer().copy(hIcon.toBuffer());
}
data.Deref(this._Marshal.PointerSize * 2, 4).toBuffer().writeUInt32LE(1);
data.Deref(this._Marshal.PointerSize == 4 ? 12 : 20, 4).toBuffer().writeUInt32LE(trayType);
data.Deref(this._Marshal.PointerSize == 4 ? 416 : 432, 4).toBuffer().writeUInt32LE(NOTIFYICON_VERSION_4);
var szTip = data.Deref(this._Marshal.PointerSize == 4 ? 24 : 40, 128);
var szInfo = data.Deref(this._Marshal.PointerSize == 4 ? 160 : 176, 256);
var szInfoTitle = data.Deref(this._Marshal.PointerSize == 4 ? 420 : 436, 64);
if (options.szTip) { Buffer.from(options.szTip).copy(szTip.toBuffer()); }
if (options.szInfo) { Buffer.from(options.szInfo).copy(szInfo.toBuffer()); }
if (options.szInfoTitle) { Buffer.from(options.szInfoTitle).copy(szInfoTitle.toBuffer()); }
var MessagePump = require('win-messagepump');
retVal = { _ObjectID: 'WindowsConsole.TrayIcon', MessagePump: new MessagePump(options) };
var retValEvents = require('events').inherits(retVal);
retValEvents.createEvent('ToastClicked');
retValEvents.createEvent('IconHover');
retValEvents.createEvent('ToastDismissed');
retVal.Options = options;
retVal.MessagePump.TrayIcon = retVal;
retVal.MessagePump.NotifyData = data;
retVal.MessagePump.WindowsConsole = this;
retVal.MessagePump.on('exit', function onExit(code) { console.log('Pump Exited'); if (this.TrayIcon) { this.TrayIcon.remove(); } });
retVal.MessagePump.on('hwnd', function onHwnd(h)
{
//console.log('Got HWND');
options.hwnd = h;
h.pointerBuffer().copy(this.NotifyData.Deref(this.WindowsConsole._Marshal.PointerSize, this.WindowsConsole._Marshal.PointerSize).toBuffer());
if(this.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_ADD, this.NotifyData).Val == 0)
{
// Something went wrong
}
});
retVal.MessagePump.on('message', function onWindowsMessage(msg)
{
if(msg.message == this.TrayIcon.Options.filter)
{
var handled = false;
if (msg.wparam == 1 && msg.lparam == 1029)
{
this.TrayIcon.emit('ToastClicked');
handled = true;
}
if (msg.wparam == 1 && msg.lparam == 512)
{
this.TrayIcon.emit('IconHover');
handled = true;
}
if (this.TrayIcon.Options.balloonOnly && msg.wparam == 1 && (msg.lparam == 1028 || msg.lparam == 1029))
{
this.TrayIcon.emit('ToastDismissed');
this.TrayIcon.remove();
handled = true;
}
if (!handled) { console.log(msg); }
}
});
retVal.remove = function remove()
{
this.MessagePump.WindowsConsole._shell32.Shell_NotifyIconA(TrayIconFlags.NIM_DELETE, this.MessagePump.NotifyData);
this.MessagePump.stop();
delete this.MessagePump.TrayIcon;
delete this.MessagePump;
};
return (retVal);
};
}
}
module.exports = new WindowsConsole();

View File

@ -0,0 +1,122 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var WH_CALLWNDPROC = 4;
var WM_QUIT = 0x0012;
function WindowsMessagePump(options)
{
this._ObjectID = 'WindowsMessagePump';
this._options = options;
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('hwnd');
emitterUtils.createEvent('error');
emitterUtils.createEvent('message');
emitterUtils.createEvent('exit');
this._child = require('ScriptContainer').Create({ processIsolation: 0 });
this._child.MessagePump = this;
this._child.prependListener('~', function _childFinalizer() { this.MessagePump.emit('exit', 0); console.log('calling stop'); this.MessagePump.stop(); });
this._child.once('exit', function onExit(code) { this.MessagePump.emit('exit', code); });
this._child.once('ready', function onReady()
{
console.log('child ready');
var execString =
"var m = require('_GenericMarshal');\
var h = null;\
var k = m.CreateNativeProxy('Kernel32.dll');\
k.CreateMethod('GetLastError');\
k.CreateMethod('GetModuleHandleA');\
var u = m.CreateNativeProxy('User32.dll');\
u.CreateMethod('GetMessageA');\
u.CreateMethod('CreateWindowExA');\
u.CreateMethod('TranslateMessage');\
u.CreateMethod('DispatchMessageA');\
u.CreateMethod('RegisterClassExA');\
u.CreateMethod('DefWindowProcA');\
var wndclass = m.CreateVariable(m.PointerSize == 4 ? 48 : 80);\
wndclass.hinstance = k.GetModuleHandleA(0);\
wndclass.cname = m.CreateVariable('MainWWWClass');\
wndclass.wndproc = m.GetGenericGlobalCallback(4);\
wndclass.toBuffer().writeUInt32LE(wndclass._size);\
wndclass.cname.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 40 : 64, m.PointerSize).toBuffer());\
wndclass.wndproc.pointerBuffer().copy(wndclass.Deref(8, m.PointerSize).toBuffer());\
wndclass.hinstance.pointerBuffer().copy(wndclass.Deref(m.PointerSize == 4 ? 20 : 24, m.PointerSize).toBuffer());\
wndclass.wndproc.on('GlobalCallback', function onWndProc(xhwnd, xmsg, wparam, lparam)\
{\
if(h==null || h.Val == xhwnd.Val)\
{\
require('ScriptContainer').send({message: xmsg.Val, wparam: wparam.Val, lparam: lparam.Val});\
var retVal = u.DefWindowProcA(xhwnd, xmsg, wparam, lparam);\
return(retVal);\
}\
});\
u.RegisterClassExA(wndclass);\
h = u.CreateWindowExA(0x00000088, wndclass.cname, 0, 0x00800000, 0, 0, 100, 100, 0, 0, 0, 0);\
if(h.Val == 0)\
{\
require('ScriptContainer').send({error: 'Error Creating Hidden Window'});\
process.exit();\
}\
require('ScriptContainer').send({hwnd: h.pointerBuffer().toString('hex')});\
require('ScriptContainer').on('data', function onData(jmsg)\
{\
if(jmsg.listen)\
{\
var msg = m.CreateVariable(m.PointerSize == 4 ? 28 : 48);\
while(u.GetMessageA(msg, h, 0, 0).Val>0)\
{\
u.TranslateMessage(msg);\
u.DispatchMessageA(msg);\
}\
process.exit();\
}\
});";
this.ExecuteString(execString);
});
this._child.on('data', function onChildData(msg)
{
if (msg.hwnd)
{
var m = require('_GenericMarshal');
this._hwnd = m.CreatePointer(Buffer.from(msg.hwnd, 'hex'));
this.MessagePump.emit('hwnd', this._hwnd);
this.send({ listen: this.MessagePump._options.filter });
}
else if(msg.message)
{
this.MessagePump.emit('message', msg);
}
else
{
console.log('Received: ', msg);
}
});
this.stop = function stop()
{
if(this._child && this._child._hwnd)
{
console.log('posting WM_QUIT');
var marshal = require('_GenericMarshal');
var User32 = marshal.CreateNativeProxy('User32.dll');
User32.CreateMethod('PostMessageA');
User32.PostMessageA(this._child._hwnd, WM_QUIT, 0, 0);
}
};
}
module.exports = WindowsMessagePump;

View File

@ -0,0 +1,167 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var KEY_QUERY_VALUE = 0x0001;
var KEY_WRITE = 0x20006;
var KEY_DATA_TYPES =
{
REG_NONE: 0,
REG_SZ: 1,
REG_EXPAND_SZ: 2,
REG_BINARY: 3,
REG_DWORD: 4,
REG_DWORD_BIG_ENDIAN: 5,
REG_LINK: 6,
REG_MULTI_SZ: 7,
REG_RESOURCE_LIST: 8,
REG_FULL_RESOURCE_DESCRIPTOR: 9,
REG_RESOURCE_REQUIREMENTS_LIST: 10,
REG_QWORD: 11
};
function windows_registry()
{
this._ObjectId = 'windows_registry';
this._marshal = require('_GenericMarshal');
this._AdvApi = this._marshal.CreateNativeProxy('Advapi32.dll');
this._AdvApi.CreateMethod('RegCreateKeyExA');
this._AdvApi.CreateMethod('RegOpenKeyExA');
this._AdvApi.CreateMethod('RegQueryValueExA');
this._AdvApi.CreateMethod('RegCloseKey');
this._AdvApi.CreateMethod('RegDeleteKeyA');
this._AdvApi.CreateMethod('RegDeleteValueA');
this._AdvApi.CreateMethod('RegSetValueExA');
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() };
this.QueryKey = function QueryKey(hkey, path, key)
{
var h = this._marshal.CreatePointer();
var len = this._marshal.CreateVariable(4);
var valType = this._marshal.CreateVariable(4);
key = this._marshal.CreateVariable(key);
var HK = this._marshal.CreatePointer(hkey);
var retVal = null;
if (this._AdvApi.RegOpenKeyExA(HK, this._marshal.CreateVariable(path), 0, KEY_QUERY_VALUE, h).Val != 0)
{
throw ('Error Opening Registry Key: ' + path);
}
if(this._AdvApi.RegQueryValueExA(h.Deref(), key, 0, 0, 0, len).Val == 0)
{
var data = this._marshal.CreateVariable(len.toBuffer().readUInt32LE());
if (this._AdvApi.RegQueryValueExA(h.Deref(), key, 0, valType, data, len).Val == 0)
{
switch(valType.toBuffer().readUInt32LE())
{
case KEY_DATA_TYPES.REG_DWORD:
retVal = data.toBuffer().readUInt32LE();
break;
case KEY_DATA_TYPES.REG_DWORD_BIG_ENDIAN:
retVal = data.toBuffer().readUInt32BE();
break;
case KEY_DATA_TYPES.REG_SZ:
retVal = data.String;
break;
case KEY_DATA_TYPES.REG_BINARY:
default:
retVal = data.toBuffer();
retVal._data = data;
break;
}
}
}
else
{
this._AdvApi.RegCloseKey(h.Deref());
throw ('Not Found');
}
this._AdvApi.RegCloseKey(h.Deref());
return (retVal);
};
this.WriteKey = function WriteKey(hkey, path, key, value)
{
var result;
var h = this._marshal.CreatePointer();
if (this._AdvApi.RegCreateKeyExA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path), 0, 0, 0, KEY_WRITE, 0, h, 0).Val != 0)
{
throw ('Error Opening Registry Key: ' + path);
}
var data;
var dataType;
switch(typeof(value))
{
case 'boolean':
dataType = KEY_DATA_TYPES.REG_DWORD;
data = this._marshal.CreateVariable(4);
data.toBuffer().writeUInt32LE(value ? 1 : 0);
break;
case 'number':
dataType = KEY_DATA_TYPES.REG_DWORD;
data = this._marshal.CreateVariable(4);
data.toBuffer().writeUInt32LE(value);
break;
case 'string':
dataType = KEY_DATA_TYPES.REG_SZ;
data = this._marshal.CreateVariable(value);
break;
default:
dataType = KEY_DATA_TYPES.REG_BINARY;
data = this._marshal.CreateVariable(value.length);
value.copy(data.toBuffer());
break;
}
if(this._AdvApi.RegSetValueExA(h.Deref(), this._marshal.CreateVariable(key), 0, dataType, data, data._size).Val != 0)
{
this._AdvApi.RegCloseKey(h.Deref());
throw ('Error writing reg key: ' + key);
}
this._AdvApi.RegCloseKey(h.Deref());
};
this.DeleteKey = function DeleteKey(hkey, path, key)
{
if(!key)
{
if(this._AdvApi.RegDeleteKeyA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path)).Val != 0)
{
throw ('Error Deleting Key: ' + path);
}
}
else
{
var h = this._marshal.CreatePointer();
var result;
if (this._AdvApi.RegOpenKeyExA(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path), 0, KEY_QUERY_VALUE | KEY_WRITE, h).Val != 0)
{
throw ('Error Opening Registry Key: ' + path);
}
if ((result = this._AdvApi.RegDeleteValueA(h.Deref(), this._marshal.CreateVariable(key)).Val) != 0)
{
this._AdvApi.RegCloseKey(h.Deref());
throw ('Error[' + result + '] Deleting Key: ' + path + '.' + key);
}
this._AdvApi.RegCloseKey(h.Deref());
}
};
}
module.exports = new windows_registry();

View File

@ -75,7 +75,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
func(certs); func(certs);
// Check if the Let's Encrypt certificate needs to be renewed. // Check if the Let's Encrypt certificate needs to be renewed.
setTimeout(obj.checkRenewCertificate, 300000); // Check in 5 minutes. setTimeout(obj.checkRenewCertificate, 60000); // Check in 1 minute.
setInterval(obj.checkRenewCertificate, 86400000); // Check again in 24 hours and every 24 hours. setInterval(obj.checkRenewCertificate, 86400000); // Check again in 24 hours and every 24 hours.
return; return;
} else { } else {
@ -111,7 +111,7 @@ module.exports.CreateLetsEncrypt = function (parent) {
if (obj.leResults == null) { return; } if (obj.leResults == null) { return; }
// TODO: Only renew on one of the peers if multi-peers are active. // TODO: Only renew on one of the peers if multi-peers are active.
// Check if we need to renew the certificate // Check if we need to renew the certificate
obj.le.renew({ duplicate: false }, obj.leResults).then(function (xresults) { obj.le.renew({ duplicate: false, domains: obj.leDomains, email: obj.parent.config.letsencrypt.email }, obj.leResults).then(function (xresults) {
obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers obj.parent.performServerCertUpdate(); // Reset the server, TODO: Reset all peers
}, function (err) { }); // If we can't renew, ignore. }, function (err) { }); // If we can't renew, ignore.
} }

View File

@ -873,6 +873,40 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
} }
break; break;
} }
case 'toast':
{
if (obj.common.validateArray(command.nodeids, 1) == false) break; // Check nodeid's
if (obj.common.validateString(command.title, 1, 512) == false) break; // Check title
if (obj.common.validateString(command.msg, 1, 4096) == false) break; // Check message
for (var i in command.nodeids) {
var nodeid = command.nodeids[i], powerActions = 0;
if (obj.common.validateString(nodeid, 1, 1024) == false) break; // Check nodeid
if ((nodeid.split('/').length == 3) && (nodeid.split('/')[1] == domain.id)) { // Validate the domain, operation only valid for current domain
// Get the device
obj.db.Get(nodeid, function (err, nodes) {
if (nodes.length != 1) return;
var node = nodes[0];
// Get the mesh for this device
var mesh = obj.parent.meshes[node.meshid];
if (mesh) {
// Check if this user has rights to do this
if (mesh.links[user._id] != null && ((mesh.links[user._id].rights & 8) != 0)) { // "Remote Control permission"
// Get this device
var agent = obj.parent.wsagents[node._id];
if (agent != null) {
// Send the power command
agent.send(JSON.stringify({ action: 'toast', title: command.title, msg: command.msg }));
}
}
}
});
}
}
break;
}
case 'getnetworkinfo': case 'getnetworkinfo':
{ {
// Argument validation // Argument validation

View File

@ -1,6 +1,6 @@
{ {
"name": "meshcentral", "name": "meshcentral",
"version": "0.1.6-q", "version": "0.1.6-s",
"keywords": [ "keywords": [
"Remote Management", "Remote Management",
"Intel AMT", "Intel AMT",

BIN
public/images/mesh-200.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -81,9 +81,6 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
} }
} }
// Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
obj.xxCloseWebRTC = function () { obj.Stop(); }
obj.xxOnMessage = function (e) { obj.xxOnMessage = function (e) {
//if (obj.debugmode == 1) { console.log('Recv', e.data); } //if (obj.debugmode == 1) { console.log('Recv', e.data); }
//console.log('Recv', e.data, obj.State); //console.log('Recv', e.data, obj.State);
@ -111,7 +108,8 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
} }
obj.webrtc.oniceconnectionstatechange = function () { obj.webrtc.oniceconnectionstatechange = function () {
if (obj.webrtc != null) { if (obj.webrtc != null) {
if ((obj.webrtc.iceConnectionState == 'disconnected') || (obj.webrtc.iceConnectionState == 'failed')) { obj.xxCloseWebRTC(); } if (obj.webrtc.iceConnectionState == 'disconnected') { obj.Stop(); }
else if (obj.webrtc.iceConnectionState == 'failed') { obj.xxCloseWebRTC(); }
} }
} }
obj.webrtc.createOffer(function (offer) { obj.webrtc.createOffer(function (offer) {
@ -206,13 +204,18 @@ var CreateAgentRedirect = function (meshserver, module, serverPublicNamePort) {
if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State); if (obj.onStateChanged != null) obj.onStateChanged(obj, obj.State);
} }
obj.Stop = function (x) { // Close the WebRTC connection, should be called if a problem occurs during WebRTC setup.
if (obj.debugmode == 1) { console.log('stop', x); } obj.xxCloseWebRTC = function () {
// Close WebRTC
if (obj.webchannel != null) { try { obj.webchannel.close(); } catch (e) { } obj.webchannel = null; } if (obj.webchannel != null) { try { obj.webchannel.close(); } catch (e) { } obj.webchannel = null; }
if (obj.webrtc != null) { try { obj.webrtc.close(); } catch (e) { } obj.webrtc = null; } if (obj.webrtc != null) { try { obj.webrtc.close(); } catch (e) { } obj.webrtc = null; }
obj.webRtcActive = false; obj.webRtcActive = false;
}
obj.Stop = function (x) {
if (obj.debugmode == 1) { console.log('stop', x); }
// Clean up WebRTC
obj.xxCloseWebRTC();
//obj.debug("Agent Redir Socket Stopped"); //obj.debug("Agent Redir Socket Stopped");
obj.connectstate = -1; obj.connectstate = -1;

View File

@ -572,6 +572,7 @@
<div id=p16events style="max-height:600px;overflow-y:scroll"></div> <div id=p16events style="max-height:600px;overflow-y:scroll"></div>
</div> </div>
<div id=p20 style=display:none> <div id=p20 style=display:none>
<img id=MainMeshImage src="images/mesh-200.png" style=border-width:0px;height:200px;width:200px;float:right>
<h1><span id=p20meshName></span> - General</h1> <h1><span id=p20meshName></span> - General</h1>
<p id=p20info></p> <p id=p20info></p>
</div> </div>
@ -2848,6 +2849,7 @@
// Show action button, only show if we have permissions 4, 8, 64 // Show action button, only show if we have permissions 4, 8, 64
if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; } if ((meshrights & 76) != 0) { x += '<input type=button value=Actions title="Perform power actions on the device" onclick=deviceActionFunction() />'; }
x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />'; x += '<input type=button value=Notes title="View notes about this device" onclick=showNotes(' + ((meshrights & 128) == 0) + ',"' + encodeURIComponent(node._id) + '") />';
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() />'; }
QH('p10html', x); QH('p10html', x);
// Show node last 7 days timeline // Show node last 7 days timeline
@ -2939,7 +2941,17 @@
function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); } function showNotesEx(buttons, tag) { meshserver.send({ action: 'setNotes', id: decodeURIComponent(tag), notes: encodeURIComponent(Q('d2devNotes').value) }); }
function deviceToastFunction() {
if (xxdialogMode) return;
setDialogMode(2, "Device Toast", 3, deviceToastFunctionEx, '<textarea id=d2devToast style=width:100%;height:80px;resize:none;overflow-y:scroll></textarea>');
}
function deviceToastFunctionEx() {
meshserver.send({ action: 'toast', nodeids: [ currentNode._id ], title: 'MeshCentral', msg: Q('d2devToast').value });
}
function deviceActionFunction() { function deviceActionFunction() {
if (xxdialogMode) return;
var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights; var meshrights = meshes[currentNode.meshid].links['user/{{{domain}}}/' + userinfo.name.toLowerCase()].rights;
var x = "Select an operation to perform on this device.<br /><br />"; var x = "Select an operation to perform on this device.<br /><br />";
var y = '<select id=d2deviceop style=float:right;width:250px>'; var y = '<select id=d2deviceop style=float:right;width:250px>';
@ -4542,7 +4554,7 @@
x += '<br><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />'; x += '<br><input type=button value=Notes title="View notes about this mesh" onclick=showNotes(false,"' + encodeURIComponent(currentMesh._id) + '") />';
x += '<br><br>'; x += '<br style=clear:both><br>';
var currentMeshLinks = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()]; var currentMeshLinks = currentMesh.links['user/{{{domain}}}/' + userinfo.name.toLowerCase()];
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>'; } 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>'; }