winevt_c 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,757 +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
- 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
-
213
- err = GetLastError();
214
- if (err != ERROR_SUCCESS) {
215
- raise_system_error(rb_eRuntimeError, err);
216
- }
217
- }
218
-
219
- // path : To wide char
220
- len =
221
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), NULL, 0);
222
- path = ALLOCV_N(WCHAR, wpathBuf, len + 1);
223
- MultiByteToWideChar(CP_UTF8, 0, RSTRING_PTR(rb_path), RSTRING_LEN(rb_path), path, len);
224
- path[len] = L'\0';
225
-
226
- // query : To wide char
227
- len = MultiByteToWideChar(
228
- CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), NULL, 0);
229
- query = ALLOCV_N(WCHAR, wqueryBuf, len + 1);
230
- MultiByteToWideChar(
231
- CP_UTF8, 0, RSTRING_PTR(rb_query), RSTRING_LEN(rb_query), query, len);
232
- query[len] = L'\0';
233
-
234
- if (hBookmark) {
235
- flags |= EvtSubscribeStartAfterBookmark;
236
- } else if (winevtSubscribe->readExistingEvents) {
237
- flags |= EvtSubscribeStartAtOldestRecord;
238
- } else {
239
- flags |= EvtSubscribeToFutureEvents;
240
- }
241
-
242
- hSubscription =
243
- EvtSubscribe(hRemoteHandle, hSignalEvent, path, query, hBookmark, NULL, NULL, flags);
244
- if (!hSubscription) {
245
- if (hBookmark != NULL) {
246
- EvtClose(hBookmark);
247
- }
248
- if (hSignalEvent != NULL) {
249
- CloseHandle(hSignalEvent);
250
- }
251
- status = GetLastError();
252
- if (rb_obj_is_kind_of(rb_session, rb_cSession)) {
253
- rb_raise(rb_eRemoteHandlerError, "Remoting subscription is not working. errCode: %ld\n", status);
254
- } else {
255
- raise_system_error(rb_eWinevtQueryError, status);
256
- }
257
- }
258
-
259
- if (winevtSubscribe->subscription != NULL) {
260
- // should be disgarded the old event subscription handle.
261
- EvtClose(winevtSubscribe->subscription);
262
- }
263
-
264
- ALLOCV_END(wpathBuf);
265
- ALLOCV_END(wqueryBuf);
266
-
267
- winevtSubscribe->signalEvent = hSignalEvent;
268
- winevtSubscribe->subscription = hSubscription;
269
- winevtSubscribe->remoteHandle = hRemoteHandle;
270
- if (hBookmark) {
271
- winevtSubscribe->bookmark = hBookmark;
272
- } else {
273
- winevtSubscribe->bookmark = EvtCreateBookmark(NULL);
274
- if (winevtSubscribe->bookmark == NULL) {
275
- if (hSubscription != NULL) {
276
- EvtClose(hSubscription);
277
- }
278
- if (hSignalEvent != NULL) {
279
- CloseHandle(hSignalEvent);
280
- }
281
- status = GetLastError();
282
- raise_system_error(rb_eWinevtQueryError, status);
283
- }
284
- }
285
-
286
- return Qtrue;
287
- }
288
-
289
- BOOL
290
- is_rate_limit_exceeded(struct WinevtSubscribe* winevtSubscribe)
291
- {
292
- time_t now;
293
-
294
- if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
295
- return FALSE;
296
-
297
- time(&now);
298
-
299
- if (now <= winevtSubscribe->lastTime) {
300
- if (winevtSubscribe->currentRate >= winevtSubscribe->rateLimit) {
301
- return TRUE;
302
- }
303
- } else {
304
- winevtSubscribe->currentRate = 0;
305
- }
306
-
307
- return FALSE;
308
- }
309
-
310
- void
311
- update_to_reflect_rate_limit_state(struct WinevtSubscribe* winevtSubscribe, ULONG count)
312
- {
313
- time_t lastTime = 0;
314
-
315
- if (winevtSubscribe->rateLimit == SUBSCRIBE_RATE_INFINITE)
316
- return;
317
-
318
- time(&lastTime);
319
- winevtSubscribe->lastTime = lastTime;
320
- winevtSubscribe->currentRate += count;
321
- }
322
-
323
- /*
324
- * Handle the next values. Since v0.6.0, this method is used for
325
- * testing only. Please use #each instead.
326
- *
327
- * @return [Boolean]
328
- *
329
- * @see each
330
- */
331
-
332
- static VALUE
333
- rb_winevt_subscribe_next(VALUE self)
334
- {
335
- EVT_HANDLE hEvents[SUBSCRIBE_ARRAY_SIZE];
336
- ULONG count = 0;
337
- DWORD status = ERROR_SUCCESS;
338
- struct WinevtSubscribe* winevtSubscribe;
339
-
340
- TypedData_Get_Struct(
341
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
342
-
343
- if (is_rate_limit_exceeded(winevtSubscribe)) {
344
- return Qfalse;
345
- }
346
-
347
- /* If subscription handle is NULL, it should return false. */
348
- if (!winevtSubscribe->subscription) {
349
- return Qfalse;
350
- }
351
-
352
- if (!EvtNext(winevtSubscribe->subscription,
353
- SUBSCRIBE_ARRAY_SIZE,
354
- hEvents,
355
- INFINITE,
356
- 0,
357
- &count)) {
358
- status = GetLastError();
359
- if (ERROR_CANCELLED == status) {
360
- return Qfalse;
361
- }
362
- if (ERROR_NO_MORE_ITEMS != status) {
363
- return Qfalse;
364
- }
365
- }
366
-
367
- if (status == ERROR_SUCCESS) {
368
- winevtSubscribe->count = count;
369
- for (int i = 0; i < count; i++) {
370
- winevtSubscribe->hEvents[i] = hEvents[i];
371
- EvtUpdateBookmark(winevtSubscribe->bookmark, winevtSubscribe->hEvents[i]);
372
- }
373
-
374
- update_to_reflect_rate_limit_state(winevtSubscribe, count);
375
-
376
- return Qtrue;
377
- }
378
-
379
- return Qfalse;
380
- }
381
-
382
- static VALUE
383
- rb_winevt_subscribe_render(VALUE self, EVT_HANDLE event)
384
- {
385
- struct WinevtSubscribe* winevtSubscribe;
386
-
387
- TypedData_Get_Struct(
388
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
389
-
390
- if (winevtSubscribe->renderAsXML) {
391
- return render_to_rb_str(event, EvtRenderEventXml);
392
- } else {
393
- return render_system_event(event, winevtSubscribe->preserveQualifiers);
394
- }
395
- }
396
-
397
- static VALUE
398
- rb_winevt_subscribe_message(EVT_HANDLE event, LocaleInfo* localeInfo, EVT_HANDLE hRemote)
399
- {
400
- WCHAR* wResult;
401
- VALUE utf8str;
402
-
403
- wResult = get_description(event, localeInfo->langID, hRemote);
404
- utf8str = wstr_to_rb_str(CP_UTF8, wResult, -1);
405
- free(wResult);
406
-
407
- return utf8str;
408
- }
409
-
410
- static VALUE
411
- rb_winevt_subscribe_string_inserts(EVT_HANDLE event)
412
- {
413
- return get_values(event);
414
- }
415
-
416
- static VALUE
417
- rb_winevt_subscribe_close_handle(VALUE self)
418
- {
419
- struct WinevtSubscribe* winevtSubscribe;
420
-
421
- TypedData_Get_Struct(
422
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
423
-
424
- for (int i = 0; i < winevtSubscribe->count; i++) {
425
- if (winevtSubscribe->hEvents[i] != NULL) {
426
- EvtClose(winevtSubscribe->hEvents[i]);
427
- winevtSubscribe->hEvents[i] = NULL;
428
- }
429
- }
430
-
431
- return Qnil;
432
- }
433
-
434
- static VALUE
435
- rb_winevt_subscribe_each_yield(VALUE self)
436
- {
437
- RETURN_ENUMERATOR(self, 0, 0);
438
- struct WinevtSubscribe* winevtSubscribe;
439
-
440
- TypedData_Get_Struct(
441
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
442
-
443
- for (int i = 0; i < winevtSubscribe->count; i++) {
444
- rb_yield_values(3,
445
- rb_winevt_subscribe_render(self, winevtSubscribe->hEvents[i]),
446
- rb_winevt_subscribe_message(winevtSubscribe->hEvents[i], winevtSubscribe->localeInfo,
447
- winevtSubscribe->remoteHandle),
448
- rb_winevt_subscribe_string_inserts(winevtSubscribe->hEvents[i]));
449
- }
450
-
451
- return Qnil;
452
- }
453
-
454
- /*
455
- * Enumerate to obtain Windows EventLog contents.
456
- *
457
- * This method yields the following:
458
- * (Stringified EventLog, Stringified detail message, Stringified
459
- * insert values)
460
- *
461
- * @yield (String,String,String)
462
- *
463
- */
464
- static VALUE
465
- rb_winevt_subscribe_each(VALUE self)
466
- {
467
- RETURN_ENUMERATOR(self, 0, 0);
468
-
469
- while (rb_winevt_subscribe_next(self)) {
470
- rb_ensure(
471
- rb_winevt_subscribe_each_yield, self, rb_winevt_subscribe_close_handle, self);
472
- }
473
-
474
- return Qnil;
475
- }
476
-
477
- /*
478
- * This method renders bookmark content which is related to Subscribe class instance.
479
- *
480
- * @return [String]
481
- */
482
- static VALUE
483
- rb_winevt_subscribe_get_bookmark(VALUE self)
484
- {
485
- struct WinevtSubscribe* winevtSubscribe;
486
-
487
- TypedData_Get_Struct(
488
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
489
-
490
- return render_to_rb_str(winevtSubscribe->bookmark, EvtRenderBookmark);
491
- }
492
-
493
- /*
494
- * This method returns rate limit value.
495
- *
496
- * @since 0.6.0
497
- * @return [Integer]
498
- */
499
- static VALUE
500
- rb_winevt_subscribe_get_rate_limit(VALUE self)
501
- {
502
- struct WinevtSubscribe* winevtSubscribe;
503
-
504
- TypedData_Get_Struct(
505
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
506
-
507
- return INT2NUM(winevtSubscribe->rateLimit);
508
- }
509
-
510
- /*
511
- * This method specifies rate limit value.
512
- *
513
- * @since 0.6.0
514
- * @param rb_rate_limit [Integer] rate_limit value
515
- */
516
- static VALUE
517
- rb_winevt_subscribe_set_rate_limit(VALUE self, VALUE rb_rate_limit)
518
- {
519
- struct WinevtSubscribe* winevtSubscribe;
520
- DWORD rateLimit;
521
-
522
- TypedData_Get_Struct(
523
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
524
-
525
- rateLimit = NUM2LONG(rb_rate_limit);
526
-
527
- if ((rateLimit != SUBSCRIBE_RATE_INFINITE) && (rateLimit < 10 || rateLimit % 10)) {
528
- rb_raise(rb_eArgError, "Specify a multiples of 10 or RATE_INFINITE constant");
529
- } else {
530
- winevtSubscribe->rateLimit = rateLimit;
531
- }
532
-
533
- return Qnil;
534
- }
535
-
536
- /*
537
- * This method returns whether render as xml or not.
538
- *
539
- * @since 0.6.0
540
- * @return [Boolean]
541
- */
542
- static VALUE
543
- rb_winevt_subscribe_render_as_xml_p(VALUE self)
544
- {
545
- struct WinevtSubscribe* winevtSubscribe;
546
-
547
- TypedData_Get_Struct(
548
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
549
-
550
- return winevtSubscribe->renderAsXML ? Qtrue : Qfalse;
551
- }
552
-
553
- /*
554
- * This method specifies whether render as xml or not.
555
- *
556
- * @since 0.6.0
557
- * @param rb_render_as_xml [Boolean]
558
- */
559
- static VALUE
560
- rb_winevt_subscribe_set_render_as_xml(VALUE self, VALUE rb_render_as_xml)
561
- {
562
- struct WinevtSubscribe* winevtSubscribe;
563
-
564
- TypedData_Get_Struct(
565
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
566
-
567
- winevtSubscribe->renderAsXML = RTEST(rb_render_as_xml);
568
-
569
- return Qnil;
570
- }
571
-
572
- /*
573
- * This method specifies whether preserving qualifiers key or not.
574
- *
575
- * @since 0.7.3
576
- * @param rb_preserve_qualifiers [Boolean]
577
- */
578
- static VALUE
579
- rb_winevt_subscribe_set_preserve_qualifiers(VALUE self, VALUE rb_preserve_qualifiers)
580
- {
581
- struct WinevtSubscribe* winevtSubscribe;
582
-
583
- TypedData_Get_Struct(
584
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
585
-
586
- winevtSubscribe->preserveQualifiers = RTEST(rb_preserve_qualifiers);
587
-
588
- return Qnil;
589
- }
590
-
591
- /*
592
- * This method returns whether preserving qualifiers or not.
593
- *
594
- * @since 0.7.3
595
- * @return [Integer]
596
- */
597
- static VALUE
598
- rb_winevt_subscribe_get_preserve_qualifiers_p(VALUE self)
599
- {
600
- struct WinevtSubscribe* winevtSubscribe;
601
-
602
- TypedData_Get_Struct(
603
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
604
-
605
- return winevtSubscribe->preserveQualifiers ? Qtrue : Qfalse;
606
- }
607
-
608
- /*
609
- * This method specifies locale with [String].
610
- *
611
- * @since 0.8.0
612
- * @param rb_locale_str [String]
613
- */
614
- static VALUE
615
- rb_winevt_subscribe_set_locale(VALUE self, VALUE rb_locale_str)
616
- {
617
- struct WinevtSubscribe* winevtSubscribe;
618
- LocaleInfo* locale_info = &default_locale;
619
-
620
- TypedData_Get_Struct(
621
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
622
-
623
- locale_info = get_locale_info_from_rb_str(rb_locale_str);
624
-
625
- winevtSubscribe->localeInfo = locale_info;
626
-
627
- return Qnil;
628
- }
629
-
630
- /*
631
- * This method obtains specified locale with [String].
632
- *
633
- * @since 0.8.0
634
- */
635
- static VALUE
636
- rb_winevt_subscribe_get_locale(VALUE self)
637
- {
638
- struct WinevtSubscribe* winevtSubscribe;
639
-
640
- TypedData_Get_Struct(
641
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
642
-
643
- if (winevtSubscribe->localeInfo->langCode) {
644
- return rb_str_new2(winevtSubscribe->localeInfo->langCode);
645
- } else {
646
- return rb_str_new2(default_locale.langCode);
647
- }
648
- }
649
-
650
- /*
651
- * This method cancels channel subscription.
652
- *
653
- * @return [Boolean]
654
- * @since 0.9.1
655
- */
656
- static VALUE
657
- rb_winevt_subscribe_cancel(VALUE self)
658
- {
659
- struct WinevtSubscribe* winevtSubscribe;
660
- BOOL result = FALSE;
661
-
662
- TypedData_Get_Struct(
663
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
664
-
665
- if (winevtSubscribe->subscription) {
666
- result = EvtCancel(winevtSubscribe->subscription);
667
- }
668
-
669
- if (result) {
670
- return Qtrue;
671
- } else {
672
- return Qfalse;
673
- }
674
- }
675
-
676
- /*
677
- * This method closes channel handles forcibly.
678
- *
679
- * @since 0.9.1
680
- */
681
- static VALUE
682
- rb_winevt_subscribe_close(VALUE self)
683
- {
684
- struct WinevtSubscribe* winevtSubscribe;
685
-
686
- TypedData_Get_Struct(
687
- self, struct WinevtSubscribe, &rb_winevt_subscribe_type, winevtSubscribe);
688
-
689
- close_handles(winevtSubscribe);
690
-
691
- return Qnil;
692
- }
693
-
694
-
695
- void
696
- Init_winevt_subscribe(VALUE rb_cEventLog)
697
- {
698
- rb_cSubscribe = rb_define_class_under(rb_cEventLog, "Subscribe", rb_cObject);
699
-
700
- rb_define_alloc_func(rb_cSubscribe, rb_winevt_subscribe_alloc);
701
-
702
- /*
703
- * For Subscribe#rate_limit=. It represents unspecified rate limit.
704
- * @since 0.6.0
705
- */
706
- rb_define_const(rb_cSubscribe, "RATE_INFINITE", SUBSCRIBE_RATE_INFINITE);
707
-
708
- rb_define_method(rb_cSubscribe, "initialize", rb_winevt_subscribe_initialize, 0);
709
- rb_define_method(rb_cSubscribe, "subscribe", rb_winevt_subscribe_subscribe, -1);
710
- rb_define_method(rb_cSubscribe, "next", rb_winevt_subscribe_next, 0);
711
- rb_define_method(rb_cSubscribe, "each", rb_winevt_subscribe_each, 0);
712
- rb_define_method(rb_cSubscribe, "bookmark", rb_winevt_subscribe_get_bookmark, 0);
713
- /*
714
- * @since 0.7.0
715
- */
716
- rb_define_method(rb_cSubscribe, "read_existing_events?", rb_winevt_subscribe_read_existing_events_p, 0);
717
- /*
718
- * @since 0.7.0
719
- */
720
- rb_define_method(rb_cSubscribe, "read_existing_events=", rb_winevt_subscribe_set_read_existing_events, 1);
721
- rb_define_method(rb_cSubscribe, "rate_limit", rb_winevt_subscribe_get_rate_limit, 0);
722
- rb_define_method(rb_cSubscribe, "rate_limit=", rb_winevt_subscribe_set_rate_limit, 1);
723
- rb_define_method(
724
- rb_cSubscribe, "render_as_xml?", rb_winevt_subscribe_render_as_xml_p, 0);
725
- rb_define_method(
726
- rb_cSubscribe, "render_as_xml=", rb_winevt_subscribe_set_render_as_xml, 1);
727
- /*
728
- * @since 0.7.3
729
- */
730
- rb_define_method(
731
- rb_cSubscribe, "preserve_qualifiers?", rb_winevt_subscribe_get_preserve_qualifiers_p, 0);
732
- /*
733
- * @since 0.7.3
734
- */
735
- rb_define_method(
736
- rb_cSubscribe, "preserve_qualifiers=", rb_winevt_subscribe_set_preserve_qualifiers, 1);
737
- /*
738
- * @since 0.8.0
739
- */
740
- rb_define_method(
741
- rb_cSubscribe, "locale", rb_winevt_subscribe_get_locale, 0);
742
- /*
743
- * @since 0.8.0
744
- */
745
- rb_define_method(
746
- rb_cSubscribe, "locale=", rb_winevt_subscribe_set_locale, 1);
747
- /*
748
- * @since 0.9.1
749
- */
750
- rb_define_method(
751
- rb_cSubscribe, "cancel", rb_winevt_subscribe_cancel, 0);
752
- /*
753
- * @since 0.9.1
754
- */
755
- rb_define_method(
756
- rb_cSubscribe, "close", rb_winevt_subscribe_close, 0);
757
- }
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
+ }