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.
This commit is contained in:
Rodolphe Breard 2019-01-12 00:17:59 +01:00
parent ea710408d4
commit 11f3712138
5 changed files with 76 additions and 42 deletions

View file

@ -13,8 +13,13 @@ pub fn event(attr: TokenStream, input: TokenStream) -> TokenStream {
let fn_body = &item.block; let fn_body = &item.block;
let fn_output = &item.decl.output; let fn_output = &item.decl.output;
let output = quote! { let output = quote! {
fn #fn_name() -> opensmtpd::EventHandler { // TODO: set the correct EventHandler type
opensmtpd::EventHandler::new(#attr, |#fn_params| #fn_output #fn_body) fn #fn_name() -> opensmtpd::EventHandler<opensmtpd::NoContext> {
// TODO: set the correct Callback type
opensmtpd::EventHandler::new(
#attr,
opensmtpd::Callback::CtxMut(|#fn_params| #fn_output #fn_body)
)
} }
}; };
output.into() output.into()

View file

@ -1,15 +1,15 @@
use env_logger::{Builder, Env}; use env_logger::{Builder, Env};
use log::{debug, info}; use log::{debug, info};
use opensmtpd::{event, handlers, Entry, Response, SmtpIn}; use opensmtpd::{event, handlers, Entry, NoContext, Response, SmtpIn};
#[event(Any)] #[event(Any)]
fn on_event(entry: &Entry) -> Response { fn on_event(_context: &mut NoContext, entry: &Entry) -> Response {
debug!("Event received: {:?}", entry); debug!("Event received: {:?}", entry);
Response::None Response::None
} }
#[event(LinkConnect)] #[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); info!("New client on session {:x}.", entry.session_id);
Response::None Response::None
} }

View file

@ -43,8 +43,8 @@ impl From<std::sync::mpsc::SendError<Entry>> for Error {
} }
} }
impl From<std::sync::mpsc::SendError<EventHandler>> for Error { impl<T> From<std::sync::mpsc::SendError<EventHandler<T>>> for Error {
fn from(error: std::sync::mpsc::SendError<EventHandler>) -> Self { fn from(error: std::sync::mpsc::SendError<EventHandler<T>>) -> Self {
Error::new(&format!("IO error: {}", error)) Error::new(&format!("IO error: {}", error))
} }
} }

View file

@ -9,12 +9,18 @@ pub enum MatchEvent {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct EventHandler { pub enum Callback<T> {
event: MatchEvent, NoCtx(fn(&Entry) -> Response),
callback: (fn(&Entry) -> Response), Ctx(fn(&T, &Entry) -> Response),
CtxMut(fn(&mut T, &Entry) -> Response),
}
#[derive(Clone)]
pub struct EventHandler<T> {
event: MatchEvent,
callback: Callback<T>,
} }
impl EventHandler {
fn get_events_from_string(event_str: &str) -> MatchEvent { fn get_events_from_string(event_str: &str) -> MatchEvent {
let mut events = Vec::new(); let mut events = Vec::new();
for name in event_str.split(" , ") { for name in event_str.split(" , ") {
@ -30,9 +36,10 @@ impl EventHandler {
MatchEvent::Evt(events) MatchEvent::Evt(events)
} }
pub fn new(event_str: &str, callback: (fn(&Entry) -> Response)) -> Self { impl<T: Clone + Default> EventHandler<T> {
pub fn new(event_str: &str, callback: Callback<T>) -> Self {
EventHandler { EventHandler {
event: EventHandler::get_events_from_string(event_str), event: get_events_from_string(event_str),
callback, callback,
} }
} }
@ -44,9 +51,27 @@ impl EventHandler {
} }
} }
pub fn call(&self, entry: &Entry) { pub fn call(&self, entry: &Entry, context: &mut T) {
match (self.callback)(entry) { let ret = match self.callback {
Response::None => {} 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 },
);
} }
} }

View file

@ -11,7 +11,7 @@ use std::thread;
pub use crate::entry::{Entry, Event}; pub use crate::entry::{Entry, Event};
pub use crate::errors::Error; 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; pub use opensmtpd_derive::event;
#[macro_export] #[macro_export]
@ -31,13 +31,16 @@ pub enum Response {
None, None,
} }
struct SessionHandler { #[derive(Clone, Default)]
pub struct NoContext;
struct SessionHandler<T> {
entry_rx: mpsc::Receiver<Entry>, entry_rx: mpsc::Receiver<Entry>,
event_handlers: Vec<EventHandler>, event_handlers: Vec<EventHandler<T>>,
} }
impl SessionHandler { impl<T: Clone + Default> SessionHandler<T> {
fn new(entry_rx: mpsc::Receiver<Entry>, handlers_rx: &mpsc::Receiver<EventHandler>) -> Self { fn new(entry_rx: mpsc::Receiver<Entry>, handlers_rx: &mpsc::Receiver<EventHandler<T>>) -> Self {
debug!( debug!(
"New thread for session {}", "New thread for session {}",
thread::current().name().unwrap() thread::current().name().unwrap()
@ -54,10 +57,11 @@ impl SessionHandler {
} }
fn read_entries(&self) { fn read_entries(&self) {
let mut context: T = Default::default();
for e in self.entry_rx.iter() { for e in self.entry_rx.iter() {
for h in self.event_handlers.iter() { for h in self.event_handlers.iter() {
if h.is_callable(&e.event) { if h.is_callable(&e.event) {
h.call(&e); h.call(&e, &mut context);
} }
} }
} }
@ -65,15 +69,15 @@ impl SessionHandler {
} }
#[derive(Default)] #[derive(Default)]
pub struct SmtpIn { pub struct SmtpIn<T> {
sessions: HashMap<u64, (mpsc::Sender<Entry>, thread::JoinHandle<()>)>, sessions: HashMap<u64, (mpsc::Sender<Entry>, thread::JoinHandle<()>)>,
event_handlers: Vec<EventHandler>, event_handlers: Vec<EventHandler<T>>,
} }
impl SmtpIn { impl<T: Clone + Default + 'static> SmtpIn<T> {
/// Read a line from the standard input. /// Read a line from the standard input.
/// Since EOF should not append, it is considered as an error. /// Since EOF should not append, it is considered as an error.
fn read() -> Result<String, Error> { fn read(&self) -> Result<String, Error> {
let mut input = String::new(); let mut input = String::new();
let nb = io::stdin().read_line(&mut input)?; let nb = io::stdin().read_line(&mut input)?;
match nb { match nb {
@ -133,7 +137,7 @@ impl SmtpIn {
} }
} }
pub fn event_handlers(&mut self, handlers: Vec<EventHandler>) -> &mut Self { pub fn event_handlers(&mut self, handlers: Vec<EventHandler<T>>) -> &mut Self {
self.event_handlers = handlers.to_owned(); self.event_handlers = handlers.to_owned();
self self
} }
@ -141,7 +145,7 @@ impl SmtpIn {
/// Run the infinite loop that will read and process input from stdin. /// Run the infinite loop that will read and process input from stdin.
pub fn run(&mut self) { pub fn run(&mut self) {
loop { loop {
let line = match SmtpIn::read() { let line = match self.read() {
Ok(l) => l, Ok(l) => l,
Err(e) => { Err(e) => {
self.graceful_exit_children(); self.graceful_exit_children();