From a302df61d49f27875f50c22b1d62eeb8992519cd Mon Sep 17 00:00:00 2001 From: Lars Reichardt Date: Fri, 14 Oct 2016 10:57:26 +0200 Subject: [PATCH 1/3] feat(transport): add sendmail transport --- src/transport/mod.rs | 1 + src/transport/sendmail/error.rs | 54 +++++++++++++++++++++++++++++++++ src/transport/sendmail/mod.rs | 44 +++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/transport/sendmail/error.rs create mode 100644 src/transport/sendmail/mod.rs diff --git a/src/transport/mod.rs b/src/transport/mod.rs index e884bb8..bd575d2 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -1,5 +1,6 @@ //! Represents an Email transport pub mod smtp; +pub mod sendmail; pub mod stub; pub mod file; diff --git a/src/transport/sendmail/error.rs b/src/transport/sendmail/error.rs new file mode 100644 index 0000000..5db1cf7 --- /dev/null +++ b/src/transport/sendmail/error.rs @@ -0,0 +1,54 @@ +//! Error and result type for sendmail transport + + +use self::Error::*; +use std::error::Error as StdError; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::io; + +/// An enum of all error kinds. +#[derive(Debug)] +pub enum Error { + /// Internal client error + Client(&'static str), + /// IO error + Io(io::Error), +} + +impl Display for Error { + fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> { + fmt.write_str(self.description()) + } +} + +impl StdError for Error { + fn description(&self) -> &str { + match *self { + Client(_) => "an unknown error occured", + Io(_) => "an I/O error occured", + } + } + + fn cause(&self) -> Option<&StdError> { + match *self { + Io(ref err) => Some(&*err as &StdError), + _ => None, + } + } +} + +impl From for Error { + fn from(err: io::Error) -> Error { + Io(err) + } +} + +impl From<&'static str> for Error { + fn from(string: &'static str) -> Error { + Client(string) + } +} + +/// SendMail result type +pub type SendmailResult = Result<(), Error>; diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs new file mode 100644 index 0000000..6d226d7 --- /dev/null +++ b/src/transport/sendmail/mod.rs @@ -0,0 +1,44 @@ +//! This transport uilizes the sendmail executable for each email. + +use email::SendableEmail; +use std::error::Error; +use std::io::prelude::*; +use std::process::{Command, Stdio}; + +use transport::EmailTransport; +use transport::sendmail::error::SendmailResult; + +pub mod error; + +/// Writes the content and the envelope information to a file +pub struct SendmailTransport; + +impl EmailTransport for SendmailTransport { + fn send(&mut self, email: T) -> SendmailResult { + // Spawn the `wc` command + let process = try!(Command::new("/usr/bin/sendmail") + .args(&email.to_addresses()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn()); + + match process.stdin.unwrap().write_all(email.message().clone().as_bytes()) { + Err(why) => error!("couldn't write to sendmail stdin: {}", + why.description()), + Ok(_) => info!("sent pangram to sendmail"), + } + + let mut s = String::new(); + match process.stdout.unwrap().read_to_string(&mut s) { + Err(why) => error!("couldn't read sendmail stdout: {}", + why.description()), + Ok(_) => info!("sendmail responded with:\n{}", s), + } + + Ok(()) + } + + fn close(&mut self) { + () + } +} From 13ee61d5cf5506826f88d202b57ae4f59f690f6d Mon Sep 17 00:00:00 2001 From: Lars Reichardt Date: Fri, 21 Oct 2016 14:32:28 +0200 Subject: [PATCH 2/3] test(transport): add sendmail transport test --- src/transport/sendmail/mod.rs | 2 +- tests/lib.rs | 1 + tests/transport_sendmail.rs | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/transport_sendmail.rs diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs index 6d226d7..da8ed7c 100644 --- a/src/transport/sendmail/mod.rs +++ b/src/transport/sendmail/mod.rs @@ -16,7 +16,7 @@ pub struct SendmailTransport; impl EmailTransport for SendmailTransport { fn send(&mut self, email: T) -> SendmailResult { // Spawn the `wc` command - let process = try!(Command::new("/usr/bin/sendmail") + let process = try!(Command::new("/usr/sbin/sendmail") .args(&email.to_addresses()) .stdin(Stdio::piped()) .stdout(Stdio::piped()) diff --git a/tests/lib.rs b/tests/lib.rs index 45a6ab9..1e446d5 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,5 +1,6 @@ extern crate lettre; mod transport_smtp; +mod transport_sendmail; mod transport_stub; mod transport_file; diff --git a/tests/transport_sendmail.rs b/tests/transport_sendmail.rs new file mode 100644 index 0000000..39f7dbb --- /dev/null +++ b/tests/transport_sendmail.rs @@ -0,0 +1,19 @@ +extern crate lettre; + +use lettre::transport::sendmail::SendmailTransport; +use lettre::transport::EmailTransport; +use lettre::email::EmailBuilder; + +#[test] +fn sendmail_transport_simple() { + let mut sender = SendmailTransport; + let email = EmailBuilder::new() + .to("root@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello") + .build() + .unwrap(); + let result = sender.send(email); + assert!(result.is_ok()); +} From 73e7aa36390a93367372182b08c5500647f555a2 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sun, 23 Oct 2016 20:42:13 +0200 Subject: [PATCH 3/3] test(transport): add sendmail transport --- src/email/mod.rs | 2 -- src/lib.rs | 22 +++++++++++++++ src/transport/file/mod.rs | 2 +- src/transport/sendmail/mod.rs | 50 +++++++++++++++++++++++------------ tests/transport_file.rs | 20 +++++++------- tests/transport_sendmail.rs | 19 ++++++------- tests/transport_smtp.rs | 16 +++++------ tests/transport_stub.rs | 16 +++++------ 8 files changed, 92 insertions(+), 55 deletions(-) diff --git a/src/email/mod.rs b/src/email/mod.rs index d04dd02..b8d0652 100644 --- a/src/email/mod.rs +++ b/src/email/mod.rs @@ -82,8 +82,6 @@ impl IntoEmail for SimpleEmail { builder.add_cc(cc_address.into_mailbox()); } - // No bcc for now - if self.reply_to.is_some() { builder.add_reply_to(self.reply_to.unwrap().into_mailbox()); } diff --git a/src/lib.rs b/src/lib.rs index 1029c49..f9524b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,6 +180,28 @@ //! let _ = email_client.quit(); //! ``` //! +//! ### Sendmail transport +//! +//! The sendmail transport sends the email using the local sendmail command. +//! +//! ```rust +//! use lettre::transport::sendmail::SendmailTransport; +//! use lettre::transport::EmailTransport; +//! use lettre::email::EmailBuilder; +//! +//! let email = EmailBuilder::new() +//! .to("root@localhost") +//! .from("user@localhost") +//! .body("Hello World!") +//! .subject("Hello") +//! .build() +//! .unwrap(); +//! +//! let mut sender = SendmailTransport::new(); +//! let result = sender.send(email); +//! assert!(result.is_ok()); +//! ``` +//! //! ### Stub transport //! //! The stub transport only logs message envelope and drops the content. It can be useful for diff --git a/src/transport/file/mod.rs b/src/transport/file/mod.rs index 61dac35..dd1ce1a 100644 --- a/src/transport/file/mod.rs +++ b/src/transport/file/mod.rs @@ -38,7 +38,7 @@ impl EmailTransport for FileEmailTransport { email.to_addresses().join("> to=<")); try!(f.write_all(log_line.as_bytes())); - try!(f.write_all(email.message().clone().as_bytes())); + try!(f.write_all(email.message().as_bytes())); info!("{} status=", log_line); diff --git a/src/transport/sendmail/mod.rs b/src/transport/sendmail/mod.rs index da8ed7c..cb19b62 100644 --- a/src/transport/sendmail/mod.rs +++ b/src/transport/sendmail/mod.rs @@ -1,7 +1,6 @@ //! This transport uilizes the sendmail executable for each email. use email::SendableEmail; -use std::error::Error; use std::io::prelude::*; use std::process::{Command, Stdio}; @@ -10,32 +9,49 @@ use transport::sendmail::error::SendmailResult; pub mod error; -/// Writes the content and the envelope information to a file -pub struct SendmailTransport; +/// Sends an email using the `sendmail` command +#[derive(Debug,Default)] +pub struct SendmailTransport { + command: String, +} + +impl SendmailTransport { + /// Creates a new transport with the default `/usr/sbin/sendmail` command + pub fn new() -> SendmailTransport { + SendmailTransport { command: "/usr/sbin/sendmail".to_string() } + } + + /// Creates a new transport to the given sendmail command + pub fn new_with_command>(command: S) -> SendmailTransport { + SendmailTransport { command: command.into() } + } +} impl EmailTransport for SendmailTransport { fn send(&mut self, email: T) -> SendmailResult { - // Spawn the `wc` command - let process = try!(Command::new("/usr/sbin/sendmail") - .args(&email.to_addresses()) + // Spawn the sendmail command + let mut process = try!(Command::new(&self.command) + .args(&["-i", "-f", &email.from_address(), &email.to_addresses().join(" ")]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn()); - match process.stdin.unwrap().write_all(email.message().clone().as_bytes()) { - Err(why) => error!("couldn't write to sendmail stdin: {}", - why.description()), - Ok(_) => info!("sent pangram to sendmail"), + match process.stdin.as_mut().unwrap().write_all(email.message().as_bytes()) { + Ok(_) => (), + Err(error) => return Err(From::from(error)), } - let mut s = String::new(); - match process.stdout.unwrap().read_to_string(&mut s) { - Err(why) => error!("couldn't read sendmail stdout: {}", - why.description()), - Ok(_) => info!("sendmail responded with:\n{}", s), + info!("Wrote message to stdin"); + + if let Ok(output) = process.wait_with_output() { + if output.status.success() { + Ok(()) + } else { + Err(From::from("The message could not be sent")) + } + } else { + Err(From::from("The sendmail process stopped")) } - - Ok(()) } fn close(&mut self) { diff --git a/tests/transport_file.rs b/tests/transport_file.rs index 01ea837..8c0beb5 100644 --- a/tests/transport_file.rs +++ b/tests/transport_file.rs @@ -1,24 +1,24 @@ extern crate lettre; +use lettre::email::{EmailBuilder, SendableEmail}; +use lettre::transport::EmailTransport; + +use lettre::transport::file::FileEmailTransport; use std::env::temp_dir; use std::fs::File; use std::fs::remove_file; use std::io::Read; -use lettre::transport::file::FileEmailTransport; -use lettre::transport::EmailTransport; -use lettre::email::{EmailBuilder, SendableEmail}; - #[test] fn file_transport() { let mut sender = FileEmailTransport::new(temp_dir()); let email = EmailBuilder::new() - .to("root@localhost") - .from("user@localhost") - .body("Hello World!") - .subject("Hello") - .build() - .unwrap(); + .to("root@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello file") + .build() + .unwrap(); let result = sender.send(email.clone()); assert!(result.is_ok()); diff --git a/tests/transport_sendmail.rs b/tests/transport_sendmail.rs index 39f7dbb..3586d34 100644 --- a/tests/transport_sendmail.rs +++ b/tests/transport_sendmail.rs @@ -1,19 +1,20 @@ extern crate lettre; -use lettre::transport::sendmail::SendmailTransport; -use lettre::transport::EmailTransport; use lettre::email::EmailBuilder; +use lettre::transport::EmailTransport; +use lettre::transport::sendmail::SendmailTransport; #[test] fn sendmail_transport_simple() { - let mut sender = SendmailTransport; + let mut sender = SendmailTransport::new(); let email = EmailBuilder::new() - .to("root@localhost") - .from("user@localhost") - .body("Hello World!") - .subject("Hello") - .build() - .unwrap(); + .to("root@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello sendmail") + .build() + .unwrap(); let result = sender.send(email); + println!("{:?}", result); assert!(result.is_ok()); } diff --git a/tests/transport_smtp.rs b/tests/transport_smtp.rs index 9af40d5..44f3d48 100644 --- a/tests/transport_smtp.rs +++ b/tests/transport_smtp.rs @@ -1,19 +1,19 @@ extern crate lettre; -use lettre::transport::smtp::SmtpTransportBuilder; -use lettre::transport::EmailTransport; use lettre::email::EmailBuilder; +use lettre::transport::EmailTransport; +use lettre::transport::smtp::SmtpTransportBuilder; #[test] fn smtp_transport_simple() { let mut sender = SmtpTransportBuilder::localhost().unwrap().build(); let email = EmailBuilder::new() - .to("root@localhost") - .from("user@localhost") - .body("Hello World!") - .subject("Hello") - .build() - .unwrap(); + .to("root@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello smtp") + .build() + .unwrap(); let result = sender.send(email); assert!(result.is_ok()); } diff --git a/tests/transport_stub.rs b/tests/transport_stub.rs index 9e4b18a..468cb80 100644 --- a/tests/transport_stub.rs +++ b/tests/transport_stub.rs @@ -1,19 +1,19 @@ extern crate lettre; -use lettre::transport::stub::StubEmailTransport; -use lettre::transport::EmailTransport; use lettre::email::EmailBuilder; +use lettre::transport::EmailTransport; +use lettre::transport::stub::StubEmailTransport; #[test] fn stub_transport() { let mut sender = StubEmailTransport; let email = EmailBuilder::new() - .to("root@localhost") - .from("user@localhost") - .body("Hello World!") - .subject("Hello") - .build() - .unwrap(); + .to("root@localhost") + .from("user@localhost") + .body("Hello World!") + .subject("Hello stub") + .build() + .unwrap(); let result = sender.send(email); assert!(result.is_ok()); }