Server bug fixing and new MeshAgent

This commit is contained in:
Ylian Saint-Hilaire 2018-08-31 15:23:42 -07:00
parent 562310bed1
commit b274b782fa
21 changed files with 719 additions and 92 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.

View File

@ -15,41 +15,41 @@ limitations under the License.
*/
function borderController()
{
this.container = null;
this.Start = function Start(user)
{
if (this.container == null) {
if (process.platform == 'win32') {
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
}
else {
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
}
this.container.addModule('monitor-info', getJSModule('monitor-info'));
this.container.addModule('monitor-border', getJSModule('monitor-border'));
this.container.addModule('promise', getJSModule('promise'));
this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
}
}
this.Stop = function Stop()
{
if (this.container != null)
{
this._container = this.container;
this._container.parent = this;
this.container = null;
this._container.once('exit', function () { this.parent._container = null; });
this._container.exit();
}
}
}
function createMeshCore(agent) {
var obj = {};
function borderController() {
this.container = null;
this.Start = function Start(user) {
if (this.container == null) {
if (process.platform == 'win32') {
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.SessionId });
}
else {
this.container = require('ScriptContainer').Create({ processIsolation: 1, sessionId: user.uid });
}
this.container.parent = this;
this.container.addModule('monitor-info', getJSModule('monitor-info'));
this.container.addModule('monitor-border', getJSModule('monitor-border'));
this.container.addModule('promise', getJSModule('promise'));
this.container.once('exit', function (code) { sendConsoleText('Border Process Exited with code: ' + code); this.parent.container = this.parent._container = null; });
this.container.ExecuteString("var border = require('monitor-border'); border.Start();");
}
}
this.Stop = function Stop() {
if (this.container != null) {
this._container = this.container;
this._container.parent = this;
this.container = null;
this._container.exit();
}
}
}
require('events').EventEmitter.call(obj, true).createEvent('loggedInUsers_Updated');
obj.on('loggedInUsers_Updated', function ()
{
@ -1457,6 +1457,8 @@ function createMeshCore(agent) {
});
require('user-sessions').emit('changed');
require('user-sessions').on('locked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has LOCKED the desktop'); });
require('user-sessions').on('unlocked', function (user) { sendConsoleText('[' + (user.Domain ? user.Domain + '\\' : '') + user.Username + '] has UNLOCKED the desktop'); });
//console.log('Stopping.');
//process.exit();
}

View File

