Rewrite the project
The previous project architecture was far too complicated and hard to maintain. The new one is much more simple. Although procedural macros are cools, they are a no-go on Rust-OpenSMTPD. Reports and filter are implemented (except data-line) but untested.
This commit is contained in:
parent
fc072743ad
commit
a6d4dd21c1
48 changed files with 1723 additions and 1493 deletions
|
@ -1,113 +0,0 @@
|
|||
// Copyright (c) 2019 Rodolphe Bréard
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::{parenthesized, Ident, Result, Token};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct OpenSmtpdAttributes {
|
||||
version: Ident,
|
||||
subsystem: Ident,
|
||||
events: Punctuated<Ident, Token![,]>,
|
||||
}
|
||||
|
||||
impl Parse for OpenSmtpdAttributes {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let version = input.parse()?;
|
||||
let _: Token![,] = input.parse()?;
|
||||
let subsystem = input.parse()?;
|
||||
let _: Token![,] = input.parse()?;
|
||||
let _match: Token![match] = input.parse()?;
|
||||
let content;
|
||||
let _ = parenthesized!(content in input);
|
||||
let events = content.parse_terminated(Ident::parse)?;
|
||||
Ok(OpenSmtpdAttributes {
|
||||
version,
|
||||
subsystem,
|
||||
events,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenSmtpdAttributes {
|
||||
pub(crate) fn get_version(&self) -> String {
|
||||
format!(
|
||||
"opensmtpd::entry::Version::{}",
|
||||
self.version.to_string().to_uppercase()
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn get_subsystem(&self) -> String {
|
||||
let subsystem = match self.subsystem.to_string().as_str() {
|
||||
"smtp_in" => "SmtpIn",
|
||||
"smtp_out" => "SmtpOut",
|
||||
_ => "",
|
||||
};
|
||||
format!("opensmtpd::entry::Subsystem::{}", subsystem)
|
||||
}
|
||||
|
||||
pub(crate) fn get_events(&self) -> String {
|
||||
let events = if self
|
||||
.events
|
||||
.iter()
|
||||
.any(|e| e.to_string().to_lowercase().as_str() == "all")
|
||||
{
|
||||
let lst = [
|
||||
"LinkAuth",
|
||||
"LinkConnect",
|
||||
"LinkDisconnect",
|
||||
"LinkIdentify",
|
||||
"LinkReset",
|
||||
"LinkTls",
|
||||
"TxBegin",
|
||||
"TxMail",
|
||||
"TxRcpt",
|
||||
"TxEnvelope",
|
||||
"TxData",
|
||||
"TxCommit",
|
||||
"TxRollback",
|
||||
"ProtocolClient",
|
||||
"ProtocolServer",
|
||||
"FilterResponse",
|
||||
"Timeout",
|
||||
];
|
||||
lst.iter()
|
||||
.map(|e| format!("opensmtpd::entry::Event::{}", e))
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
self.events
|
||||
.iter()
|
||||
.map(|e| {
|
||||
let name = match e.to_string().as_str() {
|
||||
"link_auth" => "LinkAuth",
|
||||
"link_connect" => "LinkConnect",
|
||||
"link_disconnect" => "LinkDisconnect",
|
||||
"link_identify" => "LinkIdentify",
|
||||
"link_reset" => "LinkReset",
|
||||
"link_tls" => "LinkTls",
|
||||
"tx_begin" => "TxBegin",
|
||||
"tx_mail" => "TxMail",
|
||||
"tx_rcpt" => "TxRcpt",
|
||||
"tx_envelope" => "TxEnvelope",
|
||||
"tx_data" => "TxData",
|
||||
"tx_commit" => "TxCommit",
|
||||
"tx_rollback" => "TxRollback",
|
||||
"protocol_client" => "ProtocolClient",
|
||||
"protocol_server" => "ProtocolServer",
|
||||
"filter_response" => "FilterResponse",
|
||||
"timeout" => "Timeout",
|
||||
_ => "",
|
||||
};
|
||||
format!("opensmtpd::entry::Event::{}", name)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
};
|
||||
format!("[{}]", events.join(", "))
|
||||
}
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
// Copyright (c) 2019 Rodolphe Bréard
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod attributes;
|
||||
|
||||
use attributes::OpenSmtpdAttributes;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_macro_input, ExprArray, ExprTry, ItemFn, ReturnType, TypePath};
|
||||
|
||||
macro_rules! parse_item {
|
||||
($item: expr, $type: ty) => {
|
||||
match syn::parse_str::<$type>($item) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
return TokenStream::from(e.to_compile_error());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn get_has_result(ret: &ReturnType) -> bool {
|
||||
match ret {
|
||||
ReturnType::Default => false,
|
||||
ReturnType::Type(_, _) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_inner_call(nb_args: usize, has_output: bool, has_result: bool) -> String {
|
||||
let mut call_params = Vec::new();
|
||||
if has_output {
|
||||
call_params.push("_output");
|
||||
}
|
||||
call_params.push("_entry");
|
||||
if nb_args >= 2 {
|
||||
call_params.push("_filter_ctx");
|
||||
}
|
||||
if nb_args >= 3 {
|
||||
call_params.push("_session_ctx");
|
||||
}
|
||||
let call_params = call_params.join(", ");
|
||||
let s = format!("inner_fn({})", &call_params);
|
||||
if has_result {
|
||||
return format!("{}?", s);
|
||||
}
|
||||
format!(
|
||||
"(|{params}| -> Result<(), String> {{ {inner_fn}; Ok(()) }})({params})?",
|
||||
params = &call_params,
|
||||
inner_fn = s
|
||||
)
|
||||
}
|
||||
|
||||
fn get_tokenstream(
|
||||
attr: TokenStream,
|
||||
input: TokenStream,
|
||||
type_str: &str,
|
||||
has_output: bool,
|
||||
) -> TokenStream {
|
||||
let kind = parse_item!(type_str, TypePath);
|
||||
|
||||
// Parse the procedural macro attributes
|
||||
let attr = parse_macro_input!(attr as OpenSmtpdAttributes);
|
||||
let version = parse_item!(&attr.get_version(), TypePath);
|
||||
let subsystem = parse_item!(&attr.get_subsystem(), TypePath);
|
||||
let events = parse_item!(&attr.get_events(), ExprArray);
|
||||
|
||||
// Parse the user-supplied function
|
||||
let item = parse_macro_input!(input as ItemFn);
|
||||
let fn_name = &item.sig.ident;
|
||||
let fn_params = &item.sig.inputs;
|
||||
let fn_return = &item.sig.output;
|
||||
let fn_body = &item.block;
|
||||
let has_result = get_has_result(&item.sig.output);
|
||||
let inner_call = parse_item!(
|
||||
&get_inner_call(fn_params.len(), has_output, has_result),
|
||||
ExprTry
|
||||
);
|
||||
|
||||
// Build the new function
|
||||
let output = quote! {
|
||||
fn #fn_name() -> opensmtpd::Handler::<OpenSmtpdSessionContextType, OpenSmtpdFilterContextType> {
|
||||
opensmtpd::Handler::new(
|
||||
#version,
|
||||
#kind,
|
||||
#subsystem,
|
||||
&#events,
|
||||
|_output: &mut dyn opensmtpd::output::FilterOutput, _entry: &opensmtpd::entry::Entry, _session_ctx: &mut OpenSmtpdSessionContextType, _filter_ctx: &mut OpenSmtpdFilterContextType| {
|
||||
let inner_fn = |#fn_params| #fn_return {
|
||||
#fn_body
|
||||
};
|
||||
let _ = #inner_call;
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
output.into()
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn report(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
get_tokenstream(attr, input, "opensmtpd::entry::Kind::Report", false)
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn filter(attr: TokenStream, input: TokenStream) -> TokenStream {
|
||||
get_tokenstream(attr, input, "opensmtpd::entry::Kind::Filter", true)
|
||||
}
|
Reference in a new issue