summaryrefslogtreecommitdiffstats
path: root/mail/examples/send_mail/main.rs
blob: f00a9823b2584649cac3f3f61cdef2fcc76a81ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! In this examples runs a simple command line dialog to create a mail
//! and send it to an MSA

#[cfg(not(feature = "smtp"))]
compile_error!("example `send_mail` requires feature `smtp`");

#[macro_use]
extern crate mail;
extern crate rpassword;
extern crate futures;
extern crate tokio;
extern crate soft_ascii_string;

use std::io::{self, Write};

use futures::{Stream, future};
use soft_ascii_string::SoftAsciiString;

use mail::{
    Mail,
    HeaderTryFrom,
    smtp::{
        send_batch as send_mail_batch,
        MailRequest,
        ConnectionConfig,
        ConnectionBuilder,
        auth::{Plain as AuthPlain}
    },
    error::MailError,
    default_impl::simple_context,
    header_components::Domain
};

mod cli;

fn main() {
    let msg_id_domain = Domain::try_from("company_a.test").unwrap();
    let unique_part = SoftAsciiString::from_string("c207n521cec").unwrap();
    let ctx = simple_context::new(msg_id_domain, unique_part).unwrap();
    let (msa_info, mails) = read_data().unwrap();
    let connection_config = create_connection_config(msa_info);
    let mail_requests = create_mail_requests(mails).unwrap();

    println!("[starting sending mails]");

    // We run a tokio core "just" for sending the mails,
    // normally we probably would schedule/spawn this task
    // on a existing tokio runtime.
    tokio::run(future::lazy(move || {
        send_mail_batch(mail_requests, connection_config, ctx)
            .then(|res| Ok(res))
            .for_each(|res| {
                match res {
                    Ok(_) => println!("[mail send]"),
                    Err(err) => println!("[sending mail failed] {:?}", err)
                }
                Ok(())
            })
    }));

    println!("[DONE]");
}

fn create_connection_config(msa_info: cli::MsaInfo) -> ConnectionConfig<AuthPlain> {
    let cli::MsaInfo { domain, auth } = msa_info;

    ConnectionBuilder::new(domain)
        .expect("could not resolve domain/host name of MSA")
        .auth(AuthPlain::from_username(auth.username, auth.password)
            .expect("used \\0 in username or password"))
        .build()
}

fn create_mail_requests(mails: Vec<cli::SimpleMail>) -> Result<Vec<MailRequest>, MailError> {
    use mail::headers::*;

    let requests = mails
        .into_iter()
        .map(|simple_mail| {
            let cli::SimpleMail { from, to, subject, text_body } = simple_mail;
            let mut mail = Mail::plain_text(text_body);
            mail.insert_headers(headers! {
                _From: [from],
                _To: [to],
                Subject: subject
            }?);

            Ok(MailRequest::from(mail))
        })
        .collect::<Result<Vec<_>,_>>();

    requests
}



fn read_data() -> Result<(cli::MsaInfo, Vec<cli::SimpleMail>), io::Error> {
    cli::with_dialog(|mut dialog| {
        let msa_info = dialog.read_msa_info()?;
        let mut mails = Vec::new();
        let mut more = true;

        writeln!(dialog.stdout(), "\nreading mails:")?;
        while more {
            let mail = dialog.read_simple_mail()?;
            mails.push(mail);
            dialog.prompt("Another Mail? [y/n]")?;
            more = dialog.read_yn()?;
        }
        writeln!(dialog.stdout(), "[collecting data done]")?;
        Ok((msa_info, mails))
    })
}