win32-taskscheduler 1.0.12 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5b6d6ad467e4dd3518af4776d1937f9227afbc42bf743c3f3b09480141e08e15
4
- data.tar.gz: 2a3a9d694c22b5734e30c50fe50142af6b23e6d467c8e38e33ee48bba3a15952
3
+ metadata.gz: 90e586400ec6f09917fb684fb99037a5bd2c332995ecb72c43b37222ce9e2a61
4
+ data.tar.gz: 7a081a2f498d932400424d87885a89d2f58126366f6d51d6a361d97a215c18c6
5
5
  SHA512:
6
- metadata.gz: e5ca12f5424d5f790107a1af5ff8838e7931d846cdece3959627c0500a34ff2bcdae1fa749f253a89f4b523065ca27b15f1e629dc11ad586a990854cfbcb2a83
7
- data.tar.gz: c423125d42002ae09d4bc1fab148004a325e399cbdeb5bb48fb6210f1f3f5af510a4ecd9f0345fd8ec1e4291905c214d2e8d58eb261cd5b7e791d2b3c59c9c49
6
+ metadata.gz: 4fffce988d9274141c58c5cfce948ac86ff9b03396313f2721ce7bea08c6daf3a118be23418a2b56c7cfa58628db4e7e3943c0fe011b2b7da343007f80c9a7d0
7
+ data.tar.gz: 7a282afc6a6be4a3cefd6258ee33257221e2d397c327f07329b1972178165c64df0ef89b3c408c951db7375a85a5afea4a0ad63895f3a449036f18b94db13ae4
@@ -4,27 +4,33 @@ Note: this log contains only changes from win32-taskscheduler release 0.4.0 and
4
4
  -- it does not contain the changes from prior releases. To view change history
