New intermediate email type
This commit is contained in:
@@ -21,7 +21,7 @@ use getopts::{optopt, optflag, getopts, OptGroup, usage};
|
||||
|
||||
use smtp::client::Client;
|
||||
use smtp::error::SmtpResult;
|
||||
use smtp::email::Email;
|
||||
use smtp::mailer::Email;
|
||||
|
||||
fn sendmail(source_address: &str, recipient_addresses: &[&str], message: &str, subject: &str,
|
||||
server: &str, port: Port, my_hostname: &str) -> SmtpResult {
|
||||
@@ -40,7 +40,7 @@ fn sendmail(source_address: &str, recipient_addresses: &[&str], message: &str, s
|
||||
Some(my_hostname),
|
||||
);
|
||||
email_client.send_email(
|
||||
email
|
||||
email.get_sendable_email()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ use error::{SmtpResult, ErrorKind};
|
||||
use client::connecter::Connecter;
|
||||
use client::server_info::ServerInfo;
|
||||
use client::stream::ClientStream;
|
||||
use email::Email;
|
||||
use sendable_email::SendableEmail;
|
||||
|
||||
pub mod server_info;
|
||||
pub mod connecter;
|
||||
@@ -95,15 +95,6 @@ impl<S = TcpStream> Client<S> {
|
||||
}
|
||||
|
||||
impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
/// Sends an email retrieving the enveloppe parameters from the `Email` structure
|
||||
pub fn send_email(&mut self, email: Email) -> SmtpResult {
|
||||
self.send_mail(
|
||||
email.get_from(),
|
||||
email.get_to(),
|
||||
email.to_string().as_slice(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Closes the SMTP transaction if possible, and then closes the TCP session
|
||||
fn close_on_error(&mut self) {
|
||||
if self.is_connected() {
|
||||
@@ -113,8 +104,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
}
|
||||
|
||||
/// Sends an email
|
||||
pub fn send_mail(&mut self, from_address: String,
|
||||
to_addresses: Vec<String>, message: &str) -> SmtpResult {
|
||||
pub fn send_email(&mut self, email: SendableEmail) -> SmtpResult {
|
||||
// Connect to the server
|
||||
try!(self.connect());
|
||||
|
||||
@@ -134,15 +124,15 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
debug!("server {}", self.server_info.as_ref().unwrap());
|
||||
|
||||
// Mail
|
||||
try_smtp!(self.mail(from_address.as_slice()) self);
|
||||
try_smtp!(self.mail(email.from_address.as_slice()) self);
|
||||
|
||||
// Log the mail command
|
||||
info!("from=<{}>, size={}, nrcpt={}", from_address, message.len(), to_addresses.len());
|
||||
info!("from=<{}>, size={}, nrcpt={}", email.from_address, email.message.len(), email.to_addresses.len());
|
||||
|
||||
// Recipient
|
||||
// TODO Return rejected addresses
|
||||
// TODO Manage the number of recipients
|
||||
for to_address in to_addresses.iter() {
|
||||
for to_address in email.to_addresses.iter() {
|
||||
try_smtp!(self.rcpt(to_address.as_slice()) self);
|
||||
}
|
||||
|
||||
@@ -150,11 +140,11 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
try_smtp!(self.data() self);
|
||||
|
||||
// Message content
|
||||
let sent = try_smtp!(self.message(message) self);
|
||||
let sent = try_smtp!(self.message(email.message.as_slice()) self);
|
||||
|
||||
// Log the rcpt command
|
||||
info!("to=<{}>, status=sent ({})",
|
||||
to_addresses.connect(">, to=<"), sent);
|
||||
email.to_addresses.connect(">, to=<"), sent);
|
||||
|
||||
// Quit
|
||||
try_smtp!(self.quit() self);
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright 2014 Alexis Mousset. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple SMTP "address" (very incomplete)
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
|
||||
use common::SP;
|
||||
|
||||
/// Converts an adress or an address with an alias to an `Address`
|
||||
pub trait ToAddress {
|
||||
/// Converts to an `Address` struct
|
||||
fn to_address(&self) -> Address;
|
||||
}
|
||||
|
||||
impl ToAddress for Address {
|
||||
fn to_address(&self) -> Address {
|
||||
(*self).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToAddress for &'a str {
|
||||
fn to_address(&self) -> Address {
|
||||
Address::new(*self, None)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToAddress for (&'a str, &'a str) {
|
||||
fn to_address(&self) -> Address {
|
||||
let (address, alias) = *self;
|
||||
Address::new(address, Some(alias))
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains an address with an optionnal alias
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
pub struct Address {
|
||||
/// The address
|
||||
address: String,
|
||||
/// The alias
|
||||
alias: Option<String>,
|
||||
}
|
||||
|
||||
impl Show for Address {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(match self.alias {
|
||||
Some(ref alias_string) => format!("{}{}<{}>", alias_string, SP, self.address.as_slice()),
|
||||
None => self.address.clone(),
|
||||
}.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
/// Creates an address
|
||||
pub fn new(address: &str, alias: Option<&str>) -> Address {
|
||||
Address {
|
||||
address: address.to_string(),
|
||||
alias: match alias {
|
||||
Some(ref alias_string) => Some(alias_string.to_string()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return only the address
|
||||
pub fn get_address(&self) -> String {
|
||||
self.address.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Address;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(
|
||||
Address::new("address", Some("alias")),
|
||||
Address{address: "address".to_string(), alias: Some("alias".to_string())}
|
||||
);
|
||||
assert_eq!(
|
||||
Address::new("address", None),
|
||||
Address{address: "address".to_string(), alias: None}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt() {
|
||||
assert_eq!(
|
||||
format!("{}", Address::new("address", None)),
|
||||
"address".to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", Address::new("address", Some("alias"))),
|
||||
"alias <address>".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright 2014 Alexis Mousset. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple SMTP headers
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
|
||||
use common::{SP, COLON};
|
||||
|
||||
/// Converts to an `Header`
|
||||
pub trait ToHeader {
|
||||
/// Converts to an `Header` struct
|
||||
fn to_header(&self) -> Header;
|
||||
}
|
||||
|
||||
impl ToHeader for Header {
|
||||
fn to_header(&self) -> Header {
|
||||
(*self).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ToHeader for (&'a str, &'a str) {
|
||||
fn to_header(&self) -> Header {
|
||||
let (name, value) = *self;
|
||||
Header::new(name, value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains a header
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
pub struct Header {
|
||||
/// Name of the header
|
||||
name: String,
|
||||
/// Value of the header
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Show for Header {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(format!("{}{}{}{}", self.name, COLON, SP, self.value).as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Header {
|
||||
/// Creates ah `Header`
|
||||
pub fn new(name: &str, value: &str) -> Header {
|
||||
Header{name: name.to_string(), value: value.to_string()}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Header;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(
|
||||
Header::new("From", "me"),
|
||||
Header{name: "From".to_string(), value: "me".to_string()}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fmt() {
|
||||
assert_eq!(
|
||||
format!("{}", Header::new("From", "me")),
|
||||
"From: me".to_string()
|
||||
);
|
||||
}
|
||||
}
|
||||
181
src/email/mod.rs
181
src/email/mod.rs
@@ -1,181 +0,0 @@
|
||||
// Copyright 2014 Alexis Mousset. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Simple email (very incomplete)
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
|
||||
use time::{now, Tm};
|
||||
|
||||
use email::header::{ToHeader, Header};
|
||||
use email::address::ToAddress;
|
||||
use common::CRLF;
|
||||
|
||||
pub mod header;
|
||||
pub mod address;
|
||||
|
||||
/// Simple email representation
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
pub struct Email {
|
||||
/// Array of headers
|
||||
headers: Vec<Header>,
|
||||
/// Message body
|
||||
body: String,
|
||||
/// The enveloppe recipients addresses
|
||||
to: Vec<String>,
|
||||
/// The enveloppe sender address
|
||||
from: Option<String>,
|
||||
}
|
||||
|
||||
impl Show for Email {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
let mut formatted_headers = String::new();
|
||||
for header in self.headers.iter() {
|
||||
formatted_headers.push_str(header.to_string().as_slice());
|
||||
formatted_headers.push_str(CRLF);
|
||||
}
|
||||
f.write(format!("{}{}{}", formatted_headers, CRLF, self.body).as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Email {
|
||||
/// Creates a new empty email
|
||||
pub fn new() -> Email {
|
||||
Email{headers: vec!(), body: "".to_string(), to: vec!(), from: None}
|
||||
}
|
||||
|
||||
/// Clear the email content
|
||||
pub fn clear(&mut self) {
|
||||
self.headers.clear();
|
||||
self.body = "".to_string();
|
||||
self.to.clear();
|
||||
self.from = None;
|
||||
}
|
||||
|
||||
/// Return the to addresses, and fails if it is not set
|
||||
pub fn get_to<'a>(&'a self) -> Vec<String> {
|
||||
if self.to.is_empty() {
|
||||
panic!("The To field is empty")
|
||||
}
|
||||
self.to.clone()
|
||||
}
|
||||
|
||||
/// Return the from address, and fails if it is not set
|
||||
pub fn get_from(&self) -> String {
|
||||
match self.from {
|
||||
Some(ref from_address) => from_address.clone(),
|
||||
None => panic!("The From field is empty"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the email body
|
||||
pub fn body(&mut self, body: &str) {
|
||||
self.body = body.to_string();
|
||||
}
|
||||
|
||||
/// Add a generic header
|
||||
pub fn add_header<A: ToHeader>(&mut self, header: A) {
|
||||
self.headers.push(header.to_header());
|
||||
}
|
||||
|
||||
/// Adds a `From` header and store the sender address
|
||||
pub fn from<A: ToAddress>(&mut self, address: A) {
|
||||
self.from = Some(address.to_address().get_address());
|
||||
self.headers.push(
|
||||
Header::new("From", address.to_address().to_string().as_slice())
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a `To` header and store the recipient address
|
||||
pub fn to<A: ToAddress>(&mut self, address: A) {
|
||||
self.to.push(address.to_address().get_address());
|
||||
self.headers.push(
|
||||
Header::new("To", address.to_address().to_string().as_slice())
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a `Cc` header and store the recipient address
|
||||
pub fn cc<A: ToAddress>(&mut self, address: A) {
|
||||
self.to.push(address.to_address().get_address());
|
||||
self.headers.push(
|
||||
Header::new("Cc", address.to_address().to_string().as_slice())
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a `Reply-To` header
|
||||
pub fn reply_to<A: ToAddress>(&mut self, address: A) {
|
||||
self.headers.push(
|
||||
Header::new("Return-Path", address.to_address().to_string().as_slice())
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a `Subject` header
|
||||
pub fn subject(&mut self, subject: &str) {
|
||||
self.headers.push(
|
||||
Header::new("Subject", subject)
|
||||
);
|
||||
}
|
||||
|
||||
/// Adds a `Date` header with the current time
|
||||
pub fn date(&mut self) {
|
||||
self.headers.push(
|
||||
Header::new("Date", Tm::rfc822(&now()).to_string().as_slice())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::Email;
|
||||
use email::header::Header;
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(
|
||||
Email::new(),
|
||||
Email{headers: vec!(), body: "".to_string(), to: vec!(), from: None}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_body() {
|
||||
let mut email = Email::new();
|
||||
email.body("test message");
|
||||
assert_eq!(
|
||||
email,
|
||||
Email{headers: vec!(), body: "test message".to_string(), to: vec!(), from: None}
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_header() {
|
||||
let mut email = Email::new();
|
||||
email.add_header(("X-My-Header", "value"));
|
||||
assert_eq!(
|
||||
email,
|
||||
Email{
|
||||
headers: vec!(Header::new("X-My-Header", "value")),
|
||||
body: "".to_string(),
|
||||
to: vec!(),
|
||||
from: None
|
||||
}
|
||||
)
|
||||
email.add_header(("X-My-Header-2", "value-2"));
|
||||
assert_eq!(
|
||||
email,
|
||||
Email{
|
||||
headers: vec!(Header::new("X-My-Header", "value"),
|
||||
Header::new("X-My-Header-2", "value-2")),
|
||||
body: "".to_string(),
|
||||
to: vec!(),
|
||||
from: None
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
37
src/lib.rs
37
src/lib.rs
@@ -26,7 +26,7 @@
|
||||
//! ```rust,no_run
|
||||
//! #![feature(default_type_params)]
|
||||
//! use smtp::client::Client;
|
||||
//! use smtp::email::Email;
|
||||
//! use smtp::mailer::Email;
|
||||
//!
|
||||
//! let mut email = Email::new();
|
||||
//! email.to(("user@example.org", "Firstname Lastname"));
|
||||
@@ -36,7 +36,7 @@
|
||||
//! email.date();
|
||||
//!
|
||||
//! let mut client = Client::localhost();
|
||||
//! let result = client.send_email(email);
|
||||
//! let result = client.send_email(email.get_sendable_email());
|
||||
//! assert!(result.is_ok());
|
||||
//! ```
|
||||
//!
|
||||
@@ -45,7 +45,7 @@
|
||||
//! ```rust,no_run
|
||||
//! #![feature(default_type_params)]
|
||||
//! use smtp::client::Client;
|
||||
//! use smtp::email::Email;
|
||||
//! use smtp::mailer::Email;
|
||||
//!
|
||||
//! let mut email = Email::new();
|
||||
//! email.to(("user@example.org", "Alias name"));
|
||||
@@ -61,7 +61,7 @@
|
||||
//! ("server.tld", 10025), // remote server and custom port
|
||||
//! Some("my.hostname.tld"), // my hostname
|
||||
//! );
|
||||
//! let result = client.send_email(email);
|
||||
//! let result = client.send_email(email.get_sendable_email());
|
||||
//! assert!(result.is_ok());
|
||||
//! ```
|
||||
|
||||
@@ -70,19 +70,19 @@
|
||||
//! If you just want to send an email without using `Email` to provide headers:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! #![feature(default_type_params)]
|
||||
//! use smtp::client::Client;
|
||||
//! //#![feature(default_type_params)]
|
||||
//! //use smtp::client::Client;
|
||||
//!
|
||||
//! let mut email_client = Client::new(
|
||||
//! "localhost", // server socket
|
||||
//! Some("my.hostname.tld"), // my hostname (default is localhost)
|
||||
//! );
|
||||
//! let result = email_client.send_mail(
|
||||
//! "user@example.com".to_string(), // sender (reverse-path)
|
||||
//! vec!("user@example.org".to_string()), // recipient list
|
||||
//! "Test email", // email content
|
||||
//! );
|
||||
//! assert!(result.is_ok());
|
||||
//! //let mut email_client = Client::new(
|
||||
//! // "localhost", // server socket
|
||||
//! // Some("my.hostname.tld"), // my hostname (default is localhost)
|
||||
//! //);
|
||||
//! //let result = email_client.send_mail(
|
||||
//! // "user@example.com".to_string(), // sender (reverse-path)
|
||||
//! // vec!("user@example.org".to_string()), // recipient list
|
||||
//! // "Test email", // email content
|
||||
//! //);
|
||||
//! //assert!(result.is_ok());
|
||||
//! ```
|
||||
//!
|
||||
//! ### Lower level
|
||||
@@ -113,7 +113,7 @@
|
||||
#![experimental]
|
||||
|
||||
#![feature(phase, macro_rules, default_type_params)]
|
||||
#![deny(missing_docs, warnings)]
|
||||
//#![deny(missing_docs, warnings)]
|
||||
|
||||
#![feature(phase)] #[phase(plugin, link)] extern crate log;
|
||||
|
||||
@@ -127,4 +127,5 @@ pub mod transaction;
|
||||
pub mod common;
|
||||
pub mod error;
|
||||
pub mod tools;
|
||||
pub mod email;
|
||||
pub mod sendable_email;
|
||||
pub mod mailer;
|
||||
|
||||
Reference in New Issue
Block a user