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:
parent
ea710408d4
commit
11f3712138
5 changed files with 76 additions and 42 deletions
|
@ -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<opensmtpd::NoContext> {
|
||||
// TODO: set the correct Callback type
|
||||
opensmtpd::EventHandler::new(
|
||||
#attr,
|
||||
opensmtpd::Callback::CtxMut(|#fn_params| #fn_output #fn_body)
|
||||
)
|
||||
}
|
||||
};
|
||||
output.into()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ impl From<std::sync::mpsc::SendError<Entry>> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::sync::mpsc::SendError<EventHandler>> for Error {
|
||||
fn from(error: std::sync::mpsc::SendError<EventHandler>) -> Self {
|
||||
impl<T> From<std::sync::mpsc::SendError<EventHandler<T>>> for Error {
|
||||
fn from(error: std::sync::mpsc::SendError<EventHandler<T>>) -> Self {
|
||||
Error::new(&format!("IO error: {}", error))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,30 +9,37 @@ pub enum MatchEvent {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventHandler {
|
||||
event: MatchEvent,
|
||||
callback: (fn(&Entry) -> Response),
|
||||
pub enum Callback<T> {
|
||||
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<T> {
|
||||
event: MatchEvent,
|
||||
callback: Callback<T>,
|
||||
}
|
||||
|
||||
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<T: Clone + Default> EventHandler<T> {
|
||||
pub fn new(event_str: &str, callback: Callback<T>) -> 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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T> {
|
||||
entry_rx: mpsc::Receiver<Entry>,
|
||||
event_handlers: Vec<EventHandler>,
|
||||
event_handlers: Vec<EventHandler<T>>,
|
||||
}
|
||||
|
||||
impl SessionHandler {
|
||||
fn new(entry_rx: mpsc::Receiver<Entry>, handlers_rx: &mpsc::Receiver<EventHandler>) -> Self {
|
||||
impl<T: Clone + Default> SessionHandler<T> {
|
||||
fn new(entry_rx: mpsc::Receiver<Entry>, handlers_rx: &mpsc::Receiver<EventHandler<T>>) -> 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<T> {
|
||||
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.
|
||||
/// 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 nb = io::stdin().read_line(&mut input)?;
|
||||
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
|
||||
}
|
||||
|
@ -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();
|
||||
|
|
Reference in a new issue