From b087dec2d9aabe6521dd912e0d2ea891ad562f42 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sun, 20 Aug 2017 02:31:12 +0200 Subject: [PATCH] feat(email): Add attachments support (#186) --- lettre_email/Cargo.toml | 3 ++ lettre_email/examples/smtp.rs | 3 ++ lettre_email/src/error.rs | 19 ++++++++-- lettre_email/src/lib.rs | 65 ++++++++++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/lettre_email/Cargo.toml b/lettre_email/Cargo.toml index 6aa83ef..90a86c6 100644 --- a/lettre_email/Cargo.toml +++ b/lettre_email/Cargo.toml @@ -14,6 +14,9 @@ keywords = ["email", "mailer"] [badges] travis-ci = { repository = "lettre/lettre_email" } +[dev-dependencies] +env_logger = "*" + [dependencies] email = "^0.0" mime = "^0.3" diff --git a/lettre_email/examples/smtp.rs b/lettre_email/examples/smtp.rs index f723d21..88cb892 100644 --- a/lettre_email/examples/smtp.rs +++ b/lettre_email/examples/smtp.rs @@ -1,8 +1,10 @@ extern crate lettre; extern crate lettre_email; +extern crate mime; use lettre::{EmailTransport, SmtpTransport}; use lettre_email::EmailBuilder; +use std::path::Path; fn main() { let email = EmailBuilder::new() @@ -12,6 +14,7 @@ fn main() { .from("user@example.com") .subject("Hi, Hello world") .text("Hello world.") + .attachment(Path::new("Cargo.toml"), None, mime::TEXT_PLAIN).unwrap() .build() .unwrap(); diff --git a/lettre_email/src/error.rs b/lettre_email/src/error.rs index e0929a3..462c4e7 100644 --- a/lettre_email/src/error.rs +++ b/lettre_email/src/error.rs @@ -2,16 +2,20 @@ use self::Error::*; use std::error::Error as StdError; -use std::fmt; -use std::fmt::{Display, Formatter}; +use std::io; +use std::fmt::{self, Display, Formatter}; /// An enum of all error kinds. #[derive(Debug)] pub enum Error { - /// Missinf sender + /// Missing sender MissingFrom, /// Missing recipient MissingTo, + /// Unparseable filename for attachment + CannotParseFilename, + /// IO error + Io(io::Error), } impl Display for Error { @@ -25,6 +29,15 @@ impl StdError for Error { match *self { MissingFrom => "the sender is missing", MissingTo => "the recipient is missing", + CannotParseFilename => "the attachment filename could not be parsed", + Io(ref err) => err.description(), } } } + +impl From for Error { + fn from(err: io::Error) -> Error { + Io(err) + } +} + diff --git a/lettre_email/src/lib.rs b/lettre_email/src/lib.rs index bbc9a6b..069f7f0 100644 --- a/lettre_email/src/lib.rs +++ b/lettre_email/src/lib.rs @@ -68,6 +68,9 @@ use lettre::{EmailAddress, SendableEmail}; use mime::Mime; use time::{Tm, now}; use uuid::Uuid; +use std::fs::File; +use std::path::Path; +use std::io::Read; /// Converts an address or an address with an alias to a `Header` pub trait IntoHeader { @@ -179,7 +182,7 @@ pub struct SimpleEmail { date: Option, html: Option, text: Option, - // attachments: Vec, + attachments: Vec, headers: Vec
, } @@ -272,6 +275,17 @@ impl SimpleEmail { self.date = Some(date); } + /// Adds an attachment to the message + pub fn attachment>(mut self, path: S) -> SimpleEmail { + self.add_attachment(path); + self + } + + /// Adds an attachment to the message + pub fn add_attachment>(&mut self, path: S) { + self.attachments.push(path.into()); + } + /// Sets the email body to plain text content pub fn text>(mut self, body: S) -> SimpleEmail { self.set_text(body); @@ -585,6 +599,55 @@ impl EmailBuilder { self.date_issued = true; } + /// Adds an attachment to the email + pub fn attachment(mut self, path: &Path, filename: Option<&str>, content_type: Mime) -> Result { + self.set_attachment(path, filename, content_type)?; + Ok(self) + } + + /// Adds an attachment to the email + /// If filename is not provided, the name of the file will be used. + pub fn set_attachment(&mut self, path: &Path, filename: Option<&str>, content_type: Mime) -> Result<(), Error> { + let file = File::open(path); + let body = match file { + Ok(mut f) => { + let mut data = String::new(); + let read = f.read_to_string(&mut data); + match read { + Ok(_) => data, + Err(e) => { + return Err(From::from(e)); + } + } + } + Err(e) => { + return Err(From::from(e)); + } + }; + + let actual_filename = match filename { + Some(name) => name, + None => match path.file_name() { + Some(name) => match name.to_str() { + Some(name) => name, + None => return Err(Error::CannotParseFilename), + }, + None => return Err(Error::CannotParseFilename), + }, + }; + + let content = PartBuilder::new() + .body(body) + .header(("Content-Disposition", format!("attachment; filename=\"{}\"", actual_filename))) + .header(("Content-Type", content_type.to_string())) + .build(); + + self.set_message_type(MimeMultipartType::Mixed); + self.add_child(content); + + Ok(()) + } + /// Set the message type pub fn message_type(mut self, message_type: MimeMultipartType) -> EmailBuilder { self.set_message_type(message_type);