milter_callback/lib.rs
1//! Procedural macros that generate C callback functions for use in [milter]
2//! implementation.
3//!
4//! The attribute macros in this crate facilitate the creation of FFI callback
5//! functions that are required for the configuration of a [`Milter`]. The
6//! attribute macros are used to annotate ordinary Rust functions as milter
7//! callbacks. A C function is then generated that delegates to the Rust
8//! callback, safely, and taking care of conversion between Rust/C types.
9//!
10//! Callback functions serve the purpose of *event handlers* (hence the
11//! nomenclature `on_*`) for the various ‘events’ that happen during an SMTP
12//! conversation. For each of the stages in the milter protocol there is a
13//! corresponding [attribute macro].
14//!
15//! # Usage
16//!
17//! This crate is a dependency of the [milter][crate] crate, which re-exports
18//! all macros under its namespace. It is recommended to `use` macros from the
19//! `milter` namespace and not rely on this crate directly:
20//!
21//! ```
22//! use milter::{on_connect, on_close, Milter, Status};
23//! ```
24//!
25//! The remaining sections describe some additional features of the attribute
26//! macros.
27//!
28//! # Raw string inputs
29//!
30//! By default, callbacks receive string inputs of type `&str`, that is, UTF-8
31//! strings. Where UTF-8 encoding is not desired, it is possible to substitute
32//! the C string type `&CStr` for `&str` in your handler function signature in
33//! order to receive the raw bytes instead.
34//!
35//! Contrast the following example with the one shown at [`macro@on_header`].
36//!
37//! ```
38//! # #[macro_use] extern crate milter_callback;
39//! # use milter::{Context, Status};
40//! use std::ffi::CStr;
41//!
42//! #[on_header(header_callback)]
43//! fn handle_header(context: Context<()>, name: &CStr, value: &CStr) -> Status {
44//! // ^^^^^ ^^^^^
45//! Status::Continue
46//! }
47//! ```
48//!
49//! This feature is supported wherever `&str` appears in callback function
50//! arguments.
51//!
52//! # Callback results
53//!
54//! The return type of a callback function may be wrapped in a
55//! [`milter::Result`] where desired. This is a convenience: as most context API
56//! methods return `milter::Result`s these can then be unwrapped with the `?`
57//! operator.
58//!
59//! Compare the following example with the one shown at [`macro@on_eom`]. This code
60//! fragment also demonstrates the use of the `?` operator enabled by choosing
61//! this return type.
62//!
63//! ```
64//! # #[macro_use] extern crate milter_callback;
65//! # use milter::{Context, Status};
66//! #[on_eom(eom_callback)]
67//! fn handle_eom(context: Context<()>) -> milter::Result<Status> {
68//! // ^^^^^^^^^^^^^^^^^^^^^^
69//! if let Some(version) = context.api.macro_value("v")? {
70//! println!("{}", version);
71//! }
72//!
73//! Ok(Status::Continue)
74//! }
75//! ```
76//!
77//! This feature is supported on all callback functions.
78//!
79//! # Failure modes
80//!
81//! An `Err` result returned from a callback leads to a temporary failure
82//! ([`Status::Tempfail`]) response being returned to the MTA. The milter
83//! then continues to handle requests normally.
84//!
85//! Panicking, on the other hand, leads to immediate [shutdown] of the milter.
86//! All stages switch to returning a failure response and no longer execute the
87//! handler functions (however, currently executing callback handlers are
88//! allowed to finish). The libmilter worker processes are terminated and the
89//! currently blocked invocation of `Milter::run` returns. Cleanup logic in the
90//! `close` or other stages is not executed.
91//!
92//! The principle behind the panicking behaviour is, as elsewhere, exit as
93//! quickly as possible, within the constraints posed by libmilter and the FFI
94//! interface.
95//!
96//! The above failure modes are provided as a convenience. Use explicit error
97//! handling if they don’t satisfy your requirements.
98//!
99//! [milter]: https://docs.rs/milter/0.2.4/milter
100//! [`Milter`]: https://docs.rs/milter/0.2.4/milter/struct.Milter.html
101//! [attribute macro]: #attributes
102//! [crate]: https://crates.io/crates/milter
103//! [`milter::Result`]: https://docs.rs/milter/0.2.4/milter/type.Result.html
104//! [`Status::Tempfail`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html#variant.Tempfail
105//! [shutdown]: https://docs.rs/milter/0.2.4/milter/fn.shutdown.html
106
107mod generator;
108mod tree_preds;
109
110use crate::generator::*;
111use proc_macro::TokenStream;
112use quote::quote;
113use syn::{parse_macro_input, Ident, ItemFn};
114
115macro_rules! ident {
116 ($ident:ident) => {
117 ::quote::format_ident!(::std::stringify!($ident))
118 };
119}
120
121macro_rules! c_type {
122 ($ty:ty) => {
123 ::quote::quote! { $ty }
124 };
125}
126
127/// Generates a callback function of type [`NegotiateCallback`] that delegates
128/// to the annotated function.
129///
130/// As its sole argument `on_negotiate` takes an identifier to be used as the
131/// name of the generated function.
132///
133/// The `on_negotiate` callback is called just before the `connect` stage. It
134/// enables negotiation of certain protocol features that apply to the current
135/// connection. The function arguments indicate what capabilities are available.
136/// The milter can then return a subset of these to signal the desired ones. The
137/// signature of the annotated function must be as specified below.
138///
139/// Arguments:
140///
141/// * <code>[Context]<T></code> – the callback context
142/// * [`Actions`] – the available actions
143/// * [`ProtocolOpts`] – the available milter protocol options
144///
145/// Return type:
146///
147/// * a tuple containing the fields
148/// * [`Status`] – the response status. The special response status
149/// [`Status::AllOpts`] enables all available actions and protocol stages;
150/// use [`Status::Continue`] to apply your own set of actions and protocol
151/// options.
152/// * [`Actions`] – the desired actions
153/// * [`ProtocolOpts`] – the desired protocol options
154///
155/// # Examples
156///
157/// ```
158/// # #[macro_use] extern crate milter_callback;
159/// use milter::{Actions, Context, ProtocolOpts, Status};
160/// # struct MyData;
161///
162/// #[on_negotiate(negotiate_callback)]
163/// fn handle_negotiate(
164/// context: Context<MyData>,
165/// actions: Actions,
166/// protocol_opts: ProtocolOpts,
167/// ) -> (Status, Actions, ProtocolOpts) {
168/// (Status::AllOpts, Default::default(), Default::default())
169/// }
170/// ```
171///
172/// [`NegotiateCallback`]: https://docs.rs/milter/0.2.4/milter/type.NegotiateCallback.html
173/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
174/// [`Actions`]: https://docs.rs/milter/0.2.4/milter/struct.Actions.html
175/// [`ProtocolOpts`]: https://docs.rs/milter/0.2.4/milter/struct.ProtocolOpts.html
176/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
177/// [`Status::AllOpts`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html#variant.AllOpts
178/// [`Status::Continue`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html#variant.Continue
179#[proc_macro_attribute]
180pub fn on_negotiate(attr: TokenStream, item: TokenStream) -> TokenStream {
181 let name = parse_macro_input!(attr as Ident);
182 let handler_fn = parse_macro_input!(item as ItemFn);
183
184 CallbackFn::new(name, handler_fn)
185 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
186 .input(ident!(f0), c_type!(::libc::c_ulong), Binding::Actions)
187 .input(ident!(f1), c_type!(::libc::c_ulong), Binding::ProtocolOpts)
188 .input_unbound(ident!(f2), c_type!(::libc::c_ulong))
189 .input_unbound(ident!(f3), c_type!(::libc::c_ulong))
190 .input_unbound(ident!(pf0), c_type!(*mut ::libc::c_ulong))
191 .input_unbound(ident!(pf1), c_type!(*mut ::libc::c_ulong))
192 .input_unbound(ident!(pf2), c_type!(*mut ::libc::c_ulong))
193 .input_unbound(ident!(pf3), c_type!(*mut ::libc::c_ulong))
194 .ok_result_arms(quote! {
195 ::std::result::Result::Ok((::milter::Status::AllOpts, _, _)) => ::milter::Status::AllOpts as ::milter::sfsistat,
196 ::std::result::Result::Ok((status, actions, protocol_opts)) => {
197 *pf0 = actions.bits();
198 *pf1 = protocol_opts.bits();
199 *pf2 = 0;
200 *pf3 = 0;
201 status as ::milter::sfsistat
202 }
203 })
204 .generate()
205 .into()
206}
207
208/// Generates a callback function of type [`ConnectCallback`] that delegates to
209/// the annotated function.
210///
211/// As its sole argument `on_connect` takes an identifier to be used as the name
212/// of the generated function.
213///
214/// The `on_connect` callback is called at the beginning of an SMTP connection.
215/// The signature of the annotated function must be as specified below.
216///
217/// Arguments:
218///
219/// * <code>[Context]<T></code> – the callback context
220/// * <code>&[str]</code> – the client’s hostname
221/// * <code>[Option]<[SocketAddr]></code> – the client’s internet socket
222/// address, if any
223///
224/// Return type:
225///
226/// * [`Status`] – the response status
227///
228/// # Examples
229///
230/// ```
231/// # #[macro_use] extern crate milter_callback;
232/// use milter::{Context, Status};
233/// use std::net::SocketAddr;
234/// # struct MyData;
235///
236/// #[on_connect(connect_callback)]
237/// fn handle_connect(
238/// context: Context<MyData>,
239/// hostname: &str,
240/// socket_address: Option<SocketAddr>,
241/// ) -> Status {
242/// Status::Continue
243/// }
244/// ```
245///
246/// [`ConnectCallback`]: https://docs.rs/milter/0.2.4/milter/type.ConnectCallback.html
247/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
248/// [SocketAddr]: std::net::SocketAddr
249/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
250#[proc_macro_attribute]
251pub fn on_connect(attr: TokenStream, item: TokenStream) -> TokenStream {
252 let name = parse_macro_input!(attr as Ident);
253 let handler_fn = parse_macro_input!(item as ItemFn);
254
255 CallbackFn::new(name, handler_fn)
256 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
257 .input(ident!(hostname), c_type!(*mut ::libc::c_char), Binding::Str)
258 .input(ident!(hostaddr), c_type!(*mut ::libc::sockaddr), Binding::SocketAddr)
259 .generate()
260 .into()
261}
262
263/// Generates a callback function of type [`HeloCallback`] that delegates to the
264/// annotated function.
265///
266/// As its sole argument `on_helo` takes an identifier to be used as the name of
267/// the generated function.
268///
269/// The `on_helo` callback is called when a `HELO` or `EHLO` SMTP command is
270/// received. The signature of the annotated function must be as specified
271/// below.
272///
273/// Arguments:
274///
275/// * <code>[Context]<T></code> – the callback context
276/// * <code>&[str]</code> – the client’s hostname as stated in the
277/// `HELO`/`EHLO` command
278///
279/// Return type:
280///
281/// * [`Status`] – the response status
282///
283/// # Examples
284///
285/// ```
286/// # #[macro_use] extern crate milter_callback;
287/// use milter::{Context, Status};
288/// # struct MyData;
289///
290/// #[on_helo(helo_callback)]
291/// fn handle_helo(context: Context<MyData>, helo_host: &str) -> Status {
292/// Status::Continue
293/// }
294/// ```
295///
296/// [`HeloCallback`]: https://docs.rs/milter/0.2.4/milter/type.HeloCallback.html
297/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
298/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
299#[proc_macro_attribute]
300pub fn on_helo(attr: TokenStream, item: TokenStream) -> TokenStream {
301 let name = parse_macro_input!(attr as Ident);
302 let handler_fn = parse_macro_input!(item as ItemFn);
303
304 CallbackFn::new(name, handler_fn)
305 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
306 .input(ident!(helohost), c_type!(*mut ::libc::c_char), Binding::Str)
307 .generate()
308 .into()
309}
310
311/// Generates a callback function of type [`MailCallback`] that delegates to the
312/// annotated function.
313///
314/// As its sole argument `on_mail` takes an identifier to be used as the name of
315/// the generated function.
316///
317/// The `on_mail` callback handles processing of an envelope sender (`MAIL FROM`
318/// SMTP command). The signature of the annotated function must be as specified
319/// below.
320///
321/// Arguments:
322///
323/// * <code>[Context]<T></code> – the callback context
324/// * <code>[Vec]<&[str]></code> – the SMTP command arguments, the
325/// first element being the sender address
326///
327/// Return type:
328///
329/// * [`Status`] – the response status
330///
331/// # Examples
332///
333/// ```
334/// # #[macro_use] extern crate milter_callback;
335/// use milter::{Context, Status};
336/// # struct MyData;
337///
338/// #[on_mail(mail_callback)]
339/// fn handle_mail(context: Context<MyData>, smtp_args: Vec<&str>) -> Status {
340/// Status::Continue
341/// }
342/// ```
343///
344/// [`MailCallback`]: https://docs.rs/milter/0.2.4/milter/type.MailCallback.html
345/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
346/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
347#[proc_macro_attribute]
348pub fn on_mail(attr: TokenStream, item: TokenStream) -> TokenStream {
349 let name = parse_macro_input!(attr as Ident);
350 let handler_fn = parse_macro_input!(item as ItemFn);
351
352 CallbackFn::new(name, handler_fn)
353 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
354 .input(ident!(argv), c_type!(*mut *mut ::libc::c_char), Binding::Strs)
355 .generate()
356 .into()
357}
358
359/// Generates a callback function of type [`RcptCallback`] that delegates to the
360/// annotated function.
361///
362/// As its sole argument `on_rcpt` takes an identifier to be used as the name of
363/// the generated function.
364///
365/// The `on_rcpt` callback handles processing of an envelope recipient (`RCPT
366/// TO` SMTP command). It can be called multiple times for a single message. The
367/// signature of the annotated function must be as specified below.
368///
369/// Arguments:
370///
371/// * <code>[Context]<T></code> – the callback context
372/// * <code>[Vec]<&[str]></code> – the SMTP command arguments, the
373/// first element being the recipient address
374///
375/// Return type:
376///
377/// * [`Status`] – the response status
378///
379/// # Examples
380///
381/// ```
382/// # #[macro_use] extern crate milter_callback;
383/// use milter::{Context, Status};
384/// # struct MyData;
385///
386/// #[on_rcpt(rcpt_callback)]
387/// fn handle_rcpt(context: Context<MyData>, smtp_args: Vec<&str>) -> Status {
388/// Status::Continue
389/// }
390/// ```
391///
392/// [`RcptCallback`]: https://docs.rs/milter/0.2.4/milter/type.RcptCallback.html
393/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
394/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
395#[proc_macro_attribute]
396pub fn on_rcpt(attr: TokenStream, item: TokenStream) -> TokenStream {
397 let name = parse_macro_input!(attr as Ident);
398 let handler_fn = parse_macro_input!(item as ItemFn);
399
400 CallbackFn::new(name, handler_fn)
401 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
402 .input(ident!(argv), c_type!(*mut *mut ::libc::c_char), Binding::Strs)
403 .generate()
404 .into()
405}
406
407/// Generates a callback function of type [`DataCallback`] that delegates to the
408/// annotated function.
409///
410/// As its sole argument `on_data` takes an identifier to be used as the name of
411/// the generated function.
412///
413/// The `on_data` callback is called at the start of the message content (before
414/// header and body). The signature of the annotated function must be as
415/// specified below.
416///
417/// Arguments:
418///
419/// * <code>[Context]<T></code> – the callback context
420///
421/// Return type:
422///
423/// * [`Status`] – the response status
424///
425/// # Examples
426///
427/// ```
428/// # #[macro_use] extern crate milter_callback;
429/// use milter::{Context, Status};
430/// # struct MyData;
431///
432/// #[on_data(data_callback)]
433/// fn handle_data(context: Context<MyData>) -> Status {
434/// Status::Continue
435/// }
436/// ```
437///
438/// [`DataCallback`]: https://docs.rs/milter/0.2.4/milter/type.DataCallback.html
439/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
440/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
441#[proc_macro_attribute]
442pub fn on_data(attr: TokenStream, item: TokenStream) -> TokenStream {
443 let name = parse_macro_input!(attr as Ident);
444 let handler_fn = parse_macro_input!(item as ItemFn);
445
446 CallbackFn::new(name, handler_fn)
447 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
448 .generate()
449 .into()
450}
451
452/// Generates a callback function of type [`HeaderCallback`] that delegates to
453/// the annotated function.
454///
455/// As its sole argument `on_header` takes an identifier to be used as the name
456/// of the generated function.
457///
458/// The `on_header` callback handles processing of a message header line. It can
459/// be called multiple times for a single message. If the value spans multiple
460/// lines, line breaks are represented as `\n` (a single newline), not `\r\n`.
461/// The signature of the annotated function must be as specified below.
462///
463/// Arguments:
464///
465/// * <code>[Context]<T></code> – the callback context
466/// * <code>&[str]</code> – the header name
467/// * <code>&[str]</code> – the header value
468///
469/// Return type:
470///
471/// * [`Status`] – the response status
472///
473/// # Examples
474///
475/// ```
476/// # #[macro_use] extern crate milter_callback;
477/// use milter::{Context, Status};
478/// # struct MyData;
479///
480/// #[on_header(header_callback)]
481/// fn handle_header(context: Context<MyData>, name: &str, value: &str) -> Status {
482/// Status::Continue
483/// }
484/// ```
485///
486/// [`HeaderCallback`]: https://docs.rs/milter/0.2.4/milter/type.HeaderCallback.html
487/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
488/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
489#[proc_macro_attribute]
490pub fn on_header(attr: TokenStream, item: TokenStream) -> TokenStream {
491 let name = parse_macro_input!(attr as Ident);
492 let handler_fn = parse_macro_input!(item as ItemFn);
493
494 CallbackFn::new(name, handler_fn)
495 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
496 .input(ident!(headerf), c_type!(*mut ::libc::c_char), Binding::Str)
497 .input(ident!(headerv), c_type!(*mut ::libc::c_char), Binding::Str)
498 .generate()
499 .into()
500}
501
502/// Generates a callback function of type [`EohCallback`] that delegates to the
503/// annotated function.
504///
505/// As its sole argument `on_eoh` takes an identifier to be used as the name of
506/// the generated function.
507///
508/// The `on_eoh` callback is called when the end of the message header is
509/// reached. The signature of the annotated function must be as specified below.
510///
511/// Arguments:
512///
513/// * <code>[Context]<T></code> – the callback context
514///
515/// Return type:
516///
517/// * [`Status`] – the response status
518///
519/// # Examples
520///
521/// ```
522/// # #[macro_use] extern crate milter_callback;
523/// use milter::{Context, Status};
524/// # struct MyData;
525///
526/// #[on_eoh(eoh_callback)]
527/// fn handle_eoh(context: Context<MyData>) -> Status {
528/// Status::Continue
529/// }
530/// ```
531///
532/// [`EohCallback`]: https://docs.rs/milter/0.2.4/milter/type.EohCallback.html
533/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
534/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
535#[proc_macro_attribute]
536pub fn on_eoh(attr: TokenStream, item: TokenStream) -> TokenStream {
537 let name = parse_macro_input!(attr as Ident);
538 let handler_fn = parse_macro_input!(item as ItemFn);
539
540 CallbackFn::new(name, handler_fn)
541 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
542 .generate()
543 .into()
544}
545
546/// Generates a callback function of type [`BodyCallback`] that delegates to the
547/// annotated function.
548///
549/// As its sole argument `on_body` takes an identifier to be used as the name of
550/// the generated function.
551///
552/// The `on_body` callback handles processing of message body content. Content
553/// is transferred in possibly more than one byte chunks. The callback may
554/// therefore be called multiple times for a single message. The signature of
555/// the annotated function must be as specified below.
556///
557/// Arguments:
558///
559/// * <code>[Context]<T></code> – the callback context
560/// * <code>[&[](https://doc.rust-lang.org/std/primitive.slice.html)[u8](https://doc.rust-lang.org/std/primitive.u8.html)[]](https://doc.rust-lang.org/std/primitive.slice.html)</code>
561/// – a content chunk of the message body
562///
563/// Return type:
564///
565/// * [`Status`] – the response status
566///
567/// # Examples
568///
569/// ```
570/// # #[macro_use] extern crate milter_callback;
571/// use milter::{Context, Status};
572/// # struct MyData;
573///
574/// #[on_body(body_callback)]
575/// fn handle_body(context: Context<MyData>, content_chunk: &[u8]) -> Status {
576/// Status::Continue
577/// }
578/// ```
579///
580/// [`BodyCallback`]: https://docs.rs/milter/0.2.4/milter/type.BodyCallback.html
581/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
582/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
583#[proc_macro_attribute]
584pub fn on_body(attr: TokenStream, item: TokenStream) -> TokenStream {
585 let name = parse_macro_input!(attr as Ident);
586 let handler_fn = parse_macro_input!(item as ItemFn);
587
588 CallbackFn::new(name, handler_fn)
589 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
590 .input_unbound(ident!(bodyp), c_type!(*mut ::libc::c_uchar))
591 .input_unbound(ident!(len), c_type!(::libc::size_t))
592 .extra_arg(quote! { ::std::slice::from_raw_parts(bodyp as *const u8, len) })
593 .generate()
594 .into()
595}
596
597/// Generates a callback function of type [`EomCallback`] that delegates to the
598/// annotated function.
599///
600/// As its sole argument `on_eom` takes an identifier to be used as the name of
601/// the generated function.
602///
603/// The `on_eom` callback is called when the end of the message body is reached,
604/// that is the end of the SMTP `DATA` command. This is the only stage where
605/// message-modifying [actions] can be taken. The signature of the annotated
606/// function must be as specified below.
607///
608/// Arguments:
609///
610/// * <code>[Context]<T></code> – the callback context
611///
612/// Return type:
613///
614/// * [`Status`] – the response status
615///
616/// # Examples
617///
618/// ```
619/// # #[macro_use] extern crate milter_callback;
620/// use milter::{Context, Status};
621/// # struct MyData;
622///
623/// #[on_eom(eom_callback)]
624/// fn handle_eom(context: Context<MyData>) -> Status {
625/// Status::Continue
626/// }
627/// ```
628///
629/// [`EomCallback`]: https://docs.rs/milter/0.2.4/milter/type.EomCallback.html
630/// [actions]: https://docs.rs/milter/0.2.4/milter/struct.Actions.html
631/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
632/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
633#[proc_macro_attribute]
634pub fn on_eom(attr: TokenStream, item: TokenStream) -> TokenStream {
635 let name = parse_macro_input!(attr as Ident);
636 let handler_fn = parse_macro_input!(item as ItemFn);
637
638 CallbackFn::new(name, handler_fn)
639 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
640 .generate()
641 .into()
642}
643
644/// Generates a callback function of type [`AbortCallback`] that delegates to
645/// the annotated function.
646///
647/// As its sole argument `on_abort` takes an identifier to be used as the name
648/// of the generated function.
649///
650/// The `on_abort` callback is called when processing of the current *message*
651/// is aborted by the MTA. The signature of the annotated function must be as
652/// specified below.
653///
654/// Arguments:
655///
656/// * <code>[Context]<T></code> – the callback context
657///
658/// Return type:
659///
660/// * [`Status`] – the response status
661///
662/// # Examples
663///
664/// ```
665/// # #[macro_use] extern crate milter_callback;
666/// use milter::{Context, Status};
667/// # struct MyData;
668///
669/// #[on_abort(abort_callback)]
670/// fn handle_abort(context: Context<MyData>) -> Status {
671/// Status::Continue
672/// }
673/// ```
674///
675/// [`AbortCallback`]: https://docs.rs/milter/0.2.4/milter/type.AbortCallback.html
676/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
677/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
678#[proc_macro_attribute]
679pub fn on_abort(attr: TokenStream, item: TokenStream) -> TokenStream {
680 let name = parse_macro_input!(attr as Ident);
681 let handler_fn = parse_macro_input!(item as ItemFn);
682
683 CallbackFn::new(name, handler_fn)
684 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
685 .generate()
686 .into()
687}
688
689/// Generates a callback function of type [`CloseCallback`] that delegates to
690/// the annotated function.
691///
692/// As its sole argument `on_close` takes an identifier to be used as the name
693/// of the generated function.
694///
695/// The `on_close` callback is called when an SMTP connection is closed. It is
696/// always called at the end of a connection, also when messages were aborted.
697/// It may even be called as the first and only callback (without
698/// `on_connect`!), for example when the MTA decides not to go ahead with this
699/// connection. The signature of the annotated function must be as specified
700/// below.
701///
702/// Arguments:
703///
704/// * <code>[Context]<T></code> – the callback context
705///
706/// Return type:
707///
708/// * [`Status`] – the response status
709///
710/// # Examples
711///
712/// ```
713/// # #[macro_use] extern crate milter_callback;
714/// use milter::{Context, Status};
715/// # struct MyData;
716///
717/// #[on_close(close_callback)]
718/// fn handle_close(context: Context<MyData>) -> Status {
719/// Status::Continue
720/// }
721/// ```
722///
723/// [`CloseCallback`]: https://docs.rs/milter/0.2.4/milter/type.CloseCallback.html
724/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
725/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
726#[proc_macro_attribute]
727pub fn on_close(attr: TokenStream, item: TokenStream) -> TokenStream {
728 let name = parse_macro_input!(attr as Ident);
729 let handler_fn = parse_macro_input!(item as ItemFn);
730
731 CallbackFn::new(name, handler_fn)
732 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
733 .generate()
734 .into()
735}
736
737/// Generates a callback function of type [`UnknownCallback`] that delegates to
738/// the annotated function.
739///
740/// As its sole argument `on_unknown` takes an identifier to be used as the name
741/// of the generated function.
742///
743/// The `on_unknown` callback is called for unknown SMTP commands. The signature
744/// of the annotated function must be as specified below.
745///
746/// Arguments:
747///
748/// * <code>[Context]<T></code> – the callback context
749/// * <code>&[str]</code> – the SMTP command
750///
751/// Return type:
752///
753/// * [`Status`] – the response status
754///
755/// # Examples
756///
757/// ```
758/// # #[macro_use] extern crate milter_callback;
759/// use milter::{Context, Status};
760/// # struct MyData;
761///
762/// #[on_unknown(unknown_callback)]
763/// fn handle_unknown(context: Context<MyData>, smtp_cmd: &str) -> Status {
764/// Status::Continue
765/// }
766/// ```
767///
768/// [`UnknownCallback`]: https://docs.rs/milter/0.2.4/milter/type.UnknownCallback.html
769/// [Context]: https://docs.rs/milter/0.2.4/milter/struct.Context.html
770/// [`Status`]: https://docs.rs/milter/0.2.4/milter/enum.Status.html
771#[proc_macro_attribute]
772pub fn on_unknown(attr: TokenStream, item: TokenStream) -> TokenStream {
773 let name = parse_macro_input!(attr as Ident);
774 let handler_fn = parse_macro_input!(item as ItemFn);
775
776 CallbackFn::new(name, handler_fn)
777 .input(ident!(ctx), c_type!(*mut ::milter::SMFICTX), Binding::Context)
778 .input(ident!(arg), c_type!(*const ::libc::c_char), Binding::Str)
779 .generate()
780 .into()
781}