5
5
  prior to release 0.4.0, please visit the [source repository](https://github.com/chef/win32-taskscheduler/commits).
6
6
 
7
- <!-- latest_release 1.0.12 -->
8
- ## [win32-taskscheduler-1.0.12](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.12) (2018-10-11)
7
+ <!-- latest_release unreleased -->
8
+ ## Unreleased
9
9
 
10
10
  #### Merged Pull Requests
11
- - Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
11
+ - Bump version to 2.0 [#71](https://github.com/chef/win32-taskscheduler/pull/71) ([btm](https://github.com/btm))
12
12
  <!-- latest_release -->
13
13
 
14
- <!-- release_rollup since=1.0.10 -->
15
- ### Changes since 1.0.10 release
14
+ <!-- release_rollup since=1.0.12 -->
15
+ ### Changes since 1.0.12 release
16
16
 
17
17
  #### Merged Pull Requests
18
- - Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys)) <!-- 1.0.12 -->
19
- - Refactored configure_settings [#67](https://github.com/chef/win32-taskscheduler/pull/67) ([btm](https://github.com/btm)) <!-- 1.0.11 -->
18
+ - Bump version to 2.0 [#71](https://github.com/chef/win32-taskscheduler/pull/71) ([btm](https://github.com/btm)) <!-- 2.0.0 -->
19
+ - Move helpers under the Win32::TaskScheduler namespace [#70](https://github.com/chef/win32-taskscheduler/pull/70) ([btm](https://github.com/btm)) <!-- 1.0.13 -->
20
20
  <!-- release_rollup -->
21
21
 
22
22
  <!-- latest_stable_release -->
23
+ ## [win32-taskscheduler-1.0.12](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.12) (2018-10-11)
24
+
25
+ #### Merged Pull Requests
26
+ - Refactored configure_settings [#67](https://github.com/chef/win32-taskscheduler/pull/67) ([btm](https://github.com/btm))
27
+ - Fixing user registration at Non English version of windows [#69](https://github.com/chef/win32-taskscheduler/pull/69) ([Nimesh-Msys](https://github.com/Nimesh-Msys))
28
+ <!-- latest_stable_release -->
29
+
23
30
  ## [win32-taskscheduler-1.0.10](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.10) (2018-07-24)
24
31
 
25
32
  #### Merged Pull Requests
26
33
  - Fix exists? method breaking task full path search [#62](https://github.com/chef/win32-taskscheduler/pull/62) ([Vasu1105](https://github.com/Vasu1105))
27
- <!-- latest_stable_release -->
28
34
 
29
35
  ## [win32-taskscheduler-1.0.9](https://github.com/chef/win32-taskscheduler/tree/win32-taskscheduler-1.0.9) (2018-07-23)
30
36
 
data/VERSION CHANGED
@@ -1 +1,2 @@
1
- 1.0.12
1
+ 2.0.0
2
+
@@ -1,6 +1,7 @@
1
- require_relative 'windows/helper'
2
- require_relative 'windows/time_calc_helper'
3
- require_relative 'windows/constants'
1
+ require_relative 'taskscheduler/sid'
2
+ require_relative 'taskscheduler/helper'
3
+ require_relative 'taskscheduler/time_calc_helper'
4
+ require_relative 'taskscheduler/constants'
4
5
  require_relative 'taskscheduler/version'
5
6
  require 'win32ole'
6
7
  require 'socket'
@@ -12,9 +13,10 @@ module Win32
12
13
 
13
14
  # The TaskScheduler class encapsulates a Windows scheduled task
14
15
  class TaskScheduler
15
- include Windows::TaskSchedulerHelper
16
- include Windows::TimeCalcHelper
17
- include Windows::TaskSchedulerConstants
16
+ include Win32::TaskScheduler::Helper
17
+ include Win32::TaskScheduler::TaskSchedulerConstants
18
+ include Win32::TaskScheduler::TimeCalcHelper
19
+ include Win32::TaskScheduler::SID
18
20
 
19
21
  # The Error class is typically raised if any TaskScheduler methods fail.
20
22
  class Error < StandardError; end
@@ -0,0 +1,266 @@
1
+ require_relative 'sid'
2
+
3
+ module Win32
4
+ class TaskScheduler
5
+ module TaskSchedulerConstants
6
+ SERVICE_ACCOUNT_USERS = SID::SERVICE_ACCOUNT_USERS
7
+
8
+ BUILT_IN_GROUPS = SID::BUILT_IN_GROUPS
9
+
10
+ SYSTEM_USERS = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
11
+
12
+ # Triggers
13
+
14
+ # Trigger is set to run the task a single time
15
+ TASK_TIME_TRIGGER_ONCE = 1
16
+
17
+ # Trigger is set to run the task on a daily interval
18
+ TASK_TIME_TRIGGER_DAILY = 2
19
+
20
+ # Trigger is set to run the task on specific days of a specific week & month
21
+ TASK_TIME_TRIGGER_WEEKLY = 3
22
+
23
+ # Trigger is set to run the task on specific day(s) of the month
24
+ TASK_TIME_TRIGGER_MONTHLYDATE = 4
25
+
26
+ # Trigger is set to run the task on specific day(s) of the month
27
+ TASK_TIME_TRIGGER_MONTHLYDOW = 5
28
+
29
+ # Trigger is set to run the task if the system remains idle for the amount
30
+ # of time specified by the idle wait time of the task
31
+ TASK_EVENT_TRIGGER_ON_IDLE = 6
32
+
33
+ TASK_TRIGGER_REGISTRATION = 7
34
+
35
+ # Trigger is set to run the task at system startup
36
+ TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 8
37
+
38
+ # Trigger is set to run the task when a user logs on
39
+ TASK_EVENT_TRIGGER_AT_LOGON = 9
40
+
41
+ TASK_TRIGGER_SESSION_STATE_CHANGE = 11
42
+
43
+ # Daily Tasks
44
+
45
+ # The task will run on Sunday
46
+ TASK_SUNDAY = 0x1
47
+
48
+ # The task will run on Monday
49
+ TASK_MONDAY = 0x2
50
+
51
+ # The task will run on Tuesday
52
+ TASK_TUESDAY = 0x4
53
+
54
+ # The task will run on Wednesday
55
+ TASK_WEDNESDAY = 0x8
56
+
57
+ # The task will run on Thursday
58
+ TASK_THURSDAY = 0x10
59
+
60
+ # The task will run on Friday
61
+ TASK_FRIDAY = 0x20
62
+
63
+ # The task will run on Saturday
64
+ TASK_SATURDAY = 0x40
65
+
66
+ # Weekly tasks
67
+
68
+ # The task will run between the 1st and 7th day of the month
69
+ TASK_FIRST_WEEK = 0x01
70
+
71
+ # The task will run between the 8th and 14th day of the month
72
+ TASK_SECOND_WEEK = 0x02
73
+
74
+ # The task will run between the 15th and 21st day of the month
75
+ TASK_THIRD_WEEK = 0x04
76
+
77
+ # The task will run between the 22nd and 28th day of the month
78
+ TASK_FOURTH_WEEK = 0x08
79
+
80
+ # The task will run the last seven days of the month
81
+ TASK_LAST_WEEK = 0x10
82
+
83
+ # Monthly tasks
84
+
85
+ # The task will run in January
86
+ TASK_JANUARY = 0x1
87
+
88
+ # The task will run in February
89
+ TASK_FEBRUARY = 0x2
90
+
91
+ # The task will run in March
92
+ TASK_MARCH = 0x4
93
+
94
+ # The task will run in April
95
+ TASK_APRIL = 0x8
96
+
97
+ # The task will run in May
98
+ TASK_MAY = 0x10
99
+
100
+ # The task will run in June
101
+ TASK_JUNE = 0x20
102
+
103
+ # The task will run in July
104
+ TASK_JULY = 0x40
105
+
106
+ # The task will run in August
107
+ TASK_AUGUST = 0x80
108
+
109
+ # The task will run in September
110
+ TASK_SEPTEMBER = 0x100
111
+
112
+ # The task will run in October
113
+ TASK_OCTOBER = 0x200
114
+
115
+ # The task will run in November
116
+ TASK_NOVEMBER = 0x400
117
+
118
+ # The task will run in December
119
+ TASK_DECEMBER = 0x800
120
+
121
+ # Flags
122
+
123
+ # Used when converting AT service jobs into work items
124
+ TASK_FLAG_INTERACTIVE = 0x1
125
+
126
+ # The work item will be deleted when there are no more scheduled run times
127
+ TASK_FLAG_DELETE_WHEN_DONE = 0x2
128
+
129
+ # The work item is disabled. Useful for temporarily disabling a task
130
+ TASK_FLAG_DISABLED = 0x4
131
+
132
+ # The work item begins only if the computer is not in use at the scheduled
133
+ # start time
134
+ TASK_FLAG_START_ONLY_IF_IDLE = 0x10
135
+
136
+ # The work item terminates if the computer makes an idle to non-idle
137
+ # transition while the work item is running
138
+ TASK_FLAG_KILL_ON_IDLE_END = 0x20
139
+
140
+ # The work item does not start if the computer is running on battery power
141
+ TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
142
+
143
+ # The work item ends, and the associated application quits, if the computer
144
+ # switches to battery power
145
+ TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
146
+
147
+ # The work item starts only if the computer is in a docking station
148
+ TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
149
+
150
+ # The work item created will be hidden
151
+ TASK_FLAG_HIDDEN = 0x200
152
+
153
+ # The work item runs only if there is a valid internet connection
154
+ TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
155
+
156
+ # The work item starts again if the computer makes a non-idle to idle
157
+ # transition
158
+ TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
159
+
160
+ # The work item causes the system to be resumed, or awakened, if the
161
+ # system is running on batter power
162
+ TASK_FLAG_SYSTEM_REQUIRED = 0x1000
163
+
164
+ # The work item runs only if a specified account is logged on interactively
165
+ TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
166
+
167
+ # Triggers
168
+
169
+ # The task will stop at some point in time
170
+ TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
171
+
172
+ # The task can be stopped at the end of the repetition period
173
+ TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
174
+
175
+ # The task trigger is disabled
176
+ TASK_TRIGGER_FLAG_DISABLED = 0x4
177
+
178
+ # Run Level Types
179
+ # Tasks will be run with the least privileges
180
+ TASK_RUNLEVEL_LUA = 0
181
+ # Tasks will be run with the highest privileges
182
+ TASK_RUNLEVEL_HIGHEST = 1
183
+
184
+ # Logon Types
185
+ # Used for non-NT credentials
186
+ TASK_LOGON_NONE = 0
187
+ # Use a password for logging on the user
188
+ TASK_LOGON_PASSWORD = 1
189
+ # The service will log the user on using Service For User
190
+ TASK_LOGON_S4U = 2
191
+ # Task will be run only in an existing interactive session
192
+ TASK_LOGON_INTERACTIVE_TOKEN = 3
193
+ # Group activation. The groupId field specifies the group
194
+ TASK_LOGON_GROUP = 4
195
+ # When Local System, Local Service, or Network Service account is
196
+ # being used as a security context to run the task
197
+ TASK_LOGON_SERVICE_ACCOUNT = 5
198
+ # Not in use; currently identical to TASK_LOGON_PASSWORD
199
+ TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
200
+
201
+ TASK_MAX_RUN_TIMES = 1440
202
+ TASKS_TO_RETRIEVE = 5
203
+
204
+ # Task creation
205
+
206
+ TASK_VALIDATE_ONLY = 0x1
207
+ TASK_CREATE = 0x2
208
+ TASK_UPDATE = 0x4
209
+ TASK_CREATE_OR_UPDATE = 0x6
210
+ TASK_DISABLE = 0x8
211
+ TASK_DONT_ADD_PRINCIPAL_ACE = 0x10
212
+ TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20
213
+
214
+ # Priority classes
215
+
216
+ REALTIME_PRIORITY_CLASS = 0
217
+ HIGH_PRIORITY_CLASS = 1
218
+ ABOVE_NORMAL_PRIORITY_CLASS = 2 # Or 3
219
+ NORMAL_PRIORITY_CLASS = 4 # Or 5, 6
220
+ BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
221
+ IDLE_PRIORITY_CLASS = 9 # Or 10
222
+
223
+ CLSCTX_INPROC_SERVER = 0x1
224
+ CLSID_CTask = [0x148BD520, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
225
+ CLSID_CTaskScheduler = [0x148BD52A, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
226
+ IID_ITaskScheduler = [0x148BD527, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
227
+ IID_ITask = [0x148BD524, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
228
+ IID_IPersistFile = [0x0000010b, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSC8')
229
+
230
+ # Days of month
231
+
232
+ TASK_FIRST = 0x01
233
+ TASK_SECOND = 0x02
234
+ TASK_THIRD = 0x04
235
+ TASK_FOURTH = 0x08
236
+ TASK_FIFTH = 0x10
237
+ TASK_SIXTH = 0x20
238
+ TASK_SEVENTH = 0x40
239
+ TASK_EIGHTH = 0x80
240
+ TASK_NINETH = 0x100
241
+ TASK_TENTH = 0x200
242
+ TASK_ELEVENTH = 0x400
243
+ TASK_TWELFTH = 0x800
244
+ TASK_THIRTEENTH = 0x1000
245
+ TASK_FOURTEENTH = 0x2000
246
+ TASK_FIFTEENTH = 0x4000
247
+ TASK_SIXTEENTH = 0x8000
248
+ TASK_SEVENTEENTH = 0x10000
249
+ TASK_EIGHTEENTH = 0x20000
250
+ TASK_NINETEENTH = 0x40000
251
+ TASK_TWENTIETH = 0x80000
252
+ TASK_TWENTY_FIRST = 0x100000
253
+ TASK_TWENTY_SECOND = 0x200000
254
+ TASK_TWENTY_THIRD = 0x400000
255
+ TASK_TWENTY_FOURTH = 0x800000
256
+ TASK_TWENTY_FIFTH = 0x1000000
257
+ TASK_TWENTY_SIXTH = 0x2000000
258
+ TASK_TWENTY_SEVENTH = 0x4000000
259
+ TASK_TWENTY_EIGHTH = 0x8000000
260
+ TASK_TWENTY_NINTH = 0x10000000
261
+ TASK_THIRTYETH = 0x20000000
262
+ TASK_THIRTY_FIRST = 0x40000000
263
+ TASK_LAST = 0x80000000
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,51 @@
1
+ require 'ffi'
2
+
3
+ module Win32
4
+ class TaskScheduler
5
+ module Helper
6
+ extend FFI::Library
7
+
8
+ FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
9
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
10
+ FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
11
+
12
+ ffi_lib :kernel32, :advapi32
13
+
14
+ attach_function :FormatMessage, :FormatMessageA,
15
+ [:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
16
+
17
+ attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :bool
18
+ attach_function :LookupAccountSidW, [ :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
19
+ attach_function :LocalFree, [ :pointer ], :pointer
20
+
21
+ def win_error(function, err=FFI.errno)
22
+ err_msg = ''
23
+ flags = FORMAT_MESSAGE_IGNORE_INSERTS |
24
+ FORMAT_MESSAGE_FROM_SYSTEM |
25
+ FORMAT_MESSAGE_MAX_WIDTH_MASK
26
+
27
+ # 0x0409 == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
28
+ # We use English for errors because Ruby uses English for errors.
29
+
30
+ FFI::MemoryPointer.new(:char, 1024) do |buf|
31
+ len = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
32
+ err_msg = function + ': ' + buf.read_string(len).strip
33
+ end
34
+
35
+ err_msg
36
+ end
37
+
38
+ def ole_error(function, err)
39
+ regex = /OLE error code:(.*?)\sin/
40
+ match = regex.match(err.to_s)
41
+
42
+ if match
43
+ error = match.captures.first.hex
44
+ win_error(function, error)
45
+ else
46
+ "#{function}: #{err.to_s}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,132 @@
1
+ require_relative 'helper'
2
+
3
+ module FFI
4
+ class Pointer
5
+ def read_wstring(num_wchars = nil)
6
+ if num_wchars.nil?
7
+ # Find the length of the string
8
+ length = 0
9
+ last_char = nil
10
+ while last_char != "\000\000"
11
+ length += 1
12
+ last_char = get_bytes(0, length * 2)[-2..-1]
13
+ end
14
+
15
+ num_wchars = length
16
+ end
17
+
18
+ wide_to_utf8(get_bytes(0, num_wchars * 2))
19
+ end
20
+
21
+ def wide_to_utf8(wstring)
22
+ # ensure it is actually UTF-16LE
23
+ # Ruby likes to mark binary data as ASCII-8BIT
24
+ wstring = wstring.force_encoding('UTF-16LE')
25
+
26
+ # encode it all as UTF-8 and remove trailing CRLF and NULL characters
27
+ wstring.encode('UTF-8').strip
28
+ end
29
+ end
30
+ end
31
+
32
+ module Win32
33
+ class TaskScheduler
34
+ module SID
35
+ extend Win32::TaskScheduler::Helper
36
+ ERROR_INSUFFICIENT_BUFFER = 122
37
+
38
+ def self.LocalSystem
39
+ from_string_sid('S-1-5-18')
40
+ end
41
+
42
+ def self.NtLocal
43
+ from_string_sid('S-1-5-19')
44
+ end
45
+
46
+ def self.NtNetwork
47
+ from_string_sid('S-1-5-20')
48
+ end
49
+
50
+ def self.BuiltinAdministrators
51
+ from_string_sid('S-1-5-32-544')
52
+ end
53
+
54
+ def self.BuiltinUsers
55
+ from_string_sid('S-1-5-32-545')
56
+ end
57
+
58
+ def self.Guests
59
+ from_string_sid('S-1-5-32-546')
60
+ end
61
+
62
+ # Converts a string-format security identifier (SID) into a valid, functional SID
63
+ # and returns a hash with account_name and account_simple_name
64
+ # @see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsidtosidw
65
+ #
66
+ def self.from_string_sid(string_sid)
67
+ result = FFI::MemoryPointer.new :pointer
68
+ unless ConvertStringSidToSidW(utf8_to_wide(string_sid), result)
69
+ raise FFI::LastError.error
70
+ end
71
+ result_pointer = result.read_pointer
72
+ domain, name, use = account(result_pointer)
73
+ LocalFree(result_pointer)
74
+ account_names(domain, name, use)
75
+ end
76
+
77
+ def self.utf8_to_wide(ustring)
78
+ # ensure it is actually UTF-8
79
+ # Ruby likes to mark binary data as ASCII-8BIT
80
+ ustring = (ustring + '').force_encoding('UTF-8')
81
+
82
+ # ensure we have the double-null termination Windows Wide likes
83
+ ustring += "\000\000" if ustring.empty? || ustring[-1].chr != "\000"
84
+
85
+ # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
86
+ ustring.encode('UTF-16LE')
87
+ end
88
+
89
+ # Accepts a security identifier (SID) as input.
90
+ # It retrieves the name of the account for this SID and the name of the
91
+ # first domain on which this SID is found
92
+ # @see https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw
93
+ #
94
+ def self.account(sid)
95
+ sid = sid.pointer if sid.respond_to?(:pointer)
96
+ name_size = FFI::Buffer.new(:long).write_long(0)
97
+ referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0)
98
+
99
+ if LookupAccountSidW(nil, sid, nil, name_size, nil, referenced_domain_name_size, nil)
100
+ raise 'Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!'
101
+ elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
102
+ raise FFI::LastError.error
103
+ end
104
+
105
+ name = FFI::MemoryPointer.new :char, (name_size.read_long * 2)
106
+ referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long * 2)
107
+ use = FFI::Buffer.new(:long).write_long(0)
108
+ unless LookupAccountSidW(nil, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use)
109
+ raise FFI::LastError.error
110
+ end
111
+ [referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long]
112
+ end
113
+
114
+ # Formats domain, name and returns a hash with
115
+ # account_name and account_simple_name
116
+ #
117
+ def self.account_names(domain, name, _use)
118
+ account_name = !domain.to_s.empty? ? "#{domain}\\#{name}" : name
119
+ account_simple_name = name
120
+ { account_name: account_name, account_simple_name: account_simple_name }
121
+ end
122
+
123
+ SERVICE_ACCOUNT_USERS = [self.LocalSystem, self.NtLocal, self.NtNetwork].map do |user|
124
+ [user[:account_simple_name].upcase, user[:account_name].upcase]
125
+ end.flatten.freeze
126
+
127
+ BUILT_IN_GROUPS = [self.BuiltinAdministrators, self.BuiltinUsers, self.Guests].map do |user|
128
+ [user[:account_simple_name].upcase, user[:account_name].upcase]
129
+ end.flatten.freeze
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,132 @@
1
+ module Win32
2
+ class TaskScheduler
3
+ module TimeCalcHelper
4
+
5
+ # Returns actual no of days for given month;
6
+ # Array with a 0 is defined to give actual result without
7
+ # any manipulation. eg, DAYS_IN_A_MONTH[1] = 31
8
+ # 0(NUMBER) is kept to avoid exceptions during calculations
9
+ DAYS_IN_A_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
10
+
11
+ # Returns no of days in a given month of a year
12
+ def days_in_month(month, year)
13
+ (month == 2 && is_leap_year?(year)) ? 29 : DAYS_IN_A_MONTH[month]
14
+ end
15
+
16
+ # Year is leap when it is a multiple of 4 and not a multiple of 100.
17
+ # But it can be a multiple of 400
18
+ def is_leap_year?(year)
19
+ (((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?)
20
+ end
21
+
22
+ # Returns total time duration in minutes
23
+ def time_in_minutes(time_str)
24
+ time_in_seconds(time_str) / 60
25
+ end
26
+
27
+ # Calculates total time duration in seconds
28
+ def time_in_seconds(time_str)
29
+ dt_tm_hash = time_details(time_str)
30
+ curr_time = Time.now
31
+
32
+ # Basic time variables
33
+ future_year = curr_time.year + dt_tm_hash[:year].to_i
34
+ future_month = curr_time.month + dt_tm_hash[:month].to_i
35
+ future_day = curr_time.day + dt_tm_hash[:day].to_i
36
+ future_hr = curr_time.hour + dt_tm_hash[:hour].to_i
37
+ future_min = curr_time.min + dt_tm_hash[:min].to_i
38
+ future_sec = curr_time.sec + dt_tm_hash[:sec].to_i
39
+
40
+ # 'extra value' calculations for these time variables
41
+ future_sec, future_min = extra_time(future_sec, future_min, 60)
42
+ future_min, future_hr = extra_time(future_min, future_hr, 60)
43
+ future_hr, future_day = extra_time(future_hr, future_day, 24)
44
+
45
+ # explicit method to calculate overloaded days;
46
+ # They may stretch upto years; heance leap year & months are into consideration
47
+ future_day, future_month, future_year = extra_days(future_day, future_month, future_year, curr_time.month, curr_time.year)
48
+
49
+ future_month, future_year = extra_months(future_month, future_year, curr_time.month, curr_time.year)
50
+
51
+ future_time = Time.new(future_year, future_month, future_day, future_hr, future_min, future_sec)
52
+
53
+ # Difference in time will return seconds
54
+ future_time.to_i - curr_time.to_i
55
+ end
56
+
57
+ # a will contain extra value of low_rank (in high_rank(eg min));
58
+ # b will hold actual low_rank value(ie sec) Example:
59
+ # low_rank = 65, high_rank = 2, div_val = 60
60
+ # Hence a = 1; b = 5
61
+ def extra_time(low_rank, high_rank, div_val)
62
+ a, b = low_rank.divmod(div_val)
63
+ high_rank += a; low_rank = b
64
+ [low_rank, high_rank]
65
+ end
66
+
67
+ def extra_months(month_count, year_count, init_month, init_year)
68
+ year, month_count = month_count.divmod(12)
69
+ if year.positive? && month_count.zero?
70
+ month_count = 12
71
+ year -= 1
72
+ end
73
+ year_count += year
74
+ [month_count, year_count]
75
+ end
76
+
77
+ # Returns no of actual days with all overloaded months & Years
78
+ def extra_days(days_count, month_count, year_count, init_month, init_year)
79
+ # Will keep increamenting them with surplus days
80
+ days = days_count
81
+ mth = init_month
82
+ yr = init_year
83
+
84
+ loop do
85
+ days -= days_in_month(mth, yr)
86
+ break if days <= 0
87
+ mth += 1
88
+ if mth > 12
89
+ mth = 1; yr += 1
90
+ end
91
+ days_count = days
92
+ end
93
+
94
+ # Setting actual incremented values
95
+ month_count += (mth - init_month)
96
+ year_count += (yr - init_year)
97
+
98
+ [days_count, month_count, year_count]
99
+ end
100
+
101
+ # Extracts "P_Y_M_DT_H_M_S" format and
102
+ # Returns a hash with applicable values of
103
+ # (keys =>) [:year, :month, :day, :hour, :min, :sec]
104
+ # Example: "PT3S" => {sec: 3}
105
+ def time_details(time_str)
106
+ tm_detail = {}
107
+ if time_str.to_s != ''
108
+ # time_str will be like "PxxYxxMxxDTxxHxxMxxS"
109
+ # Ignoring 'P' and extracting date and time
110
+ dt, tm = time_str[1..-1].split('T')
111
+
112
+ # Replacing strings
113
+ if dt.to_s != ''
114
+ dt['Y'] = 'year' if dt['Y']; dt['M'] = 'month' if dt['M']; dt['D'] = 'day' if dt['D']
115
+ dt_tm_array_to_hash(dt, tm_detail)
116
+ end
117
+
118
+ if tm.to_s != ''
119
+ tm['H'] = 'hour' if tm['H']; tm['M'] = 'min' if tm['M']; tm['S'] = 'sec' if tm['S']
120
+ dt_tm_array_to_hash(tm, tm_detail)
121
+ end
122
+ end
123
+ tm_detail
124
+ end
125
+
126
+ # Method to convert date/time array to hash
127
+ def dt_tm_array_to_hash(arr, tm_detail)
128
+ arr.split(/(\d+)/)[1..-1].each_slice(2).inject(tm_detail) { |h, i| h[i.last.to_sym] = i.first; h }
129
+ end
130
+ end
131
+ end
132
+ end
@@ -1,6 +1,6 @@
1
1
  module Win32
2
2
  class TaskScheduler
3
3
  # The version of the win32-taskscheduler library
4
- VERSION = '1.0.12'.freeze
4
+ VERSION = '2.0.0'.freeze
5
5
  end
6
6
  end
@@ -1,11 +1,11 @@
1
- require 'win32/windows/time_calc_helper'
1
+ require 'win32/taskscheduler/time_calc_helper'
2
2
  require 'spec_helper'
3
3
 
4
- RSpec.describe Windows::TimeCalcHelper do
4
+ RSpec.describe Win32::TaskScheduler::TimeCalcHelper do
5
5
  let(:object) { klass.new }
6
6
  let(:klass) do
7
7
  Class.new do
8
- include Windows::TimeCalcHelper
8
+ include Win32::TaskScheduler::TimeCalcHelper
9
9
  end
10
10
  end
11
11
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'win32/taskscheduler'
3
- require 'win32/windows/constants'
3
+ require 'win32/taskscheduler/constants'
4
4
 
5
5
  RSpec.describe Win32::TaskScheduler, :windows_only do
6
6
  describe 'Ensuring trigger constants' do
@@ -1,11 +1,11 @@
1
1
  require 'spec_helper'
2
- require 'win32/windows/time_calc_helper'
2
+ require 'win32/taskscheduler/time_calc_helper'
3
3
 
4
- RSpec.describe Windows::TimeCalcHelper do
4
+ RSpec.describe Win32::TaskScheduler::TimeCalcHelper do
5
5
  let(:object) { klass.new }
6
6
  let(:klass) do
7
7
  Class.new do
8
- include Windows::TimeCalcHelper
8
+ include Win32::TaskScheduler::TimeCalcHelper
9
9
  end
10
10
  end
11
11
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: win32-taskscheduler
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.12
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Park Heesob
@@ -117,17 +117,17 @@ files:
117
117
  - examples/taskscheduler_example.rb
118
118
  - lib/win32-taskscheduler.rb
119
119
  - lib/win32/taskscheduler.rb
120
+ - lib/win32/taskscheduler/constants.rb
121
+ - lib/win32/taskscheduler/helper.rb
122
+ - lib/win32/taskscheduler/sid.rb
123
+ - lib/win32/taskscheduler/time_calc_helper.rb
120
124
  - lib/win32/taskscheduler/version.rb
121
- - lib/win32/windows/constants.rb
122
- - lib/win32/windows/helper.rb
123
- - lib/win32/windows/sid.rb
124
- - lib/win32/windows/time_calc_helper.rb
125
- - spec/functional/win32/taskschedular_spec.rb
126
- - spec/functional/win32/windows/time_calc_helper_spec.rb
125
+ - spec/functional/win32/taskscheduler/time_calc_helper_spec.rb
126
+ - spec/functional/win32/taskscheduler_spec.rb
127
127
  - spec/spec_helper.rb
128
- - spec/unit/win32/taskschedular_spec.rb
129
- - spec/unit/win32/windows/constants_spec.rb
130
- - spec/unit/win32/windows/time_calc_helper_spec.rb
128
+ - spec/unit/win32/taskscheduler/constants_spec.rb
129
+ - spec/unit/win32/taskscheduler/time_calc_helper_spec.rb
130
+ - spec/unit/win32/taskscheduler_spec.rb
131
131
  - test/test_taskscheduler.rb
132
132
  - win32-taskscheduler.gemspec
133
133
  homepage: http://github.com/chef/win32-taskscheduler
@@ -1,263 +0,0 @@
1
- require_relative 'sid'
2
- module Windows
3
- module TaskSchedulerConstants
4
- SERVICE_ACCOUNT_USERS = SID::SERVICE_ACCOUNT_USERS
5
-
6
- BUILT_IN_GROUPS = SID::BUILT_IN_GROUPS
7
-
8
- SYSTEM_USERS = SERVICE_ACCOUNT_USERS + BUILT_IN_GROUPS
9
-
10
- # Triggers
11
-
12
- # Trigger is set to run the task a single time
13
- TASK_TIME_TRIGGER_ONCE = 1
14
-
15
- # Trigger is set to run the task on a daily interval
16
- TASK_TIME_TRIGGER_DAILY = 2
17
-
18
- # Trigger is set to run the task on specific days of a specific week & month
19
- TASK_TIME_TRIGGER_WEEKLY = 3
20
-
21
- # Trigger is set to run the task on specific day(s) of the month
22
- TASK_TIME_TRIGGER_MONTHLYDATE = 4
23
-
24
- # Trigger is set to run the task on specific day(s) of the month
25
- TASK_TIME_TRIGGER_MONTHLYDOW = 5
26
-
27
- # Trigger is set to run the task if the system remains idle for the amount
28
- # of time specified by the idle wait time of the task
29
- TASK_EVENT_TRIGGER_ON_IDLE = 6
30
-
31
- TASK_TRIGGER_REGISTRATION = 7
32
-
33
- # Trigger is set to run the task at system startup
34
- TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 8
35
-
36
- # Trigger is set to run the task when a user logs on
37
- TASK_EVENT_TRIGGER_AT_LOGON = 9
38
-
39
- TASK_TRIGGER_SESSION_STATE_CHANGE = 11
40
-
41
- # Daily Tasks
42
-
43
- # The task will run on Sunday
44
- TASK_SUNDAY = 0x1
45
-
46
- # The task will run on Monday
47
- TASK_MONDAY = 0x2
48
-
49
- # The task will run on Tuesday
50
- TASK_TUESDAY = 0x4
51
-
52
- # The task will run on Wednesday
53
- TASK_WEDNESDAY = 0x8
54
-
55
- # The task will run on Thursday
56
- TASK_THURSDAY = 0x10
57
-
58
- # The task will run on Friday
59
- TASK_FRIDAY = 0x20
60
-
61
- # The task will run on Saturday
62
- TASK_SATURDAY = 0x40
63
-
64
- # Weekly tasks
65
-
66
- # The task will run between the 1st and 7th day of the month
67
- TASK_FIRST_WEEK = 0x01
68
-
69
- # The task will run between the 8th and 14th day of the month
70
- TASK_SECOND_WEEK = 0x02
71
-
72
- # The task will run between the 15th and 21st day of the month
73
- TASK_THIRD_WEEK = 0x04
74
-
75
- # The task will run between the 22nd and 28th day of the month
76
- TASK_FOURTH_WEEK = 0x08
77
-
78
- # The task will run the last seven days of the month
79
- TASK_LAST_WEEK = 0x10
80
-
81
- # Monthly tasks
82
-
83
- # The task will run in January
84
- TASK_JANUARY = 0x1
85
-
86
- # The task will run in February
87
- TASK_FEBRUARY = 0x2
88
-
89
- # The task will run in March
90
- TASK_MARCH = 0x4
91
-
92
- # The task will run in April
93
- TASK_APRIL = 0x8
94
-
95
- # The task will run in May
96
- TASK_MAY = 0x10
97
-
98
- # The task will run in June
99
- TASK_JUNE = 0x20
100
-
101
- # The task will run in July
102
- TASK_JULY = 0x40
103
-
104
- # The task will run in August
105
- TASK_AUGUST = 0x80
106
-
107
- # The task will run in September
108
- TASK_SEPTEMBER = 0x100
109
-
110
- # The task will run in October
111
- TASK_OCTOBER = 0x200
112
-
113
- # The task will run in November
114
- TASK_NOVEMBER = 0x400
115
-
116
- # The task will run in December
117
- TASK_DECEMBER = 0x800
118
-
119
- # Flags
120
-
121
- # Used when converting AT service jobs into work items
122
- TASK_FLAG_INTERACTIVE = 0x1
123
-
124
- # The work item will be deleted when there are no more scheduled run times
125
- TASK_FLAG_DELETE_WHEN_DONE = 0x2
126
-
127
- # The work item is disabled. Useful for temporarily disabling a task
128
- TASK_FLAG_DISABLED = 0x4
129
-
130
- # The work item begins only if the computer is not in use at the scheduled
131
- # start time
132
- TASK_FLAG_START_ONLY_IF_IDLE = 0x10
133
-
134
- # The work item terminates if the computer makes an idle to non-idle
135
- # transition while the work item is running
136
- TASK_FLAG_KILL_ON_IDLE_END = 0x20
137
-
138
- # The work item does not start if the computer is running on battery power
139
- TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
140
-
141
- # The work item ends, and the associated application quits, if the computer
142
- # switches to battery power
143
- TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
144
-
145
- # The work item starts only if the computer is in a docking station
146
- TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
147
-
148
- # The work item created will be hidden
149
- TASK_FLAG_HIDDEN = 0x200
150
-
151
- # The work item runs only if there is a valid internet connection
152
- TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
153
-
154
- # The work item starts again if the computer makes a non-idle to idle
155
- # transition
156
- TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
157
-
158
- # The work item causes the system to be resumed, or awakened, if the
159
- # system is running on batter power
160
- TASK_FLAG_SYSTEM_REQUIRED = 0x1000
161
-
162
- # The work item runs only if a specified account is logged on interactively
163
- TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
164
-
165
- # Triggers
166
-
167
- # The task will stop at some point in time
168
- TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
169
-
170
- # The task can be stopped at the end of the repetition period
171
- TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
172
-
173
- # The task trigger is disabled
174
- TASK_TRIGGER_FLAG_DISABLED = 0x4
175
-
176
- # Run Level Types
177
- # Tasks will be run with the least privileges
178
- TASK_RUNLEVEL_LUA = 0
179
- # Tasks will be run with the highest privileges
180
- TASK_RUNLEVEL_HIGHEST = 1
181
-
182
- # Logon Types
183
- # Used for non-NT credentials
184
- TASK_LOGON_NONE = 0
185
- # Use a password for logging on the user
186
- TASK_LOGON_PASSWORD = 1
187
- # The service will log the user on using Service For User
188
- TASK_LOGON_S4U = 2
189
- # Task will be run only in an existing interactive session
190
- TASK_LOGON_INTERACTIVE_TOKEN = 3
191
- # Group activation. The groupId field specifies the group
192
- TASK_LOGON_GROUP = 4
193
- # When Local System, Local Service, or Network Service account is
194
- # being used as a security context to run the task
195
- TASK_LOGON_SERVICE_ACCOUNT = 5
196
- # Not in use; currently identical to TASK_LOGON_PASSWORD
197
- TASK_LOGON_INTERACTIVE_TOKEN_OR_PASSWORD = 6
198
-
199
- TASK_MAX_RUN_TIMES = 1440
200
- TASKS_TO_RETRIEVE = 5
201
-
202
- # Task creation
203
-
204
- TASK_VALIDATE_ONLY = 0x1
205
- TASK_CREATE = 0x2
206
- TASK_UPDATE = 0x4
207
- TASK_CREATE_OR_UPDATE = 0x6
208
- TASK_DISABLE = 0x8
209
- TASK_DONT_ADD_PRINCIPAL_ACE = 0x10
210
- TASK_IGNORE_REGISTRATION_TRIGGERS = 0x20
211
-
212
- # Priority classes
213
-
214
- REALTIME_PRIORITY_CLASS = 0
215
- HIGH_PRIORITY_CLASS = 1
216
- ABOVE_NORMAL_PRIORITY_CLASS = 2 # Or 3
217
- NORMAL_PRIORITY_CLASS = 4 # Or 5, 6
218
- BELOW_NORMAL_PRIORITY_CLASS = 7 # Or 8
219
- IDLE_PRIORITY_CLASS = 9 # Or 10
220
-
221
- CLSCTX_INPROC_SERVER = 0x1
222
- CLSID_CTask = [0x148BD520, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
223
- CLSID_CTaskScheduler = [0x148BD52A, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
224
- IID_ITaskScheduler = [0x148BD527, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
225
- IID_ITask = [0x148BD524, 0xA2AB, 0x11CE, 0xB1, 0x1F, 0x00, 0xAA, 0x00, 0x53, 0x05, 0x03].pack('LSSC8')
226
- IID_IPersistFile = [0x0000010b, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46].pack('LSSC8')
227
-
228
- # Days of month
229
-
230
- TASK_FIRST = 0x01
231
- TASK_SECOND = 0x02
232
- TASK_THIRD = 0x04
233
- TASK_FOURTH = 0x08
234
- TASK_FIFTH = 0x10
235
- TASK_SIXTH = 0x20
236
- TASK_SEVENTH = 0x40
237
- TASK_EIGHTH = 0x80
238
- TASK_NINETH = 0x100
239
- TASK_TENTH = 0x200
240
- TASK_ELEVENTH = 0x400
241
- TASK_TWELFTH = 0x800
242
- TASK_THIRTEENTH = 0x1000
243
- TASK_FOURTEENTH = 0x2000
244
- TASK_FIFTEENTH = 0x4000
245
- TASK_SIXTEENTH = 0x8000
246
- TASK_SEVENTEENTH = 0x10000
247
- TASK_EIGHTEENTH = 0x20000
248
- TASK_NINETEENTH = 0x40000
249
- TASK_TWENTIETH = 0x80000
250
- TASK_TWENTY_FIRST = 0x100000
251
- TASK_TWENTY_SECOND = 0x200000
252
- TASK_TWENTY_THIRD = 0x400000
253
- TASK_TWENTY_FOURTH = 0x800000
254
- TASK_TWENTY_FIFTH = 0x1000000
255
- TASK_TWENTY_SIXTH = 0x2000000
256
- TASK_TWENTY_SEVENTH = 0x4000000
257
- TASK_TWENTY_EIGHTH = 0x8000000
258
- TASK_TWENTY_NINTH = 0x10000000
259
- TASK_THIRTYETH = 0x20000000
260
- TASK_THIRTY_FIRST = 0x40000000
261
- TASK_LAST = 0x80000000
262
- end
263
- end
@@ -1,49 +0,0 @@
1
- require 'ffi'
2
-
3
- module Windows
4
- module TaskSchedulerHelper
5
- extend FFI::Library
6
-
7
- FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
8
- FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
9
- FORMAT_MESSAGE_MAX_WIDTH_MASK = 0x000000FF
10
-
11
- ffi_lib :kernel32, :advapi32
12
-
13
- attach_function :FormatMessage, :FormatMessageA,
14
- [:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
15
-
16
- attach_function :ConvertStringSidToSidW, [ :pointer, :pointer ], :bool
17
- attach_function :LookupAccountSidW, [ :pointer, :pointer, :pointer, :pointer, :pointer, :pointer, :pointer ], :bool
18
- attach_function :LocalFree, [ :pointer ], :pointer
19
-
20
- def win_error(function, err=FFI.errno)
21
- err_msg = ''
22
- flags = FORMAT_MESSAGE_IGNORE_INSERTS |
23
- FORMAT_MESSAGE_FROM_SYSTEM |
24
- FORMAT_MESSAGE_MAX_WIDTH_MASK
25
-
26
- # 0x0409 == MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
27
- # We use English for errors because Ruby uses English for errors.
28
-
29
- FFI::MemoryPointer.new(:char, 1024) do |buf|
30
- len = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
31
- err_msg = function + ': ' + buf.read_string(len).strip
32
- end
33
-
34
- err_msg
35
- end
36
-
37
- def ole_error(function, err)
38
- regex = /OLE error code:(.*?)\sin/
39
- match = regex.match(err.to_s)
40
-
41
- if match
42
- error = match.captures.first.hex
43
- win_error(function, error)
44
- else
45
- "#{function}: #{err.to_s}"
46
- end
47
- end
48
- end
49
- end
@@ -1,128 +0,0 @@
1
- require_relative 'helper'
2
-
3
- module FFI
4
- class Pointer
5
- def read_wstring(num_wchars = nil)
6
- if num_wchars.nil?
7
- # Find the length of the string
8
- length = 0
9
- last_char = nil
10
- while last_char != "\000\000"
11
- length += 1
12
- last_char = get_bytes(0, length * 2)[-2..-1]
13
- end
14
-
15
- num_wchars = length
16
- end
17
-
18
- wide_to_utf8(get_bytes(0, num_wchars * 2))
19
- end
20
-
21
- def wide_to_utf8(wstring)
22
- # ensure it is actually UTF-16LE
23
- # Ruby likes to mark binary data as ASCII-8BIT
24
- wstring = wstring.force_encoding('UTF-16LE')
25
-
26
- # encode it all as UTF-8 and remove trailing CRLF and NULL characters
27
- wstring.encode('UTF-8').strip
28
- end
29
- end
30
- end
31
-
32
- class SID
33
- extend Windows::TaskSchedulerHelper
34
- ERROR_INSUFFICIENT_BUFFER = 122
35
-
36
- def self.LocalSystem
37
- from_string_sid('S-1-5-18')
38
- end
39
-
40
- def self.NtLocal
41
- from_string_sid('S-1-5-19')
42
- end
43
-
44
- def self.NtNetwork
45
- from_string_sid('S-1-5-20')
46
- end
47
-
48
- def self.BuiltinAdministrators
49
- from_string_sid('S-1-5-32-544')
50
- end
51
-
52
- def self.BuiltinUsers
53
- from_string_sid('S-1-5-32-545')
54
- end
55
-
56
- def self.Guests
57
- from_string_sid('S-1-5-32-546')
58
- end
59
-
60
- # Converts a string-format security identifier (SID) into a valid, functional SID
61
- # and returns a hash with account_name and account_simple_name
62
- # @see https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsidtosidw
63
- #
64
- def self.from_string_sid(string_sid)
65
- result = FFI::MemoryPointer.new :pointer
66
- unless ConvertStringSidToSidW(utf8_to_wide(string_sid), result)
67
- raise FFI::LastError.error
68
- end
69
- result_pointer = result.read_pointer
70
- domain, name, use = account(result_pointer)
71
- LocalFree(result_pointer)
72
- account_names(domain, name, use)
73
- end
74
-
75
- def self.utf8_to_wide(ustring)
76
- # ensure it is actually UTF-8
77
- # Ruby likes to mark binary data as ASCII-8BIT
78
- ustring = (ustring + '').force_encoding('UTF-8')
79
-
80
- # ensure we have the double-null termination Windows Wide likes
81
- ustring += "\000\000" if ustring.empty? || ustring[-1].chr != "\000"
82
-
83
- # encode it all as UTF-16LE AKA Windows Wide Character AKA Windows Unicode
84
- ustring.encode('UTF-16LE')
85
- end
86
-
87
- # Accepts a security identifier (SID) as input.
88
- # It retrieves the name of the account for this SID and the name of the
89
- # first domain on which this SID is found
90
- # @see https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-lookupaccountsidw
91
- #
92
- def self.account(sid)
93
- sid = sid.pointer if sid.respond_to?(:pointer)
94
- name_size = FFI::Buffer.new(:long).write_long(0)
95
- referenced_domain_name_size = FFI::Buffer.new(:long).write_long(0)
96
-
97
- if LookupAccountSidW(nil, sid, nil, name_size, nil, referenced_domain_name_size, nil)
98
- raise 'Expected ERROR_INSUFFICIENT_BUFFER from LookupAccountSid, and got no error!'
99
- elsif FFI::LastError.error != ERROR_INSUFFICIENT_BUFFER
100
- raise FFI::LastError.error
101
- end
102
-
103
- name = FFI::MemoryPointer.new :char, (name_size.read_long * 2)
104
- referenced_domain_name = FFI::MemoryPointer.new :char, (referenced_domain_name_size.read_long * 2)
105
- use = FFI::Buffer.new(:long).write_long(0)
106
- unless LookupAccountSidW(nil, sid, name, name_size, referenced_domain_name, referenced_domain_name_size, use)
107
- raise FFI::LastError.error
108
- end
109
- [referenced_domain_name.read_wstring(referenced_domain_name_size.read_long), name.read_wstring(name_size.read_long), use.read_long]
110
- end
111
-
112
- # Formats domain, name and returns a hash with
113
- # account_name and account_simple_name
114
- #
115
- def self.account_names(domain, name, _use)
116
- account_name = !domain.to_s.empty? ? "#{domain}\\#{name}" : name
117
- account_simple_name = name
118
- { account_name: account_name, account_simple_name: account_simple_name }
119
- end
120
-
121
- SERVICE_ACCOUNT_USERS = [self.LocalSystem, self.NtLocal, self.NtNetwork].map do |user|
122
- [user[:account_simple_name].upcase, user[:account_name].upcase]
123
- end.flatten.freeze
124
-
125
- BUILT_IN_GROUPS = [self.BuiltinAdministrators, self.BuiltinUsers, self.Guests].map do |user|
126
- [user[:account_simple_name].upcase, user[:account_name].upcase]
127
- end.flatten.freeze
128
- end
@@ -1,130 +0,0 @@
1
- module Windows
2
- module TimeCalcHelper
3
-
4
- # Returns actual no of days for given month;
5
- # Array with a 0 is defined to give actual result without
6
- # any manipulation. eg, DAYS_IN_A_MONTH[1] = 31
7
- # 0(NUMBER) is kept to avoid exceptions during calculations
8
- DAYS_IN_A_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
9
-
10
- # Returns no of days in a given month of a year
11
- def days_in_month(month, year)
12
- (month == 2 && is_leap_year?(year)) ? 29 : DAYS_IN_A_MONTH[month]
13
- end
14
-
15
- # Year is leap when it is a multiple of 4 and not a multiple of 100.
16
- # But it can be a multiple of 400
17
- def is_leap_year?(year)
18
- (((year % 4).zero? && !(year % 100).zero?) || (year % 400).zero?)
19
- end
20
-
21
- # Returns total time duration in minutes
22
- def time_in_minutes(time_str)
23
- time_in_seconds(time_str) / 60
24
- end
25
-
26
- # Calculates total time duration in seconds
27
- def time_in_seconds(time_str)
28
- dt_tm_hash = time_details(time_str)
29
- curr_time = Time.now
30
-
31
- # Basic time variables
32
- future_year = curr_time.year + dt_tm_hash[:year].to_i
33
- future_month = curr_time.month + dt_tm_hash[:month].to_i
34
- future_day = curr_time.day + dt_tm_hash[:day].to_i
35
- future_hr = curr_time.hour + dt_tm_hash[:hour].to_i
36
- future_min = curr_time.min + dt_tm_hash[:min].to_i
37
- future_sec = curr_time.sec + dt_tm_hash[:sec].to_i
38
-
39
- # 'extra value' calculations for these time variables
40
- future_sec, future_min = extra_time(future_sec, future_min, 60)
41
- future_min, future_hr = extra_time(future_min, future_hr, 60)
42
- future_hr, future_day = extra_time(future_hr, future_day, 24)
43
-
44
- # explicit method to calculate overloaded days;
45
- # They may stretch upto years; heance leap year & months are into consideration
46
- future_day, future_month, future_year = extra_days(future_day, future_month, future_year, curr_time.month, curr_time.year)
47
-
48
- future_month, future_year = extra_months(future_month, future_year, curr_time.month, curr_time.year)
49
-
50
- future_time = Time.new(future_year, future_month, future_day, future_hr, future_min, future_sec)
51
-
52
- # Difference in time will return seconds
53
- future_time.to_i - curr_time.to_i
54
- end
55
-
56
- # a will contain extra value of low_rank (in high_rank(eg min));
57
- # b will hold actual low_rank value(ie sec) Example:
58
- # low_rank = 65, high_rank = 2, div_val = 60
59
- # Hence a = 1; b = 5
60
- def extra_time(low_rank, high_rank, div_val)
61
- a, b = low_rank.divmod(div_val)
62
- high_rank += a; low_rank = b
63
- [low_rank, high_rank]
64
- end
65
-
66
- def extra_months(month_count, year_count, init_month, init_year)
67
- year, month_count = month_count.divmod(12)
68
- if year.positive? && month_count.zero?
69
- month_count = 12
70
- year -= 1
71
- end
72
- year_count += year
73
- [month_count, year_count]
74
- end
75
-
76
- # Returns no of actual days with all overloaded months & Years
77
- def extra_days(days_count, month_count, year_count, init_month, init_year)
78
- # Will keep increamenting them with surplus days
79
- days = days_count
80
- mth = init_month
81
- yr = init_year
82
-
83
- loop do
84
- days -= days_in_month(mth, yr)
85
- break if days <= 0
86
- mth += 1
87
- if mth > 12
88
- mth = 1; yr += 1
89
- end
90
- days_count = days
91
- end
92
-
93
- # Setting actual incremented values
94
- month_count += (mth - init_month)
95
- year_count += (yr - init_year)
96
-
97
- [days_count, month_count, year_count]
98
- end
99
-
100
- # Extracts "P_Y_M_DT_H_M_S" format and
101
- # Returns a hash with applicable values of
102
- # (keys =>) [:year, :month, :day, :hour, :min, :sec]
103
- # Example: "PT3S" => {sec: 3}
104
- def time_details(time_str)
105
- tm_detail = {}
106
- if time_str.to_s != ''
107
- # time_str will be like "PxxYxxMxxDTxxHxxMxxS"
108
- # Ignoring 'P' and extracting date and time
109
- dt, tm = time_str[1..-1].split('T')
110
-
111
- # Replacing strings
112
- if dt.to_s != ''
113
- dt['Y'] = 'year' if dt['Y']; dt['M'] = 'month' if dt['M']; dt['D'] = 'day' if dt['D']
114
- dt_tm_array_to_hash(dt, tm_detail)
115
- end
116
-
117
- if tm.to_s != ''
118
- tm['H'] = 'hour' if tm['H']; tm['M'] = 'min' if tm['M']; tm['S'] = 'sec' if tm['S']
119
- dt_tm_array_to_hash(tm, tm_detail)
120
- end
121
- end
122
- tm_detail
123
- end
124
-
125
- # Method to convert date/time array to hash
126
- def dt_tm_array_to_hash(arr, tm_detail)
127
- arr.split(/(\d+)/)[1..-1].each_slice(2).inject(tm_detail) { |h, i| h[i.last.to_sym] = i.first; h }
128
- end
129
- end
130
- end