@ -109,17 +109,22 @@ function serviceManager()
}
return admin;
};
this.getProgramFolder = function getProgramFolder() {
if (require('os').arch() == 'x64') { // 64 bit Windows
if (this.GM.PointerSize == 4) { return process.env['ProgramFiles(x86)']; } // 32 Bit App
return process.env['ProgramFiles']; // 64 bit App
}
return process.env['ProgramFiles']; // 32 bit Windows
};
this.getServiceFolder = function getServiceFolder()
this.getProgramFolder = function getProgramFolder()
{
return this.getProgramFolder() + '\\mesh';
if (require('os').arch() == 'x64')
{
// 64 bit Windows
if (this.GM.PointerSize == 4)
{
return process.env['ProgramFiles(x86)']; // 32 Bit App
}
return process.env['ProgramFiles']; // 64 bit App
}
// 32 bit Windows
return process.env['ProgramFiles'];
};
this.getServiceFolder = function getServiceFolder() { return this.getProgramFolder() + '\\mesh'; };
this.enumerateService = function () {
var machineName = this.GM.CreatePointer();

View File

@ -14,10 +14,28 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var NOTIFY_FOR_THIS_SESSION = 0;
var NOTIFY_FOR_ALL_SESSIONS = 1;
var WM_WTSSESSION_CHANGE = 0x02B1;
var WTS_CONSOLE_CONNECT = (0x1);
var WTS_CONSOLE_DISCONNECT = (0x2);
var WTS_REMOTE_CONNECT = (0x3);
var WTS_REMOTE_DISCONNECT = (0x4);
var WTS_SESSION_LOGON = (0x5);
var WTS_SESSION_LOGOFF = (0x6);
var WTS_SESSION_LOCK = (0x7);
var WTS_SESSION_UNLOCK = (0x8);
var WTS_SESSION_REMOTE_CONTROL = (0x9);
var WTS_SESSION_CREATE = (0xA);
var WTS_SESSION_TERMINATE = (0xB);
function UserSessions()
{
this._ObjectID = 'user-sessions';
require('events').EventEmitter.call(this, true).createEvent('changed');
require('events').EventEmitter.call(this, true)
.createEvent('changed')
.createEvent('locked')
.createEvent('unlocked');
this.enumerateUsers = function enumerateUsers()
{
@ -37,12 +55,15 @@ function UserSessions()
if (process.platform == 'win32')
{
this._serviceHooked = false;
this._marshal = require('_GenericMarshal');
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
this._kernel32.CreateMethod('GetLastError');
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
this._wts.CreateMethod('WTSEnumerateSessionsA');
this._wts.CreateMethod('WTSQuerySessionInformationA');
this._wts.CreateMethod('WTSRegisterSessionNotification');
this._wts.CreateMethod('WTSUnRegisterSessionNotification');
this._wts.CreateMethod('WTSFreeMemory');
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
this.InfoClass =
@ -124,9 +145,52 @@ function UserSessions()
if (cb) { cb(retVal); }
return (retVal);
};
this._immediate = setImmediate(function (self)
{
if (self._serviceHooked) { return; } // If we were hooked by a service, we won't need to do anything further
// We need to spin up a message pump, and fetch a window handle
var message_pump = require('win-message-pump');
self._messagepump = new message_pump({ filter: WM_WTSSESSION_CHANGE });
self._messagepump.on('exit', function (code) { self._wts.WTSUnRegisterSessionNotification(self.hwnd); });
self._messagepump.on('hwnd', function (h)
{
self.hwnd = h;
// Now that we have a window handle, we can register it to receive Windows Messages
self._wts.WTSRegisterSessionNotification(self.hwnd, NOTIFY_FOR_ALL_SESSIONS);
});
self._messagepump.on('message', function (msg)
{
if (msg.message == WM_WTSSESSION_CHANGE)
{
switch(msg.wparam)
{
case WTS_SESSION_LOCK:
self.enumerateUsers().then(function (users)
{
if (users[msg.lparam]) { self.emit('locked', users[msg.lparam]); }
});
break;
case WTS_SESSION_UNLOCK:
self.enumerateUsers().then(function (users)
{
if (users[msg.lparam]) { self.emit('unlocked', users[msg.lparam]); }
});
break;
}
}
});
}, this);
}
else
{
this._linuxWatcher = require('fs').watch('/var/run/utmp');
this._linuxWatcher.user_session = this;
this._linuxWatcher.on('change', function (a, b)
{
this.user_session.emit('changed');
});
this.Self = function Self()
{
var promise = require('promise');
@ -277,13 +341,26 @@ function UserSessions()
function showActiveOnly(source)
{
var retVal = [];
var unique = {};
var usernames = [];
var tmp;
for (var i in source)
{
if (source[i].State == 'Active')
{
retVal.push(source[i]);
tmp = (source[i].Domain ? (source[i].Domain + '\\') : '') + source[i].Username;
if (!unique[tmp]) { unique[tmp] = tmp;}
}
}
for (var i in unique)
{
usernames.push(i);
}
Object.defineProperty(retVal, 'usernames', { value: usernames });
return (retVal);
}
function getTokens(str)

View File

@ -0,0 +1,401 @@
/*
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 parseServiceStatus(token)
{
var j = {};
var serviceType = token.Deref(0, 4).IntVal;
j.isFileSystemDriver = ((serviceType & 0x00000002) == 0x00000002);
j.isKernelDriver = ((serviceType & 0x00000001) == 0x00000001);
j.isSharedProcess = ((serviceType & 0x00000020) == 0x00000020);
j.isOwnProcess = ((serviceType & 0x00000010) == 0x00000010);
j.isInteractive = ((serviceType & 0x00000100) == 0x00000100);
switch (token.Deref((1 * 4), 4).toBuffer().readUInt32LE())
{
case 0x00000005:
j.state = 'CONTINUE_PENDING';
break;
case 0x00000006:
j.state = 'PAUSE_PENDING';
break;
case 0x00000007:
j.state = 'PAUSED';
break;
case 0x00000004:
j.state = 'RUNNING';
break;
case 0x00000002:
j.state = 'START_PENDING';
break;
case 0x00000003:
j.state = 'STOP_PENDING';
break;
case 0x00000001:
j.state = 'STOPPED';
break;
}
var controlsAccepted = token.Deref((2 * 4), 4).toBuffer().readUInt32LE();
j.controlsAccepted = [];
if ((controlsAccepted & 0x00000010) == 0x00000010)
{
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDADD');
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDREMOVE');
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDENABLE');
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDDISABLE');
}
if ((controlsAccepted & 0x00000008) == 0x00000008) { j.controlsAccepted.push('SERVICE_CONTROL_PARAMCHANGE'); }
if ((controlsAccepted & 0x00000002) == 0x00000002) { j.controlsAccepted.push('SERVICE_CONTROL_PAUSE'); j.controlsAccepted.push('SERVICE_CONTROL_CONTINUE'); }
if ((controlsAccepted & 0x00000100) == 0x00000100) { j.controlsAccepted.push('SERVICE_CONTROL_PRESHUTDOWN'); }
if ((controlsAccepted & 0x00000004) == 0x00000004) { j.controlsAccepted.push('SERVICE_CONTROL_SHUTDOWN'); }
if ((controlsAccepted & 0x00000001) == 0x00000001) { j.controlsAccepted.push('SERVICE_CONTROL_STOP'); }
if ((controlsAccepted & 0x00000020) == 0x00000020) { j.controlsAccepted.push('SERVICE_CONTROL_HARDWAREPROFILECHANGE'); }
if ((controlsAccepted & 0x00000040) == 0x00000040) { j.controlsAccepted.push('SERVICE_CONTROL_POWEREVENT'); }
if ((controlsAccepted & 0x00000080) == 0x00000080) { j.controlsAccepted.push('SERVICE_CONTROL_SESSIONCHANGE'); }
j.pid = token.Deref((7 * 4), 4).toBuffer().readUInt32LE();
return (j);
}
function serviceManager()
{
this._ObjectID = 'service-manager';
if (process.platform == 'win32')
{
this.GM = require('_GenericMarshal');
this.proxy = this.GM.CreateNativeProxy('Advapi32.dll');
this.proxy.CreateMethod('OpenSCManagerA');
this.proxy.CreateMethod('EnumServicesStatusExA');
this.proxy.CreateMethod('OpenServiceA');
this.proxy.CreateMethod('QueryServiceStatusEx');
this.proxy.CreateMethod('ControlService');
this.proxy.CreateMethod('StartServiceA');
this.proxy.CreateMethod('CloseServiceHandle');
this.proxy.CreateMethod('CreateServiceA');
this.proxy.CreateMethod('ChangeServiceConfig2A');
this.proxy.CreateMethod('DeleteService');
this.proxy.CreateMethod('AllocateAndInitializeSid');
this.proxy.CreateMethod('CheckTokenMembership');
this.proxy.CreateMethod('FreeSid');
this.proxy2 = this.GM.CreateNativeProxy('Kernel32.dll');
this.proxy2.CreateMethod('GetLastError');
this.isAdmin = function isAdmin() {
var NTAuthority = this.GM.CreateVariable(6);
NTAuthority.toBuffer().writeInt8(5, 5);
var AdministratorsGroup = this.GM.CreatePointer();
var admin = false;
if (this.proxy.AllocateAndInitializeSid(NTAuthority, 2, 32, 544, 0, 0, 0, 0, 0, 0, AdministratorsGroup).Val != 0)
{
var member = this.GM.CreateInteger();
if (this.proxy.CheckTokenMembership(0, AdministratorsGroup.Deref(), member).Val != 0)
{
if (member.toBuffer().readUInt32LE() != 0) { admin = true; }
}
this.proxy.FreeSid(AdministratorsGroup.Deref());
}
return admin;
};
this.getProgramFolder = function getProgramFolder()
{
if (require('os').arch() == 'x64')
{
// 64 bit Windows
if (this.GM.PointerSize == 4)
{
return process.env['ProgramFiles(x86)']; // 32 Bit App
}
return process.env['ProgramFiles']; // 64 bit App
}
// 32 bit Windows
return process.env['ProgramFiles'];
};
this.getServiceFolder = function getServiceFolder() { return this.getProgramFolder() + '\\mesh'; };
this.enumerateService = function () {
var machineName = this.GM.CreatePointer();
var dbName = this.GM.CreatePointer();
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004);
var bytesNeeded = this.GM.CreatePointer();
var servicesReturned = this.GM.CreatePointer();
var resumeHandle = this.GM.CreatePointer();
//var services = this.proxy.CreateVariable(262144);
var success = this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, 0x00, 0x00, bytesNeeded, servicesReturned, resumeHandle, 0x00);
if (bytesNeeded.IntVal <= 0) {
throw ('error enumerating services');
}
var sz = bytesNeeded.IntVal;
var services = this.GM.CreateVariable(sz);
this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, services, sz, bytesNeeded, servicesReturned, resumeHandle, 0x00);
console.log("servicesReturned", servicesReturned.IntVal);
var ptrSize = dbName._size;
var blockSize = 36 + (2 * ptrSize);
blockSize += ((ptrSize - (blockSize % ptrSize)) % ptrSize);
var retVal = [];
for (var i = 0; i < servicesReturned.IntVal; ++i) {
var token = services.Deref(i * blockSize, blockSize);
var j = {};
j.name = token.Deref(0, ptrSize).Deref().String;
j.displayName = token.Deref(ptrSize, ptrSize).Deref().String;
j.status = parseServiceStatus(token.Deref(2 * ptrSize, 36));
retVal.push(j);
}
this.proxy.CloseServiceHandle(handle);
return (retVal);
}
this.getService = function (name) {
var serviceName = this.GM.CreateVariable(name);
var ptr = this.GM.CreatePointer();
var bytesNeeded = this.GM.CreateVariable(ptr._size);
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004 | 0x0020 | 0x0010);
if (handle.Val == 0) { throw ('could not open ServiceManager'); }
var h = this.proxy.OpenServiceA(handle, serviceName, 0x0004 | 0x0020 | 0x0010 | 0x00010000);
if (h.Val != 0) {
var success = this.proxy.QueryServiceStatusEx(h, 0, 0, 0, bytesNeeded);
var status = this.GM.CreateVariable(bytesNeeded.toBuffer().readUInt32LE());
success = this.proxy.QueryServiceStatusEx(h, 0, status, status._size, bytesNeeded);
if (success != 0) {
retVal = {};
retVal.status = parseServiceStatus(status);
retVal._scm = handle;
retVal._service = h;
retVal._GM = this.GM;
retVal._proxy = this.proxy;
require('events').inherits(retVal);
retVal.on('~', function () { this._proxy.CloseServiceHandle(this); this._proxy.CloseServiceHandle(this._scm); });
retVal.name = name;
retVal.stop = function () {
if (this.status.state == 'RUNNING') {
var newstate = this._GM.CreateVariable(36);
var success = this._proxy.ControlService(this._service, 0x00000001, newstate);
if (success == 0) {
throw (this.name + '.stop() failed');
}
}
else {
throw ('cannot call ' + this.name + '.stop(), when current state is: ' + this.status.state);
}
}
retVal.start = function () {
if (this.status.state == 'STOPPED') {
var success = this._proxy.StartServiceA(this._service, 0, 0);
if (success == 0) {
throw (this.name + '.start() failed');
}
}
else {
throw ('cannot call ' + this.name + '.start(), when current state is: ' + this.status.state);
}
}
return (retVal);
}
else {
}
}
this.proxy.CloseServiceHandle(handle);
throw ('could not find service: ' + name);
}
}
this.installService = function installService(options)
{
if (process.platform == 'win32')
{
if (!this.isAdmin()) { throw ('Installing as Service, requires admin'); }
// Before we start, we need to copy the binary to the right place
var folder = this.getServiceFolder();
if (!require('fs').existsSync(folder)) { require('fs').mkdirSync(folder); }
require('fs').copyFileSync(options.servicePath, folder + '\\' + options.name + '.exe');
options.servicePath = folder + '\\' + options.name + '.exe';
var servicePath = this.GM.CreateVariable('"' + options.servicePath + '"');
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0002);
if (handle.Val == 0) { throw ('error opening SCManager'); }
var serviceName = this.GM.CreateVariable(options.name);
var displayName = this.GM.CreateVariable(options.name);
var allAccess = 0x000F01FF;
var serviceType;
switch (options.startType) {
case 'BOOT_START':
serviceType = 0x00;
break;
case 'SYSTEM_START':
serviceType = 0x01;
break;
case 'AUTO_START':
serviceType = 0x02;
break;
case 'DEMAND_START':
serviceType = 0x03;
break;
default:
serviceType = 0x04; // Disabled
break;
}
var h = this.proxy.CreateServiceA(handle, serviceName, displayName, allAccess, 0x10 | 0x100, serviceType, 0, servicePath, 0, 0, 0, 0, 0);
if (h.Val == 0) { this.proxy.CloseServiceHandle(handle); throw ('Error Creating Service: ' + this.proxy2.GetLastError().Val); }
if (options.description) {
console.log(options.description);
var dscPtr = this.GM.CreatePointer();
dscPtr.Val = this.GM.CreateVariable(options.description);
if (this.proxy.ChangeServiceConfig2A(h, 1, dscPtr) == 0) {
this.proxy.CloseServiceHandle(h);
this.proxy.CloseServiceHandle(handle);
throw ('Unable to set description');
}
}
this.proxy.CloseServiceHandle(h);
this.proxy.CloseServiceHandle(handle);
return (this.getService(options.name));
}
if(process.platform == 'linux')
{
switch (this.getServiceType())
{
case 'init':
require('fs').copyFileSync(options.servicePath, '/etc/init.d/' + options.name);
console.log('copying ' + options.servicePath);
var m = require('fs').statSync('/etc/init.d/' + options.name).mode;
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
require('fs').chmodSync('/etc/init.d/' + options.name, m);
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
this._update._moduleName = options.name;
this._update.on('exit', function onUpdateRC_d() { console.log(this._moduleName + ' installed'); process.exit(); });
this._update.stdout.on('data', function (chunk) { });
this._update.stdin.write('update-rc.d ' + options.name + ' defaults\n');
this._update.stdin.write('exit\n');
//update-rc.d meshagent defaults # creates symlinks for rc.d
//service meshagent start
break;
case 'systemd':
var serviceDescription = options.description ? options.description : 'MeshCentral Agent';
if (!require('fs').existsSync('/usr/local/mesh')) { require('fs').mkdirSync('/usr/local/mesh'); }
require('fs').copyFileSync(options.servicePath, '/usr/local/mesh/' + options.name);
var m = require('fs').statSync('/usr/local/mesh/' + options.name).mode;
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
require('fs').chmodSync('/usr/local/mesh/' + options.name, m);
require('fs').writeFileSync('/lib/systemd/system/' + options.name + '.service', '[Unit]\nDescription=' + serviceDescription + '\n[Service]\nExecStart=/usr/local/mesh/' + options.name + '\nStandardOutput=null\nRestart=always\nRestartSec=3\n[Install]\nWantedBy=multi-user.target\nAlias=' + options.name + '.service\n', { flags: 'w' });
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
this._update._moduleName = options.name;
this._update.on('exit', function onUpdateRC_d() { console.log(this._moduleName + ' installed'); process.exit(); });
this._update.stdout.on('data', function (chunk) { });
this._update.stdin.write('systemctl enable ' + options.name + '.service\n');
this._update.stdin.write('exit\n');
break;
default: // unknown platform service type
break;
}
}
}
this.uninstallService = function uninstallService(name)
{
if (typeof (name) == 'object') { name = name.name; }
if (process.platform == 'win32')
{
if (!this.isAdmin()) { throw ('Uninstalling a service, requires admin'); }
var service = this.getService(name);
if (service.status.state == undefined || service.status.state == 'STOPPED')
{
if (this.proxy.DeleteService(service._service) == 0)
{
throw ('Uninstall Service for: ' + name + ', failed with error: ' + this.proxy2.GetLastError());
}
else
{
try
{
require('fs').unlinkSync(this.getServiceFolder() + '\\' + name + '.exe');
}
catch(e)
{
}
}
}
else
{
throw ('Cannot uninstall service: ' + name + ', because it is: ' + service.status.state);
}
}
else if(process.platform == 'linux')
{
switch (this.getServiceType())
{
case 'init':
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
this._update._svcname = name;
this._update.on('exit', function onUninstallExit() {
try {
require('fs').unlinkSync('/etc/init.d/' + this._svcname);
console.log(this._svcname + ' uninstalled');
}
catch (e) {
console.log(this._svcname + ' could not be uninstalled')
}
process.exit();
});
this._update.stdout.on('data', function (chunk) { });
this._update.stdin.write('service ' + name + ' stop\n');
this._update.stdin.write('update-rc.d -f ' + name + ' remove\n');
this._update.stdin.write('exit\n');
break;
case 'systemd':
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
this._update._svcname = name;
this._update.on('exit', function onUninstallExit() {
try {
require('fs').unlinkSync('/usr/local/mesh/' + this._svcname);
require('fs').unlinkSync('/lib/systemd/system/' + this._svcname + '.service');
console.log(this._svcname + ' uninstalled');
}
catch (e) {
console.log(this._svcname + ' could not be uninstalled')
}
process.exit();
});
this._update.stdout.on('data', function (chunk) { });
this._update.stdin.write('systemctl stop ' + name + '.service\n');
this._update.stdin.write('systemctl disable ' + name + '.service\n');
this._update.stdin.write('exit\n');
break;
default: // unknown platform service type
break;
}
}
}
if(process.platform == 'linux')
{
this.getServiceType = function getServiceType()
{
return (require('process-manager').getProcessInfo(1).Name);
};
}
}
module.exports = serviceManager;

View File

@ -18,38 +18,40 @@ var toasters = {};
function Toaster()
{
this._ObjectID = '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.title = title;
retVal.caption = caption;
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'));
if (process.platform == 'win32')
{
emitter.createEvent('Clicked');
var str = "\
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.Active[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-message-pump', getJSModule('win-message-pump'));
var str = "\
try{\
var toast = require('win-console');\
var balloon = toast.SetTrayIcon({ szInfo: '" + caption + "', szInfoTitle: '" + title + "', balloonOnly: true });\
@ -61,11 +63,62 @@ function Toaster()
}\
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);
retVal._child.ExecuteString(str);
toasters[retVal._hashCode()] = retVal;
retVal.on('Dismissed', function () { delete toasters[this._hashCode()]; });
console.log('Returning');
return (retVal);
}
else
{
if(!require('fs').existsSync('/usr/bin/notify-send'))
{
throw ('Toast not supported on this platform');
}
Object.defineProperty(retVal, '_sessions', {
value: require('user-sessions').Current(function onCurrentSession(sessions)
{
this._cchild = require('child_process').execFile('/usr/bin/whoami', ['whoami'], { type: require('child_process').SpawnTypes.TERM });
this._cchild.stdout.on('data', function (chunk)
{
if (chunk.toString().split('\r\n')[0] == 'root')
{
if (sessions[':0'].State != 'Connected' && sessions[':0'].State != 'Active')
{
// No logged in user owns the display
this.parent.parent.Parent.emit('Dismissed');
return;
}
// We root, so we need to direct to DISPLAY=:0
this.parent.parent._notify = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
this.parent.parent._notify.stdin.write('su - ' + sessions[':0'].Username + ' -c "DISPLAY=:0 notify-send \'' + this.parent.parent.Parent.title + '\' \'' + this.parent.parent.Parent.caption + '\'"\n');
this.parent.parent._notify.stdin.write('exit\n');
this.parent.parent._notify.stdout.on('data', function (chunk) { });
}
else
{
// We ain't root, so that means we can just call send-notify directly
this.parent.parent._notify = require('child_process').execFile('/usr/bin/notify-send', ['notify-send', this.parent.parent.Parent.title, this.parent.parent.Parent.caption], { type: require('child_process').SpawnTypes.TERM });
this.parent.parent._notify.stdout.on('data', function (chunk) { });
}
// NOTIFY-SEND has a bug where timeouts don't work, so the default is 10 seconds
this.parent.parent.Parent._timeout = setTimeout(function onFakeDismissed(obj)
{
obj.emit('Dismissed');
}, 10000, this.parent.parent.Parent);
});
this._cchild.parent = this;
})
});
retVal._sessions.Parent = retVal;
toasters[retVal._hashCode()] = retVal;
retVal.on('Dismissed', function () { delete toasters[this._hashCode()]; });
return (retVal);
}
};
}

View File

@ -14,10 +14,28 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
var NOTIFY_FOR_THIS_SESSION = 0;
var NOTIFY_FOR_ALL_SESSIONS = 1;
var WM_WTSSESSION_CHANGE = 0x02B1;
var WTS_CONSOLE_CONNECT = (0x1);
var WTS_CONSOLE_DISCONNECT = (0x2);
var WTS_REMOTE_CONNECT = (0x3);
var WTS_REMOTE_DISCONNECT = (0x4);
var WTS_SESSION_LOGON = (0x5);
var WTS_SESSION_LOGOFF = (0x6);
var WTS_SESSION_LOCK = (0x7);
var WTS_SESSION_UNLOCK = (0x8);
var WTS_SESSION_REMOTE_CONTROL = (0x9);
var WTS_SESSION_CREATE = (0xA);
var WTS_SESSION_TERMINATE = (0xB);
function UserSessions()
{
this._ObjectID = 'user-sessions';
require('events').EventEmitter.call(this, true).createEvent('changed');
require('events').EventEmitter.call(this, true)
.createEvent('changed')
.createEvent('locked')
.createEvent('unlocked');
this.enumerateUsers = function enumerateUsers()
{
@ -37,12 +55,15 @@ function UserSessions()
if (process.platform == 'win32')
{
this._serviceHooked = false;
this._marshal = require('_GenericMarshal');
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
this._kernel32.CreateMethod('GetLastError');
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
this._wts.CreateMethod('WTSEnumerateSessionsA');
this._wts.CreateMethod('WTSQuerySessionInformationA');
this._wts.CreateMethod('WTSRegisterSessionNotification');
this._wts.CreateMethod('WTSUnRegisterSessionNotification');
this._wts.CreateMethod('WTSFreeMemory');
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
this.InfoClass =
@ -124,9 +145,52 @@ function UserSessions()
if (cb) { cb(retVal); }
return (retVal);
};
this._immediate = setImmediate(function (self)
{
if (self._serviceHooked) { return; } // If we were hooked by a service, we won't need to do anything further
// We need to spin up a message pump, and fetch a window handle
var message_pump = require('win-message-pump');
self._messagepump = new message_pump({ filter: WM_WTSSESSION_CHANGE });
self._messagepump.on('exit', function (code) { self._wts.WTSUnRegisterSessionNotification(self.hwnd); });
self._messagepump.on('hwnd', function (h)
{
self.hwnd = h;
// Now that we have a window handle, we can register it to receive Windows Messages
self._wts.WTSRegisterSessionNotification(self.hwnd, NOTIFY_FOR_ALL_SESSIONS);
});
self._messagepump.on('message', function (msg)
{
if (msg.message == WM_WTSSESSION_CHANGE)
{
switch(msg.wparam)
{
case WTS_SESSION_LOCK:
self.enumerateUsers().then(function (users)
{
if (users[msg.lparam]) { self.emit('locked', users[msg.lparam]); }
});
break;
case WTS_SESSION_UNLOCK:
self.enumerateUsers().then(function (users)
{
if (users[msg.lparam]) { self.emit('unlocked', users[msg.lparam]); }
});
break;
}
}
});
}, this);
}
else
{
this._linuxWatcher = require('fs').watch('/var/run/utmp');
this._linuxWatcher.user_session = this;
this._linuxWatcher.on('change', function (a, b)
{
this.user_session.emit('changed');
});
this.Self = function Self()
{
var promise = require('promise');
@ -277,13 +341,26 @@ function UserSessions()
function showActiveOnly(source)
{
var retVal = [];
var unique = {};
var usernames = [];
var tmp;
for (var i in source)
{
if (source[i].State == 'Active')
{
retVal.push(source[i]);
tmp = (source[i].Domain ? (source[i].Domain + '\\') : '') + source[i].Username;
if (!unique[tmp]) { unique[tmp] = tmp;}
}
}
for (var i in unique)
{
usernames.push(i);
}
Object.defineProperty(retVal, 'usernames', { value: usernames });
return (retVal);
}
function getTokens(str)

View File

@ -37,7 +37,7 @@ function WindowsConsole()
{
if (process.platform == 'win32')
{
this._ObjectID = 'WindowsConsole';
this._ObjectID = 'win-console';
this._Marshal = require('_GenericMarshal');
this._kernel32 = this._Marshal.CreateNativeProxy("kernel32.dll");
this._user32 = this._Marshal.CreateNativeProxy("user32.dll");
@ -102,7 +102,7 @@ function WindowsConsole()
if (options.szInfoTitle) { Buffer.from(options.szInfoTitle).copy(szInfoTitle.toBuffer()); }
var MessagePump = require('win-messagepump');
var MessagePump = require('win-message-pump');
retVal = { _ObjectID: 'WindowsConsole.TrayIcon', MessagePump: new MessagePump(options) };
var retValEvents = require('events').inherits(retVal);
retValEvents.createEvent('ToastClicked');

View File

@ -19,7 +19,7 @@ var WM_QUIT = 0x0012;
function WindowsMessagePump(options)
{
this._ObjectID = 'WindowsMessagePump';
this._ObjectID = 'win-message-pump';
this._options = options;
var emitterUtils = require('events').inherits(this);
emitterUtils.createEvent('hwnd');
@ -29,11 +29,10 @@ function WindowsMessagePump(options)
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.prependListener('~', function _childFinalizer() { this.MessagePump.emit('exit', 0); 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;\
@ -110,7 +109,6 @@ function WindowsMessagePump(options)
{
if(this._child && this._child._hwnd)
{
console.log('posting WM_QUIT');
var marshal = require('_GenericMarshal');
var User32 = marshal.CreateNativeProxy('User32.dll');
User32.CreateMethod('PostMessageA');

View File

@ -35,7 +35,7 @@ var KEY_DATA_TYPES =
function windows_registry()
{
this._ObjectId = 'windows_registry';
this._ObjectId = 'win-registry';
this._marshal = require('_GenericMarshal');
this._AdvApi = this._marshal.CreateNativeProxy('Advapi32.dll');
this._AdvApi.CreateMethod('RegCreateKeyExA');

View File

@ -1204,6 +1204,10 @@ process.on('SIGINT', function () { if (meshserver != null) { meshserver.Stop();
// Load the really basic modules
var meshserver = null;
function mainStart(args) {
// Check the NodeJS is version 6 or better.
if (Number(process.version.match(/^v(\d+\.\d+)/)[1]) < 6) { console.log("MeshCentral requires Node v6.x or above, current version is " + process.version + "."); return; }
// Check for any missing modules.
InstallModules(['minimist'], function () {
// Get the server configuration
var config = getConfig();

View File

@ -493,7 +493,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if (chguser) {
if (obj.common.validateString(command.email, 1, 256) && (chguser.email != command.email)) { chguser.email = command.email; change = 1; }
if ((command.emailVerified === true || command.emailVerified === false) && (chguser.emailVerified != command.emailVerified)) { chguser.emailVerified = command.emailVerified; change = 1; }
if (obj.common.validateInt(command.quota, 0) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
if ((obj.common.validateInt(command.quota, 0) || command.quota == null) && (command.quota != chguser.quota)) { chguser.quota = command.quota; if (chguser.quota == null) { delete chguser.quota; } change = 1; }
if ((user.siteadmin == 0xFFFFFFFF) && obj.common.validateInt(command.siteadmin) && (chguser.siteadmin != command.siteadmin)) { chguser.siteadmin = command.siteadmin; change = 1; }
if (change == 1) {
obj.db.SetUser(chguser);

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.1.9-v",
"version": "0.1.9-w",
"keywords": [
"Remote Management",
"Intel AMT",

File diff suppressed because one or more lines are too long

View File

@ -4981,11 +4981,19 @@
p5setActions();
}
function getNiceSize(bytes) {
if (bytes <= 0) return 'Storage limit exceed';
if (bytes < 2048) return bytes + ' bytes remaining';
if (bytes < 2097152) return Math.round(bytes / 1024) + ' kilobytes remaining';
if (bytes < 2147483648) return Math.round(bytes / 1024 / 1024) + ' megabytes remaining';
return Math.round(bytes / 1024 / 1024 / 1024) + ' gigabytes remaining';
}
function p5getQuotabar(f) {
while (f.t > 1 && f.t != 4) { f = f.parent; }
if ((f.t != 1 && f.t != 4) || (f.maxbytes == null)) return '';
var tf = Math.floor(f.s / 1024), tq = Math.floor((f.maxbytes - f.s) / 1024);
return '<span title="' + tf + "k in " + f.c + " file" + (f.c > 1?'s':'') + ". " + (Math.floor(f.maxbytes / 1024)) + 'k maxinum">' + ((tq < 0)?('Storage limit exceed'):(tq + 'k remaining')) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' /></span>';
var tf = Math.floor(f.s / 1024), tq = (f.maxbytes - f.s);
return '<span title="' + tf + "k in " + f.c + " file" + (f.c > 1 ? 's' : '') + ". " + (Math.floor(f.maxbytes / 1024 / 1024)) + 'k maxinum">' + getNiceSize(tq) + ' <progress style=height:10px;width:100px value=' + f.s + ' max=' + f.maxbytes + ' /></span>';
}
function p5showPublicLink(u) { setDialogMode(2, "Public Link", 1, null, '<input type=text style=width:100% value="' + u + '" readonly />'); }
@ -5057,13 +5065,14 @@
names.push(file.name);
sizes.push(file.size);
types.push(file.type);
reader.onload = function(event) {
reader.onload = function (event) {
console.log(event.target.result.length);
datas.push(event.target.result);
if (--readercount == 0) {
Q('p5fileDragName').value = names.join('*');
Q('p5fileDragSize').value = sizes.join('*');
Q('p5fileDragType').value = types.join('*');
Q('p5fileDragData').value = datas.join('*');
Q('p5fileDragData').value = datas.join('*'); // TODO: This will not work for large files!!! *****************
Q('p5fileDragLink').value = encodeURIComponent(filetreelinkpath);
Q('p5loginSubmit2').click();
}

View File

@ -935,15 +935,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (objid.startsWith('user/')) {
var user = obj.users[objid];
if (user == null) return 0;
if (user.siteadmin == 0xFFFFFFFF) return null; // Administrators have no user limit
if ((user.quota != null) && (typeof user.quota == 'number')) { return user.quota; }
if ((domain != null) && (domain.userQuota != null) && (typeof domain.userQuota == 'number')) { return domain.userQuota; }
return 1048576; // By default, the server will have a 1 meg limit on user accounts
if ((domain != null) && (domain.userquota != null) && (typeof domain.userquota == 'number')) { return domain.userquota; }
return null; // By default, the user will have no limit
} else if (objid.startsWith('mesh/')) {
var mesh = obj.meshes[objid];
if (mesh == null) return 0;
if ((mesh.quota != null) && (typeof mesh.quota == 'number')) { return mesh.quota; }
if ((domain != null) && (domain.meshQuota != null) && (typeof domain.meshQuota == 'number')) { return domain.meshQuota; }
return 1048576; // By default, the server will have a 1 meg limit on mesh accounts
if ((domain != null) && (domain.meshquota != null) && (typeof domain.meshquota == 'number')) { return domain.meshquota; }
return null; // By default, the mesh will have no limit
}
return 0;
};
@ -997,12 +998,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var multiparty = require('multiparty');
var form = new multiparty.Form();
form.parse(req, function (err, fields, files) {
if ((fields == null) || (fields.link == null) || (fields.link.length != 1)) { res.sendStatus(404); return; }
if ((fields == null) || (fields.link == null) || (fields.link.length != 1)) { /*console.log('UploadFile, Invalid Fields:', fields, files);*/ res.sendStatus(404); return; }
var xfile = obj.getServerFilePath(user, domain, decodeURIComponent(fields.link[0]));
if (xfile == null) { res.sendStatus(404); return; }
// Get total bytes in the path
var totalsize = readTotalFileSize(xfile.fullpath);
if (totalsize < xfile.quota) { // Check if the quota is not already broken
if ((xfile.quota == null) || (totalsize < xfile.quota)) { // Check if the quota is not already broken
if (fields.name != null) {
// Upload method where all the file data is within the fields.
var names = fields.name[0].split('*'), sizes = fields.size[0].split('*'), types = fields.type[0].split('*'), datas = fields.data[0].split('*');
@ -1010,7 +1011,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
for (var i = 0; i < names.length; i++) {
if (obj.common.IsFilenameValid(names[i]) == false) { res.sendStatus(404); return; }
var filedata = new Buffer(datas[i].split(',')[1], 'base64');
if ((totalsize + filedata.length) < xfile.quota) { // Check if quota would not be broken if we add this file
if ((xfile.quota == null) || ((totalsize + filedata.length) < xfile.quota)) { // Check if quota would not be broken if we add this file
// Create the user folder if needed
(function (fullpath, filename, filedata) {
obj.fs.mkdir(xfile.fullpath, function () {
@ -1027,7 +1028,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
// More typical upload method, the file data is in a multipart mime post.
for (var i in files.files) {
var file = files.files[i], fpath = obj.path.join(xfile.fullpath, file.originalFilename);
if (obj.common.IsFilenameValid(file.originalFilename) && ((totalsize + file.size) < xfile.quota)) { // Check if quota would not be broken if we add this file
if (obj.common.IsFilenameValid(file.originalFilename) && ((xfile.quota == null) || ((totalsize + file.size) < xfile.quota))) { // Check if quota would not be broken if we add this file
obj.fs.rename(file.path, fpath, function () {
obj.parent.DispatchEvent([user._id], obj, 'updatefiles'); // Fire an event causing this user to update this files
});