stalwartlabs / mail-send

E-mail delivery library for Rust with DKIM support
https://docs.rs/mail-send/
Apache License 2.0
209 stars 21 forks source link

Integration with mail-parser #4

Closed mripard closed 2 years ago

mripard commented 2 years ago

Hi, it's me again

What I'm trying to build requires to store mails on disk, before reading them back and sending them.

Storing them on disk using mail-parser is easy thanks to serde. An alternative could be to store them raw which is trivial too.

However, sending them through mail-send seems to be a bit of a pain point. Indeed, the IntoMessage trait that Transport.send expects is implemented for mail-parser's Message and mail-builder MessageBuilder.

However, neither mail-parser::Message nor mail-builder::MessageBuilder support building the structure from the raw text or through Deserialize.

I guess implementing IntoMessage for mail-parser::Message could work, but it feels like both mail-parser::Message and mail-send::smtp::message::Message could be the same structure, that would implement Serialize/Deserialize?

Thanks!

mdecimus commented 2 years ago

Hi,

Do you need to send the email exactly as it was? If that is the case, you could send it in raw format as in this example:

https://github.com/stalwartlabs/mail-send/blob/main/examples/smtp_raw_message.rs

mripard commented 2 years ago

It works indeed, but we would still need to parse the raw message using MessageParser in order to retrieve the to and from fields.

We end up with something like this

use mail_builder::MessageBuilder;
use mail_parser::Message as MessageParser;
use mail_send::{smtp::message::Message, Transport};

#[tokio::main]
async fn main() {
    let message = MessageBuilder::new()
        .from(("Test", "test@example.org"))
        .to("recipient@example.org")
        .subject("Test Email")
        .text_body("Hi Mark!");

    let bytes = message.write_to_vec()
        .unwrap();

    let msg = MessageParser::parse(&bytes)
        .unwrap();

    let from = match msg.get_from() {
        mail_parser::HeaderValue::Address(address) => address.address.as_ref().unwrap(),
        _ => todo!(),
    }.to_string();

    let to = match msg.get_to() {
        mail_parser::HeaderValue::Address(address) => address.address.as_ref().unwrap(),
        _ => todo!(),
    }.to_string();

    let msg_sent = Message::empty()
        .from(from)
        .to(to)
        .body(&bytes);

    Transport::new("smtp.example.org")
        .credentials("test@example.org", "passwd")
        .connect_tls()
        .await
        .unwrap()
        .send(msg_sent)
        .await
        .unwrap()
}

Which is still fairly tedious (and possibly incomplete)

mripard commented 2 years ago

did you add something to 0.2.2 to make this any easier? I didn't find anything obvious to me