diff --git a/opensmtpd/examples/dummy.rs b/opensmtpd/examples/dummy.rs index 673dab1..a01eb14 100644 --- a/opensmtpd/examples/dummy.rs +++ b/opensmtpd/examples/dummy.rs @@ -8,7 +8,7 @@ fn on_event(entry: &Entry) { #[event(LinkConnect)] fn on_connect(entry: &Entry) { - info!("New client on session {:x}.", entry.session_id); + info!("New client on session {:x}.", entry.get_session_id()); } fn main() { diff --git a/opensmtpd/examples/session_event_counter.rs b/opensmtpd/examples/session_event_counter.rs index f75359e..90d54c4 100644 --- a/opensmtpd/examples/session_event_counter.rs +++ b/opensmtpd/examples/session_event_counter.rs @@ -9,7 +9,7 @@ struct MyContext { #[report(Any)] fn on_report(ctx: &mut MyContext, entry: &Entry) { ctx.nb += 1; - info!("Event received: {}, {}", entry.session_id, ctx.nb); + info!("Event received: {}, {}", entry.get_session_id(), ctx.nb); } fn main() { diff --git a/opensmtpd/src/entry.rs b/opensmtpd/src/entry.rs index 03c82b2..99f7b94 100644 --- a/opensmtpd/src/entry.rs +++ b/opensmtpd/src/entry.rs @@ -16,6 +16,11 @@ use nom::sequence::preceded; use nom::IResult; use std::str::FromStr; +#[derive(Clone, Debug, PartialEq)] +enum Version { + V1, +} + #[derive(Clone, Debug, PartialEq)] pub enum Kind { Report, @@ -43,20 +48,23 @@ pub enum Event { TxRollback, ProtocolClient, ProtocolServer, - Timeout, FilterResponse, + Timeout, } impl FromStr for Event { type Err = Error; fn from_str(s: &str) -> Result { - let s = s - .to_lowercase() - .replace("link", "link-") - .replace("tx", "tx-") - .replace("protocol", "protocol-") - .replace("filter", "filter-"); + let s = s.to_lowercase(); + let s = if !s.contains('-') { + s.replace("link", "link-") + .replace("tx", "tx-") + .replace("protocol", "protocol-") + .replace("filter", "filter-") + } else { + s + }; let (_, evt) = parse_event(&s)?; Ok(evt) } @@ -78,8 +86,8 @@ impl ToString for Event { Event::TxRollback => "tx-rollback", Event::ProtocolClient => "protocol-client", Event::ProtocolServer => "protocol-server", - Event::Timeout => "timeout", Event::FilterResponse => "filter-response", + Event::Timeout => "timeout", }; String::from(s) } @@ -98,15 +106,9 @@ impl TimeVal { } #[derive(Debug)] -pub struct Entry { - pub kind: Kind, - pub version: u8, - pub timestamp: TimeVal, - pub subsystem: Subsystem, - pub event: Event, - pub token: Option, - pub session_id: u64, - pub params: Option, +pub enum Entry { + V1Report(V1Report), + V1Filter(V1Filter), } impl FromStr for Entry { @@ -118,6 +120,48 @@ impl FromStr for Entry { } } +impl Entry { + pub fn get_event(&self) -> Event { + match self { + Entry::V1Report(r) => r.event.to_owned(), + Entry::V1Filter(f) => f.event.to_owned(), + } + } + + pub fn get_session_id(&self) -> u64 { + match self { + Entry::V1Report(r) => r.session_id, + Entry::V1Filter(f) => f.session_id, + } + } + + pub fn is_disconnect(&self) -> bool { + match self { + Entry::V1Report(r) => r.event == Event::LinkDisconnect, + Entry::V1Filter(_) => false, + } + } +} + +#[derive(Debug)] +pub struct V1Report { + pub timestamp: TimeVal, + pub subsystem: Subsystem, + pub event: Event, + pub session_id: u64, + pub params: Option, +} + +#[derive(Debug)] +pub struct V1Filter { + pub timestamp: TimeVal, + pub subsystem: Subsystem, + pub event: Event, + pub session_id: u64, + pub token: u64, + pub params: Option, +} + fn separator(input: &str) -> IResult<&str, &str> { tag("|")(input) } @@ -129,8 +173,8 @@ fn parse_kind(input: &str) -> IResult<&str, Kind> { ))(input) } -fn parse_version(input: &str) -> IResult<&str, u8> { - map_res(digit1, |s: &str| s.parse::())(input) +fn parse_version(input: &str) -> IResult<&str, Version> { + value(Version::V1, tag("1"))(input) } fn parse_timestamp(input: &str) -> IResult<&str, TimeVal> { @@ -163,8 +207,8 @@ fn parse_event(input: &str) -> IResult<&str, Event> { value(Event::TxRollback, tag("tx-rollback")), value(Event::ProtocolClient, tag("protocol-client")), value(Event::ProtocolServer, tag("protocol-server")), - value(Event::Timeout, tag("timeout")), value(Event::FilterResponse, tag("filter-response")), + value(Event::Timeout, tag("timeout")), ))(input) } @@ -181,38 +225,63 @@ fn parse_params(input: &str) -> IResult<&str, String> { Ok((input, params.0.into_iter().collect())) } -fn parse_entry(input: &str) -> IResult<&str, Entry> { - let (input, kind) = parse_kind(input)?; - let (input, _) = separator(input)?; - let (input, version) = parse_version(input)?; - let (input, _) = separator(input)?; +fn parse_v1_report(input: &str) -> IResult<&str, Entry> { let (input, timestamp) = parse_timestamp(input)?; let (input, _) = separator(input)?; let (input, subsystem) = parse_subsystem(input)?; let (input, _) = separator(input)?; let (input, event) = parse_event(input)?; - let (input, token) = if kind == Kind::Filter { - let (input, _) = separator(input)?; - let (input, token) = parse_token(input)?; - (input, Some(token)) - } else { - (input, None) - }; let (input, _) = separator(input)?; let (input, session_id) = parse_session_id(input)?; let (input, params) = opt(preceded(separator, parse_params))(input)?; if params.is_none() { let _ = line_ending(input)?; } - let entry = Entry { - kind, - version, + let report = V1Report { timestamp, subsystem, event, - token, session_id, params, }; + Ok((input, Entry::V1Report(report))) +} + +fn parse_v1_filter(input: &str) -> IResult<&str, Entry> { + let (input, timestamp) = parse_timestamp(input)?; + let (input, _) = separator(input)?; + let (input, subsystem) = parse_subsystem(input)?; + let (input, _) = separator(input)?; + let (input, event) = parse_event(input)?; + let (input, _) = separator(input)?; + let (input, token) = parse_token(input)?; + let (input, _) = separator(input)?; + let (input, session_id) = parse_session_id(input)?; + let (input, params) = opt(preceded(separator, parse_params))(input)?; + if params.is_none() { + let _ = line_ending(input)?; + } + let filter = V1Filter { + timestamp, + subsystem, + event, + session_id, + token, + params, + }; + Ok((input, Entry::V1Filter(filter))) +} + +fn parse_entry(input: &str) -> IResult<&str, Entry> { + let (input, kind) = parse_kind(input)?; + let (input, _) = separator(input)?; + let (input, version) = parse_version(input)?; + let (input, _) = separator(input)?; + let (input, entry) = match version { + Version::V1 => match kind { + Kind::Report => parse_v1_report(input)?, + Kind::Filter => parse_v1_filter(input)?, + }, + }; Ok((input, entry)) } diff --git a/opensmtpd/src/lib.rs b/opensmtpd/src/lib.rs index 757510a..763a5f1 100644 --- a/opensmtpd/src/lib.rs +++ b/opensmtpd/src/lib.rs @@ -91,22 +91,23 @@ impl SmtpIn { /// already exists, creates it. fn dispatch(&mut self, input: &str) -> Result<(), Error> { let entry = Entry::from_str(input)?; - let id = entry.session_id; - let disconnect = entry.event == Event::LinkDisconnect; + let id = entry.get_session_id(); + let disconnect = entry.is_disconnect(); let channel = match self.sessions.get(&id) { Some((r, _)) => r, None => { let (handlers_tx, handlers_rx) = mpsc::channel(); let (entry_tx, entry_rx) = mpsc::channel(); - let name = entry.session_id.to_string(); + let name = entry.get_session_id().to_string(); let handle = thread::Builder::new().name(name).spawn(move || { SessionHandler::new(entry_rx, &handlers_rx).read_entries(); })?; for h in self.event_handlers.iter() { handlers_tx.send(h.clone())?; } - self.sessions.insert(entry.session_id, (entry_tx, handle)); - let (r, _) = &self.sessions[&entry.session_id]; + self.sessions + .insert(entry.get_session_id(), (entry_tx, handle)); + let (r, _) = &self.sessions[&entry.get_session_id()]; r } }; diff --git a/opensmtpd/src/session_handler.rs b/opensmtpd/src/session_handler.rs index 9c7eb79..90eff57 100644 --- a/opensmtpd/src/session_handler.rs +++ b/opensmtpd/src/session_handler.rs @@ -41,7 +41,7 @@ impl SessionHandler { let mut context: T = Default::default(); for e in self.entry_rx.iter() { for h in self.event_handlers.iter() { - if h.is_callable(&e.event) { + if h.is_callable(&e.get_event()) { h.call(&e, &mut context); } }