From 11f37121389ff2dea2061a507dadaaf73495791c Mon Sep 17 00:00:00 2001 From: Rodolphe Breard Date: Sat, 12 Jan 2019 00:17:59 +0100 Subject: [PATCH] Add a context object the callbacks Many filters requires to keep a state within a session. Although it not currently possible to do so, the callback now accept a mutable object of type NoContext. This is the first step, the next one will be to accept any type that implements both Clone and Default. An other step will be to allow the user to define different callback parameters depending on his/her needs. --- opensmtpd-derive/src/lib.rs | 9 ++++- opensmtpd/examples/dummy.rs | 6 +-- opensmtpd/src/errors.rs | 4 +- opensmtpd/src/event_handlers.rs | 71 ++++++++++++++++++++++----------- opensmtpd/src/lib.rs | 28 +++++++------ 5 files changed, 76 insertions(+), 42 deletions(-) diff --git a/opensmtpd-derive/src/lib.rs b/opensmtpd-derive/src/lib.rs index 09a81d1..d6dc370 100644 --- a/opensmtpd-derive/src/lib.rs +++ b/opensmtpd-derive/src/lib.rs @@ -13,8 +13,13 @@ pub fn event(attr: TokenStream, input: TokenStream) -> TokenStream { let fn_body = &item.block; let fn_output = &item.decl.output; let output = quote! { - fn #fn_name() -> opensmtpd::EventHandler { - opensmtpd::EventHandler::new(#attr, |#fn_params| #fn_output #fn_body) + // TODO: set the correct EventHandler type + fn #fn_name() -> opensmtpd::EventHandler { + // TODO: set the correct Callback type + opensmtpd::EventHandler::new( + #attr, + opensmtpd::Callback::CtxMut(|#fn_params| #fn_output #fn_body) + ) } }; output.into() diff --git a/opensmtpd/examples/dummy.rs b/opensmtpd/examples/dummy.rs index 593b455..076af59 100644 --- a/opensmtpd/examples/dummy.rs +++ b/opensmtpd/examples/dummy.rs @@ -1,15 +1,15 @@ use env_logger::{Builder, Env}; use log::{debug, info}; -use opensmtpd::{event, handlers, Entry, Response, SmtpIn}; +use opensmtpd::{event, handlers, Entry, NoContext, Response, SmtpIn}; #[event(Any)] -fn on_event(entry: &Entry) -> Response { +fn on_event(_context: &mut NoContext, entry: &Entry) -> Response { debug!("Event received: {:?}", entry); Response::None } #[event(LinkConnect)] -fn on_connect(entry: &Entry) -> Response { +fn on_connect(_context: &mut NoContext, entry: &Entry) -> Response { info!("New client on session {:x}.", entry.session_id); Response::None } diff --git a/opensmtpd/src/errors.rs b/opensmtpd/src/errors.rs index 95ad784..26f707b 100644 --- a/opensmtpd/src/errors.rs +++ b/opensmtpd/src/errors.rs @@ -43,8 +43,8 @@ impl From> for Error { } } -impl From> for Error { - fn from(error: std::sync::mpsc::SendError) -> Self { +impl From>> for Error { + fn from(error: std::sync::mpsc::SendError>) -> Self { Error::new(&format!("IO error: {}", error)) } } diff --git a/opensmtpd/src/event_handlers.rs b/opensmtpd/src/event_handlers.rs index c573eb7..3543d84 100644 --- a/opensmtpd/src/event_handlers.rs +++ b/opensmtpd/src/event_handlers.rs @@ -9,30 +9,37 @@ pub enum MatchEvent { } #[derive(Clone)] -pub struct EventHandler { - event: MatchEvent, - callback: (fn(&Entry) -> Response), +pub enum Callback { + NoCtx(fn(&Entry) -> Response), + Ctx(fn(&T, &Entry) -> Response), + CtxMut(fn(&mut T, &Entry) -> Response), } -impl EventHandler { - fn get_events_from_string(event_str: &str) -> MatchEvent { - let mut events = Vec::new(); - for name in event_str.split(" , ") { - match name { - "Any" | "All" => { - return MatchEvent::All; - } - _ => if let Ok(e) = Event::from_str(name) { - events.push(e); - }, - } - } - MatchEvent::Evt(events) - } +#[derive(Clone)] +pub struct EventHandler { + event: MatchEvent, + callback: Callback, +} - pub fn new(event_str: &str, callback: (fn(&Entry) -> Response)) -> Self { +fn get_events_from_string(event_str: &str) -> MatchEvent { + let mut events = Vec::new(); + for name in event_str.split(" , ") { + match name { + "Any" | "All" => { + return MatchEvent::All; + } + _ => if let Ok(e) = Event::from_str(name) { + events.push(e); + }, + } + } + MatchEvent::Evt(events) +} + +impl EventHandler { + pub fn new(event_str: &str, callback: Callback) -> Self { EventHandler { - event: EventHandler::get_events_from_string(event_str), + event: get_events_from_string(event_str), callback, } } @@ -44,9 +51,27 @@ impl EventHandler { } } - pub fn call(&self, entry: &Entry) { - match (self.callback)(entry) { - Response::None => {} + pub fn call(&self, entry: &Entry, context: &mut T) { + let ret = match self.callback { + Callback::NoCtx(f) => f(entry), + Callback::Ctx(f) => f(context, entry), + Callback::CtxMut(f) => f(context, entry), }; + match ret { + Response::None => {} + } + } +} + +#[cfg(test)] +mod test { + use crate::*; + + #[test] + fn test_eventhandler_build() { + EventHandler::new( + "Any", + |_context: &mut NoContext, _entry: &Entry| -> Response { Response::None }, + ); } } diff --git a/opensmtpd/src/lib.rs b/opensmtpd/src/lib.rs index 99600a8..c50f4f0 100644 --- a/opensmtpd/src/lib.rs +++ b/opensmtpd/src/lib.rs @@ -11,7 +11,7 @@ use std::thread; pub use crate::entry::{Entry, Event}; pub use crate::errors::Error; -pub use crate::event_handlers::{EventHandler, MatchEvent}; +pub use crate::event_handlers::{Callback, EventHandler, MatchEvent}; pub use opensmtpd_derive::event; #[macro_export] @@ -31,13 +31,16 @@ pub enum Response { None, } -struct SessionHandler { +#[derive(Clone, Default)] +pub struct NoContext; + +struct SessionHandler { entry_rx: mpsc::Receiver, - event_handlers: Vec, + event_handlers: Vec>, } -impl SessionHandler { - fn new(entry_rx: mpsc::Receiver, handlers_rx: &mpsc::Receiver) -> Self { +impl SessionHandler { + fn new(entry_rx: mpsc::Receiver, handlers_rx: &mpsc::Receiver>) -> Self { debug!( "New thread for session {}", thread::current().name().unwrap() @@ -54,10 +57,11 @@ impl SessionHandler { } fn read_entries(&self) { + 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) { - h.call(&e); + h.call(&e, &mut context); } } } @@ -65,15 +69,15 @@ impl SessionHandler { } #[derive(Default)] -pub struct SmtpIn { +pub struct SmtpIn { sessions: HashMap, thread::JoinHandle<()>)>, - event_handlers: Vec, + event_handlers: Vec>, } -impl SmtpIn { +impl SmtpIn { /// Read a line from the standard input. /// Since EOF should not append, it is considered as an error. - fn read() -> Result { + fn read(&self) -> Result { let mut input = String::new(); let nb = io::stdin().read_line(&mut input)?; match nb { @@ -133,7 +137,7 @@ impl SmtpIn { } } - pub fn event_handlers(&mut self, handlers: Vec) -> &mut Self { + pub fn event_handlers(&mut self, handlers: Vec>) -> &mut Self { self.event_handlers = handlers.to_owned(); self } @@ -141,7 +145,7 @@ impl SmtpIn { /// Run the infinite loop that will read and process input from stdin. pub fn run(&mut self) { loop { - let line = match SmtpIn::read() { + let line = match self.read() { Ok(l) => l, Err(e) => { self.graceful_exit_children();