Begin to work on email content
This commit is contained in:
@@ -12,3 +12,6 @@ license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
|
||||
keywords = ["email", "smtp"]
|
||||
|
||||
[dependencies]
|
||||
time = "*"
|
||||
|
||||
@@ -7,32 +7,35 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(default_type_params)]
|
||||
#![feature(phase)] #[phase(plugin, link)] extern crate log;
|
||||
|
||||
|
||||
extern crate smtp;
|
||||
extern crate getopts;
|
||||
|
||||
use std::io::stdin;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
use std::string::String;
|
||||
use std::io::net::ip::Port;
|
||||
use std::os;
|
||||
use getopts::{optopt, optflag, getopts, OptGroup, usage};
|
||||
use std::io::net::tcp::TcpStream;
|
||||
|
||||
use smtp::client::Client;
|
||||
use smtp::error::SmtpResult;
|
||||
//use smtp::email::Email;
|
||||
|
||||
fn sendmail(source_address: &str, recipient_addresses: &[&str], message: &str,
|
||||
server: &str, port: Port, my_hostname: &str) -> SmtpResult {
|
||||
let mut email_client: Client<TcpStream> =
|
||||
let mut email_client =
|
||||
Client::new(
|
||||
(server, port),
|
||||
Some(my_hostname),
|
||||
);
|
||||
email_client.send_mail::<TcpStream>(
|
||||
source_address,
|
||||
recipient_addresses,
|
||||
message,
|
||||
source_address,
|
||||
recipient_addresses,
|
||||
message,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
|
||||
use std::string::String;
|
||||
use std::error::FromError;
|
||||
use std::io::net::ip::SocketAddr;
|
||||
use std::io::net::ip::ToSocketAddr;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
use std::io::net::ip::{SocketAddr, ToSocketAddr};
|
||||
|
||||
use tools::{get_first_word, unquote_email_address};
|
||||
use tools::get_first_word;
|
||||
use common::{CRLF, MESSAGE_ENDING};
|
||||
use response::Response;
|
||||
use extension::Extension;
|
||||
@@ -30,7 +30,7 @@ pub mod connecter;
|
||||
pub mod stream;
|
||||
|
||||
/// Structure that implements the SMTP client
|
||||
pub struct Client<S> {
|
||||
pub struct Client<S = TcpStream> {
|
||||
/// TCP stream between client and server
|
||||
/// Value is None before connection
|
||||
stream: Option<S>,
|
||||
@@ -71,7 +71,7 @@ macro_rules! check_command_sequence (
|
||||
})
|
||||
)
|
||||
|
||||
impl<S> Client<S> {
|
||||
impl<S = TcpStream> Client<S> {
|
||||
/// Creates a new SMTP client
|
||||
///
|
||||
/// It does not connects to the server, but only create the `Client`
|
||||
@@ -86,7 +86,7 @@ impl<S> Client<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
/// Closes the SMTP transaction if possible, and then closes the TCP session
|
||||
fn close_on_error<S>(&mut self) {
|
||||
if self.is_connected::<S>() {
|
||||
@@ -267,14 +267,14 @@ impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
};
|
||||
|
||||
self.send_command(
|
||||
Command::Mail(unquote_email_address(from_address).to_string(), options)
|
||||
Command::Mail(from_address.to_string(), options)
|
||||
)
|
||||
}
|
||||
|
||||
/// Sends a RCPT command
|
||||
pub fn rcpt<S>(&mut self, to_address: &str) -> SmtpResult {
|
||||
self.send_command(
|
||||
Command::Recipient(unquote_email_address(to_address).to_string(), None)
|
||||
Command::Recipient(to_address.to_string(), None)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,5 +37,8 @@ pub static CR: &'static str = "\r";
|
||||
/// Line feed
|
||||
pub static LF: &'static str = "\n";
|
||||
|
||||
/// Colon
|
||||
pub static COLON: &'static str = ":";
|
||||
|
||||
/// The ending of message content
|
||||
pub static MESSAGE_ENDING: &'static str = "\r\n.\r\n";
|
||||
|
||||
106
src/email/address.rs
Normal file
106
src/email/address.rs
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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"
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
|
||||
use common::SP;
|
||||
|
||||
/// TODO
|
||||
pub trait ToAddress {
|
||||
/// TODO
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// TODO
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
pub struct Address {
|
||||
/// TODO
|
||||
address: String,
|
||||
/// TODO
|
||||
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 {
|
||||
/// TODO
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
63
src/email/header.rs
Normal file
63
src/email/header.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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};
|
||||
|
||||
/// TODO
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
pub struct Header {
|
||||
/// TODO
|
||||
name: String,
|
||||
/// TODO
|
||||
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 {
|
||||
/// TODO
|
||||
pub fn new(name: &str, value: &str) -> Header {
|
||||
Header{name: name.to_string(), value: value.to_string()}
|
||||
}
|
||||
}
|
||||
|
||||
// impl Str for Header {
|
||||
// fn as_slice<'a>(&'a self) -> &'a str {
|
||||
// self.clone().to_string().clone().as_slice()
|
||||
// }
|
||||
// }
|
||||
|
||||
#[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()
|
||||
);
|
||||
}
|
||||
}
|
||||
108
src/email/mod.rs
Normal file
108
src/email/mod.rs
Normal file
@@ -0,0 +1,108 @@
|
||||
// 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
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
|
||||
use time::{now, Tm};
|
||||
|
||||
use email::header::Header;
|
||||
use email::address::ToAddress;
|
||||
use common::CRLF;
|
||||
//use client::Client;
|
||||
//use error::SmtpResult;
|
||||
|
||||
pub mod header;
|
||||
pub mod address;
|
||||
|
||||
/// TODO
|
||||
pub struct Email {
|
||||
/// Array of headers
|
||||
headers: Vec<Header>,
|
||||
/// Message body
|
||||
body: Option<String>,
|
||||
/// TODO cc bcc to
|
||||
to: Vec<String>,
|
||||
/// TODO
|
||||
from: Option<String>,
|
||||
}
|
||||
|
||||
impl Show for Email {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(format!("{}{}{}", self.headers, CRLF, self.body).as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl Email {
|
||||
/// TODO
|
||||
// pub fn send(&self, client: Client) -> SmtpResult {
|
||||
// let test: Vec<&str> = self.to.iter().map(|s| s.as_slice()).collect();
|
||||
// //let to_vec: &[&str] = self.to.iter().map(|s| s.as_slice()).collect().as_slice();
|
||||
// client.send_mail(
|
||||
// self.from.unwrap().as_slice(),
|
||||
// test.as_slice(),
|
||||
// self.to_string().as_slice(),
|
||||
// )
|
||||
// }
|
||||
|
||||
/// TODO
|
||||
pub fn new() -> Email {
|
||||
Email{headers: vec!(), body: None, to: vec!(), from: None}
|
||||
}
|
||||
|
||||
// TODO : standard headers method
|
||||
|
||||
/// TODO
|
||||
pub fn body(&mut self, body: &str) {
|
||||
self.body = Some(body.to_string());
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn add_header(&mut self, header: Header) {
|
||||
self.headers.push(header);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
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())
|
||||
);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
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())
|
||||
);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn reply_to<A: ToAddress>(&mut self, address: A) {
|
||||
self.headers.push(
|
||||
Header::new("Return-Path", address.to_address().to_string().as_slice())
|
||||
);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn subject(&mut self, subject: &str) {
|
||||
self.headers.push(
|
||||
Header::new("Subject", subject)
|
||||
);
|
||||
}
|
||||
|
||||
/// TODO
|
||||
pub fn date(&mut self) {
|
||||
self.headers.push(
|
||||
Header::new("Date", Tm::rfc822(&now()).to_string().as_slice())
|
||||
);
|
||||
}
|
||||
}
|
||||
11
src/lib.rs
11
src/lib.rs
@@ -26,11 +26,12 @@
|
||||
//! If you just want to send an email:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! #![feature(default_type_params)]
|
||||
//! use std::io::net::tcp::TcpStream;
|
||||
//! use smtp::client::Client;
|
||||
//! use smtp::common::SMTP_PORT;
|
||||
//!
|
||||
//! let mut email_client: Client<TcpStream> =
|
||||
//! let mut email_client =
|
||||
//! Client::new(
|
||||
//! ("localhost", SMTP_PORT), // server socket
|
||||
//! Some("myhost"), // my hostname (default is localhost)
|
||||
@@ -47,11 +48,12 @@
|
||||
//! You can also send commands, here is a simple email transaction without error handling:
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! #![feature(default_type_params)]
|
||||
//! use std::io::net::tcp::TcpStream;
|
||||
//! use smtp::client::Client;
|
||||
//! use smtp::common::SMTP_PORT;
|
||||
//!
|
||||
//! let mut email_client: Client<TcpStream> =
|
||||
//! let mut email_client =
|
||||
//! Client::new(
|
||||
//! ("localhost", SMTP_PORT), // server socket
|
||||
//! Some("myhost"), // my hostname (default is localhost)
|
||||
@@ -70,11 +72,13 @@
|
||||
#![doc(html_root_url = "http://amousset.github.io/rust-smtp/smtp/")]
|
||||
#![experimental]
|
||||
|
||||
#![feature(phase, macro_rules, if_let)]
|
||||
#![feature(phase, macro_rules, if_let, default_type_params)]
|
||||
#![deny(missing_docs, warnings)]
|
||||
|
||||
#![feature(phase)] #[phase(plugin, link)] extern crate log;
|
||||
|
||||
extern crate time;
|
||||
|
||||
pub mod client;
|
||||
pub mod command;
|
||||
pub mod extension;
|
||||
@@ -83,3 +87,4 @@ pub mod transaction;
|
||||
pub mod common;
|
||||
pub mod error;
|
||||
pub mod tools;
|
||||
pub mod email;
|
||||
|
||||
47
src/tools.rs
47
src/tools.rs
@@ -14,32 +14,6 @@ use std::str::replace;
|
||||
|
||||
use common::{CR, LF, CRLF};
|
||||
|
||||
/// Adds quotes to emails if needed
|
||||
#[inline]
|
||||
pub fn quote_email_address(address: &str) -> String {
|
||||
match address.len() {
|
||||
0 ... 1 => format!("<{}>", address),
|
||||
_ => match (address.slice_to(1),
|
||||
address.slice_from(address.len() - 1)) {
|
||||
("<", ">") => address.to_string(),
|
||||
_ => format!("<{}>", address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes quotes from emails if needed
|
||||
#[inline]
|
||||
pub fn unquote_email_address(address: &str) -> &str {
|
||||
match address.len() {
|
||||
0 ... 1 => address,
|
||||
_ => match (address.slice_to(1),
|
||||
address.slice_from(address.len() - 1)) {
|
||||
("<", ">") => address.slice(1, address.len() - 1),
|
||||
_ => address,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the trailing line return at the end of a string
|
||||
#[inline]
|
||||
pub fn remove_trailing_crlf(string: &str) -> &str {
|
||||
@@ -79,26 +53,7 @@ pub fn escape_dot(string: &str) -> String {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{quote_email_address, unquote_email_address,
|
||||
remove_trailing_crlf, get_first_word, escape_crlf, escape_dot};
|
||||
|
||||
#[test]
|
||||
fn test_quote_email_address() {
|
||||
assert_eq!(quote_email_address("address").as_slice(), "<address>");
|
||||
assert_eq!(quote_email_address("<address>").as_slice(), "<address>");
|
||||
assert_eq!(quote_email_address("a").as_slice(), "<a>");
|
||||
assert_eq!(quote_email_address("").as_slice(), "<>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unquote_email_address() {
|
||||
assert_eq!(unquote_email_address("<address>"), "address");
|
||||
assert_eq!(unquote_email_address("address"), "address");
|
||||
assert_eq!(unquote_email_address("<address"), "<address");
|
||||
assert_eq!(unquote_email_address("<>"), "");
|
||||
assert_eq!(unquote_email_address("a"), "a");
|
||||
assert_eq!(unquote_email_address(""), "");
|
||||
}
|
||||
use super::{remove_trailing_crlf, get_first_word, escape_crlf, escape_dot};
|
||||
|
||||
#[test]
|
||||
fn test_remove_trailing_crlf() {
|
||||
|
||||
Reference in New Issue
Block a user