///|
/// Extracted Windows Event Log fields.
pub(all) struct Event {
record_id : UInt64
event_id : UInt
level : Int
provider : String
channel : String
computer : String
timestamp : UInt64
time_text : String
message : String
data : Array[(String, String)]
} derive(Debug)
///|
fn parse_uint_decimal(text : String) -> UInt {
let mut n = 0U
for i = 0; i < text.length(); i = i + 1 {
let cp = text[i].to_int()
if cp >= 48 && cp <= 57 {
n = n * 10U + (cp - 48).reinterpret_as_uint()
}
}
n
}
///|
fn parse_int_decimal(text : String) -> Int {
let mut n = 0
let mut sign = 1
for i = 0; i < text.length(); i = i + 1 {
let cp = text[i].to_int()
if cp == 45 && i == 0 {
sign = -1
} else if cp >= 48 && cp <= 57 {
n = n * 10 + (cp - 48)
}
}
n * sign
}
///|
fn node_text(
node : BinNode,
subs : Array[SubCell],
style : TimeStyle,
) -> Result[String, EvtxError] {
match node {
ValueText(text) => Ok(text)
NormalSub(index, _t) =>
if index < 0 || index >= subs.length() {
Err(InvalidSubstitution("index"))
} else {
sub_text(subs[index], style)
}
OptionalSub(index, _t) =>
if index < 0 || index >= subs.length() {
Err(InvalidSubstitution("index"))
} else {
sub_text(subs[index], style)
}
OpenStart(_n, _a, children) => {
let out = StringBuilder()
for i = 0; i < children.length(); i = i + 1 {
out.write_string(
match node_text(children[i], subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
)
}
Ok(out.to_string())
}
_ => Ok("")
}
}
///|
fn attr_value(
attrs : Array[BinAttr],
key : String,
subs : Array[SubCell],
style : TimeStyle,
) -> Result[String, EvtxError] {
for i = 0; i < attrs.length(); i = i + 1 {
if attrs[i].name == key {
return node_text(attrs[i].value, subs, style)
}
}
Ok("")
}
///|
fn walk_event(
node : BinNode,
subs : Array[SubCell],
style : TimeStyle,
acc : Event,
) -> Result[Event, EvtxError] {
match node {
OpenStart(name, attrs, children) => {
let mut next = acc
if name == "Provider" {
next = Event::{
..next,
provider: match attr_value(attrs, "Name", subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
}
} else if name == "EventID" {
next = Event::{
..next,
event_id: parse_uint_decimal(
match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
),
}
} else if name == "Level" {
next = Event::{
..next,
level: parse_int_decimal(
match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
),
}
} else if name == "TimeCreated" {
next = Event::{
..next,
time_text: match attr_value(attrs, "SystemTime", subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
}
} else if name == "EventRecordID" {
let rec_text = match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
}
let rec_n = parse_uint_decimal(rec_text)
next = Event::{
..next,
record_id: rec_n
.reinterpret_as_int()
.to_int64()
.reinterpret_as_uint64(),
}
} else if name == "Channel" {
next = Event::{
..next,
channel: match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
}
} else if name == "Computer" {
next = Event::{
..next,
computer: match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
},
}
} else if name == "Data" {
let key = match attr_value(attrs, "Name", subs, style) {
Ok(v) => v
Err(e) => return Err(e)
}
let value = match node_text(node, subs, style) {
Ok(v) => v
Err(e) => return Err(e)
}
let data = next.data
data.push((key, value))
if key == "Message" || next.message.length() == 0 {
next = Event::{ ..next, message: value, data, }
} else {
next = Event::{ ..next, data, }
}
}
let mut cur = next
for i = 0; i < children.length(); i = i + 1 {
cur = match walk_event(children[i], subs, style, cur) {
Ok(v) => v
Err(e) => return Err(e)
}
}
Ok(cur)
}
_ => Ok(acc)
}
}
///|
/// Pull conventional System/EventData fields out of a parsed record.
pub fn EvtxRecord::event(
self : EvtxRecord,
style : TimeStyle,
) -> Result[Event, EvtxError] {
let mut acc = Event::{
record_id: self.number,
event_id: 0U,
level: 0,
provider: "",
channel: "",
computer: "",
timestamp: self.timestamp,
time_text: format_filetime(self.timestamp, style),
message: "",
data: [],
}
for i = 0; i < self.root.template.length(); i = i + 1 {
acc = match
walk_event(self.root.template[i], self.root.substitutions, style, acc) {
Ok(v) => v
Err(e) => return Err(e)
}
}
Ok(acc)
}
///|
pub fn EvtxFile::events(
self : EvtxFile,
style : TimeStyle,
) -> Result[Array[Event], EvtxError] {
let out : Array[Event] = []
for i = 0; i < self.records.length(); i = i + 1 {
let ev = match self.records[i].event(style) {
Ok(v) => v
Err(e) => return Err(e)
}
out.push(ev)
}
Ok(out)
}
///|
pub extend Event with @moonbitlang/core/debug.Debug::{to_repr}