Initial commit

This commit is contained in:
2025-05-11 15:19:30 +02:00
commit 65b97306ed
8 changed files with 1484 additions and 0 deletions

65
src/parser.rs Normal file
View File

@@ -0,0 +1,65 @@
use crate::models::{LogEntry, SMTPSession};
use chrono::{Datelike, NaiveDateTime};
use regex::Regex;
use std::collections::HashMap;
lazy_static::lazy_static! {
static ref LOG_LINE_RE: Regex = Regex::new(
r"^(?P<month>\w+)\s+(?P<day>\d+)\s+(?P<time>\d+:\d+:\d+)\s+(?P<hostname>\S+)\s+(?P<process>\S+):\s+(?P<message>.*)$"
).unwrap();
}
pub fn parse_log_file(contents: &str) -> anyhow::Result<Vec<SMTPSession>> {
let mut sessions: HashMap<String, SMTPSession> = HashMap::new();
for line in contents.lines() {
if let Some(captures) = LOG_LINE_RE.captures(line) {
let month = &captures["month"];
let day = &captures["day"];
let time = &captures["time"];
let hostname = captures["hostname"].to_string();
let process = captures["process"].to_string();
let message = captures["message"].to_string();
// Create a timestamp string in the format that chrono can parse
let year = chrono::Local::now().year();
let timestamp_str = format!("{} {} {} {}", year, month, day, time);
let timestamp = match NaiveDateTime::parse_from_str(&timestamp_str, "%Y %b %d %H:%M:%S") {
Ok(ts) => ts,
Err(_) => continue, // Skip lines with invalid timestamps
};
let entry = LogEntry {
timestamp,
hostname,
process,
message: message.clone(),
};
// Extract session ID from the message
if let Some(id_caps) = crate::models::SESSION_ID_RE.captures(&message) {
let session_id = id_caps[1].to_string();
// Get or create the session
let session = sessions.entry(session_id.clone())
.or_insert_with(|| SMTPSession::new(&session_id));
// Add the entry to the session
session.add_entry(entry);
// Update session information from the message
session.update_from_message(&message);
}
}
}
// Convert the HashMap to a Vec and sort by start time (most recent first)
let mut sessions: Vec<SMTPSession> = sessions.into_iter().map(|(_, v)| v).collect();
sessions.sort_by(|a, b| {
let a_time = a.start_time.unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::MIN_UTC.naive_local());
let b_time = b.start_time.unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::MIN_UTC.naive_local());
b_time.cmp(&a_time) // Sort in descending order (newest first)
});
Ok(sessions)
}