winevt_c 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,757 +1,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
-
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
+
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
+ }