1#![allow(dead_code)] use super::types::{
6 CorruptedEvent, Event, EventId, EventLevel, ProcessId, RecordId, RenderFormat, ThreadId,
7 intern_channel, intern_field_name, intern_provider,
8};
9use crate::error::Result;
10use quick_xml::Reader;
11use quick_xml::events::Event as XmlEvent;
12use std::borrow::Cow;
13use std::collections::HashMap;
14use std::ffi::c_void;
15use std::sync::{Arc, OnceLock, RwLock};
16use std::time::{Duration, SystemTime, UNIX_EPOCH};
17use windows::Win32::System::EventLog::*;
18use windows::core::PWSTR;
19
20static PUBLISHER_METADATA_CACHE: OnceLock<Arc<RwLock<HashMap<String, EvtPublisherHandle>>>> =
23 OnceLock::new();
24
25#[derive(Clone)]
27struct EvtPublisherHandle(EVT_HANDLE);
28
29impl Drop for EvtPublisherHandle {
30 fn drop(&mut self) {
31 if !self.0.is_invalid() {
32 unsafe {
33 let _ = EvtClose(self.0);
34 }
35 }
36 }
37}
38
39#[allow(dead_code)] fn get_publisher_metadata(provider_name: &str) -> Result<Option<EvtPublisherHandle>> {
42 let cache = PUBLISHER_METADATA_CACHE.get_or_init(|| Arc::new(RwLock::new(HashMap::new())));
43
44 {
46 let map = cache.read().unwrap();
48 if let Some(handle) = map.get(provider_name) {
49 return Ok(Some(handle.clone()));
50 }
51 }
52
53 let provider_wide: Vec<u16> = provider_name
55 .encode_utf16()
56 .chain(std::iter::once(0))
57 .collect();
58
59 let handle = unsafe {
60 EvtOpenPublisherMetadata(
61 EVT_HANDLE::default(), PWSTR(provider_wide.as_ptr() as *mut u16),
63 PWSTR::null(), 0, 0, )
67 };
68
69 match handle {
70 Ok(h) => {
71 let wrapped = EvtPublisherHandle(h);
72 cache
74 .write()
75 .unwrap()
76 .insert(provider_name.to_string(), wrapped.clone());
77 Ok(Some(wrapped))
78 }
79 Err(_) => {
80 Ok(None)
83 }
84 }
85}
86
87pub fn render_event(
89 event_handle: EVT_HANDLE,
90 format: RenderFormat,
91 include_event_data: bool,
92 parse_message: bool,
93) -> std::result::Result<Event, CorruptedEvent> {
94 let mut event = match format {
95 RenderFormat::Values => render_event_values(event_handle)?,
96 RenderFormat::Xml => render_event_xml(event_handle)?,
97 };
98
99 if include_event_data && format == RenderFormat::Xml {
101 let mut buffer = vec![0u8; 16384];
105 let mut buffer_used = 0u32;
106 let mut prop_count = 0u32;
107
108 let result = unsafe {
109 EvtRender(
110 EVT_HANDLE::default(),
111 event_handle,
112 EvtRenderEventXml.0,
113 buffer.len() as u32,
114 Some(buffer.as_mut_ptr() as *mut c_void),
115 &mut buffer_used,
116 &mut prop_count,
117 )
118 };
119
120 if result.is_ok() {
121 let xml_bytes = &buffer[..buffer_used as usize];
122 let xml_str = String::from_utf16_lossy(unsafe {
123 std::slice::from_raw_parts(xml_bytes.as_ptr() as *const u16, xml_bytes.len() / 2)
124 });
125
126 event.data = extract_event_data_from_xml(&xml_str);
127 }
128 }
129
130 if parse_message {
132 event.formatted_message = format_message(event_handle, event.provider.as_ref())
133 .ok()
134 .flatten();
135 }
136
137 Ok(event)
138}
139
140fn render_event_values(event_handle: EVT_HANDLE) -> std::result::Result<Event, CorruptedEvent> {
144 let mut buffer = vec![0u8; 8192]; let mut buffer_used = 0u32;
146 let mut prop_count = 0u32;
147
148 let result = unsafe {
150 EvtRender(
151 EVT_HANDLE::default(), event_handle,
153 EvtRenderEventValues.0,
154 buffer.len() as u32,
155 Some(buffer.as_mut_ptr() as *mut c_void),
156 &mut buffer_used,
157 &mut prop_count,
158 )
159 };
160
161 if result.is_err() {
162 return Err(CorruptedEvent {
163 record_id: None,
164 component: "EvtRender_Values".into(),
165 reason: "Failed to render event values".into(),
166 });
167 }
168
169 let variant_ptr = buffer.as_ptr() as *const EVT_VARIANT;
171 let variants = unsafe { std::slice::from_raw_parts(variant_ptr, prop_count as usize) };
172
173 let mut event = Event::default();
176
177 if !variants.is_empty() {
178 if !variants.is_empty() {
180 let variant = &variants[0];
181 event.id = EventId::new(extract_u32_from_variant(variant).unwrap_or(0));
182 }
183
184 if variants.len() > 1 {
186 let variant = &variants[1];
187 let level_val = extract_u8_from_variant(variant).unwrap_or(4);
188 event.level = EventLevel::from_code(level_val).unwrap_or(EventLevel::Verbose);
189 }
190
191 if variants.len() > 2 {
193 let variant = &variants[2];
194 if let Some(provider) = extract_string_from_variant(variant) {
195 event.provider = intern_provider(&provider);
196 }
197 }
198
199 if variants.len() > 3 {
201 let variant = &variants[3];
202 if let Some(channel) = extract_string_from_variant(variant) {
203 event.channel = intern_channel(&channel);
204 }
205 }
206
207 if variants.len() > 4 {
209 let variant = &variants[4];
210 if let Some(computer) = extract_string_from_variant(variant) {
211 event.computer = computer;
212 }
213 }
214
215 if variants.len() > 5 {
217 let variant = &variants[5];
218 if let Some(timestamp) = extract_systemtime_from_variant(variant) {
219 event.timestamp = Some(timestamp);
220 }
221 }
222
223 if variants.len() > 6 {
225 let variant = &variants[6];
226 if let Some(rid) = extract_u64_from_variant(variant) {
227 event.record_id = Some(RecordId::new(rid));
228 }
229 }
230
231 if variants.len() > 7 {
233 let variant = &variants[7];
234 if let Some(pid) = extract_u32_from_variant(variant) {
235 event.process_id = Some(ProcessId::new(pid));
236 }
237 }
238
239 if variants.len() > 8 {
241 let variant = &variants[8];
242 if let Some(tid) = extract_u32_from_variant(variant) {
243 event.thread_id = Some(ThreadId::new(tid));
244 }
245 }
246
247 if variants.len() > 9 {
249 event.data = Some(HashMap::new());
250 }
251 }
252
253 Ok(event)
254}
255
256fn render_event_xml(event_handle: EVT_HANDLE) -> std::result::Result<Event, CorruptedEvent> {
260 let mut buffer = vec![0u8; 16384]; let mut buffer_used = 0u32;
262 let mut prop_count = 0u32;
263
264 let result = unsafe {
265 EvtRender(
266 EVT_HANDLE::default(),
267 event_handle,
268 EvtRenderEventXml.0,
269 buffer.len() as u32,
270 Some(buffer.as_mut_ptr() as *mut c_void),
271 &mut buffer_used,
272 &mut prop_count,
273 )
274 };
275
276 if result.is_err() {
277 return Err(CorruptedEvent {
278 record_id: None,
279 component: Cow::Borrowed("EvtRender_Xml"),
280 reason: Cow::Borrowed("Failed to render event as XML"),
281 });
282 }
283
284 let xml_bytes = &buffer[..buffer_used as usize];
286 let xml_str = String::from_utf16_lossy(unsafe {
287 std::slice::from_raw_parts(xml_bytes.as_ptr() as *const u16, xml_bytes.len() / 2)
288 });
289
290 parse_event_xml_with_quick_xml(&xml_str)
292}
293
294fn parse_event_xml_with_quick_xml(xml_str: &str) -> std::result::Result<Event, CorruptedEvent> {
298 let mut reader = Reader::from_str(xml_str);
299 reader.config_mut().trim_text(true);
300
301 let mut event = Event::default();
302 let mut buf = Vec::new();
303 let mut current_tag = String::new();
304 let mut in_system = false;
305
306 loop {
307 match reader.read_event_into(&mut buf) {
308 Ok(XmlEvent::Start(e)) => {
309 let tag_name = String::from_utf8_lossy(e.name().as_ref()).into_owned();
310
311 if tag_name == "System" {
312 in_system = true;
313 } else if in_system {
314 current_tag = tag_name.clone();
315
316 if tag_name == "Provider" {
318 for attr in e.attributes() {
319 if let Ok(attr) = attr
320 && attr.key.as_ref() == b"Name"
321 && let Ok(value) = String::from_utf8(attr.value.to_vec())
322 {
323 event.provider = intern_provider(&value);
324 }
325 }
326 } else if tag_name == "TimeCreated" {
327 for attr in e.attributes() {
328 if let Ok(attr) = attr
329 && attr.key.as_ref() == b"SystemTime"
330 && let Ok(value) = String::from_utf8(attr.value.to_vec())
331 {
332 event.timestamp = parse_iso8601_timestamp(&value);
333 }
334 }
335 }
336 }
337 }
338 Ok(XmlEvent::End(e)) => {
339 let tag_name = String::from_utf8_lossy(e.name().as_ref()).into_owned();
340 if tag_name == "System" {
341 in_system = false;
342 }
343 current_tag.clear();
344 }
345 Ok(XmlEvent::Text(e)) if in_system && !current_tag.is_empty() => {
346 let value = String::from_utf8_lossy(e.as_ref()).into_owned();
347
348 match current_tag.as_str() {
349 "EventID" => {
350 event.id = EventId::new(value.parse().unwrap_or(0));
351 }
352 "Level" => {
353 let level_val: u8 = value.parse().unwrap_or(4);
354 event.level =
355 EventLevel::from_code(level_val).unwrap_or(EventLevel::Verbose);
356 }
357 "Channel" => {
358 event.channel = intern_channel(&value);
359 }
360 "Computer" => {
361 event.computer = value;
362 }
363 "EventRecordID" => {
364 if let Ok(rid) = value.parse::<u64>() {
365 event.record_id = Some(RecordId::new(rid));
366 }
367 }
368 _ => {}
369 }
370 }
371 Ok(XmlEvent::Empty(e)) => {
372 let tag_name = String::from_utf8_lossy(e.name().as_ref()).into_owned();
374
375 if in_system && tag_name == "Execution" {
376 for attr in e.attributes().flatten() {
377 let attr_name = String::from_utf8_lossy(attr.key.as_ref());
378 if let Ok(value) = String::from_utf8(attr.value.to_vec()) {
379 match attr_name.as_ref() {
380 "ProcessID" => {
381 if let Ok(pid) = value.parse::<u32>() {
382 event.process_id = Some(ProcessId::new(pid));
383 }
384 }
385 "ThreadID" => {
386 if let Ok(tid) = value.parse::<u32>() {
387 event.thread_id = Some(ThreadId::new(tid));
388 }
389 }
390 _ => {}
391 }
392 }
393 }
394 }
395 }
396 Ok(XmlEvent::Eof) => break,
397 Err(_) => {
398 return Err(CorruptedEvent {
399 record_id: event.record_id.map(|r| r.as_u64()),
400 component: Cow::Borrowed("quick-xml"),
401 reason: Cow::Borrowed("XML parsing error"),
402 });
403 }
404 _ => {}
405 }
406 buf.clear();
407 }
408
409 Ok(event)
410}
411
412fn extract_event_data_from_xml(xml: &str) -> Option<HashMap<Cow<'static, str>, String>> {
417 let mut reader = Reader::from_str(xml);
418 reader.config_mut().trim_text(true);
419
420 let mut data_fields = HashMap::new();
421 let mut buf = Vec::new();
422 let mut in_event_data = false;
423 let mut current_field_name: Option<String> = None;
424
425 loop {
426 match reader.read_event_into(&mut buf) {
427 Ok(XmlEvent::Start(e)) if e.name().as_ref() == b"EventData" => {
428 in_event_data = true;
429 }
430 Ok(XmlEvent::End(e)) if e.name().as_ref() == b"EventData" => {
431 in_event_data = false;
432 }
433 Ok(XmlEvent::Start(e)) if in_event_data && e.name().as_ref() == b"Data" => {
434 current_field_name = e
436 .attributes()
437 .filter_map(|a| a.ok())
438 .find(|attr| attr.key.as_ref() == b"Name")
439 .and_then(|attr| String::from_utf8(attr.value.to_vec()).ok());
440 }
441 Ok(XmlEvent::Text(e)) if in_event_data && current_field_name.is_some() => {
442 let value = String::from_utf8_lossy(e.as_ref()).into_owned();
444 let field_name = current_field_name.take().unwrap();
445 data_fields.insert(intern_field_name(&field_name), value);
446 }
447 Ok(XmlEvent::Eof) => break,
448 Err(_) => break, _ => {}
450 }
451 buf.clear();
452 }
453
454 if data_fields.is_empty() {
455 None
456 } else {
457 Some(data_fields)
458 }
459}
460
461fn parse_iso8601_timestamp(time_str: &str) -> Option<SystemTime> {
465 if time_str.len() < 20 {
467 return None;
468 }
469
470 let year: i32 = time_str.get(0..4)?.parse().ok()?;
472 let month: u32 = time_str.get(5..7)?.parse().ok()?;
473 let day: u32 = time_str.get(8..10)?.parse().ok()?;
474 let hour: u32 = time_str.get(11..13)?.parse().ok()?;
475 let minute: u32 = time_str.get(14..16)?.parse().ok()?;
476 let second: u32 = time_str.get(17..19)?.parse().ok()?;
477
478 let microseconds: u32 = if time_str.len() > 20 && time_str.as_bytes()[19] == b'.' {
480 time_str.get(20..26)?.parse().ok().unwrap_or(0)
481 } else {
482 0
483 };
484
485 let mut days_since_epoch: i64 = 0;
487
488 for y in 1970..year {
490 days_since_epoch += if is_leap_year(y) { 366 } else { 365 };
491 }
492
493 const DAYS_IN_MONTH: [u32; 12] = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
495 for m in 1..month {
496 days_since_epoch += DAYS_IN_MONTH[(m - 1) as usize] as i64;
497 if m == 2 && is_leap_year(year) {
499 days_since_epoch += 1;
500 }
501 }
502
503 days_since_epoch += (day - 1) as i64;
505
506 let total_seconds =
508 days_since_epoch * 86400 + (hour as i64 * 3600) + (minute as i64 * 60) + second as i64;
509
510 let duration =
512 Duration::from_secs(total_seconds as u64) + Duration::from_micros(microseconds as u64);
513 Some(UNIX_EPOCH + duration)
514}
515
516fn is_leap_year(year: i32) -> bool {
518 (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
519}
520
521#[allow(dead_code)]
523fn extract_u8_from_variant(variant: &EVT_VARIANT) -> Option<u8> {
524 unsafe {
525 if variant.Type == 2u32 {
526 Some(variant.Anonymous.ByteVal)
528 } else {
529 None
530 }
531 }
532}
533
534fn extract_u16_from_variant(variant: &EVT_VARIANT) -> Option<u16> {
535 unsafe {
536 if variant.Type == 6u32 {
537 Some(variant.Anonymous.UInt16Val)
539 } else {
540 None
541 }
542 }
543}
544
545fn extract_u32_from_variant(variant: &EVT_VARIANT) -> Option<u32> {
546 unsafe {
547 if variant.Type == 8u32 {
548 Some(variant.Anonymous.UInt32Val)
550 } else {
551 None
552 }
553 }
554}
555
556fn extract_u64_from_variant(variant: &EVT_VARIANT) -> Option<u64> {
557 unsafe {
558 if variant.Type == 10u32 {
559 Some(variant.Anonymous.UInt64Val)
561 } else {
562 None
563 }
564 }
565}
566
567fn extract_string_from_variant(variant: &EVT_VARIANT) -> Option<String> {
568 unsafe {
569 if variant.Type == 21u32 {
570 let pwstr = variant.Anonymous.StringVal;
572 if !pwstr.is_null() {
573 let len = (0..).take_while(|&i| *pwstr.0.offset(i) != 0).count();
574 let slice = std::slice::from_raw_parts(pwstr.0, len);
575 return Some(String::from_utf16_lossy(slice).to_string());
576 }
577 }
578 None
579 }
580}
581
582fn extract_systemtime_from_variant(variant: &EVT_VARIANT) -> Option<SystemTime> {
583 unsafe {
584 if variant.Type == 29u32 {
585 let filetime = variant.Anonymous.FileTimeVal;
587 const FILETIME_EPOCH_DIFF: u64 = 116444736000000000; if filetime > FILETIME_EPOCH_DIFF {
592 let unix_time_100ns = filetime - FILETIME_EPOCH_DIFF;
593 let seconds = unix_time_100ns / 10_000_000;
594 let nanos = (unix_time_100ns % 10_000_000) * 100;
595
596 return Some(UNIX_EPOCH + std::time::Duration::new(seconds, nanos as u32));
597 }
598 }
599 None
600 }
601}
602
603pub fn format_message(event_handle: EVT_HANDLE, provider_name: &str) -> Result<Option<String>> {
608 let metadata = get_publisher_metadata(provider_name)?;
610
611 let Some(metadata_handle) = metadata else {
613 return Ok(None);
614 };
615
616 let mut buffer_size = 4096u32;
618 let mut buffer: Vec<u16>;
619
620 loop {
621 buffer = vec![0u16; (buffer_size / 2) as usize];
622 let mut buffer_used = 0u32;
623
624 let result = unsafe {
625 EvtFormatMessage(
626 metadata_handle.0,
627 event_handle,
628 0, None, EvtFormatMessageEvent.0, Some(&mut buffer[..]),
632 &mut buffer_used,
633 )
634 };
635
636 if result.is_ok() {
637 let len = if buffer_used > 0 {
639 (buffer_used / 2) as usize
640 } else {
641 0
642 };
643 if len > 0 && len <= buffer.len() {
644 let actual_len = if len > 0 && buffer[len - 1] == 0 {
646 len - 1
647 } else {
648 len
649 };
650 let message = String::from_utf16_lossy(&buffer[..actual_len]);
651 return Ok(Some(message));
652 }
653 return Ok(None);
654 }
655
656 let error = unsafe { windows::Win32::Foundation::GetLastError() };
658 if error.0 == 122 {
659 buffer_size = buffer_used;
662 if buffer_size > 1048576 {
663 return Ok(None); }
666 continue;
667 } else {
668 return Ok(None);
670 }
671 }
672}
673
674#[cfg(test)]
675mod tests {
676 use super::*;
677
678 #[test]
679 fn test_publisher_metadata_cache_initialization() {
680 let cache = PUBLISHER_METADATA_CACHE.get_or_init(|| Arc::new(RwLock::new(HashMap::new())));
682
683 let map = cache.read().unwrap();
684 assert!(map.is_empty());
685 }
686}