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
115
116
117
118
119
120
121
122
123
/*!

# Mail dispatch abstraction

samotop-delivery is a set of transports to deliver mail to,
notably to SMTP/LMTP, but also maildir... It is used in Samotop
as a dispatch solution for incoming mail, but you can use it to send mail, too.

# Features
 - [x] Do it SMTP style:
    - [x] Speak SMTP
    - [x] Speak LMTP
    - [x] Connect over TCP
    - [x] Connect over Unix sockets
    - [x] Connect to a Child process IO
    - [x] TLS support on all connections
    - [x] Reuse established connections
 - [x] Do it locally:
    - [x] Write mail to a MailDir
    - [x] Write mail to lozizol journal
    - [ ] Write mail to an MBox file - contributions welcome
    - [x] Write mail to a single dir - fit for debug only
 - [x] Popular integrations:
    - [x] Send mail with sendmail

LMTP on Unix socket enables wide range of local delivery integrations, dovecot or postfix for instance. Some mail delivery programs speak LMTP, too.

# Credits

This is a fork of [async-smtp](https://github.com/async-email/async-smtp/releases/tag/v0.3.4)
from the awesome [delta.chat](https://delta.chat) project.

*/

#![deny(
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    missing_debug_implementations,
    clippy::unwrap_used
)]

#[macro_use]
extern crate log;

pub mod dir;
mod dispatch;
#[cfg(feature = "file-transport")]
pub mod file;
#[cfg(feature = "journal-transport")]
pub mod journal;
#[cfg(feature = "sendmail-transport")]
pub mod sendmail;
#[cfg(feature = "smtp-transport")]
pub mod smtp;
pub mod stub;
pub mod types;

pub mod prelude {
    pub use crate::dir::*;
    #[cfg(feature = "file-transport")]
    pub use crate::file::*;
    #[cfg(feature = "journal-transport")]
    pub use crate::journal::*;
    #[cfg(feature = "sendmail-transport")]
    pub use crate::sendmail::*;
    #[cfg(feature = "smtp-transport")]
    pub use crate::smtp::*;
    pub use crate::types::*;
    pub use crate::{MailDataStream, Transport};
}

use crate::types::*;
use async_std::io::{copy, Write};
use samotop_core::common::*;
use std::{fmt, pin::Pin};

pub type SyncFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Sync + Send + 'a>>;
type SendResult<T> = std::result::Result<<T as Transport>::DataStream, <T as Transport>::Error>;

/// Transport method for emails
pub trait Transport: std::fmt::Debug {
    /// Result type for the transport
    type DataStream: MailDataStream;
    type Error;

    /// Start sending e-mail and return a stream to write the body to
    fn send_stream<'s, 'a>(&'s self, envelope: Envelope) -> SyncFuture<'a, SendResult<Self>>
    where
        's: 'a;

    /// Send the email
    fn send<'s, 'r, 'a, R>(
        &'s self,
        envelope: Envelope,
        mut message: R,
    ) -> SyncFuture<'a, SendResult<Self>>
    where
        Self::DataStream: Unpin + Send + Sync,
        Self::Error: From<std::io::Error>,
        R: io::Read + Unpin + Send + Sync + 'r,
        's: 'a,
        'r: 'a,
    {
        let stream = self.send_stream(envelope);
        Box::pin(async move {
            let mut stream = stream.await?;
            copy(&mut message, &mut stream).await?;
            poll_fn(|cx| Pin::new(&mut stream).poll_close(cx)).await?;
            Ok(stream)
        })
    }
}

pub trait MailDataStream: fmt::Debug + io::Write {
    /// Return the result of sending the mail.
    /// This should return false if the mail has not been fully dispatched.
    /// In other words, the test should fail if the mail data stream hasn't been closed.
    fn is_done(&self) -> bool;
}