samotop_delivery/
lib.rs

1/*!
2
3# Mail dispatch abstraction
4
5samotop-delivery is a set of transports to deliver mail to,
6notably to SMTP/LMTP, but also maildir... It is used in Samotop
7as a dispatch solution for incoming mail, but you can use it to send mail, too.
8
9# Features
10 - [x] Do it SMTP style:
11    - [x] Speak SMTP
12    - [x] Speak LMTP
13    - [x] Connect over TCP
14    - [x] Connect over Unix sockets
15    - [x] Connect to a Child process IO
16    - [x] TLS support on all connections
17    - [x] Reuse established connections
18 - [x] Do it locally:
19    - [x] Write mail to a MailDir
20    - [x] Write mail to lozizol journal
21    - [ ] Write mail to an MBox file - contributions welcome
22    - [x] Write mail to a single dir - fit for debug only
23 - [x] Popular integrations:
24    - [x] Send mail with sendmail
25
26LMTP on Unix socket enables wide range of local delivery integrations, dovecot or postfix for instance. Some mail delivery programs speak LMTP, too.
27
28# Credits
29
30This is a fork of [async-smtp](https://github.com/async-email/async-smtp/releases/tag/v0.3.4)
31from the awesome [delta.chat](https://delta.chat) project.
32
33*/
34
35#![deny(
36    missing_copy_implementations,
37    trivial_casts,
38    trivial_numeric_casts,
39    unsafe_code,
40    unstable_features,
41    unused_import_braces,
42    missing_debug_implementations,
43    clippy::unwrap_used
44)]
45
46#[macro_use]
47extern crate log;
48
49pub mod dir;
50mod dispatch;
51#[cfg(feature = "file-transport")]
52pub mod file;
53#[cfg(feature = "journal-transport")]
54pub mod journal;
55#[cfg(feature = "sendmail-transport")]
56pub mod sendmail;
57#[cfg(feature = "smtp-transport")]
58pub mod smtp;
59pub mod stub;
60pub mod types;
61
62pub mod prelude {
63    pub use crate::dir::*;
64    #[cfg(feature = "file-transport")]
65    pub use crate::file::*;
66    #[cfg(feature = "journal-transport")]
67    pub use crate::journal::*;
68    #[cfg(feature = "sendmail-transport")]
69    pub use crate::sendmail::*;
70    #[cfg(feature = "smtp-transport")]
71    pub use crate::smtp::*;
72    pub use crate::types::*;
73    pub use crate::{MailDataStream, Transport};
74}
75
76use crate::types::*;
77use async_std::io::{copy, Write};
78use samotop_core::common::*;
79use std::{fmt, pin::Pin};
80
81pub type SyncFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Sync + Send + 'a>>;
82type SendResult<T> = std::result::Result<<T as Transport>::DataStream, <T as Transport>::Error>;
83
84/// Transport method for emails
85pub trait Transport: std::fmt::Debug {
86    /// Result type for the transport
87    type DataStream: MailDataStream;
88    type Error;
89
90    /// Start sending e-mail and return a stream to write the body to
91    fn send_stream<'s, 'a>(&'s self, envelope: Envelope) -> SyncFuture<'a, SendResult<Self>>
92    where
93        's: 'a;
94
95    /// Send the email
96    fn send<'s, 'r, 'a, R>(
97        &'s self,
98        envelope: Envelope,
99        mut message: R,
100    ) -> SyncFuture<'a, SendResult<Self>>
101    where
102        Self::DataStream: Unpin + Send + Sync,
103        Self::Error: From<std::io::Error>,
104        R: io::Read + Unpin + Send + Sync + 'r,
105        's: 'a,
106        'r: 'a,
107    {
108        let stream = self.send_stream(envelope);
109        Box::pin(async move {
110            let mut stream = stream.await?;
111            copy(&mut message, &mut stream).await?;
112            poll_fn(|cx| Pin::new(&mut stream).poll_close(cx)).await?;
113            Ok(stream)
114        })
115    }
116}
117
118pub trait MailDataStream: fmt::Debug + io::Write {
119    /// Return the result of sending the mail.
120    /// This should return false if the mail has not been fully dispatched.
121    /// In other words, the test should fail if the mail data stream hasn't been closed.
122    fn is_done(&self) -> bool;
123}