mirror of
				https://github.com/Ylianst/MeshCentral.git
				synced 2025-03-09 15:40:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			750 lines
		
	
	
		
			No EOL
		
	
	
		
			30 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			750 lines
		
	
	
		
			No EOL
		
	
	
		
			30 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /*
 | |
| 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 NOTIFY_FOR_THIS_SESSION = 0;
 | |
| var NOTIFY_FOR_ALL_SESSIONS = 1;
 | |
| var WM_WTSSESSION_CHANGE = 0x02B1;
 | |
| var WM_POWERBROADCAST = 0x218;
 | |
| var PBT_POWERSETTINGCHANGE = 0x8013;
 | |
| var PBT_APMSUSPEND = 0x4;
 | |
| var PBT_APMRESUMESUSPEND = 0x7;
 | |
| var PBT_APMRESUMEAUTOMATIC = 0x12;
 | |
| var PBT_APMPOWERSTATUSCHANGE = 0xA;
 | |
| 
 | |
| 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);
 | |
| 
 | |
| var GUID_ACDC_POWER_SOURCE;
 | |
| var GUID_BATTERY_PERCENTAGE_REMAINING;
 | |
| var GUID_CONSOLE_DISPLAY_STATE;
 | |
| 
 | |
| function UserSessions()
 | |
| {
 | |
|     this._ObjectID = 'user-sessions';
 | |
|     require('events').EventEmitter.call(this, true)
 | |
|         .createEvent('changed')
 | |
|         .createEvent('locked')
 | |
|         .createEvent('unlocked');
 | |
| 
 | |
|     this.enumerateUsers = function enumerateUsers()
 | |
|     {
 | |
|         var promise = require('promise');
 | |
|         var p = new promise(function (res, rej)
 | |
|         {
 | |
|             this.__resolver = res;
 | |
|             this.__rejector = rej;
 | |
|         });
 | |
|         p.__handler = function __handler(users)
 | |
|         {
 | |
|             p.__resolver(users);
 | |
|         };
 | |
|         try
 | |
|         {
 | |
|             this.Current(p.__handler);
 | |
|         }
 | |
|         catch(e)
 | |
|         {
 | |
|             p.__rejector(e);
 | |
|         }
 | |
|         p.parent = this;
 | |
|         return (p);
 | |
|     }
 | |
| 
 | |
|     if (process.platform == 'win32')
 | |
|     {
 | |
|         this._serviceHooked = false;
 | |
|         this._marshal = require('_GenericMarshal');
 | |
|         this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
 | |
|         this._kernel32.CreateMethod('GetLastError');
 | |
|         
 | |
|         try
 | |
|         {
 | |
|             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');
 | |
|         }
 | |
|         catch(exc)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         this._advapi = this._marshal.CreateNativeProxy('Advapi32.dll');
 | |
|         this._advapi.CreateMethod('AllocateAndInitializeSid');
 | |
|         this._advapi.CreateMethod('CheckTokenMembership');
 | |
|         this._advapi.CreateMethod('FreeSid');
 | |
| 
 | |
|         this._user32 = this._marshal.CreateNativeProxy('user32.dll');
 | |
|         this._user32.CreateMethod({ method: 'RegisterPowerSettingNotification', threadDispatch: 1});
 | |
|         this._user32.CreateMethod('UnregisterPowerSettingNotification');
 | |
|         this._rpcrt = this._marshal.CreateNativeProxy('Rpcrt4.dll');
 | |
|         this._rpcrt.CreateMethod('UuidFromStringA');
 | |
|         this._rpcrt.StringToUUID = function StringToUUID(guid)
 | |
|         {
 | |
|             var retVal = StringToUUID.us._marshal.CreateVariable(16);
 | |
|             if(StringToUUID.us._rpcrt.UuidFromStringA(StringToUUID.us._marshal.CreateVariable(guid), retVal).Val == 0)
 | |
|             {
 | |
|                 return (retVal);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 throw ('Could not convert string to UUID');
 | |
|             }
 | |
|         }
 | |
|         this._rpcrt.StringToUUID.us = this;
 | |
| 
 | |
|         GUID_ACDC_POWER_SOURCE = this._rpcrt.StringToUUID('5d3e9a59-e9D5-4b00-a6bd-ff34ff516548');
 | |
|         GUID_BATTERY_PERCENTAGE_REMAINING = this._rpcrt.StringToUUID('a7ad8041-b45a-4cae-87a3-eecbb468a9e1');
 | |
|         GUID_CONSOLE_DISPLAY_STATE = this._rpcrt.StringToUUID('6fe69556-704a-47a0-8f24-c28d936fda47');
 | |
| 
 | |
|         this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
 | |
|         this.InfoClass =
 | |
|             {
 | |
|                 'WTSInitialProgram': 0,
 | |
|                 'WTSApplicationName': 1,
 | |
|                 'WTSWorkingDirectory': 2,
 | |
|                 'WTSOEMId': 3,
 | |
|                 'WTSSessionId': 4,
 | |
|                 'WTSUserName': 5,
 | |
|                 'WTSWinStationName': 6,
 | |
|                 'WTSDomainName': 7,
 | |
|                 'WTSConnectState': 8,
 | |
|                 'WTSClientBuildNumber': 9,
 | |
|                 'WTSClientName': 10,
 | |
|                 'WTSClientDirectory': 11,
 | |
|                 'WTSClientProductId': 12,
 | |
|                 'WTSClientHardwareId': 13,
 | |
|                 'WTSClientAddress': 14,
 | |
|                 'WTSClientDisplay': 15,
 | |
|                 'WTSClientProtocolType': 16,
 | |
|                 'WTSIdleTime': 17,
 | |
|                 'WTSLogonTime': 18,
 | |
|                 'WTSIncomingBytes': 19,
 | |
|                 'WTSOutgoingBytes': 20,
 | |
|                 'WTSIncomingFrames': 21,
 | |
|                 'WTSOutgoingFrames': 22,
 | |
|                 'WTSClientInfo': 23,
 | |
|                 'WTSSessionInfo': 24,
 | |
|                 'WTSSessionInfoEx': 25,
 | |
|                 'WTSConfigInfo': 26,
 | |
|                 'WTSValidationInfo': 27,
 | |
|                 'WTSSessionAddressV4': 28,
 | |
|                 'WTSIsRemoteSession': 29
 | |
|             };
 | |
| 
 | |
|         this.isRoot = function isRoot()
 | |
|         {
 | |
|             var NTAuthority = this._marshal.CreateVariable(6);
 | |
|             NTAuthority.toBuffer().writeInt8(5, 5);
 | |
| 
 | |
|             var AdministratorsGroup = this._marshal.CreatePointer();
 | |
|             var admin = false;
 | |
| 
 | |
|             if (this._advapi.AllocateAndInitializeSid(NTAuthority, 2, 32, 544, 0, 0, 0, 0, 0, 0, AdministratorsGroup).Val != 0)
 | |
|             {
 | |
|                 var member = this._marshal.CreateInteger();
 | |
|                 if (this._advapi.CheckTokenMembership(0, AdministratorsGroup.Deref(), member).Val != 0)
 | |
|                 {
 | |
|                     if (member.toBuffer().readUInt32LE() != 0) { admin = true; }
 | |
|                 }
 | |
|                 this._advapi.FreeSid(AdministratorsGroup.Deref());
 | |
|             }
 | |
|             return admin;
 | |
|         }
 | |
| 
 | |
|         this.getSessionAttribute = function getSessionAttribute(sessionId, attr)
 | |
|         {
 | |
|             var buffer = this._marshal.CreatePointer();
 | |
|             var bytesReturned = this._marshal.CreateVariable(4);
 | |
| 
 | |
|             if (this._wts.WTSQuerySessionInformationA(0, sessionId, attr, buffer, bytesReturned).Val == 0)
 | |
|             {
 | |
|                 throw ('Error calling WTSQuerySessionInformation: ' + this._kernel32.GetLastError.Val);
 | |
|             }
 | |
| 
 | |
|             var retVal = buffer.Deref().String;
 | |
| 
 | |
|             this._wts.WTSFreeMemory(buffer.Deref());
 | |
|             return (retVal);
 | |
|         };
 | |
| 
 | |
|         this.Current = function Current(cb)
 | |
|         {
 | |
|             var retVal = {};
 | |
|             var pinfo = this._marshal.CreatePointer();
 | |
|             var count = this._marshal.CreateVariable(4);
 | |
|             if (this._wts.WTSEnumerateSessionsA(0, 0, 1, pinfo, count).Val == 0)
 | |
|             {
 | |
|                 throw ('Error calling WTSEnumerateSessionsA: ' + this._kernel32.GetLastError().Val);
 | |
|             }
 | |
| 
 | |
|             for (var i = 0; i < count.toBuffer().readUInt32LE() ; ++i)
 | |
|             {
 | |
|                 var info = pinfo.Deref().Deref(i * (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize == 4 ? 12 : 24);
 | |
|                 var j = { SessionId: info.toBuffer().readUInt32LE() };
 | |
|                 j.StationName = info.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize).Deref().String;
 | |
|                 j.State = this.SessionStates[info.Deref(this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE()];
 | |
|                 if (j.State == 'Active') {
 | |
|                     j.Username = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSUserName);
 | |
|                     j.Domain = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSDomainName);
 | |
|                 }
 | |
|                 retVal[j.SessionId] = j;
 | |
|             }
 | |
| 
 | |
|             this._wts.WTSFreeMemory(pinfo.Deref());
 | |
| 
 | |
|             Object.defineProperty(retVal, 'Active', { value: showActiveOnly(retVal) });
 | |
|             if (cb) { cb(retVal); }
 | |
|             return (retVal);
 | |
|         };
 | |
| 
 | |
| 
 | |
|         // We need to spin up a message pump, and fetch a window handle
 | |
|         var message_pump = require('win-message-pump');
 | |
|         this._messagepump = new message_pump({ filter: WM_WTSSESSION_CHANGE }); this._messagepump.parent = this;     
 | |
|         this._messagepump.on('exit', function (code) { this.parent._wts.WTSUnRegisterSessionNotification(this.parent.hwnd); });
 | |
|         this._messagepump.on('hwnd', function (h)
 | |
|         {
 | |
|             this.parent.hwnd = h;
 | |
| 
 | |
|             // We need to yield, and do this in the next event loop pass, becuase we don't want to call 'RegisterPowerSettingNotification'
 | |
|             // from the messagepump 'thread', because we are actually on the microstack thread, such that the message pump thread, is holding
 | |
|             // on a semaphore for us to return. If we call now, we may deadlock on Windows 7, becuase it will try to notify immediately
 | |
|             this.immediate = setImmediate(function (self)
 | |
|             {
 | |
|                 // Now that we have a window handle, we can register it to receive Windows Messages
 | |
|                 if (self.parent._wts) { self.parent._wts.WTSRegisterSessionNotification(self.parent.hwnd, NOTIFY_FOR_ALL_SESSIONS); }
 | |
|                 self.parent._user32.ACDC_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_ACDC_POWER_SOURCE, 0);
 | |
|                 self.parent._user32.BATT_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_BATTERY_PERCENTAGE_REMAINING, 0);
 | |
|                 self.parent._user32.DISP_H = self.parent._user32.RegisterPowerSettingNotification(self.parent.hwnd, GUID_CONSOLE_DISPLAY_STATE, 0);
 | |
|                 //console.log(self.parent._user32.ACDC_H.Val, self.parent._user32.BATT_H.Val, self.parent._user32.DISP_H.Val);
 | |
|             }, this);
 | |
|         });
 | |
|         this._messagepump.on('message', function (msg)
 | |
|         {
 | |
|             switch(msg.message)
 | |
|             {
 | |
|                 case WM_WTSSESSION_CHANGE:
 | |
|                     switch(msg.wparam)
 | |
|                     {
 | |
|                         case WTS_SESSION_LOCK:
 | |
|                             this.parent.enumerateUsers().then(function (users)
 | |
|                             {
 | |
|                                 if (users[msg.lparam]) { this.parent.emit('locked', users[msg.lparam]); }
 | |
|                             });
 | |
|                             break;
 | |
|                         case WTS_SESSION_UNLOCK:
 | |
|                             this.parent.enumerateUsers().then(function (users)
 | |
|                             {
 | |
|                                 if (users[msg.lparam]) { this.parent.emit('unlocked', users[msg.lparam]); }
 | |
|                             });
 | |
|                             break;
 | |
|                         case WTS_SESSION_LOGON:
 | |
|                         case WTS_SESSION_LOGOFF:
 | |
|                             this.parent.emit('changed');
 | |
|                             break;
 | |
|                     }
 | |
|                     break;
 | |
|                 case WM_POWERBROADCAST:
 | |
|                     switch(msg.wparam)
 | |
|                     {
 | |
|                         default:
 | |
|                             console.log('WM_POWERBROADCAST [UNKNOWN wparam]: ' + msg.wparam);
 | |
|                             break;
 | |
|                         case PBT_APMSUSPEND:
 | |
|                             require('power-monitor').emit('sx', 'SLEEP');
 | |
|                             break;
 | |
|                         case PBT_APMRESUMEAUTOMATIC:
 | |
|                             require('power-monitor').emit('sx', 'RESUME_NON_INTERACTIVE');
 | |
|                             break;
 | |
|                         case PBT_APMRESUMESUSPEND:
 | |
|                             require('power-monitor').emit('sx', 'RESUME_INTERACTIVE');
 | |
|                             break;
 | |
|                         case PBT_APMPOWERSTATUSCHANGE:
 | |
|                             require('power-monitor').emit('changed');
 | |
|                             break;
 | |
|                         case PBT_POWERSETTINGCHANGE:
 | |
|                             var lparam = this.parent._marshal.CreatePointer(Buffer.from(msg.lparam_hex, 'hex'));
 | |
|                             var data = lparam.Deref(20, lparam.Deref(16, 4).toBuffer().readUInt32LE(0)).toBuffer();
 | |
|                             switch(lparam.Deref(0, 16).toBuffer().toString('hex'))
 | |
|                             {
 | |
|                                 case GUID_ACDC_POWER_SOURCE.Deref(0, 16).toBuffer().toString('hex'):
 | |
|                                     switch(data.readUInt32LE(0))
 | |
|                                     {
 | |
|                                         case 0:
 | |
|                                             require('power-monitor').emit('acdc', 'AC');
 | |
|                                             break;
 | |
|                                         case 1:
 | |
|                                             require('power-monitor').emit('acdc', 'BATTERY');
 | |
|                                             break;
 | |
|                                         case 2:
 | |
|                                             require('power-monitor').emit('acdc', 'HOT');
 | |
|                                             break;
 | |
|                                     }
 | |
|                                     break;
 | |
|                                 case GUID_BATTERY_PERCENTAGE_REMAINING.Deref(0, 16).toBuffer().toString('hex'):
 | |
|                                     require('power-monitor').emit('batteryLevel', data.readUInt32LE(0));
 | |
|                                     break;
 | |
|                                 case GUID_CONSOLE_DISPLAY_STATE.Deref(0, 16).toBuffer().toString('hex'):
 | |
|                                     switch(data.readUInt32LE(0))
 | |
|                                     {
 | |
|                                         case 0:
 | |
|                                             require('power-monitor').emit('display', 'OFF');
 | |
|                                             break;
 | |
|                                         case 1:
 | |
|                                             require('power-monitor').emit('display', 'ON');
 | |
|                                             break;
 | |
|                                         case 2:
 | |
|                                             require('power-monitor').emit('display', 'DIMMED');
 | |
|                                             break;
 | |
|                                     }
 | |
|                                     break;
 | |
|                             }
 | |
|                             break;
 | |
|                     }
 | |
|                     break;
 | |
|                 default:
 | |
|                     break;
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     else if(process.platform == 'linux')
 | |
|     {
 | |
|         var dbus = require('linux-dbus');
 | |
|         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._users = function _users()
 | |
|         {
 | |
|             var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.stdin.write('awk -F: \'($3 >= 0) {printf "%s:%s\\n", $1, $3}\' /etc/passwd\nexit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             var ret = {}, tokens;
 | |
|             for (var ln in lines)
 | |
|             {
 | |
|                 tokens = lines[ln].split(':');
 | |
|                 if (tokens[0]) { ret[tokens[0]] = tokens[1]; }           
 | |
|             }
 | |
|             return (ret);
 | |
|         }
 | |
|         this._uids = function _uids() {
 | |
|             var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.stdin.write('awk -F: \'($3 >= 0) {printf "%s:%s\\n", $1, $3}\' /etc/passwd\nexit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             var ret = {}, tokens;
 | |
|             for (var ln in lines) {
 | |
|                 tokens = lines[ln].split(':');
 | |
|                 if (tokens[0]) { ret[tokens[1]] = tokens[0]; }
 | |
|             }
 | |
|             return (ret);
 | |
|         }
 | |
|         this.Self = function Self()
 | |
|         {
 | |
|             var promise = require('promise');
 | |
|             var p = new promise(function (res, rej)
 | |
|             {
 | |
|                 this.__resolver = res; this.__rejector = rej;
 | |
|                 this.__child = require('child_process').execFile('/usr/bin/id', ['id', '-u']);
 | |
|                 this.__child.promise = this;
 | |
|                 this.__child.stdout._txt = '';
 | |
|                 this.__child.stdout.on('data', function (chunk) { this._txt += chunk.toString(); });
 | |
|                 this.__child.on('exit', function (code)
 | |
|                 {
 | |
|                     try
 | |
|                     {
 | |
|                         parseInt(this.stdout._txt);
 | |
|                     }
 | |
|                     catch (e)
 | |
|                     {
 | |
|                         this.promise.__rejector('invalid uid');
 | |
|                         return;
 | |
|                     }
 | |
| 
 | |
|                     var id = parseInt(this.stdout._txt);
 | |
|                     this.promise.__resolver(id);
 | |
|                 });
 | |
|             });
 | |
|             return (p);
 | |
|         };
 | |
|         this.Current = function Current(cb)
 | |
|         {
 | |
|             var retVal = {};
 | |
|             retVal._ObjectID = 'UserSession'
 | |
|             Object.defineProperty(retVal, '_callback', { value: cb });
 | |
|             Object.defineProperty(retVal, '_child', { value: 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 = [];
 | |
|                 var users = {};
 | |
| 
 | |
|                 for(var i in lines)
 | |
|                 {
 | |
|                     if (lines[i])
 | |
|                     {
 | |
|                         var tokens = getTokens(lines[i]);
 | |
|                         var s = { Username: tokens[0], SessionId: tokens[1] }
 | |
|                         if (tokens[3].includes('still logged in'))
 | |
|                         {
 | |
|                             s.State = 'Active';
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             s.LastActive = tokens[3];
 | |
|                         }
 | |
| 
 | |
|                         sessions.push(s);
 | |
|                     }
 | |
|                 }
 | |
|                 sessions.pop();
 | |
| 
 | |
| 
 | |
|                 var usernames = {};
 | |
|                 var promises = [];
 | |
| 
 | |
|                 for (var i in sessions)
 | |
|                 {
 | |
|                     if (sessions[i].Username != 'reboot')
 | |
|                     {
 | |
|                         users[sessions[i].SessionId] = sessions[i];
 | |
|                         if(usernames[sessions[i].Username] == null)
 | |
|                         {
 | |
|                             usernames[sessions[i].Username] = -1;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 try
 | |
|                 {
 | |
|                     require('promise');
 | |
|                 }
 | |
|                 catch(e)
 | |
|                 {
 | |
|                     Object.defineProperty(users, 'Active', { value: showActiveOnly(users) });
 | |
|                     if (this.Parent._callback) { this.Parent._callback.call(this.Parent, users); }
 | |
|                     return;
 | |
|                 }
 | |
| 
 | |
|                 var promise = require('promise');
 | |
|                 for (var n in usernames)
 | |
|                 {
 | |
|                     var p = new promise(function (res, rej)
 | |
|                     {
 | |
|                         this.__username = n;
 | |
|                         this.__resolver = res; this.__rejector = rej;
 | |
|                         this.__child = require('child_process').execFile('/usr/bin/id', ['id', '-u', n]);
 | |
|                         this.__child.promise = this;
 | |
|                         this.__child.stdout._txt = '';
 | |
|                         this.__child.stdout.on('data', function (chunk) { this._txt += chunk.toString(); });
 | |
|                         this.__child.on('exit', function (code)
 | |
|                         {
 | |
|                             try
 | |
|                             {
 | |
|                                 parseInt(this.stdout._txt);
 | |
|                             }
 | |
|                             catch(e)
 | |
|                             {
 | |
|                                 this.promise.__rejector('invalid uid');
 | |
|                                 return;
 | |
|                             }
 | |
| 
 | |
|                             var id = parseInt(this.stdout._txt);
 | |
|                             this.promise.__resolver(id);
 | |
|                         });
 | |
|                     });
 | |
|                     promises.push(p);
 | |
|                 }
 | |
|                 promise.all(promises).then(function (plist)
 | |
|                 {
 | |
|                     // Done
 | |
|                     var table = {};
 | |
|                     for(var i in plist)
 | |
|                     {
 | |
|                         table[plist[i].__username] = plist[i]._internal.completedArgs[0];
 | |
|                     }
 | |
|                     for(var i in users)
 | |
|                     {
 | |
|                         users[i].uid = table[users[i].Username];
 | |
|                     }
 | |
|                     Object.defineProperty(users, 'Active', { value: showActiveOnly(users) });
 | |
|                     if (retVal._callback) { retVal._callback.call(retVal, users); }
 | |
|                 }, function (reason)
 | |
|                 {
 | |
|                     // Failed
 | |
|                     Object.defineProperty(users, 'Active', { value: showActiveOnly(users) });
 | |
|                     if (retVal._callback) { retVal._callback.call(retVal, users); }
 | |
|                 });
 | |
|             });
 | |
|             retVal._child.stdout.Parent = retVal._child;
 | |
|             retVal._child.stdout.on('data', function (chunk) { this.Parent._txt += chunk.toString(); });
 | |
| 
 | |
|             return (retVal);
 | |
|         }
 | |
|         this._recheckLoggedInUsers = function _recheckLoggedInUsers()
 | |
|         {
 | |
|             this.enumerateUsers().then(function (u)
 | |
|             {
 | |
| 
 | |
|                 if (u.Active.length > 0)
 | |
|                 {
 | |
|                     // There is already a user logged in, so we can monitor DBUS for lock/unlock
 | |
|                     if (this.parent._linux_lock_watcher != null && this.parent._linux_lock_watcher.uid != u.Active[0].uid)
 | |
|                     {
 | |
|                         delete this.parent._linux_lock_watcher;
 | |
|                     }
 | |
|                     this.parent._linux_lock_watcher = new dbus(process.env['XDG_CURRENT_DESKTOP'] == 'Unity' ? 'com.ubuntu.Upstart0_6' : 'org.gnome.ScreenSaver', u.Active[0].uid);
 | |
|                     this.parent._linux_lock_watcher.user_session = this.parent;
 | |
|                     this.parent._linux_lock_watcher.on('signal', function (s)
 | |
|                     {
 | |
|                         var p = this.user_session.enumerateUsers();
 | |
|                         p.signalData = s.data[0];
 | |
|                         p.then(function (u)
 | |
|                         {
 | |
|                             switch (this.signalData)
 | |
|                             {
 | |
|                                 case true:
 | |
|                                 case 'desktop-lock':
 | |
|                                     this.parent.emit('locked', u.Active[0]);
 | |
|                                     break;
 | |
|                                 case false:
 | |
|                                 case 'desktop-unlock':
 | |
|                                     this.parent.emit('unlocked', u.Active[0]);
 | |
|                                     break;
 | |
|                             }
 | |
|                         });
 | |
|                     });
 | |
|                 }
 | |
|                 else if (this.parent._linux_lock_watcher != null)
 | |
|                 {
 | |
|                     delete this.parent._linux_lock_watcher;
 | |
|                 }
 | |
|             });
 | |
| 
 | |
|         };
 | |
|         this.on('changed', this._recheckLoggedInUsers); // For linux Lock/Unlock monitoring, we need to watch for LogOn/LogOff, and keep track of the UID.
 | |
| 
 | |
|         
 | |
|         // First step, is to see if there is a user logged in:
 | |
|         this._recheckLoggedInUsers();
 | |
|     }
 | |
|     else if(process.platform == 'darwin')
 | |
|     {
 | |
|         this._users = function ()
 | |
|         {
 | |
|             var child = require('child_process').execFile('/usr/bin/dscl', ['dscl', '.', 'list', '/Users', 'UniqueID']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.stdin.write('exit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             var tokens, i;
 | |
|             var users = {};
 | |
| 
 | |
|             for (i = 0; i < lines.length; ++i) {
 | |
|                 tokens = lines[i].split(' ');
 | |
|                 if (tokens[0]) { users[tokens[0]] = tokens[tokens.length - 1]; }
 | |
|             }
 | |
| 
 | |
|             return (users);
 | |
|         }
 | |
|         this._uids = function () {
 | |
|             var child = require('child_process').execFile('/usr/bin/dscl', ['dscl', '.', 'list', '/Users', 'UniqueID']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.stdin.write('exit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             var tokens, i;
 | |
|             var users = {};
 | |
| 
 | |
|             for (i = 0; i < lines.length; ++i) {
 | |
|                 tokens = lines[i].split(' ');
 | |
|                 if (tokens[0]) { users[tokens[tokens.length - 1]] = tokens[0]; }
 | |
|             }
 | |
| 
 | |
|             return (users);
 | |
|         }
 | |
|         this._idTable = function()
 | |
|         {
 | |
|             var table = {};
 | |
|             var child = require('child_process').execFile('/usr/bin/id', ['id']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n')[0].split(' ');
 | |
|             for (var i = 0; i < lines.length; ++i) {
 | |
|                 var types = lines[i].split('=');
 | |
|                 var tokens = types[1].split(',');
 | |
|                 table[types[0]] = {};
 | |
| 
 | |
|                 for (var j in tokens) {
 | |
|                     var idarr = tokens[j].split('(');
 | |
|                     var id = idarr[0];
 | |
|                     var name = idarr[1].substring(0, idarr[1].length - 1).trim();
 | |
|                     table[types[0]][name] = id;
 | |
|                     table[types[0]][id] = name;
 | |
|                 }
 | |
|             }
 | |
|             return (table);
 | |
|         }
 | |
|         this.Current = function (cb)
 | |
|         {
 | |
|             var users = {};
 | |
|             var table = this._idTable();
 | |
|             var child = require('child_process').execFile('/usr/bin/last', ['last']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             for (var i = 0; i < lines.length && lines[i].length > 0; ++i)
 | |
|             {
 | |
|                 if (!users[lines[i].split(' ')[0]])
 | |
|                 {
 | |
|                     try
 | |
|                     {
 | |
|                         users[lines[i].split(' ')[0]] = { Username: lines[i].split(' ')[0], State: lines[i].split('still logged in').length > 1 ? 'Active' : 'Inactive', uid: table.uid[lines[i].split(' ')[0]] };
 | |
|                     }
 | |
|                     catch(e)
 | |
|                     {}
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if(users[lines[i].split(' ')[0]].State != 'Active' && lines[i].split('still logged in').length > 1)
 | |
|                     {
 | |
|                         users[lines[i].split(' ')[0]].State = 'Active';
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             Object.defineProperty(users, 'Active', { value: showActiveOnly(users) });
 | |
|             if (cb) { cb.call(this, users); }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if(process.platform == 'linux' || process.platform == 'darwin')
 | |
|     {
 | |
|         this._self = function _self()
 | |
|         {
 | |
|             var child = require('child_process').execFile('/usr/bin/id', ['id', '-u']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.waitExit();
 | |
|             return (parseInt(child.stdout.str));
 | |
|         }
 | |
|         this.isRoot = function isRoot()
 | |
|         {
 | |
|             return (this._self() == 0);
 | |
|         }
 | |
|         this.consoleUid = function consoleUid()
 | |
|         {
 | |
|             var checkstr = process.platform == 'darwin' ? 'console' : ((process.env['DISPLAY'])?process.env['DISPLAY']:':0')
 | |
|             var child = require('child_process').execFile('/bin/sh', ['sh']);
 | |
|             child.stdout.str = '';
 | |
|             child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
 | |
|             child.stdin.write('who\nexit\n');
 | |
|             child.waitExit();
 | |
| 
 | |
|             var lines = child.stdout.str.split('\n');
 | |
|             var tokens, i, j;
 | |
|             for (i in lines)
 | |
|             {
 | |
|                 tokens = lines[i].split(' ');
 | |
|                 for (j = 1; j < tokens.length; ++j)
 | |
|                 {
 | |
|                     if (tokens[j].length > 0)
 | |
|                     {
 | |
|                         return (parseInt(this._users()[tokens[0]]));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             throw ('nobody logged into console');
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
| }
 | |
| 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)
 | |
| {
 | |
|     var columns = [];
 | |
|     var i;
 | |
| 
 | |
|     columns.push(str.substring(0, (i=str.indexOf(' '))));
 | |
|     while (str[++i] == ' ');
 | |
|     columns.push(str.substring(i, (i=str.substring(i).indexOf(' ') + i)));
 | |
|     while (str[++i] == ' ');
 | |
|     columns.push(str.substring(i, (i=str.substring(i).indexOf(' ') + i)));
 | |
|     while (str[++i] == ' ');
 | |
|     var status = str.substring(i).trim();
 | |
|     columns.push(status);
 | |
| 
 | |
|     return (columns);
 | |
| }
 | |
| 
 | |
| module.exports = new UserSessions(); |