win32-taskscheduler 1.0.12 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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