diff --git a/modules/util-dns.js b/modules/util-dns.js index 36c2b08..70ac8ba 100644 --- a/modules/util-dns.js +++ b/modules/util-dns.js @@ -14,8 +14,17 @@ See the License for the specific language governing permissions and limitations under the License. */ +// +// This module is used to query the DNS server address from the OS +// + function windows_dns() { + // + // Reference for GetNetworkParams() can be found at: + // https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getnetworkparams + // + var ret = []; var ip = require('_GenericMarshal').CreateNativeProxy('Iphlpapi.dll'); ip.CreateMethod('GetNetworkParams'); @@ -31,12 +40,16 @@ function windows_dns() do { ret.push(dnsList.Deref(require('_GenericMarshal').PointerSize, 16).toBuffer().toString()); - } while ((dnsList = dnsList.Deref(0, require('_GenericMarshal').PointerSize).Deref().Deref(0, 48)).Val != 0); + } while ((dnsList = dnsList.Deref(0, require('_GenericMarshal').PointerSize).Deref().Deref(0, 48)).Val != 0); // Itereate the list } return (ret); } function linux_dns() { + + // + // The linux implementation will look for the dns address in /etc/resolve.conf + // var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function () { }); @@ -70,6 +83,9 @@ function linux_dns() function macos_dns() { + // + // macOS implementation will use the system utility scutil to fetch the dns address + // var child = require('child_process').execFile('/bin/sh', ['sh']); child.stdout.str = ''; child.stdout.on('data', function (c) { this.str += c.toString(); }); child.stderr.on('data', function () { }); @@ -108,13 +124,13 @@ switch (process.platform) { case 'linux': case 'freebsd': - module.exports = linux_dns; + module.exports = linux_dns; // Linux and BSD will use /etc/resolve.conf break; case 'win32': - module.exports = windows_dns; + module.exports = windows_dns; // Windows will use Iphlpapi break; case 'darwin': - module.exports = macos_dns; + module.exports = macos_dns; // macOS will use scutil break; default: module.exports = function () { return ([]); }; diff --git a/modules/win-info.js b/modules/win-info.js index 4b0e8f9..def4a35 100644 --- a/modules/win-info.js +++ b/modules/win-info.js @@ -14,8 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ + +// +// This module fetches various Windows System information, such as pending reboot status, +// volume defrag state, installed applications, windows update status, etc +// + + var promise = require('promise'); +// +// This function queries WMI to fetch Windows Update Status +// function qfe() { var child = require('child_process').execFile(process.env['windir'] + '\\System32\\wbem\\wmic.exe', ['wmic', 'qfe', 'list', 'full', '/FORMAT:CSV']); @@ -41,6 +51,8 @@ function qfe() } return (result); } + +// This function uses Windows Powershell to fetch metadata about the currently configured AntiVirus function av() { var child = require('child_process').execFile(process.env['windir'] + '\\System32\\WindowsPowerShell\\v1.0\\powershell.exe', ['powershell', '-noprofile', '-nologo', '-command', '-'], {}); @@ -77,6 +89,11 @@ function av() } return (result); } + +// +// This function uses the defrag system utility to query defrag state of the specified volume +// +// Note: options.volume must be specified function defrag(options) { var ret = new promise(function (res, rej) { this._res = res; this._rej = rej; }); @@ -142,6 +159,8 @@ function defrag(options) }); return (ret); } + +// Helper/Shortcut for registry query function regQuery(H, Path, Key) { try @@ -153,6 +172,8 @@ function regQuery(H, Path, Key) return (null); } } + +// This function returns details on any system pending reboots function pendingReboot() { var tmp = null; @@ -177,6 +198,9 @@ function pendingReboot() return (ret); } +// +// Returns a promise that fetches the list of installed applications +// function installedApps() { var promise = require('promise'); diff --git a/modules/win-registry.js b/modules/win-registry.js index 01419bb..72980d4 100644 --- a/modules/win-registry.js +++ b/modules/win-registry.js @@ -1,5 +1,5 @@ /* -Copyright 2018 Intel Corporation +Copyright 2018-2022 Intel Corporation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +18,10 @@ var KEY_QUERY_VALUE = 0x0001; var KEY_ENUMERATE_SUB_KEYS = 0x0008; var KEY_WRITE = 0x20006; +// +// Registry +// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/25cce700-7fcf-4bb6-a2f3-0f6d08430a55 +// var KEY_DATA_TYPES = { REG_NONE: 0, @@ -39,18 +43,18 @@ function windows_registry() this._ObjectId = 'win-registry'; this._marshal = require('_GenericMarshal'); this._Kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll'); - this._Kernel32.CreateMethod('FileTimeToSystemTime'); + this._Kernel32.CreateMethod('FileTimeToSystemTime'); // https://learn.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-filetimetosystemtime this._AdvApi = this._marshal.CreateNativeProxy('Advapi32.dll'); - this._AdvApi.CreateMethod('RegCreateKeyExW'); - this._AdvApi.CreateMethod('RegEnumKeyExW'); - this._AdvApi.CreateMethod('RegEnumValueW'); - this._AdvApi.CreateMethod('RegOpenKeyExW'); - this._AdvApi.CreateMethod('RegQueryInfoKeyW'); - this._AdvApi.CreateMethod('RegQueryValueExW'); - this._AdvApi.CreateMethod('RegCloseKey'); - this._AdvApi.CreateMethod('RegDeleteKeyW'); - this._AdvApi.CreateMethod('RegDeleteValueW'); - this._AdvApi.CreateMethod('RegSetValueExW'); + this._AdvApi.CreateMethod('RegCreateKeyExW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regcreatekeyexw + this._AdvApi.CreateMethod('RegEnumKeyExW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regenumkeyexw + this._AdvApi.CreateMethod('RegEnumValueW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regenumvaluew + this._AdvApi.CreateMethod('RegOpenKeyExW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexw + this._AdvApi.CreateMethod('RegQueryInfoKeyW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryinfokeyw + this._AdvApi.CreateMethod('RegQueryValueExW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regqueryvalueexw + this._AdvApi.CreateMethod('RegCloseKey'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regclosekey + this._AdvApi.CreateMethod('RegDeleteKeyW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletekeyw + this._AdvApi.CreateMethod('RegDeleteValueW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regdeletevaluew + this._AdvApi.CreateMethod('RegSetValueExW'); // https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexw 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) @@ -65,6 +69,7 @@ function windows_registry() if (!path) { path = ''; } + // Try to open the registry key for enumeration first. if ((err = this._AdvApi.RegOpenKeyExW(HK, this._marshal.CreateVariable(path, { wide: true }), 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, h).Val) != 0) { throw ('Opening Registry Key: ' + path + ' => Returned Error: ' + err); @@ -78,6 +83,10 @@ function windows_registry() { switch (valType.toBuffer().readUInt32LE()) { + // + // Registry Value Types can be found at: + // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types + // case KEY_DATA_TYPES.REG_DWORD: retVal = data.toBuffer().readUInt32LE(); break; @@ -157,6 +166,8 @@ function windows_registry() this._AdvApi.RegCloseKey(h.Deref()); return (retVal); }; + + // Query the last time the key was modified this.QueryKeyLastModified = function QueryKeyLastModified(hkey, path, key) { var v; @@ -167,6 +178,7 @@ function windows_registry() if (key) { key = this._marshal.CreateVariable(key, { wide: true }); } if (!path) { path = ''; } + // Open the registry key if ((err = this._AdvApi.RegOpenKeyExW(HK, this._marshal.CreateVariable(path, { wide: true }), 0, KEY_QUERY_VALUE, h).Val) != 0) { throw ('Opening Registry Key: ' + path + ' => Returned Error: ' + err); @@ -187,20 +199,24 @@ function windows_registry() var securityDescriptor = this._marshal.CreateVariable(4); var lastWriteTime = this._marshal.CreateVariable(8); + // Get the metadata for the registry value v = this._AdvApi.RegQueryInfoKeyW(h.Deref(), achClass, achClassSize, 0, numSubKeys, longestSubkeySize, longestClassString, numValues, longestValueName, longestValueData, securityDescriptor, lastWriteTime); if (v.Val != 0) { throw ('RegQueryInfoKeyW() returned error: ' + v.Val); } + // Convert the time format var systime = this._marshal.CreateVariable(16); if (this._Kernel32.FileTimeToSystemTime(lastWriteTime, systime).Val == 0) { throw ('Error parsing time'); } return (require('fs').convertFileTime(lastWriteTime)); }; + this.WriteKey = function WriteKey(hkey, path, key, value) { var result; var h = this._marshal.CreatePointer(); + // Create the registry key if (this._AdvApi.RegCreateKeyExW(this._marshal.CreatePointer(hkey), this._marshal.CreateVariable(path, { wide: true }), 0, 0, 0, KEY_WRITE, 0, h, 0).Val != 0) { throw ('Error Opening Registry Key: ' + path); @@ -209,8 +225,13 @@ function windows_registry() var data; var dataType; + // Create the value entry switch(typeof(value)) { + // + // Registry Value Types can be found at: + // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types + // case 'boolean': dataType = KEY_DATA_TYPES.REG_DWORD; data = this._marshal.CreateVariable(4); @@ -232,6 +253,7 @@ function windows_registry() break; } + // Save the registry value if (this._AdvApi.RegSetValueExW(h.Deref(), key?this._marshal.CreateVariable(key, { wide: true }):0, 0, dataType, data, data._size).Val != 0) { this._AdvApi.RegCloseKey(h.Deref()); @@ -239,6 +261,8 @@ function windows_registry() } this._AdvApi.RegCloseKey(h.Deref()); }; + + // Delete a registry entry this.DeleteKey = function DeleteKey(hkey, path, key) { if(!key) @@ -264,6 +288,10 @@ function windows_registry() this._AdvApi.RegCloseKey(h.Deref()); } }; + + // + // This function trys to convert a user name, to a windows security descriptor, which is used as the registry key for user entries + // this.usernameToUserKey = function usernameToUserKey(user) { var domain = null @@ -275,6 +303,7 @@ function windows_registry() try { + // Try to fetch the current domain if(domain==null) { domain = require('win-wmi').query('ROOT\\CIMV2', "SELECT * FROM Win32_ComputerSystem", ['Name'])[0].Name; @@ -290,6 +319,7 @@ function windows_registry() var sid = user; if (typeof (user) == 'string') { + // Try to find the Session ID for the specified domain user var r = this.QueryKey(this.HKEY.LocalMachine, 'SAM\\SAM\\Domains\\Account\\Users\\Names\\' + user); sid = r.default._type; } @@ -298,6 +328,7 @@ function windows_registry() { if(u.subkeys[i].endsWith('-' + sid)) { + // Try to find the Descriptor Key with the SID that we found return (u.subkeys[i]); } } @@ -312,6 +343,7 @@ function windows_registry() { if(entries.subkeys[i].split('-').length>5 && !entries.subkeys[i].endsWith('_Classes')) { + // This will look at the list of domain users that have recently logged into the system try { if (this.QueryKey(this.HKEY.Users, entries.subkeys[i] + '\\Volatile Environment', 'USERDOMAIN') == domain) diff --git a/modules/win-securitycenter.js b/modules/win-securitycenter.js index f7c4d73..ccb52f5 100644 --- a/modules/win-securitycenter.js +++ b/modules/win-securitycenter.js @@ -14,6 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ + +// +// win-securitycenter queries Windows Security Center to determine status of AntiVirus, Firewall, and Auto Update +// It should be noted that Window Security Center is not present on Windows Server Skus. +// + var seccenter = null; var WSC_SECURITY_PROVIDER_FIREWALL = 0x1; var WSC_SECURITY_PROVIDER_AUTOUPDATE_SETTINGS = 0x2; @@ -27,10 +33,14 @@ var WSC_SECURITY_PROVIDER_HEALTH_SNOOZE = 3; // Yellow pillar in English try { + // + // Try to dynamically load the APIs for WSC, becuase it is not + // present on Windows Server SKUs + // seccenter = require('_GenericMarshal').CreateNativeProxy('Wscapi.dll'); - seccenter.CreateMethod('WscGetSecurityProviderHealth'); - seccenter.CreateMethod('WscRegisterForChanges'); - seccenter.CreateMethod('WscUnRegisterChanges'); + seccenter.CreateMethod('WscGetSecurityProviderHealth'); // https://learn.microsoft.com/en-us/windows/win32/api/wscapi/nf-wscapi-wscgetsecurityproviderhealth + seccenter.CreateMethod('WscRegisterForChanges'); // https://learn.microsoft.com/en-us/windows/win32/api/wscapi/nf-wscapi-wscregisterforchanges + seccenter.CreateMethod('WscUnRegisterChanges'); // https://learn.microsoft.com/en-us/windows/win32/api/wscapi/nf-wscapi-wscunregisterchanges } catch(e) { @@ -63,6 +73,7 @@ function getStatus() var ret = { firewall: 'UNKNOWN', antiVirus: 'UNKNOWN', autoUpdate: 'UNKNOWN' }; if (seccenter != null) { + // Fetch the current status of Firewall, AntiVirus, and AutoUpdate var status = require('_GenericMarshal').CreateVariable(4); if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_FIREWALL, status).Val == 0) { ret.firewall = statusString(status.toBuffer().readUInt32LE()); } if (seccenter.WscGetSecurityProviderHealth(WSC_SECURITY_PROVIDER_ANTIVIRUS, status).Val == 0) { ret.antiVirus = statusString(status.toBuffer().readUInt32LE()); } @@ -73,6 +84,9 @@ function getStatus() if (process.platform == 'win32' && seccenter != null) { + // + // Setup the event handler for when system status changes + // var j = { status: getStatus }; require('events').EventEmitter.call(j, true) .createEvent('changed'); @@ -86,10 +100,11 @@ if (process.platform == 'win32' && seccenter != null) }); j.on('~', function () { + // Unregister our event handler if (seccenter.WscUnRegisterChanges(this._H).Val == 0) { } }); - if (seccenter.WscRegisterForChanges(0, j._H, j._EV, require('_GenericMarshal').ObjectToPtr(j)).Val == 0) + if (seccenter.WscRegisterForChanges(0, j._H, j._EV, require('_GenericMarshal').ObjectToPtr(j)).Val == 0) // Setup event handling { j._H = j._H.Deref(); } diff --git a/modules/win-tasks.js b/modules/win-tasks.js index 2730b2a..614949a 100644 --- a/modules/win-tasks.js +++ b/modules/win-tasks.js @@ -63,16 +63,19 @@ const TASK_DONT_ADD_PRINCIPAL_ACE = 0x10; const TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20; var OleAut32 = GM.CreateNativeProxy('OleAut32.dll'); -OleAut32.CreateMethod('SafeArrayAccessData'); -OleAut32.CreateMethod('SafeArrayCreate'); -OleAut32.CreateMethod('SafeArrayCreateVector'); -OleAut32.CreateMethod('SafeArrayPutElement'); -OleAut32.CreateMethod('SafeArrayDestroy'); -OleAut32.CreateMethod('VariantClear'); -OleAut32.CreateMethod('VariantInit'); -OleAut32.CreateMethod('SysAllocString'); +OleAut32.CreateMethod('SafeArrayAccessData'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayaccessdata +OleAut32.CreateMethod('SafeArrayCreate'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreate +OleAut32.CreateMethod('SafeArrayCreateVector'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraycreatevector +OleAut32.CreateMethod('SafeArrayPutElement'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayputelement +OleAut32.CreateMethod('SafeArrayDestroy'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearraydestroy +OleAut32.CreateMethod('VariantClear'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-variantclear +OleAut32.CreateMethod('VariantInit'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-variantinit +OleAut32.CreateMethod('SysAllocString'); // https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-sysallocstring +// +// This function converts an array of strings, to a variant array of BSTR +// function ConvertStringArray(strarr) { if (!strarr || !Array.isArray(strarr)) { return (GM.CreateVariable(24)); } @@ -112,6 +115,10 @@ const UnknownFunctions = 'Release' ]; +// +// Reference for ITaskService interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itaskservice +// const TaskServiceFunctions = [ 'QueryInterface', @@ -131,6 +138,11 @@ const TaskServiceFunctions = 'get_ConnectedDomain', 'get_HighestVersion' ]; + +// +// Reference for ITaskFolder interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itaskfolder +// const TaskFolderFunctions = [ 'QueryInterface', @@ -154,6 +166,11 @@ const TaskFolderFunctions = 'GetSecurityDescriptor', 'SetSecurityDescriptor' ]; + +// +// Reference for IRegistrationInfo interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iregistrationinfo +// const RegistrationInfoFunctions = [ 'QueryInterface', @@ -182,6 +199,11 @@ const RegistrationInfoFunctions = 'get_Source', 'put_Source' ]; + +// +// Reference for ITaskDefinition interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itaskdefinition +// const TaskDefinitionFunctions = [ 'QueryInterface', @@ -206,6 +228,11 @@ const TaskDefinitionFunctions = 'get_XmlText', 'put_XmlText' ]; + +// +// Reference for IPrincipal interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iprincipal +// const PrincipalFunctions = [ 'QueryInterface', @@ -228,6 +255,11 @@ const PrincipalFunctions = 'get_RunLevel', 'put_RunLevel' ]; + +// +// Reference for ITaskSettings interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itasksettings +// const TaskSettingsFunctions = [ 'QueryInterface', @@ -278,6 +310,11 @@ const TaskSettingsFunctions = 'get_NetworkSettings', 'put_NetworkSettings' ]; + +// +// Reference for IIdleSettings interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iidlesettings +// const IdleSettingsFunctions = [ 'QueryInterface', @@ -296,6 +333,11 @@ const IdleSettingsFunctions = 'get_RestartOnIdle', 'put_RestartOnIdle' ]; + +// +// Reference for ITriggerCollection interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itriggercollection +// const TriggerCollectionFunctions = [ 'QueryInterface', @@ -312,6 +354,11 @@ const TriggerCollectionFunctions = 'Remove', 'Clear' ]; + +// +// Reference for ITrigger interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itrigger +// const TriggerFunctions = [ 'QueryInterface', @@ -335,6 +382,11 @@ const TriggerFunctions = 'get_Enabled', 'put_Enabled' ]; + +// +// Reference for ITimeTrigger interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-itimetrigger +// const TimeTriggerFunctions = [ 'QueryInterface', @@ -360,6 +412,11 @@ const TimeTriggerFunctions = 'get_RandomDelay', 'put_RandomDelay' ]; + +// +// Reference for IActionCollection interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iactioncollection +// const ActionCollectionFunctions = [ 'QueryInterface', @@ -380,6 +437,11 @@ const ActionCollectionFunctions = 'get_Context', 'put_Context' ]; + +// +// Reference for IAction interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iaction +// const ActionFunctions = [ 'QueryInterface', @@ -393,6 +455,11 @@ const ActionFunctions = 'put_Id', 'get_Type' ]; + +// +// Reference for IExecAction interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iexecaction +// const ExecActionFunctions = [ 'QueryInterface', @@ -412,6 +479,11 @@ const ExecActionFunctions = 'get_WorkingDirectory', 'put_WorkingDirectory' ]; + +// +// Reference for IRegisteredTask interface can be found at: +// https://learn.microsoft.com/en-us/windows/win32/api/taskschd/nn-taskschd-iregisteredtask +// const RegisteredTaskFunctions = [ 'QueryInterface', @@ -441,6 +513,10 @@ const RegisteredTaskFunctions = 'GetRunTimes' ]; +// +// JavaScript abstraction for Task. This constructor adds the "Run" function, and +// adds a finalizer to handle cleanup and unregistration +// function taskObject(j) { this._task = j; @@ -463,6 +539,9 @@ function taskObject(j) }) } +// +// Finds and returns the specified Task +// function getTask(options) { var hr; @@ -473,6 +552,7 @@ function getTask(options) var rootFolder = GM.CreatePointer(); var task = GM.CreatePointer(); + // Connect to the TaskScheduler COM object var taskService = require('win-com').createInstance(require('win-com').CLSIDFromString(CLSID_TaskScheduler), require('win-com').IID_IUnknown); taskService.funcs = require('win-com').marshalFunctions(taskService, TaskServiceFunctions); @@ -483,6 +563,8 @@ function getTask(options) taskService.funcs.Release(taskService); throw ('ITaskService::Connect failed ' + hr.Val); } + + // Get the folder object hr = taskService.funcs.GetFolder(taskService, GM.CreateVariable('\\', { wide: true }), rootFolder); if (hr.Val != 0) { @@ -491,6 +573,7 @@ function getTask(options) } rootFolder.funcs = require('win-com').marshalFunctions(rootFolder.Deref(), TaskFolderFunctions); + // Get the tasks for that folder object hr = rootFolder.funcs.GetTask(rootFolder.Deref(), GM.CreateVariable(options.name, { wide: true }), task); if (hr.Val != 0) { @@ -499,12 +582,16 @@ function getTask(options) throw ('Failed to get Task: ' + options.name + ' [' + hr.Val + ']'); } task.funcs = require('win-com').marshalFunctions(task.Deref(), RegisteredTaskFunctions); - task.funcs.Run._callType = CUSTOM_HANDLER | 2; + task.funcs.Run._callType = CUSTOM_HANDLER | 2; // This must be declared like this, so that VARIANT can be marshaled correctly task._rf = rootFolder; task._ts = taskService; return (new taskObject(task)); } + +// +// Delete a scheduled task +// function deleteTask(options) { if (typeof (options) == 'string') { options = { name: options } } @@ -515,6 +602,7 @@ function deleteTask(options) var password = GM.CreateVariable(24); var rootFolder = GM.CreatePointer(); + // Instantiate and connect to the Window Task Scheduler service var taskService = require('win-com').createInstance(require('win-com').CLSIDFromString(CLSID_TaskScheduler), require('win-com').IID_IUnknown); taskService.funcs = require('win-com').marshalFunctions(taskService, TaskServiceFunctions); hr = taskService.funcs.Connect._callType = 1; @@ -524,6 +612,8 @@ function deleteTask(options) taskService.funcs.Release(taskService); throw ('ITaskService::Connect failed ' + hr.Val); } + + // Get the root folder hr = taskService.funcs.GetFolder(taskService, GM.CreateVariable('\\', { wide: true }), rootFolder); if (hr.Val != 0) { @@ -532,6 +622,8 @@ function deleteTask(options) } console.info1('Deleting Task: ' + options.name); rootFolder.funcs = require('win-com').marshalFunctions(rootFolder.Deref(), TaskFolderFunctions); + + // Delete the specified task hr = rootFolder.funcs.DeleteTask(rootFolder.Deref(), GM.CreateVariable(options.name, { wide: true }), 0); if (hr.Val != 0) { @@ -542,15 +634,21 @@ function deleteTask(options) rootFolder.funcs.Release(rootFolder.Deref()); taskService.funcs.Release(taskService); } + +// +// Add a new task to the Windows Task Scheduler +// function addTask(options) { + // Set some defaults if they are not specified if (!options) { throw ('Need to specify options object'); } if (!options.author) { options.author = 'win-task'; } if (!options.id) { options.id = 'win-task'; } - if (!options.startTime) { options.startTime = '2021-01-01T00:00'; } if (!options.endTime) { options.endTime = '2021-01-01T00:30'; } + + // Instantiate the Windows Task Scheduler service var taskService = require('win-com').createInstance(require('win-com').CLSIDFromString(CLSID_TaskScheduler), require('win-com').IID_IUnknown); taskService.funcs = require('win-com').marshalFunctions(taskService, TaskServiceFunctions); @@ -573,7 +671,7 @@ function addTask(options) var execAction = GM.CreatePointer(); var registeredTask = GM.CreatePointer(); - + // Connect to the Task Scheduler taskService.funcs.Connect._callType = 1; hr = taskService.funcs.Connect(taskService, serverName, user, domain, password); if (hr.Val != 0) @@ -589,6 +687,7 @@ function addTask(options) } rootFolder.funcs = require('win-com').marshalFunctions(rootFolder.Deref(), TaskFolderFunctions); + // Create an empty task hr = taskService.funcs.NewTask(taskService, 0, task); taskService.funcs.Release(taskService); // No longer needed going forward if (hr.Val != 0) @@ -597,6 +696,7 @@ function addTask(options) throw ('ITaskService failed to create new task ' + hr.Val); } + // Fetch the registration data for the ampty task task.funcs = require('win-com').marshalFunctions(task.Deref(), TaskDefinitionFunctions); hr = task.funcs.get_RegistrationInfo(task.Deref(), regInfo); if (hr.Val != 0) @@ -618,7 +718,7 @@ function addTask(options) if (options.userID != null || options.user) { - if (options.user == 'SYSTEM') { options.userID = 'S-1-5-18'; } + if (options.user == 'SYSTEM') { options.userID = 'S-1-5-18'; } // If the task is to run as SYSTEM, set the security descriptor for SYSTEM hr = task.funcs.get_Principal(task.Deref(), principal); if (hr.Val != 0) { @@ -632,6 +732,7 @@ function addTask(options) { try { + // If the task is to run as user, we need to fetch the Windows Security Descriptor for that user options.userID = require('win-registry').usernameToUserKey({ user: options.user, domain: options.domain }); } catch (z) @@ -654,6 +755,7 @@ function addTask(options) if (options.userID) { + // Set the security descriptor for the task hr = principal.funcs.put_UserId(principal.Deref(), GM.CreateVariable(options.userID, { wide: true })); if (hr.Val != 0) { @@ -675,6 +777,7 @@ function addTask(options) } taskSettings.funcs = require('win-com').marshalFunctions(taskSettings.Deref(), TaskSettingsFunctions); + // Set some atrtibutes, so that the task will run, regardless of AC power state and network state if(taskSettings.funcs.put_StopIfGoingOnBatteries(taskSettings.Deref(), 0).Val != 0 || taskSettings.funcs.put_DisallowStartIfOnBatteries(taskSettings.Deref(), 0).Val != 0 || taskSettings.funcs.put_RunOnlyIfNetworkAvailable(taskSettings.Deref(), 0).Val != 0) @@ -693,6 +796,7 @@ function addTask(options) } triggerCollection.funcs = require('win-com').marshalFunctions(triggerCollection.Deref(), TriggerCollectionFunctions); + // Create the trigger hr = triggerCollection.funcs.Create(triggerCollection.Deref(), TASK_TRIGGER_TIME, unknownTrigger); if (hr.Val != 0) { @@ -719,6 +823,7 @@ function addTask(options) throw ('ITaskService failed to get put TriggerID ' + hr.Val); } + // Set the start time hr = timeTrigger.funcs.put_StartBoundary(timeTrigger.Deref(), GM.CreateVariable(options.startTime, { wide: true })); if (hr.Val != 0) { @@ -727,6 +832,7 @@ function addTask(options) throw ('ITaskService failed to set StartBoundary ' + hr.Val); } + // Set the end time, if specified if (options.endTime) { hr = timeTrigger.funcs.put_EndBoundary(timeTrigger.Deref(), GM.CreateVariable(options.endTime, { wide: true })); @@ -738,6 +844,7 @@ function addTask(options) } } + // Fetch the ActionCollection hr = task.funcs.get_Actions(task.Deref(), actionCollection); if (hr.Val != 0) { @@ -747,6 +854,7 @@ function addTask(options) } actionCollection.funcs = require('win-com').marshalFunctions(actionCollection.Deref(), ActionCollectionFunctions); + // Now we're going to create an ExecAction hr = actionCollection.funcs.Create(actionCollection.Deref(), TASK_ACTION_EXEC, taskAction); if (hr.Val != 0) { @@ -773,6 +881,7 @@ function addTask(options) throw ('ITaskService failed to put action path ' + hr.Val); } + // Set the exec arguments if specified if (options.arguments && Array.isArray(options.arguments)) { hr = execAction.funcs.put_Arguments(execAction.Deref(), GM.CreateVariable(options.arguments.join(' '), { wide: true })); @@ -783,6 +892,8 @@ function addTask(options) throw ('ITaskService failed to put action arguments ' + hr.Val); } } + + // Set the working path if specified if (options.workingDirectory) { hr = execAction.funcs.put_WorkingDirectory(execAction.Deref(), GM.CreateVariable(options.workingDirectory, { wide: true })); @@ -794,6 +905,7 @@ function addTask(options) } } + // Register the new task var vvar = GM.CreateVariable(GM.PointerSize == 8 ? 24 : 16); rootFolder.funcs.RegisterTaskDefinition._callType = 1 | CUSTOM_HANDLER; hr = rootFolder.funcs.RegisterTaskDefinition(