winevt_c 0.8.0 → 0.9.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,425 @@
1
+ #include <winevt_c.h>
2
+
3
+ /* clang-format off */
4
+ /*
5
+ * Document-class: Winevt::EventLog::Session
6
+ *
7
+ * Manage Session information for Windows EventLog.
8
+ *
9
+ * @example
10
+ * require 'winevt'
11
+ *
12
+ * @session = Winevt::EventLog::Session.new("127.0.0.1")
13
+ *
14
+ * @session.domain = "<EXAMPLEGROUP>"
15
+ * @session.username = "<username>"
16
+ * @session.password = "<password>"
17
+ * # Then pass @session veriable into Winevt::EventLog::Query or
18
+ * # Winevt::EventLog::Subscribe#subscribe
19
+ * @query = Winevt::EventLog::Query.new(
20
+ * "Application",
21
+ * "*[System[(Level <= 3) and TimeCreated[timediff(@SystemTime) <= 86400000]]]",
22
+ * @session
23
+ * )
24
+ * # some stuff.
25
+ *
26
+ * @subscribe = Winevt::EventLog::Subscribe.new
27
+ * @subscribe.subscribe(
28
+ * "Application",
29
+ * "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]",
30
+ * @session
31
+ * )
32
+ * # And some stuff.
33
+ * @since v0.9.0
34
+ */
35
+ /* clang-format on */
36
+
37
+ VALUE rb_cSession;
38
+ VALUE rb_cRpcLoginFlag;
39
+
40
+ static void session_free(void* ptr);
41
+
42
+ static const rb_data_type_t rb_winevt_session_type = { "winevt/session",
43
+ {
44
+ 0,
45
+ session_free,
46
+ 0,
47
+ },
48
+ NULL,
49
+ NULL,
50
+ RUBY_TYPED_FREE_IMMEDIATELY };
51
+
52
+ static void
53
+ session_free(void* ptr)
54
+ {
55
+ struct WinevtSession* winevtSession = (struct WinevtSession*)ptr;
56
+
57
+ if (winevtSession->server)
58
+ free(winevtSession->server);
59
+ if (winevtSession->domain)
60
+ free(winevtSession->domain);
61
+ if (winevtSession->username)
62
+ free(winevtSession->username);
63
+ if (winevtSession->password)
64
+ free(winevtSession->password);
65
+
66
+ xfree(ptr);
67
+ }
68
+
69
+ static VALUE
70
+ rb_winevt_session_alloc(VALUE klass)
71
+ {
72
+ VALUE obj;
73
+ struct WinevtSession* winevtSession;
74
+ obj = TypedData_Make_Struct(
75
+ klass, struct WinevtSession, &rb_winevt_session_type, winevtSession);
76
+ return obj;
77
+ }
78
+
79
+ /*
80
+ * Initalize Session class.
81
+ *
82
+ * @overload initialize(server, domain=nil, username=nil, password=nil, flags=Winevt::EventLog::Session::RpcLoginFlag::AuthDefault)
83
+ * @param server [String] Server ip address or fqdn.
84
+ * @param domain [String] Domain name.
85
+ * @param username [String] username on remote server.
86
+ * @param password [String] Remote server user password.
87
+ * @param flags [Integer] Flags for authentication method choices.
88
+ * @return [Session]
89
+ *
90
+ */
91
+
92
+ static VALUE
93
+ rb_winevt_session_initialize(VALUE self)
94
+ {
95
+ struct WinevtSession* winevtSession;
96
+
97
+ TypedData_Get_Struct(
98
+ self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
99
+
100
+ winevtSession->server = NULL;
101
+ winevtSession->domain = NULL;
102
+ winevtSession->username = NULL;
103
+ winevtSession->password = NULL;
104
+ winevtSession->flags = EvtRpcLoginAuthDefault;
105
+
106
+ return Qnil;
107
+ }
108
+
109
+ /*
110
+ * This method returns server for remoting access.
111
+ *
112
+ * @return [String]
113
+ */
114
+ static VALUE
115
+ rb_winevt_session_get_server(VALUE self)
116
+ {
117
+ struct WinevtSession* winevtSession;
118
+
119
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
120
+
121
+ if (winevtSession->server) {
122
+ return wstr_to_rb_str(CP_UTF8, winevtSession->server, -1);
123
+ } else {
124
+ return rb_str_new2("(NULL)");
125
+ }
126
+ }
127
+
128
+ /*
129
+ * This method specifies server for remoting access.
130
+ *
131
+ * @param rb_server [String] server
132
+ */
133
+ static VALUE
134
+ rb_winevt_session_set_server(VALUE self, VALUE rb_server)
135
+ {
136
+ struct WinevtSession* winevtSession;
137
+ DWORD len;
138
+ VALUE vserverBuf;
139
+ PWSTR wServer;
140
+
141
+ Check_Type(rb_server, T_STRING);
142
+
143
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
144
+
145
+ len =
146
+ MultiByteToWideChar(CP_UTF8, 0,
147
+ RSTRING_PTR(rb_server), RSTRING_LEN(rb_server),
148
+ NULL, 0);
149
+ wServer = ALLOCV_N(WCHAR, vserverBuf, len + 1);
150
+ MultiByteToWideChar(CP_UTF8, 0,
151
+ RSTRING_PTR(rb_server), RSTRING_LEN(rb_server),
152
+ wServer, len);
153
+ winevtSession->server = _wcsdup(wServer);
154
+ wServer[len] = L'\0';
155
+
156
+ ALLOCV_END(vserverBuf);
157
+
158
+ return Qnil;
159
+ }
160
+
161
+ /*
162
+ * This method returns domain for remoting access.
163
+ *
164
+ * @return [String]
165
+ */
166
+ static VALUE
167
+ rb_winevt_session_get_domain(VALUE self)
168
+ {
169
+ struct WinevtSession* winevtSession;
170
+
171
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
172
+
173
+ if (winevtSession->domain) {
174
+ return wstr_to_rb_str(CP_UTF8, winevtSession->domain, -1);
175
+ } else {
176
+ return rb_str_new2("(NULL)");
177
+ }
178
+ }
179
+
180
+ /*
181
+ * This method specifies domain for remoting access.
182
+ *
183
+ * @param rb_domain [String] domain
184
+ */
185
+ static VALUE
186
+ rb_winevt_session_set_domain(VALUE self, VALUE rb_domain)
187
+ {
188
+ struct WinevtSession* winevtSession;
189
+ DWORD len;
190
+ VALUE vdomainBuf;
191
+ PWSTR wDomain;
192
+
193
+ Check_Type(rb_domain, T_STRING);
194
+
195
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
196
+
197
+ len =
198
+ MultiByteToWideChar(CP_UTF8, 0,
199
+ RSTRING_PTR(rb_domain), RSTRING_LEN(rb_domain),
200
+ NULL, 0);
201
+ wDomain = ALLOCV_N(WCHAR, vdomainBuf, len + 1);
202
+ MultiByteToWideChar(CP_UTF8, 0,
203
+ RSTRING_PTR(rb_domain), RSTRING_LEN(rb_domain),
204
+ wDomain, len);
205
+ wDomain[len] = L'\0';
206
+
207
+ winevtSession->domain = _wcsdup(wDomain);
208
+
209
+ ALLOCV_END(vdomainBuf);
210
+
211
+ return Qnil;
212
+ }
213
+
214
+ /*
215
+ * This method returns username for remoting access.
216
+ *
217
+ * @return [String]
218
+ */
219
+ static VALUE
220
+ rb_winevt_session_get_username(VALUE self)
221
+ {
222
+ struct WinevtSession* winevtSession;
223
+
224
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
225
+
226
+ if (winevtSession->username) {
227
+ return wstr_to_rb_str(CP_UTF8, winevtSession->username, -1);
228
+ } else {
229
+ return rb_str_new2("(NULL)");
230
+ }
231
+ }
232
+
233
+ /*
234
+ * This method specifies username for remoting access.
235
+ *
236
+ * @param rb_username [String] username
237
+ */
238
+ static VALUE
239
+ rb_winevt_session_set_username(VALUE self, VALUE rb_username)
240
+ {
241
+ struct WinevtSession* winevtSession;
242
+ DWORD len;
243
+ VALUE vusernameBuf;
244
+ PWSTR wUsername;
245
+
246
+ Check_Type(rb_username, T_STRING);
247
+
248
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
249
+
250
+ len =
251
+ MultiByteToWideChar(CP_UTF8, 0,
252
+ RSTRING_PTR(rb_username), RSTRING_LEN(rb_username),
253
+ NULL, 0);
254
+ wUsername = ALLOCV_N(WCHAR, vusernameBuf, len + 1);
255
+ MultiByteToWideChar(CP_UTF8, 0,
256
+ RSTRING_PTR(rb_username), RSTRING_LEN(rb_username),
257
+ wUsername, len);
258
+ wUsername[len] = L'\0';
259
+
260
+ winevtSession->username = _wcsdup(wUsername);
261
+
262
+ ALLOCV_END(vusernameBuf);
263
+
264
+ return Qnil;
265
+ }
266
+
267
+ /*
268
+ * This method returns password for remoting access.
269
+ *
270
+ * @return [String]
271
+ */
272
+ static VALUE
273
+ rb_winevt_session_get_password(VALUE self)
274
+ {
275
+ struct WinevtSession* winevtSession;
276
+
277
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
278
+
279
+ if (winevtSession->password) {
280
+ return wstr_to_rb_str(CP_UTF8, winevtSession->password, -1);
281
+ } else {
282
+ return rb_str_new2("(NULL)");
283
+ }
284
+ }
285
+
286
+ /*
287
+ * This method specifies password for remoting access.
288
+ *
289
+ * @param rb_password [String] password
290
+ */
291
+ static VALUE
292
+ rb_winevt_session_set_password(VALUE self, VALUE rb_password)
293
+ {
294
+ struct WinevtSession* winevtSession;
295
+ DWORD len;
296
+ VALUE vpasswordBuf;
297
+ PWSTR wPassword;
298
+
299
+ Check_Type(rb_password, T_STRING);
300
+
301
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
302
+
303
+ len =
304
+ MultiByteToWideChar(CP_UTF8, 0,
305
+ RSTRING_PTR(rb_password), RSTRING_LEN(rb_password),
306
+ NULL, 0);
307
+ wPassword = ALLOCV_N(WCHAR, vpasswordBuf, len + 1);
308
+ MultiByteToWideChar(CP_UTF8, 0,
309
+ RSTRING_PTR(rb_password), RSTRING_LEN(rb_password),
310
+ wPassword, len);
311
+ wPassword[len] = L'\0';
312
+
313
+ winevtSession->password = _wcsdup(wPassword);
314
+
315
+ ALLOCV_END(vpasswordBuf);
316
+
317
+ return Qnil;
318
+ }
319
+
320
+ /*
321
+ * This method returns flags for remoting access.
322
+ *
323
+ * @return [Integer]
324
+ */
325
+ static VALUE
326
+ rb_winevt_session_get_flags(VALUE self)
327
+ {
328
+ struct WinevtSession* winevtSession;
329
+
330
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
331
+
332
+ return LONG2NUM(winevtSession->flags);
333
+ }
334
+
335
+ static DWORD
336
+ get_session_rpc_login_flag_from_cstr(char* flag_str)
337
+ {
338
+ if (strcmp(flag_str, "default") == 0)
339
+ return EvtRpcLoginAuthDefault;
340
+ else if (strcmp(flag_str, "negociate") == 0)
341
+ return EvtRpcLoginAuthNegotiate;
342
+ else if (strcmp(flag_str, "kerberos") == 0)
343
+ return EvtRpcLoginAuthKerberos;
344
+ else if (strcmp(flag_str, "ntlm") == 0)
345
+ return EvtRpcLoginAuthNTLM;
346
+ else
347
+ rb_raise(rb_eArgError, "Unknown rpc login flag: %s", flag_str);
348
+
349
+ return 0;
350
+ }
351
+
352
+
353
+ /*
354
+ * This method specifies flags for remoting access.
355
+ *
356
+ * @param rb_flags [Integer] flags
357
+ */
358
+ static VALUE
359
+ rb_winevt_session_set_flags(VALUE self, VALUE rb_flags)
360
+ {
361
+ struct WinevtSession* winevtSession;
362
+ EVT_RPC_LOGIN_FLAGS flags = EvtRpcLoginAuthDefault;
363
+
364
+ TypedData_Get_Struct(self, struct WinevtSession, &rb_winevt_session_type, winevtSession);
365
+
366
+ switch(TYPE(rb_flags)) {
367
+ case T_SYMBOL:
368
+ flags = get_session_rpc_login_flag_from_cstr(RSTRING_PTR(rb_sym2str(rb_flags)));
369
+ break;
370
+ case T_STRING:
371
+ flags = get_session_rpc_login_flag_from_cstr(StringValuePtr(rb_flags));
372
+ break;
373
+ case T_FIXNUM:
374
+ flags = NUM2LONG(rb_flags);
375
+ break;
376
+ default:
377
+ rb_raise(rb_eArgError, "Expected Symbol, String or Fixnum in flags");
378
+ }
379
+ winevtSession->flags = flags;
380
+
381
+ return Qnil;
382
+ }
383
+
384
+ void
385
+ Init_winevt_session(VALUE rb_cEventLog)
386
+ {
387
+ rb_cSession = rb_define_class_under(rb_cEventLog, "Session", rb_cObject);
388
+
389
+ rb_define_alloc_func(rb_cSession, rb_winevt_session_alloc);
390
+
391
+ rb_cRpcLoginFlag = rb_define_module_under(rb_cSession, "RpcLoginFlag");
392
+
393
+ rb_define_method(rb_cSession, "initialize", rb_winevt_session_initialize, 0);
394
+ rb_define_method(rb_cSession, "server", rb_winevt_session_get_server, 0);
395
+ rb_define_method(rb_cSession, "server=", rb_winevt_session_set_server, 1);
396
+ rb_define_method(rb_cSession, "domain", rb_winevt_session_get_domain, 0);
397
+ rb_define_method(rb_cSession, "domain=", rb_winevt_session_set_domain, 1);
398
+ rb_define_method(rb_cSession, "username", rb_winevt_session_get_username, 0);
399
+ rb_define_method(rb_cSession, "username=", rb_winevt_session_set_username, 1);
400
+ rb_define_method(rb_cSession, "password", rb_winevt_session_get_password, 0);
401
+ rb_define_method(rb_cSession, "password=", rb_winevt_session_set_password, 1);
402
+ rb_define_method(rb_cSession, "flags", rb_winevt_session_get_flags, 0);
403
+ rb_define_method(rb_cSession, "flags=", rb_winevt_session_set_flags, 1);
404
+
405
+ /*
406
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthDefault
407
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
408
+ */
409
+ rb_define_const(rb_cRpcLoginFlag, "AuthDefault", LONG2NUM(EvtRpcLoginAuthDefault));
410
+ /*
411
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthNegociate
412
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
413
+ */
414
+ rb_define_const(rb_cRpcLoginFlag, "AuthNegociate", LONG2NUM(EvtRpcLoginAuthNegotiate));
415
+ /*
416
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthKerberos
417
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
418
+ */
419
+ rb_define_const(rb_cRpcLoginFlag, "AuthKerberos", LONG2NUM(EvtRpcLoginAuthKerberos));
420
+ /*
421
+ * EVT_RPC_LOGIN_FLAGS enumeration: EvtRpcLoginAuthNTLM
422
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/ne-winevt-evt_rpc_login_flags
423
+ */
424
+ rb_define_const(rb_cRpcLoginFlag, "AuthNTLM", LONG2NUM(EvtRpcLoginAuthNTLM));
425
+ }
@@ -1,652 +1,756 @@
1
- #include <winevt_c.h>
2
-
3
- /* clang-format off */
4
- /*
5
- * Document-class: Winevt::EventLog::Subscribe
6
- *
7
- * Subscribe Windows EventLog channel.
8
- *
9
- * @example
10
- * require 'winevt'
11
- *
12
- * @subscribe = Winevt::EventLog::Subscribe.new
13
- * @subscribe.tail = true
14
- * @subscribe.rate_limit = 80
15
- * @subscribe.subscribe(
16
- * "Application", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]"
17
- * )
18
- * while true do
19
- * @subscribe.each do |eventlog, message, string_inserts|
20
- * puts ({eventlog: eventlog, data: message})
21
- * end
22
- * sleep(0.1)
23
- * end
24
- *
25
- * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtsubscribe
26
- */
27
- /* clang-format on */
28
-
29
- static void subscribe_free(void* ptr);
30
-
31
- static const rb_data_type_t rb_winevt_subscribe_type = { "winevt/subscribe",
32
- {
33
- 0,
34
- subscribe_free,
35
- 0,
36
- },
37
- NULL,
38
- NULL,
39
- RUBY_TYPED_FREE_IMMEDIATELY };
40
-
41
- static void
42
- subscribe_free(void* ptr)
43
- {
44
- struct WinevtSubscribe* winevtSubscribe = (struct WinevtSubscribe*)ptr;
45
- if (winevtSubscribe->signalEvent)
46
- CloseHandle(winevtSubscribe->signalEvent);
47
-
48
- if (winevtSubscribe->subscription)
49
- EvtClose(winevtSubscribe->subscription);
50
-
51
- if (winevtSubscribe->bookmark)
52
- EvtClose(winevtSubscribe->bookmark);
53
-
54
- for (int i = 0; i < winevtSubscribe->count; i++) {
55
- if (winevtSubscribe->hEvents[i]) {
56
- EvtClose(winevtSubscribe->hEvents[i]);
57
- }
58
- }
59
-
60
- xfree(ptr);
61
- }
62
-
63
- static VALUE
64
- rb_winevt_subscribe_alloc(VALUE klass)
65
- {
66
- VALUE obj;
67
- struct WinevtSubscribe* winevtSubscribe;
68
- obj = TypedData_Make_Struct(
69
- klass, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
70
- return obj;
71
- }
72
-
73
- /*
74
- * Initalize Subscribe class.
75
- *
76
- * @return [Subscribe]
77
- *
78
- */
79
- static VALUE
80
- rb_winevt_subscribe_initialize(VALUE self)
81
- {
82
- struct WinevtSubscribe* winevtSubscribe;
83
-
84
- TypedData_Get_Struct(
85
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
86
-
87
- winevtSubscribe->rateLimit = SUBSCRIBE_RATE_INFINITE;
88
- winevtSubscribe->lastTime = 0;
89
- winevtSubscribe->currentRate = 0;
90
- winevtSubscribe->renderAsXML = TRUE;
91
- winevtSubscribe->readExistingEvents = TRUE;
92
- winevtSubscribe->preserveQualifiers = FALSE;
93
- winevtSubscribe->localeInfo = &default_locale;
94
-
95
- return Qnil;
96
- }
97
-
98
- /*
99
- * This method specifies whether read existing events or not.
100
- *
101
- * @param rb_read_existing_events_p [Boolean]
102
- */
103
- static VALUE
104
- rb_winevt_subscribe_set_read_existing_events(VALUE self, VALUE rb_read_existing_events_p)
105
- {
106
- struct WinevtSubscribe* winevtSubscribe;
107
-
108
- TypedData_Get_Struct(
109
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
110
-
111
- winevtSubscribe->readExistingEvents = RTEST(rb_read_existing_events_p);
112
-
113
- return Qnil;
114
- }
115
-
116
- /*
117
- * This method returns whether read existing events or not.
118
- *
119
- * @return [Boolean]
120
- */
121
- static VALUE
122
- rb_winevt_subscribe_read_existing_events_p(VALUE self)
123
- {
124
- struct WinevtSubscribe* winevtSubscribe;
125
-
126
- TypedData_Get_Struct(
127
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
128
-
129
- return winevtSubscribe->readExistingEvents ? Qtrue : Qfalse;
130
- }
131
-
132
- /*
133
- * Subscribe into a Windows EventLog channel.
134
- *
135
- * @overload subscribe(path, query, options={})
136
- * @param path [String] Subscribe Channel
137
- * @param query [String] Query string for channel
138
- * @option options [Bookmark] bookmark Bookmark class instance.
139
- * @return [Boolean]
140
- *
141
- */
142
- static VALUE
143
- rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
144
- {
145
- VALUE rb_path, rb_query, rb_bookmark;
146
- EVT_HANDLE hSubscription = NULL, hBookmark = NULL;
147
- HANDLE hSignalEvent;
148
- DWORD len, flags = 0L;
149
- VALUE wpathBuf, wqueryBuf, wBookmarkBuf;
150
- PWSTR path, query, bookmarkXml;
151
- DWORD status = ERROR_SUCCESS;
152
- struct WinevtSubscribe* winevtSubscribe;
153
-
154
- hSignalEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
155
-
156
- TypedData_Get_Struct(
157
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
158
-
159
- rb_scan_args(argc, argv, "21", &rb_path, &rb_query, &rb_bookmark);
160
- Check_Type(rb_path, T_STRING);
161
- Check_Type(rb_query, T_STRING);
162
-
163
- if (rb_obj_is_kind_of(rb_bookmark, rb_cString)) {
164
- // bookmarkXml : To wide char
165
- len = MultiByteToWideChar(
166
- CP_UTF8, 0, RSTRING_PTR(rb_bookmark), RSTRING_LEN(rb_bookmark), NULL, 0);
167
- bookmarkXml = ALLOCV_N(WCHAR, wBookmarkBuf, len + 1);
168
- MultiByteToWideChar(CP_UTF8,
169
- 0,
170
- RSTRING_PTR(rb_bookmark),
171
- RSTRING_LEN(rb_bookmark),
172
- bookmarkXml,
173
- len);
174
- bookmarkXml[len] = L'\0';
175
- hBookmark = EvtCreateBookmark(bookmarkXml);
176
- ALLOCV_END(wBookmarkBuf);
177
- if (hBookmark == NULL) {
178
- status = GetLastError();
179
- raise_system_error(rb_eWinevtQueryError, status);
180
- }
181
- }
182
-
183
- // path : To wide char
184
- len =
185
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), NULL, 0);
186
- path = ALLOCV_N(WCHAR, wpathBuf, len + 1);
187
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), path, len);
188
- path[len] = L'\0';
189
-
190
- // query : To wide char
191
- len = MultiByteToWideChar(
192
- CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), NULL, 0);
193
- query = ALLOCV_N(WCHAR, wqueryBuf, len + 1);
194
- MultiByteToWideChar(
195
- CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), query, len);
196
- query[len] = L'\0';
197
-
198
- if (hBookmark) {
199
- flags |= EvtSubscribeStartAfterBookmark;
200
- } else if (winevtSubscribe->readExistingEvents) {
201
- flags |= EvtSubscribeStartAtOldestRecord;
202
- } else {
203
- flags |= EvtSubscribeToFutureEvents;
204
- }
205
-
206
- hSubscription =
207
- EvtSubscribe(NULL, hSignalEvent, path, query, hBookmark, NULL, NULL, flags);
208
- if (!hSubscription) {
209
- if (hBookmark != NULL) {
210
- EvtClose(hBookmark);
211
- }
212
- if (hSignalEvent != NULL) {
213
- CloseHandle(hSignalEvent);
214
- }
215
- status = GetLastError();
216
- raise_system_error(rb_eWinevtQueryError, status);
217
- }
218
-
219
- if (winevtSubscribe->subscription != NULL) {
220
- // should be disgarded the old event subscription handle.
221
- EvtClose(winevtSubscribe->subscription);
222
- }
223
-
224
- ALLOCV_END(wpathBuf);
225
- ALLOCV_END(wqueryBuf);
226
-
227
- winevtSubscribe->signalEvent = hSignalEvent;
228
- winevtSubscribe->subscription = hSubscription;
229
- if (hBookmark) {
230
- winevtSubscribe->bookmark = hBookmark;
231
- } else {
232
- winevtSubscribe->bookmark = EvtCreateBookmark(NULL);
233
- if (winevtSubscribe->bookmark == NULL) {
234
- if (hSubscription != NULL) {
235
- EvtClose(hSubscription);
236
- }
237
- if (hSignalEvent != NULL) {
238
- CloseHandle(hSignalEvent);
239
- }
240
- status = GetLastError();
241
- raise_system_error(rb_eWinevtQueryError, status);
242
- }
243
- }
244
-
245
- return Qtrue;
246
- }
247
-
248
- BOOL
249
- is_rate_limit_exceeded(struct WinevtSubscribe* winevtSubscribe)
250
- {
251
- time_t now;
252
-
253
- if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
254
- return FALSE;
255
-
256
- time(&now);
257
-
258
- if (now <= winevtSubscribe->lastTime) {
259
- if (winevtSubscribe->currentRate >= winevtSubscribe->rateLimit) {
260
- return TRUE;
261
- }
262
- } else {
263
- winevtSubscribe->currentRate = 0;
264
- }
265
-
266
- return FALSE;
267
- }
268
-
269
- void
270
- update_to_reflect_rate_limit_state(struct WinevtSubscribe* winevtSubscribe, ULONG count)
271
- {
272
- time_t lastTime = 0;
273
-
274
- if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
275
- return;
276
-
277
- time(&lastTime);
278
- winevtSubscribe->lastTime = lastTime;
279
- winevtSubscribe->currentRate += count;
280
- }
281
-
282
- /*
283
- * Handle the next values. Since v0.6.0, this method is used for
284
- * testing only. Please use #each instead.
285
- *
286
- * @return [Boolean]
287
- *
288
- * @see each
289
- */
290
-
291
- static VALUE
292
- rb_winevt_subscribe_next(VALUE self)
293
- {
294
- EVT_HANDLE hEvents[SUBSCRIBE_ARRAY_SIZE];
295
- ULONG count = 0;
296
- DWORD status = ERROR_SUCCESS;
297
- struct WinevtSubscribe* winevtSubscribe;
298
-
299
- TypedData_Get_Struct(
300
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
301
-
302
- if (is_rate_limit_exceeded(winevtSubscribe)) {
303
- return Qfalse;
304
- }
305
-
306
- if (!EvtNext(winevtSubscribe->subscription,
307
- SUBSCRIBE_ARRAY_SIZE,
308
- hEvents,
309
- INFINITE,
310
- 0,
311
- &count)) {
312
- status = GetLastError();
313
- if (ERROR_NO_MORE_ITEMS != status) {
314
- return Qfalse;
315
- }
316
- }
317
-
318
- if (status == ERROR_SUCCESS) {
319
- winevtSubscribe->count = count;
320
- for (int i = 0; i < count; i++) {
321
- winevtSubscribe->hEvents[i] = hEvents[i];
322
- EvtUpdateBookmark(winevtSubscribe->bookmark, winevtSubscribe->hEvents[i]);
323
- }
324
-
325
- update_to_reflect_rate_limit_state(winevtSubscribe, count);
326
-
327
- return Qtrue;
328
- }
329
-
330
- return Qfalse;
331
- }
332
-
333
- static VALUE
334
- rb_winevt_subscribe_render(VALUE self, EVT_HANDLE event)
335
- {
336
- struct WinevtSubscribe* winevtSubscribe;
337
-
338
- TypedData_Get_Struct(
339
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
340
-
341
- if (winevtSubscribe->renderAsXML) {
342
- return render_to_rb_str(event, EvtRenderEventXml);
343
- } else {
344
- return render_system_event(event, winevtSubscribe->preserveQualifiers);
345
- }
346
- }
347
-
348
- static VALUE
349
- rb_winevt_subscribe_message(EVT_HANDLE event, LocaleInfo* localeInfo)
350
- {
351
- WCHAR* wResult;
352
- VALUE utf8str;
353
-
354
- wResult = get_description(event, localeInfo->langID);
355
- utf8str = wstr_to_rb_str(CP_UTF8, wResult, -1);
356
- free(wResult);
357
-
358
- return utf8str;
359
- }
360
-
361
- static VALUE
362
- rb_winevt_subscribe_string_inserts(EVT_HANDLE event)
363
- {
364
- return get_values(event);
365
- }
366
-
367
- static VALUE
368
- rb_winevt_subscribe_close_handle(VALUE self)
369
- {
370
- struct WinevtSubscribe* winevtSubscribe;
371
-
372
- TypedData_Get_Struct(
373
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
374
-
375
- for (int i = 0; i < winevtSubscribe->count; i++) {
376
- if (winevtSubscribe->hEvents[i] != NULL) {
377
- EvtClose(winevtSubscribe->hEvents[i]);
378
- winevtSubscribe->hEvents[i] = NULL;
379
- }
380
- }
381
-
382
- return Qnil;
383
- }
384
-
385
- static VALUE
386
- rb_winevt_subscribe_each_yield(VALUE self)
387
- {
388
- RETURN_ENUMERATOR(self, 0, 0);
389
- struct WinevtSubscribe* winevtSubscribe;
390
-
391
- TypedData_Get_Struct(
392
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
393
-
394
- for (int i = 0; i < winevtSubscribe->count; i++) {
395
- rb_yield_values(3,
396
- rb_winevt_subscribe_render(self, winevtSubscribe->hEvents[i]),
397
- rb_winevt_subscribe_message(winevtSubscribe->hEvents[i], winevtSubscribe->localeInfo),
398
- rb_winevt_subscribe_string_inserts(winevtSubscribe->hEvents[i]));
399
- }
400
-
401
- return Qnil;
402
- }
403
-
404
- /*
405
- * Enumerate to obtain Windows EventLog contents.
406
- *
407
- * This method yields the following:
408
- * (Stringified EventLog, Stringified detail message, Stringified
409
- * insert values)
410
- *
411
- * @yield (String,String,String)
412
- *
413
- */
414
- static VALUE
415
- rb_winevt_subscribe_each(VALUE self)
416
- {
417
- RETURN_ENUMERATOR(self, 0, 0);
418
-
419
- while (rb_winevt_subscribe_next(self)) {
420
- rb_ensure(
421
- rb_winevt_subscribe_each_yield, self, rb_winevt_subscribe_close_handle, self);
422
- }
423
-
424
- return Qnil;
425
- }
426
-
427
- /*
428
- * This method renders bookmark content which is related to Subscribe class instance.
429
- *
430
- * @return [String]
431
- */
432
- static VALUE
433
- rb_winevt_subscribe_get_bookmark(VALUE self)
434
- {
435
- struct WinevtSubscribe* winevtSubscribe;
436
-
437
- TypedData_Get_Struct(
438
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
439
-
440
- return render_to_rb_str(winevtSubscribe->bookmark, EvtRenderBookmark);
441
- }
442
-
443
- /*
444
- * This method returns rate limit value.
445
- *
446
- * @since 0.6.0
447
- * @return [Integer]
448
- */
449
- static VALUE
450
- rb_winevt_subscribe_get_rate_limit(VALUE self)
451
- {
452
- struct WinevtSubscribe* winevtSubscribe;
453
-
454
- TypedData_Get_Struct(
455
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
456
-
457
- return INT2NUM(winevtSubscribe->rateLimit);
458
- }
459
-
460
- /*
461
- * This method specifies rate limit value.
462
- *
463
- * @since 0.6.0
464
- * @param rb_rate_limit [Integer] rate_limit value
465
- */
466
- static VALUE
467
- rb_winevt_subscribe_set_rate_limit(VALUE self, VALUE rb_rate_limit)
468
- {
469
- struct WinevtSubscribe* winevtSubscribe;
470
- DWORD rateLimit;
471
-
472
- TypedData_Get_Struct(
473
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
474
-
475
- rateLimit = NUM2LONG(rb_rate_limit);
476
-
477
- if ((rateLimit != SUBSCRIBE_RATE_INFINITE) && (rateLimit < 10 || rateLimit % 10)) {
478
- rb_raise(rb_eArgError, "Specify a multiples of 10 or RATE_INFINITE constant");
479
- } else {
480
- winevtSubscribe->rateLimit = rateLimit;
481
- }
482
-
483
- return Qnil;
484
- }
485
-
486
- /*
487
- * This method returns whether render as xml or not.
488
- *
489
- * @since 0.6.0
490
- * @return [Boolean]
491
- */
492
- static VALUE
493
- rb_winevt_subscribe_render_as_xml_p(VALUE self)
494
- {
495
- struct WinevtSubscribe* winevtSubscribe;
496
-
497
- TypedData_Get_Struct(
498
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
499
-
500
- return winevtSubscribe->renderAsXML ? Qtrue : Qfalse;
501
- }
502
-
503
- /*
504
- * This method specifies whether render as xml or not.
505
- *
506
- * @since 0.6.0
507
- * @param rb_render_as_xml [Boolean]
508
- */
509
- static VALUE
510
- rb_winevt_subscribe_set_render_as_xml(VALUE self, VALUE rb_render_as_xml)
511
- {
512
- struct WinevtSubscribe* winevtSubscribe;
513
-
514
- TypedData_Get_Struct(
515
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
516
-
517
- winevtSubscribe->renderAsXML = RTEST(rb_render_as_xml);
518
-
519
- return Qnil;
520
- }
521
-
522
- /*
523
- * This method specifies whether preserving qualifiers key or not.
524
- *
525
- * @since 0.7.3
526
- * @param rb_render_as_xml [Boolean]
527
- */
528
- static VALUE
529
- rb_winevt_subscribe_set_preserve_qualifiers(VALUE self, VALUE rb_preserve_qualifiers)
530
- {
531
- struct WinevtSubscribe* winevtSubscribe;
532
-
533
- TypedData_Get_Struct(
534
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
535
-
536
- winevtSubscribe->preserveQualifiers = RTEST(rb_preserve_qualifiers);
537
-
538
- return Qnil;
539
- }
540
-
541
- /*
542
- * This method returns whether preserving qualifiers or not.
543
- *
544
- * @since 0.7.3
545
- * @return [Integer]
546
- */
547
- static VALUE
548
- rb_winevt_subscribe_get_preserve_qualifiers_p(VALUE self)
549
- {
550
- struct WinevtSubscribe* winevtSubscribe;
551
-
552
- TypedData_Get_Struct(
553
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
554
-
555
- return winevtSubscribe->preserveQualifiers ? Qtrue : Qfalse;
556
- }
557
-
558
- /*
559
- * This method specifies locale with [String].
560
- *
561
- * @since 0.8.0
562
- * @param rb_locale_str [String]
563
- */
564
- static VALUE
565
- rb_winevt_subscribe_set_locale(VALUE self, VALUE rb_locale_str)
566
- {
567
- struct WinevtSubscribe* winevtSubscribe;
568
- LocaleInfo* locale_info = &default_locale;
569
-
570
- TypedData_Get_Struct(
571
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
572
-
573
- locale_info = get_locale_from_rb_str(rb_locale_str);
574
-
575
- winevtSubscribe->localeInfo = locale_info;
576
-
577
- return Qnil;
578
- }
579
-
580
- /*
581
- * This method obtains specified locale with [String].
582
- *
583
- * @since 0.8.0
584
- */
585
- static VALUE
586
- rb_winevt_subscribe_get_locale(VALUE self)
587
- {
588
- struct WinevtSubscribe* winevtSubscribe;
589
-
590
- TypedData_Get_Struct(
591
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
592
-
593
- if (winevtSubscribe->localeInfo->langCode) {
594
- return rb_str_new2(winevtSubscribe->localeInfo->langCode);
595
- } else {
596
- return rb_str_new2(default_locale.langCode);
597
- }
598
- }
599
-
600
- void
601
- Init_winevt_subscribe(VALUE rb_cEventLog)
602
- {
603
- rb_cSubscribe = rb_define_class_under(rb_cEventLog, "Subscribe", rb_cObject);
604
-
605
- rb_define_alloc_func(rb_cSubscribe, rb_winevt_subscribe_alloc);
606
-
607
- /*
608
- * For Subscribe#rate_limit=. It represents unspecified rate limit.
609
- * @since 0.6.0
610
- */
611
- rb_define_const(rb_cSubscribe, "RATE_INFINITE", SUBSCRIBE_RATE_INFINITE);
612
-
613
- rb_define_method(rb_cSubscribe, "initialize", rb_winevt_subscribe_initialize, 0);
614
- rb_define_method(rb_cSubscribe, "subscribe", rb_winevt_subscribe_subscribe, -1);
615
- rb_define_method(rb_cSubscribe, "next", rb_winevt_subscribe_next, 0);
616
- rb_define_method(rb_cSubscribe, "each", rb_winevt_subscribe_each, 0);
617
- rb_define_method(rb_cSubscribe, "bookmark", rb_winevt_subscribe_get_bookmark, 0);
618
- /*
619
- * @since 0.7.0
620
- */
621
- rb_define_method(rb_cSubscribe, "read_existing_events?", rb_winevt_subscribe_read_existing_events_p, 0);
622
- /*
623
- * @since 0.7.0
624
- */
625
- rb_define_method(rb_cSubscribe, "read_existing_events=", rb_winevt_subscribe_set_read_existing_events, 1);
626
- rb_define_method(rb_cSubscribe, "rate_limit", rb_winevt_subscribe_get_rate_limit, 0);
627
- rb_define_method(rb_cSubscribe, "rate_limit=", rb_winevt_subscribe_set_rate_limit, 1);
628
- rb_define_method(
629
- rb_cSubscribe, "render_as_xml?", rb_winevt_subscribe_render_as_xml_p, 0);
630
- rb_define_method(
631
- rb_cSubscribe, "render_as_xml=", rb_winevt_subscribe_set_render_as_xml, 1);
632
- /*
633
- * @since 0.7.3
634
- */
635
- rb_define_method(
636
- rb_cSubscribe, "preserve_qualifiers?", rb_winevt_subscribe_get_preserve_qualifiers_p, 0);
637
- /*
638
- * @since 0.7.3
639
- */
640
- rb_define_method(
641
- rb_cSubscribe, "preserve_qualifiers=", rb_winevt_subscribe_set_preserve_qualifiers, 1);
642
- /*
643
- * @since 0.8.0
644
- */
645
- rb_define_method(
646
- rb_cSubscribe, "locale", rb_winevt_subscribe_get_locale, 0);
647
- /*
648
- * @since 0.8.0
649
- */
650
- rb_define_method(
651
- rb_cSubscribe, "locale=", rb_winevt_subscribe_set_locale, 1);
652
- }
1
+ #include <winevt_c.h>
2
+
3
+ /* clang-format off */
4
+ /*
5
+ * Document-class: Winevt::EventLog::Subscribe
6
+ *
7
+ * Subscribe Windows EventLog channel.
8
+ *
9
+ * @example
10
+ * require 'winevt'
11
+ *
12
+ * @subscribe = Winevt::EventLog::Subscribe.new
13
+ * @subscribe.tail = true
14
+ * @subscribe.rate_limit = 80
15
+ * @subscribe.subscribe(
16
+ * "Application", "*[System[(Level <= 4) and TimeCreated[timediff(@SystemTime) <= 86400000]]]"
17
+ * )
18
+ * while true do
19
+ * @subscribe.each do |eventlog, message, string_inserts|
20
+ * puts ({eventlog: eventlog, data: message})
21
+ * end
22
+ * sleep(0.1)
23
+ * end
24
+ *
25
+ * @see https://docs.microsoft.com/en-us/windows/win32/api/winevt/nf-winevt-evtsubscribe
26
+ */
27
+ /* clang-format on */
28
+
29
+ static void subscribe_free(void* ptr);
30
+
31
+ static const rb_data_type_t rb_winevt_subscribe_type = { "winevt/subscribe",
32
+ {
33
+ 0,
34
+ subscribe_free,
35
+ 0,
36
+ },
37
+ NULL,
38
+ NULL,
39
+ RUBY_TYPED_FREE_IMMEDIATELY };
40
+
41
+ static void
42
+ close_handles(struct WinevtSubscribe* winevtSubscribe)
43
+ {
44
+ if (winevtSubscribe->signalEvent) {
45
+ CloseHandle(winevtSubscribe->signalEvent);
46
+ winevtSubscribe->signalEvent = NULL;
47
+ }
48
+
49
+ if (winevtSubscribe->subscription) {
50
+ EvtClose(winevtSubscribe->subscription);
51
+ winevtSubscribe->subscription = NULL;
52
+ }
53
+
54
+ if (winevtSubscribe->bookmark) {
55
+ EvtClose(winevtSubscribe->bookmark);
56
+ winevtSubscribe->bookmark = NULL;
57
+ }
58
+
59
+ for (int i = 0; i < winevtSubscribe->count; i++) {
60
+ if (winevtSubscribe->hEvents[i]) {
61
+ EvtClose(winevtSubscribe->hEvents[i]);
62
+ winevtSubscribe->hEvents[i] = NULL;
63
+ }
64
+ }
65
+ winevtSubscribe->count = 0;
66
+
67
+ if (winevtSubscribe->remoteHandle) {
68
+ EvtClose(winevtSubscribe->remoteHandle);
69
+ winevtSubscribe->remoteHandle = NULL;
70
+ }
71
+ }
72
+
73
+ static void
74
+ subscribe_free(void* ptr)
75
+ {
76
+ struct WinevtSubscribe* winevtSubscribe = (struct WinevtSubscribe*)ptr;
77
+ close_handles(winevtSubscribe);
78
+
79
+ xfree(ptr);
80
+ }
81
+
82
+ static VALUE
83
+ rb_winevt_subscribe_alloc(VALUE klass)
84
+ {
85
+ VALUE obj;
86
+ struct WinevtSubscribe* winevtSubscribe;
87
+ obj = TypedData_Make_Struct(
88
+ klass, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
89
+ return obj;
90
+ }
91
+
92
+ /*
93
+ * Initalize Subscribe class.
94
+ *
95
+ * @return [Subscribe]
96
+ *
97
+ */
98
+ static VALUE
99
+ rb_winevt_subscribe_initialize(VALUE self)
100
+ {
101
+ struct WinevtSubscribe* winevtSubscribe;
102
+
103
+ TypedData_Get_Struct(
104
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
105
+
106
+ winevtSubscribe->rateLimit = SUBSCRIBE_RATE_INFINITE;
107
+ winevtSubscribe->lastTime = 0;
108
+ winevtSubscribe->currentRate = 0;
109
+ winevtSubscribe->renderAsXML = TRUE;
110
+ winevtSubscribe->readExistingEvents = TRUE;
111
+ winevtSubscribe->preserveQualifiers = FALSE;
112
+ winevtSubscribe->localeInfo = &default_locale;
113
+
114
+ return Qnil;
115
+ }
116
+
117
+ /*
118
+ * This method specifies whether read existing events or not.
119
+ *
120
+ * @param rb_read_existing_events_p [Boolean]
121
+ */
122
+ static VALUE
123
+ rb_winevt_subscribe_set_read_existing_events(VALUE self, VALUE rb_read_existing_events_p)
124
+ {
125
+ struct WinevtSubscribe* winevtSubscribe;
126
+
127
+ TypedData_Get_Struct(
128
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
129
+
130
+ winevtSubscribe->readExistingEvents = RTEST(rb_read_existing_events_p);
131
+
132
+ return Qnil;
133
+ }
134
+
135
+ /*
136
+ * This method returns whether read existing events or not.
137
+ *
138
+ * @return [Boolean]
139
+ */
140
+ static VALUE
141
+ rb_winevt_subscribe_read_existing_events_p(VALUE self)
142
+ {
143
+ struct WinevtSubscribe* winevtSubscribe;
144
+
145
+ TypedData_Get_Struct(
146
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
147
+
148
+ return winevtSubscribe->readExistingEvents ? Qtrue : Qfalse;
149
+ }
150
+
151
+ /*
152
+ * Subscribe into a Windows EventLog channel.
153
+ *
154
+ * @overload subscribe(path, query, bookmark=nil, session=nil)
155
+ * @param path [String] Subscribe Channel
156
+ * @param query [String] Query string for channel
157
+ * @param bookmark [Bookmark] bookmark Bookmark class instance.
158
+ * @param session [Session] Session information for remoting access.
159
+ * @return [Boolean]
160
+ *
161
+ */
162
+ static VALUE
163
+ rb_winevt_subscribe_subscribe(int argc, VALUE* argv, VALUE self)
164
+ {
165
+ VALUE rb_path, rb_query, rb_bookmark, rb_session;
166
+ EVT_HANDLE hSubscription = NULL, hBookmark = NULL;
167
+ HANDLE hSignalEvent;
168
+ EVT_HANDLE hRemoteHandle = NULL;
169
+ DWORD len, flags = 0L;
170
+ DWORD err = ERROR_SUCCESS;
171
+ VALUE wpathBuf, wqueryBuf, wBookmarkBuf;
172
+ PWSTR path, query, bookmarkXml;
173
+ DWORD status = ERROR_SUCCESS;
174
+ struct WinevtSession* winevtSession;
175
+ struct WinevtSubscribe* winevtSubscribe;
176
+
177
+ hSignalEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
178
+
179
+ TypedData_Get_Struct(
180
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
181
+
182
+ rb_scan_args(argc, argv, "22", &rb_path, &rb_query, &rb_bookmark, &rb_session);
183
+ Check_Type(rb_path, T_STRING);
184
+ Check_Type(rb_query, T_STRING);
185
+
186
+ if (rb_obj_is_kind_of(rb_bookmark, rb_cString)) {
187
+ // bookmarkXml : To wide char
188
+ len = MultiByteToWideChar(
189
+ CP_UTF8, 0, RSTRING_PTR(rb_bookmark), RSTRING_LEN(rb_bookmark), NULL, 0);
190
+ bookmarkXml = ALLOCV_N(WCHAR, wBookmarkBuf, len + 1);
191
+ MultiByteToWideChar(CP_UTF8,
192
+ 0,
193
+ RSTRING_PTR(rb_bookmark),
194
+ RSTRING_LEN(rb_bookmark),
195
+ bookmarkXml,
196
+ len);
197
+ bookmarkXml[len] = L'\0';
198
+ hBookmark = EvtCreateBookmark(bookmarkXml);
199
+ ALLOCV_END(wBookmarkBuf);
200
+ if (hBookmark == NULL) {
201
+ status = GetLastError();
202
+ raise_system_error(rb_eWinevtQueryError, status);
203
+ }
204
+ }
205
+ if (rb_obj_is_kind_of(rb_session, rb_cSession)) {
206
+ winevtSession = EventSession(rb_session);
207
+ hRemoteHandle = connect_to_remote(winevtSession->server,
208
+ winevtSession->domain,
209
+ winevtSession->username,
210
+ winevtSession->password,
211
+ winevtSession->flags,
212
+ &err);
213
+ if (err != ERROR_SUCCESS) {
214
+ raise_system_error(rb_eRuntimeError, err);
215
+ }
216
+ }
217
+
218
+ // path : To wide char
219
+ len =
220
+ MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), NULL, 0);
221
+ path = ALLOCV_N(WCHAR, wpathBuf, len + 1);
222
+ MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), path, len);
223
+ path[len] = L'\0';
224
+
225
+ // query : To wide char
226
+ len = MultiByteToWideChar(
227
+ CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), NULL, 0);
228
+ query = ALLOCV_N(WCHAR, wqueryBuf, len + 1);
229
+ MultiByteToWideChar(
230
+ CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), query, len);
231
+ query[len] = L'\0';
232
+
233
+ if (hBookmark) {
234
+ flags |= EvtSubscribeStartAfterBookmark;
235
+ } else if (winevtSubscribe->readExistingEvents) {
236
+ flags |= EvtSubscribeStartAtOldestRecord;
237
+ } else {
238
+ flags |= EvtSubscribeToFutureEvents;
239
+ }
240
+
241
+ hSubscription =
242
+ EvtSubscribe(hRemoteHandle, hSignalEvent, path, query, hBookmark, NULL, NULL, flags);
243
+ if (!hSubscription) {
244
+ status = GetLastError();
245
+ if (hBookmark != NULL) {
246
+ EvtClose(hBookmark);
247
+ }
248
+ if (hSignalEvent != NULL) {
249
+ CloseHandle(hSignalEvent);
250
+ }
251
+ if (rb_obj_is_kind_of(rb_session, rb_cSession)) {
252
+ rb_raise(rb_eRemoteHandlerError, "Remoting subscription is not working. errCode: %ld\n", status);
253
+ } else {
254
+ raise_system_error(rb_eWinevtQueryError, status);
255
+ }
256
+ }
257
+
258
+ if (winevtSubscribe->subscription != NULL) {
259
+ // should be disgarded the old event subscription handle.
260
+ EvtClose(winevtSubscribe->subscription);
261
+ }
262
+
263
+ ALLOCV_END(wpathBuf);
264
+ ALLOCV_END(wqueryBuf);
265
+
266
+ winevtSubscribe->signalEvent = hSignalEvent;
267
+ winevtSubscribe->subscription = hSubscription;
268
+ winevtSubscribe->remoteHandle = hRemoteHandle;
269
+ if (hBookmark) {
270
+ winevtSubscribe->bookmark = hBookmark;
271
+ } else {
272
+ winevtSubscribe->bookmark = EvtCreateBookmark(NULL);
273
+ if (winevtSubscribe->bookmark == NULL) {
274
+ status = GetLastError();
275
+ if (hSubscription != NULL) {
276
+ EvtClose(hSubscription);
277
+ }
278
+ if (hSignalEvent != NULL) {
279
+ CloseHandle(hSignalEvent);
280
+ }
281
+ raise_system_error(rb_eWinevtQueryError, status);
282
+ }
283
+ }
284
+
285
+ return Qtrue;
286
+ }
287
+
288
+ BOOL
289
+ is_rate_limit_exceeded(struct WinevtSubscribe* winevtSubscribe)
290
+ {
291
+ time_t now;
292
+
293
+ if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
294
+ return FALSE;
295
+
296
+ time(&now);
297
+
298
+ if (now <= winevtSubscribe->lastTime) {
299
+ if (winevtSubscribe->currentRate >= winevtSubscribe->rateLimit) {
300
+ return TRUE;
301
+ }
302
+ } else {
303
+ winevtSubscribe->currentRate = 0;
304
+ }
305
+
306
+ return FALSE;
307
+ }
308
+
309
+ void
310
+ update_to_reflect_rate_limit_state(struct WinevtSubscribe* winevtSubscribe, ULONG count)
311
+ {
312
+ time_t lastTime = 0;
313
+
314
+ if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
315
+ return;
316
+
317
+ time(&lastTime);
318
+ winevtSubscribe->lastTime = lastTime;
319
+ winevtSubscribe->currentRate += count;
320
+ }
321
+
322
+ /*
323
+ * Handle the next values. Since v0.6.0, this method is used for
324
+ * testing only. Please use #each instead.
325
+ *
326
+ * @return [Boolean]
327
+ *
328
+ * @see each
329
+ */
330
+
331
+ static VALUE
332
+ rb_winevt_subscribe_next(VALUE self)
333
+ {
334
+ EVT_HANDLE hEvents[SUBSCRIBE_ARRAY_SIZE];
335
+ ULONG count = 0;
336
+ DWORD status = ERROR_SUCCESS;
337
+ struct WinevtSubscribe* winevtSubscribe;
338
+
339
+ TypedData_Get_Struct(
340
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
341
+
342
+ if (is_rate_limit_exceeded(winevtSubscribe)) {
343
+ return Qfalse;
344
+ }
345
+
346
+ /* If subscription handle is NULL, it should return false. */
347
+ if (!winevtSubscribe->subscription) {
348
+ return Qfalse;
349
+ }
350
+
351
+ if (!EvtNext(winevtSubscribe->subscription,
352
+ SUBSCRIBE_ARRAY_SIZE,
353
+ hEvents,
354
+ INFINITE,
355
+ 0,
356
+ &count)) {
357
+ status = GetLastError();
358
+ if (ERROR_CANCELLED == status) {
359
+ return Qfalse;
360
+ }
361
+ if (ERROR_NO_MORE_ITEMS != status) {
362
+ return Qfalse;
363
+ }
364
+ }
365
+
366
+ if (status == ERROR_SUCCESS) {
367
+ winevtSubscribe->count = count;
368
+ for (int i = 0; i < count; i++) {
369
+ winevtSubscribe->hEvents[i] = hEvents[i];
370
+ EvtUpdateBookmark(winevtSubscribe->bookmark, winevtSubscribe->hEvents[i]);
371
+ }
372
+
373
+ update_to_reflect_rate_limit_state(winevtSubscribe, count);
374
+
375
+ return Qtrue;
376
+ }
377
+
378
+ return Qfalse;
379
+ }
380
+
381
+ static VALUE
382
+ rb_winevt_subscribe_render(VALUE self, EVT_HANDLE event)
383
+ {
384
+ struct WinevtSubscribe* winevtSubscribe;
385
+
386
+ TypedData_Get_Struct(
387
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
388
+
389
+ if (winevtSubscribe->renderAsXML) {
390
+ return render_to_rb_str(event, EvtRenderEventXml);
391
+ } else {
392
+ return render_system_event(event, winevtSubscribe->preserveQualifiers);
393
+ }
394
+ }
395
+
396
+ static VALUE
397
+ rb_winevt_subscribe_message(EVT_HANDLE event, LocaleInfo* localeInfo, EVT_HANDLE hRemote)
398
+ {
399
+ WCHAR* wResult;
400
+ VALUE utf8str;
401
+
402
+ wResult = get_description(event, localeInfo->langID, hRemote);
403
+ utf8str = wstr_to_rb_str(CP_UTF8, wResult, -1);
404
+ free(wResult);
405
+
406
+ return utf8str;
407
+ }
408
+
409
+ static VALUE
410
+ rb_winevt_subscribe_string_inserts(EVT_HANDLE event)
411
+ {
412
+ return get_values(event);
413
+ }
414
+
415
+ static VALUE
416
+ rb_winevt_subscribe_close_handle(VALUE self)
417
+ {
418
+ struct WinevtSubscribe* winevtSubscribe;
419
+
420
+ TypedData_Get_Struct(
421
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
422
+
423
+ for (int i = 0; i < winevtSubscribe->count; i++) {
424
+ if (winevtSubscribe->hEvents[i] != NULL) {
425
+ EvtClose(winevtSubscribe->hEvents[i]);
426
+ winevtSubscribe->hEvents[i] = NULL;
427
+ }
428
+ }
429
+
430
+ return Qnil;
431
+ }
432
+
433
+ static VALUE
434
+ rb_winevt_subscribe_each_yield(VALUE self)
435
+ {
436
+ RETURN_ENUMERATOR(self, 0, 0);
437
+ struct WinevtSubscribe* winevtSubscribe;
438
+
439
+ TypedData_Get_Struct(
440
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
441
+
442
+ for (int i = 0; i < winevtSubscribe->count; i++) {
443
+ rb_yield_values(3,
444
+ rb_winevt_subscribe_render(self, winevtSubscribe->hEvents[i]),
445
+ rb_winevt_subscribe_message(winevtSubscribe->hEvents[i], winevtSubscribe->localeInfo,
446
+ winevtSubscribe->remoteHandle),
447
+ rb_winevt_subscribe_string_inserts(winevtSubscribe->hEvents[i]));
448
+ }
449
+
450
+ return Qnil;
451
+ }
452
+
453
+ /*
454
+ * Enumerate to obtain Windows EventLog contents.
455
+ *
456
+ * This method yields the following:
457
+ * (Stringified EventLog, Stringified detail message, Stringified
458
+ * insert values)
459
+ *
460
+ * @yield (String,String,String)
461
+ *
462
+ */
463
+ static VALUE
464
+ rb_winevt_subscribe_each(VALUE self)
465
+ {
466
+ RETURN_ENUMERATOR(self, 0, 0);
467
+
468
+ while (rb_winevt_subscribe_next(self)) {
469
+ rb_ensure(
470
+ rb_winevt_subscribe_each_yield, self, rb_winevt_subscribe_close_handle, self);
471
+ }
472
+
473
+ return Qnil;
474
+ }
475
+
476
+ /*
477
+ * This method renders bookmark content which is related to Subscribe class instance.
478
+ *
479
+ * @return [String]
480
+ */
481
+ static VALUE
482
+ rb_winevt_subscribe_get_bookmark(VALUE self)
483
+ {
484
+ struct WinevtSubscribe* winevtSubscribe;
485
+
486
+ TypedData_Get_Struct(
487
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
488
+
489
+ return render_to_rb_str(winevtSubscribe->bookmark, EvtRenderBookmark);
490
+ }
491
+
492
+ /*
493
+ * This method returns rate limit value.
494
+ *
495
+ * @since 0.6.0
496
+ * @return [Integer]
497
+ */
498
+ static VALUE
499
+ rb_winevt_subscribe_get_rate_limit(VALUE self)
500
+ {
501
+ struct WinevtSubscribe* winevtSubscribe;
502
+
503
+ TypedData_Get_Struct(
504
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
505
+
506
+ return INT2NUM(winevtSubscribe->rateLimit);
507
+ }
508
+
509
+ /*
510
+ * This method specifies rate limit value.
511
+ *
512
+ * @since 0.6.0
513
+ * @param rb_rate_limit [Integer] rate_limit value
514
+ */
515
+ static VALUE
516
+ rb_winevt_subscribe_set_rate_limit(VALUE self, VALUE rb_rate_limit)
517
+ {
518
+ struct WinevtSubscribe* winevtSubscribe;
519
+ DWORD rateLimit;
520
+
521
+ TypedData_Get_Struct(
522
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
523
+
524
+ rateLimit = NUM2LONG(rb_rate_limit);
525
+
526
+ if ((rateLimit != SUBSCRIBE_RATE_INFINITE) && (rateLimit < 10 || rateLimit % 10)) {
527
+ rb_raise(rb_eArgError, "Specify a multiples of 10 or RATE_INFINITE constant");
528
+ } else {
529
+ winevtSubscribe->rateLimit = rateLimit;
530
+ }
531
+
532
+ return Qnil;
533
+ }
534
+
535
+ /*
536
+ * This method returns whether render as xml or not.
537
+ *
538
+ * @since 0.6.0
539
+ * @return [Boolean]
540
+ */
541
+ static VALUE
542
+ rb_winevt_subscribe_render_as_xml_p(VALUE self)
543
+ {
544
+ struct WinevtSubscribe* winevtSubscribe;
545
+
546
+ TypedData_Get_Struct(
547
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
548
+
549
+ return winevtSubscribe->renderAsXML ? Qtrue : Qfalse;
550
+ }
551
+
552
+ /*
553
+ * This method specifies whether render as xml or not.
554
+ *
555
+ * @since 0.6.0
556
+ * @param rb_render_as_xml [Boolean]
557
+ */
558
+ static VALUE
559
+ rb_winevt_subscribe_set_render_as_xml(VALUE self, VALUE rb_render_as_xml)
560
+ {
561
+ struct WinevtSubscribe* winevtSubscribe;
562
+
563
+ TypedData_Get_Struct(
564
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
565
+
566
+ winevtSubscribe->renderAsXML = RTEST(rb_render_as_xml);
567
+
568
+ return Qnil;
569
+ }
570
+
571
+ /*
572
+ * This method specifies whether preserving qualifiers key or not.
573
+ *
574
+ * @since 0.7.3
575
+ * @param rb_preserve_qualifiers [Boolean]
576
+ */
577
+ static VALUE
578
+ rb_winevt_subscribe_set_preserve_qualifiers(VALUE self, VALUE rb_preserve_qualifiers)
579
+ {
580
+ struct WinevtSubscribe* winevtSubscribe;
581
+
582
+ TypedData_Get_Struct(
583
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
584
+
585
+ winevtSubscribe->preserveQualifiers = RTEST(rb_preserve_qualifiers);
586
+
587
+ return Qnil;
588
+ }
589
+
590
+ /*
591
+ * This method returns whether preserving qualifiers or not.
592
+ *
593
+ * @since 0.7.3
594
+ * @return [Integer]
595
+ */
596
+ static VALUE
597
+ rb_winevt_subscribe_get_preserve_qualifiers_p(VALUE self)
598
+ {
599
+ struct WinevtSubscribe* winevtSubscribe;
600
+
601
+ TypedData_Get_Struct(
602
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
603
+
604
+ return winevtSubscribe->preserveQualifiers ? Qtrue : Qfalse;
605
+ }
606
+
607
+ /*
608
+ * This method specifies locale with [String].
609
+ *
610
+ * @since 0.8.0
611
+ * @param rb_locale_str [String]
612
+ */
613
+ static VALUE
614
+ rb_winevt_subscribe_set_locale(VALUE self, VALUE rb_locale_str)
615
+ {
616
+ struct WinevtSubscribe* winevtSubscribe;
617
+ LocaleInfo* locale_info = &default_locale;
618
+
619
+ TypedData_Get_Struct(
620
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
621
+
622
+ locale_info = get_locale_info_from_rb_str(rb_locale_str);
623
+
624
+ winevtSubscribe->localeInfo = locale_info;
625
+
626
+ return Qnil;
627
+ }
628
+
629
+ /*
630
+ * This method obtains specified locale with [String].
631
+ *
632
+ * @since 0.8.0
633
+ */
634
+ static VALUE
635
+ rb_winevt_subscribe_get_locale(VALUE self)
636
+ {
637
+ struct WinevtSubscribe* winevtSubscribe;
638
+
639
+ TypedData_Get_Struct(
640
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
641
+
642
+ if (winevtSubscribe->localeInfo->langCode) {
643
+ return rb_str_new2(winevtSubscribe->localeInfo->langCode);
644
+ } else {
645
+ return rb_str_new2(default_locale.langCode);
646
+ }
647
+ }
648
+
649
+ /*
650
+ * This method cancels channel subscription.
651
+ *
652
+ * @return [Boolean]
653
+ * @since 0.9.1
654
+ */
655
+ static VALUE
656
+ rb_winevt_subscribe_cancel(VALUE self)
657
+ {
658
+ struct WinevtSubscribe* winevtSubscribe;
659
+ BOOL result = FALSE;
660
+
661
+ TypedData_Get_Struct(
662
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
663
+
664
+ if (winevtSubscribe->subscription) {
665
+ result = EvtCancel(winevtSubscribe->subscription);
666
+ }
667
+
668
+ if (result) {
669
+ return Qtrue;
670
+ } else {
671
+ return Qfalse;
672
+ }
673
+ }
674
+
675
+ /*
676
+ * This method closes channel handles forcibly.
677
+ *
678
+ * @since 0.9.1
679
+ */
680
+ static VALUE
681
+ rb_winevt_subscribe_close(VALUE self)
682
+ {
683
+ struct WinevtSubscribe* winevtSubscribe;
684
+
685
+ TypedData_Get_Struct(
686
+ self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
687
+
688
+ close_handles(winevtSubscribe);
689
+
690
+ return Qnil;
691
+ }
692
+
693
+
694
+ void
695
+ Init_winevt_subscribe(VALUE rb_cEventLog)
696
+ {
697
+ rb_cSubscribe = rb_define_class_under(rb_cEventLog, "Subscribe", rb_cObject);
698
+
699
+ rb_define_alloc_func(rb_cSubscribe, rb_winevt_subscribe_alloc);
700
+
701
+ /*
702
+ * For Subscribe#rate_limit=. It represents unspecified rate limit.
703
+ * @since 0.6.0
704
+ */
705
+ rb_define_const(rb_cSubscribe, "RATE_INFINITE", SUBSCRIBE_RATE_INFINITE);
706
+
707
+ rb_define_method(rb_cSubscribe, "initialize", rb_winevt_subscribe_initialize, 0);
708
+ rb_define_method(rb_cSubscribe, "subscribe", rb_winevt_subscribe_subscribe, -1);
709
+ rb_define_method(rb_cSubscribe, "next", rb_winevt_subscribe_next, 0);
710
+ rb_define_method(rb_cSubscribe, "each", rb_winevt_subscribe_each, 0);
711
+ rb_define_method(rb_cSubscribe, "bookmark", rb_winevt_subscribe_get_bookmark, 0);
712
+ /*
713
+ * @since 0.7.0
714
+ */
715
+ rb_define_method(rb_cSubscribe, "read_existing_events?", rb_winevt_subscribe_read_existing_events_p, 0);
716
+ /*
717
+ * @since 0.7.0
718
+ */
719
+ rb_define_method(rb_cSubscribe, "read_existing_events=", rb_winevt_subscribe_set_read_existing_events, 1);
720
+ rb_define_method(rb_cSubscribe, "rate_limit", rb_winevt_subscribe_get_rate_limit, 0);
721
+ rb_define_method(rb_cSubscribe, "rate_limit=", rb_winevt_subscribe_set_rate_limit, 1);
722
+ rb_define_method(
723
+ rb_cSubscribe, "render_as_xml?", rb_winevt_subscribe_render_as_xml_p, 0);
724
+ rb_define_method(
725
+ rb_cSubscribe, "render_as_xml=", rb_winevt_subscribe_set_render_as_xml, 1);
726
+ /*
727
+ * @since 0.7.3
728
+ */
729
+ rb_define_method(
730
+ rb_cSubscribe, "preserve_qualifiers?", rb_winevt_subscribe_get_preserve_qualifiers_p, 0);
731
+ /*
732
+ * @since 0.7.3
733
+ */
734
+ rb_define_method(
735
+ rb_cSubscribe, "preserve_qualifiers=", rb_winevt_subscribe_set_preserve_qualifiers, 1);
736
+ /*
737
+ * @since 0.8.0
738
+ */
739
+ rb_define_method(
740
+ rb_cSubscribe, "locale", rb_winevt_subscribe_get_locale, 0);
741
+ /*
742
+ * @since 0.8.0
743
+ */
744
+ rb_define_method(
745
+ rb_cSubscribe, "locale=", rb_winevt_subscribe_set_locale, 1);
746
+ /*
747
+ * @since 0.9.1
748
+ */
749
+ rb_define_method(
750
+ rb_cSubscribe, "cancel", rb_winevt_subscribe_cancel, 0);
751
+ /*
752
+ * @since 0.9.1
753
+ */
754
+ rb_define_method(
755
+ rb_cSubscribe, "close", rb_winevt_subscribe_close, 0);
756
+ }