Search code examples
rustmilter

How to use mail filter context data?


I am trying to write a mail filter in Rust using the milter crate. I built the example on a Linux VM and it all works fine. However, the example is using u32 as the type of context injected into their handlers, a quite simple example. I instead need to store a string from the handle_header callback through to the handle_eom handler so I can use an incoming header to set the envelope from.

If I log the value of the header in handle_header to console, it writes correctly but by the time it arrives in handle_eom, it has been corrupted/overwritten whatever. I thought that context was supposed to be specifically for this scenario but it seems weird that it uses type inference rather than e.g. a pointer to an object that you can just assign whatever you want to it.

Is my understanding of context wrong or is the code incorrect?

I tried using value and &value in handle_header and it behaves the same way.

use milter::*;

fn main() {
    Milter::new("inet:3000@localhost")
        .name("BounceRewriteFilter")
        .on_header(header_callback)
        .on_eom(eom_callback)
        .on_abort(abort_callback)
        .actions(Actions::ADD_HEADER | Actions::REPLACE_SENDER)
        .run()
        .expect("milter execution failed");
}

#[on_header(header_callback)]
fn handle_header<'a>(mut context: Context<&'a str>, header: &str, value: &'a str) -> milter::Result<Status> {
    if header == "Set-Return-Path" {
        match context.data.borrow_mut() {
            Some(retpath) => *retpath = &value,
            None => {
                context.data.replace(value)?;
            }
        }
    }

    Ok(Status::Continue)
}

#[on_eom(eom_callback)]
fn handle_eom(mut context: Context<&str>) -> milter::Result<Status> {
    match context.data.take() {
        Ok(result) => {
            println!("Set-return-path header is {}", result.unwrap());
            context.api.replace_sender(result.unwrap(), None::<&str>)?;
        }
        Err(_error) => {}

    }

    Ok(Status::Continue)
}

Solution

  • Thanks to glts on Github, the author of the crate, the problem was that the string slices passed into the handle_header method were not borrowed by the external code that stores the data pointer so by the time that handle_eom is called, the memory has been reused for something else.

    All I had to do was change Context<&str> to Context<String> and convert the strings using mystr.to_owned() and in the reverse direction val = &*mystring