Rust upgrade.
This commit is contained in:
@@ -11,9 +11,9 @@
|
||||
|
||||
//! TODO
|
||||
|
||||
use std::io::IoResult;
|
||||
use std::io::net::ip::SocketAddr;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
use std::old_io::IoResult;
|
||||
use std::old_io::net::ip::SocketAddr;
|
||||
use std::old_io::net::tcp::TcpStream;
|
||||
|
||||
/// A trait for the concept of opening a stream connected to a IP socket address.
|
||||
pub trait Connecter {
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
|
||||
//! SMTP client
|
||||
|
||||
use std::ascii::AsciiExt;
|
||||
use std::string::String;
|
||||
use std::error::FromError;
|
||||
use std::io::net::tcp::TcpStream;
|
||||
use std::io::net::ip::{SocketAddr, ToSocketAddr};
|
||||
use std::old_io::net::tcp::TcpStream;
|
||||
use std::old_io::net::ip::{SocketAddr, ToSocketAddr};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -33,18 +34,18 @@ pub mod connecter;
|
||||
pub mod stream;
|
||||
|
||||
/// Represents the configuration of a client
|
||||
#[deriving(Clone)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Configuration {
|
||||
/// Maximum connection reuse
|
||||
///
|
||||
/// Zero means no limitation
|
||||
pub connection_reuse_count_limit: uint,
|
||||
pub connection_reuse_count_limit: usize,
|
||||
/// Enable connection reuse
|
||||
pub enable_connection_reuse: bool,
|
||||
/// Maximum recipients
|
||||
pub destination_recipient_limit: uint,
|
||||
pub destination_recipient_limit: usize,
|
||||
/// Maximum line length
|
||||
pub line_length_limit: uint,
|
||||
pub line_length_limit: usize,
|
||||
/// Name sent during HELO or EHLO
|
||||
pub hello_name: String,
|
||||
}
|
||||
@@ -64,7 +65,7 @@ pub struct Client<S = TcpStream> {
|
||||
/// Panic state
|
||||
panic: bool,
|
||||
/// Connection reuse counter
|
||||
connection_reuse_count: uint,
|
||||
connection_reuse_count: usize,
|
||||
/// Current message id
|
||||
current_message: Option<Uuid>,
|
||||
/// Configuration of the client
|
||||
@@ -72,16 +73,16 @@ pub struct Client<S = TcpStream> {
|
||||
}
|
||||
|
||||
macro_rules! try_smtp (
|
||||
($err: expr $client: ident) => ({
|
||||
($err: expr, $client: ident) => ({
|
||||
match $err {
|
||||
Ok(val) => val,
|
||||
Err(err) => close_and_return_err!(err $client),
|
||||
Err(err) => close_and_return_err!(err, $client),
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
macro_rules! close_and_return_err (
|
||||
($err: expr $client: ident) => ({
|
||||
($err: expr, $client: ident) => ({
|
||||
if !$client.panic {
|
||||
$client.panic = true;
|
||||
$client.close();
|
||||
@@ -94,7 +95,7 @@ macro_rules! check_command_sequence (
|
||||
($command: ident $client: ident) => ({
|
||||
if !$client.state.is_allowed(&$command) {
|
||||
close_and_return_err!(
|
||||
Response{code: 503, message: Some("Bad sequence of commands".to_string())} $client
|
||||
Response{code: 503, message: Some("Bad sequence of commands".to_string())}, $client
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -141,7 +142,7 @@ impl<S = TcpStream> Client<S> {
|
||||
}
|
||||
|
||||
/// Set the maximum number of emails sent using one connection
|
||||
pub fn set_connection_reuse_count_limit(&mut self, count: uint) {
|
||||
pub fn set_connection_reuse_count_limit(&mut self, count: usize) {
|
||||
self.configuration.connection_reuse_count_limit = count
|
||||
}
|
||||
|
||||
@@ -189,16 +190,16 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
if let Err(error) = self.ehlo() {
|
||||
match error.kind {
|
||||
ErrorKind::PermanentError(Response{code: 550, message: _}) => {
|
||||
try_smtp!(self.helo() self);
|
||||
try_smtp!(self.helo(), self);
|
||||
},
|
||||
_ => {
|
||||
try_smtp!(Err(error) self)
|
||||
try_smtp!(Err(error), self)
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Print server information
|
||||
debug!("server {}", self.server_info.as_ref().unwrap());
|
||||
//debug!("server {}", self.server_info.as_ref().unwrap());
|
||||
}
|
||||
|
||||
self.current_message = Some(Uuid::new_v4());
|
||||
@@ -210,17 +211,17 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
let message = email.message();
|
||||
|
||||
// Mail
|
||||
try_smtp!(self.mail(from_address.as_slice()) self);
|
||||
try_smtp!(self.mail(from_address.as_slice()), self);
|
||||
|
||||
// Recipient
|
||||
// TODO Return rejected addresses
|
||||
// TODO Limit the number of recipients
|
||||
for to_address in to_addresses.iter() {
|
||||
try_smtp!(self.rcpt(to_address.as_slice()) self);
|
||||
try_smtp!(self.rcpt(to_address.as_slice()), self);
|
||||
}
|
||||
|
||||
// Data
|
||||
try_smtp!(self.data() self);
|
||||
try_smtp!(self.data(), self);
|
||||
|
||||
// Message content
|
||||
self.message(message.as_slice())
|
||||
@@ -234,15 +235,15 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
|
||||
// Connect should not be called when the client is already connected
|
||||
if self.stream.is_some() {
|
||||
close_and_return_err!("The connection is already established" self);
|
||||
close_and_return_err!("The connection is already established", self);
|
||||
}
|
||||
|
||||
// Try to connect
|
||||
self.stream = Some(try!(Connecter::connect(self.server_addr)));
|
||||
|
||||
// Log the connection
|
||||
info!("connection established to {}",
|
||||
self.stream.as_mut().unwrap().peer_name().unwrap());
|
||||
//info!("connection established to {}",
|
||||
// self.stream.as_mut().unwrap().peer_name().unwrap());
|
||||
|
||||
let result = try!(self.stream.as_mut().unwrap().get_reply());
|
||||
|
||||
@@ -255,7 +256,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
fn command(&mut self, command: Command) -> SmtpResult {
|
||||
// for now we do not support SMTPUTF8
|
||||
if !command.is_ascii() {
|
||||
close_and_return_err!("Non-ASCII string" self);
|
||||
close_and_return_err!("Non-ASCII string", self);
|
||||
}
|
||||
|
||||
self.send_server(command, None)
|
||||
@@ -279,7 +280,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
message, MESSAGE_ENDING
|
||||
),
|
||||
None => self.stream.as_mut().unwrap().send_and_get_response(
|
||||
command.to_string().as_slice(), CRLF
|
||||
format! ("{}", command) .as_slice(), CRLF
|
||||
),
|
||||
});
|
||||
|
||||
@@ -334,7 +335,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
None => close_and_return_err!(Response{
|
||||
code: 503,
|
||||
message: Some("Bad sequence of commands".to_string()),
|
||||
} self),
|
||||
}, self),
|
||||
};
|
||||
|
||||
// Checks message encoding according to the server's capability
|
||||
@@ -350,7 +351,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
|
||||
if result.is_ok() {
|
||||
// Log the mail command
|
||||
info!("{}: from=<{}>", self.current_message.as_ref().unwrap(), from_address);
|
||||
//info!("{}: from=<{}>", self.current_message.as_ref().unwrap(), from_address);
|
||||
}
|
||||
|
||||
result
|
||||
@@ -364,7 +365,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
|
||||
if result.is_ok() {
|
||||
// Log the rcpt command
|
||||
info!("{}: to=<{}>", self.current_message.as_ref().unwrap(), to_address);
|
||||
//info!("{}: to=<{}>", self.current_message.as_ref().unwrap(), to_address);
|
||||
}
|
||||
|
||||
result
|
||||
@@ -383,13 +384,13 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
None => close_and_return_err!(Response{
|
||||
code: 503,
|
||||
message: Some("Bad sequence of commands".to_string()),
|
||||
} self)
|
||||
}, self)
|
||||
};
|
||||
|
||||
// Check message encoding
|
||||
if !server_info.supports_feature(Extension::EightBitMime).is_some() {
|
||||
if !message_content.is_ascii() {
|
||||
close_and_return_err!("Server does not accepts UTF-8 strings" self);
|
||||
if !message_content.as_bytes().is_ascii() {
|
||||
close_and_return_err!("Server does not accepts UTF-8 strings", self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +400,7 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
close_and_return_err!(Response{
|
||||
code: 552,
|
||||
message: Some("Message exceeds fixed maximum message size".to_string()),
|
||||
} self);
|
||||
}, self);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -409,8 +410,8 @@ impl<S: Connecter + ClientStream + Clone = TcpStream> Client<S> {
|
||||
// Increment the connection reuse counter
|
||||
self.connection_reuse_count = self.connection_reuse_count + 1;
|
||||
// Log the message
|
||||
info!("{}: conn_use={}, size={}, status=sent ({})", self.current_message.as_ref().unwrap(),
|
||||
self.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
|
||||
//info!("{}: conn_use={}, size={}, status=sent ({})", self.current_message.as_ref().unwrap(),
|
||||
// self.connection_reuse_count, message_content.len(), result.as_ref().ok().unwrap());
|
||||
}
|
||||
|
||||
self.current_message = None;
|
||||
|
||||
@@ -10,12 +10,12 @@
|
||||
//! Information about a server
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Show, Formatter};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use extension::Extension;
|
||||
|
||||
/// Contains information about an SMTP server
|
||||
#[deriving(Clone)]
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct ServerInfo {
|
||||
/// Server name
|
||||
///
|
||||
@@ -28,16 +28,14 @@ pub struct ServerInfo {
|
||||
pub esmtp_features: Option<Vec<Extension>>,
|
||||
}
|
||||
|
||||
impl Show for ServerInfo {
|
||||
impl Display for ServerInfo {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write(
|
||||
format!("{} with {}",
|
||||
write!(f, "{} with {}",
|
||||
self.name,
|
||||
match self.esmtp_features {
|
||||
Some(ref features) => features.to_string(),
|
||||
Some(ref features) => format! ("{:?}", features),
|
||||
None => "no supported features".to_string(),
|
||||
}
|
||||
).as_bytes()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,17 @@
|
||||
|
||||
//! TODO
|
||||
|
||||
use std::io::net::tcp::TcpStream;
|
||||
use std::io::IoResult;
|
||||
use std::old_io::net::tcp::TcpStream;
|
||||
use std::old_io::IoResult;
|
||||
use std::str::from_utf8;
|
||||
use std::vec::Vec;
|
||||
use std::error::FromError;
|
||||
|
||||
use error::SmtpResult;
|
||||
use response::Response;
|
||||
use tools::{escape_crlf, escape_dot};
|
||||
use tools::{escape_dot};
|
||||
|
||||
static BUFFER_SIZE: uint = 1024;
|
||||
static BUFFER_SIZE: usize = 1024;
|
||||
|
||||
/// TODO
|
||||
pub trait ClientStream {
|
||||
@@ -36,7 +36,7 @@ impl ClientStream for TcpStream {
|
||||
fn send_and_get_response(&mut self, string: &str, end: &str) -> SmtpResult {
|
||||
try!(self.write_str(format!("{}{}", escape_dot(string), end).as_slice()));
|
||||
|
||||
debug!("Wrote: {}", escape_crlf(escape_dot(string).as_slice()));
|
||||
//debug!("Wrote: {}", escape_crlf(escape_dot(string).as_slice()));
|
||||
|
||||
self.get_reply()
|
||||
}
|
||||
@@ -54,7 +54,7 @@ impl ClientStream for TcpStream {
|
||||
Ok(bytes_read) => {
|
||||
more = bytes_read == BUFFER_SIZE;
|
||||
if bytes_read > 0 {
|
||||
from_utf8(buf.slice_to(bytes_read)).unwrap()
|
||||
from_utf8(&buf[..bytes_read]).unwrap()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
@@ -64,7 +64,7 @@ impl ClientStream for TcpStream {
|
||||
};
|
||||
result.push_str(response);
|
||||
}
|
||||
debug!("Read: {}", escape_crlf(result.as_slice()));
|
||||
//debug!("Read: {}", escape_crlf(result.as_slice()));
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
@@ -73,8 +73,8 @@ impl ClientStream for TcpStream {
|
||||
let response = try!(self.read_into_string());
|
||||
|
||||
match response.as_slice().parse::<Response>() {
|
||||
Some(response) => Ok(response),
|
||||
None => Err(FromError::from_error("Could not parse response"))
|
||||
Ok(response) => Ok(response),
|
||||
Err(_) => Err(FromError::from_error("Could not parse response"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
|
||||
#![unstable]
|
||||
|
||||
use std::ascii::AsciiExt;
|
||||
use std::error::FromError;
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
use response::Response;
|
||||
use error::SmtpResult;
|
||||
@@ -22,7 +23,7 @@ use common::SP;
|
||||
///
|
||||
/// We do not implement the following SMTP commands, as they were deprecated in RFC 5321
|
||||
/// and must not be used by clients: `SEND`, `SOML`, `SAML`, `TURN`.
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
#[derive(PartialEq,Eq,Clone,Debug)]
|
||||
pub enum Command {
|
||||
/// A fake command to represent the connection step
|
||||
Connect,
|
||||
@@ -54,9 +55,9 @@ pub enum Command {
|
||||
Quit,
|
||||
}
|
||||
|
||||
impl Show for Command {
|
||||
impl Display for Command {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write( match *self {
|
||||
write! (f, "{}", match *self {
|
||||
Command::Connect => "CONNECT".to_string(),
|
||||
Command::StartTls => "STARTTLS".to_string(),
|
||||
Command::ExtendedHello(ref my_hostname) => format!("EHLO {}", my_hostname),
|
||||
@@ -76,7 +77,7 @@ impl Show for Command {
|
||||
Command::Help(Some(ref argument)) => format!("HELP {}", argument),
|
||||
Command::Noop => "NOOP".to_string(),
|
||||
Command::Quit => "QUIT".to_string(),
|
||||
}.as_bytes())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#![unstable]
|
||||
|
||||
use std::io::net::ip::Port;
|
||||
use std::old_io::net::ip::Port;
|
||||
|
||||
/// Default smtp port
|
||||
pub static SMTP_PORT: Port = 25;
|
||||
|
||||
30
src/error.rs
30
src/error.rs
@@ -12,14 +12,16 @@
|
||||
#![unstable]
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::IoError;
|
||||
use std::old_io::IoError;
|
||||
use std::error::FromError;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::fmt::Error as FmtError;
|
||||
|
||||
use response::Response;
|
||||
use self::ErrorKind::{TransientError, PermanentError, UnknownError, InternalIoError};
|
||||
|
||||
/// An enum of all error kinds.
|
||||
#[deriving(PartialEq, Eq, Clone, Show)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum ErrorKind {
|
||||
/// Transient error
|
||||
///
|
||||
@@ -36,7 +38,7 @@ pub enum ErrorKind {
|
||||
}
|
||||
|
||||
/// smtp error type
|
||||
#[deriving(PartialEq, Eq, Clone, Show)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct SmtpError {
|
||||
/// Error kind
|
||||
pub kind: ErrorKind,
|
||||
@@ -71,7 +73,7 @@ impl FromError<Response> for SmtpError {
|
||||
let kind = match response.code/100 {
|
||||
4 => TransientError(response),
|
||||
5 => PermanentError(response),
|
||||
_ => UnknownError(response.to_string()),
|
||||
_ => UnknownError(format! ("{:?}", response)),
|
||||
};
|
||||
let desc = match kind {
|
||||
TransientError(_) => "a permanent error occured during the SMTP transaction",
|
||||
@@ -97,6 +99,17 @@ impl FromError<&'static str> for SmtpError {
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for SmtpError {
|
||||
fn fmt (&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||
match self.kind {
|
||||
TransientError(ref response) => write! (fmt, "{:?}", response),
|
||||
PermanentError(ref response) => write! (fmt, "{:?}", response),
|
||||
UnknownError(ref string) => write! (fmt, "{}", string),
|
||||
InternalIoError(ref err) => write! (fmt, "{:?}", err.detail),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for SmtpError {
|
||||
fn description(&self) -> &str {
|
||||
match self.kind {
|
||||
@@ -105,15 +118,6 @@ impl Error for SmtpError {
|
||||
}
|
||||
}
|
||||
|
||||
fn detail(&self) -> Option<String> {
|
||||
match self.kind {
|
||||
TransientError(ref response) => Some(response.to_string()),
|
||||
PermanentError(ref response) => Some(response.to_string()),
|
||||
UnknownError(ref string) => Some(string.to_string()),
|
||||
InternalIoError(ref err) => err.detail.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
match self.kind {
|
||||
InternalIoError(ref err) => Some(&*err as &Error),
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#![unstable]
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
use std::result::Result as RResult;
|
||||
|
||||
use common::CRLF;
|
||||
use response::Response;
|
||||
use self::Extension::{EightBitMime, SmtpUtfEight, StartTls, Size};
|
||||
|
||||
/// Supported ESMTP keywords
|
||||
#[deriving(PartialEq,Eq,Copy,Clone)]
|
||||
#[derive(PartialEq,Eq,Copy,Clone,Debug)]
|
||||
pub enum Extension {
|
||||
/// 8BITMIME keyword
|
||||
///
|
||||
@@ -36,38 +37,39 @@ pub enum Extension {
|
||||
/// SIZE keyword
|
||||
///
|
||||
/// RFC 1427 : https://tools.ietf.org/html/rfc1427
|
||||
Size(uint),
|
||||
Size(usize),
|
||||
}
|
||||
|
||||
impl Show for Extension {
|
||||
impl Display for Extension {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(
|
||||
write! (f, "{}",
|
||||
match self {
|
||||
&EightBitMime => "8BITMIME".to_string(),
|
||||
&SmtpUtfEight => "SMTPUTF8".to_string(),
|
||||
&StartTls => "STARTTLS".to_string(),
|
||||
&Size(ref size) => format!("SIZE={}", size)
|
||||
}.as_bytes()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Extension {
|
||||
// TODO: check RFC
|
||||
fn from_str(s: &str) -> Option<Extension> {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> RResult<Extension, &'static str> {
|
||||
let splitted : Vec<&str> = s.splitn(1, ' ').collect();
|
||||
match splitted.len() {
|
||||
1 => match splitted[0] {
|
||||
"8BITMIME" => Some(EightBitMime),
|
||||
"SMTPUTF8" => Some(SmtpUtfEight),
|
||||
"STARTTLS" => Some(StartTls),
|
||||
_ => None,
|
||||
"8BITMIME" => Ok(EightBitMime),
|
||||
"SMTPUTF8" => Ok(SmtpUtfEight),
|
||||
"STARTTLS" => Ok(StartTls),
|
||||
_ => Err("Unknown extension"),
|
||||
},
|
||||
2 => match (splitted[0], splitted[1].parse::<uint>()) {
|
||||
("SIZE", Some(size)) => Some(Size(size)),
|
||||
_ => None,
|
||||
2 => match (splitted[0], splitted[1].parse::<usize>()) {
|
||||
("SIZE", Ok(size)) => Ok(Size(size)),
|
||||
_ => Err("Can't parse size"),
|
||||
},
|
||||
_ => None,
|
||||
_ => Err("Empty extension?"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,9 +89,9 @@ impl Extension {
|
||||
/// Parses supported ESMTP features
|
||||
pub fn parse_esmtp_response(message: &str) -> Option<Vec<Extension>> {
|
||||
let mut esmtp_features = Vec::new();
|
||||
for line in message.split_str(CRLF) {
|
||||
if let Some(Response{code: 250, message}) = line.parse::<Response>() {
|
||||
if let Some(keyword) = message.unwrap().as_slice().parse::<Extension>() {
|
||||
for line in message.split(CRLF) {
|
||||
if let Ok(Response{code: 250, message}) = line.parse::<Response>() {
|
||||
if let Ok(keyword) = message.unwrap().as_slice().parse::<Extension>() {
|
||||
esmtp_features.push(keyword);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -127,12 +127,12 @@
|
||||
#![crate_type = "lib"]
|
||||
|
||||
#![doc(html_root_url = "http://amousset.github.io/rust-smtp/smtp/")]
|
||||
#![experimental]
|
||||
|
||||
#![feature(phase, macro_rules, default_type_params)]
|
||||
#![deny(missing_docs, warnings)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#![feature(phase)] #[phase(plugin, link)] extern crate log;
|
||||
#![feature(plugin,core,old_io,io)]
|
||||
#![plugin(log)]
|
||||
extern crate log;
|
||||
|
||||
extern crate time;
|
||||
extern crate uuid;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
//! Simple SMTP "address" (very incomplete)
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
use common::SP;
|
||||
|
||||
@@ -39,7 +39,7 @@ impl<'a> ToAddress for (&'a str, &'a str) {
|
||||
}
|
||||
|
||||
/// Contains an address with an optionnal alias
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
#[derive(PartialEq,Eq,Clone,Debug)]
|
||||
pub struct Address {
|
||||
/// The address
|
||||
address: String,
|
||||
@@ -47,12 +47,12 @@ pub struct Address {
|
||||
alias: Option<String>,
|
||||
}
|
||||
|
||||
impl Show for Address {
|
||||
impl Display for Address {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(match self.alias {
|
||||
write! (f, "{}", match self.alias {
|
||||
Some(ref alias_string) => format!("{}{}<{}>", alias_string, SP, self.address.as_slice()),
|
||||
None => self.address.clone(),
|
||||
}.as_bytes())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
use time::Tm;
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
use common::{SP, COLON};
|
||||
use mailer::address::Address;
|
||||
@@ -36,7 +36,7 @@ impl<'a> ToHeader for (&'a str, &'a str) {
|
||||
}
|
||||
|
||||
/// Contains a header
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
#[derive(PartialEq,Eq,Clone,Debug)]
|
||||
pub enum Header {
|
||||
/// `To`
|
||||
To(Address),
|
||||
@@ -62,9 +62,9 @@ pub enum Header {
|
||||
Other(String, String),
|
||||
}
|
||||
|
||||
impl Show for Header {
|
||||
impl Display for Header {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(format!("{}{}{}{}",
|
||||
write! (f, "{}{}{}{}",
|
||||
match *self {
|
||||
Header::To(_) => "To",
|
||||
Header::From(_) => "From",
|
||||
@@ -80,18 +80,18 @@ impl Show for Header {
|
||||
},
|
||||
COLON, SP,
|
||||
match *self {
|
||||
Header::To(ref address) => address.to_string(),
|
||||
Header::From(ref address) => address.to_string(),
|
||||
Header::Cc(ref address) => address.to_string(),
|
||||
Header::ReplyTo(ref address) => address.to_string(),
|
||||
Header::Sender(ref address) => address.to_string(),
|
||||
Header::To(ref address) => format! ("{}", address),
|
||||
Header::From(ref address) => format! ("{}", address),
|
||||
Header::Cc(ref address) => format! ("{}", address),
|
||||
Header::ReplyTo(ref address) => format! ("{}", address),
|
||||
Header::Sender(ref address) => format! ("{}", address),
|
||||
Header::Date(ref date) => Tm::rfc822(date).to_string(),
|
||||
Header::Subject(ref subject) => subject.clone(),
|
||||
Header::MimeVersion => "1.0".to_string(),
|
||||
Header::ContentType(ref string) => string.clone(),
|
||||
Header::MessageId(ref string) => string.clone(),
|
||||
Header::Other(_, ref value) => value.clone(),
|
||||
}).as_bytes())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
//! Simple email (very incomplete)
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
|
||||
use time::{now, Tm};
|
||||
|
||||
@@ -22,7 +22,7 @@ pub mod header;
|
||||
pub mod address;
|
||||
|
||||
/// Simple email representation
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
#[derive(PartialEq,Eq,Clone,Debug)]
|
||||
pub struct Email {
|
||||
/// Array of headers
|
||||
headers: Vec<Header>,
|
||||
@@ -34,14 +34,14 @@ pub struct Email {
|
||||
from: Option<String>,
|
||||
}
|
||||
|
||||
impl Show for Email {
|
||||
impl Display 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(format! ("{}", header) .as_slice());
|
||||
formatted_headers.push_str(CRLF);
|
||||
}
|
||||
f.write(format!("{}{}{}", formatted_headers, CRLF, self.body).as_bytes())
|
||||
write! (f, "{}{}{}", formatted_headers, CRLF, self.body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +147,7 @@ impl SendableEmail for Email {
|
||||
}
|
||||
|
||||
fn message(&self) -> String {
|
||||
self.to_string()
|
||||
format! ("{}", self)
|
||||
}
|
||||
|
||||
/// Adds a `Message-ID` header
|
||||
|
||||
@@ -12,14 +12,15 @@
|
||||
#![unstable]
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use std::fmt::{Display, Formatter, Result};
|
||||
use std::result::Result as RResult;
|
||||
|
||||
use tools::remove_trailing_crlf;
|
||||
|
||||
/// Contains an SMTP reply, with separed code and message
|
||||
///
|
||||
/// The text message is optional, only the code is mandatory
|
||||
#[deriving(PartialEq,Eq,Clone)]
|
||||
#[derive(PartialEq,Eq,Clone,Debug)]
|
||||
pub struct Response {
|
||||
/// Server response code
|
||||
pub code: u16,
|
||||
@@ -27,43 +28,44 @@ pub struct Response {
|
||||
pub message: Option<String>
|
||||
}
|
||||
|
||||
impl Show for Response {
|
||||
impl Display for Response {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
f.write(
|
||||
write! (f, "{}",
|
||||
match self.clone().message {
|
||||
Some(message) => format!("{} {}", self.code, message),
|
||||
None => format!("{}", self.code),
|
||||
}.as_bytes()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Response {
|
||||
fn from_str(s: &str) -> Option<Response> {
|
||||
type Err = &'static str;
|
||||
fn from_str(s: &str) -> RResult<Response, &'static str> {
|
||||
// If the string is too short to be a response code
|
||||
if s.len() < 3 {
|
||||
None
|
||||
Err("len < 3")
|
||||
// If we have only a code, with or without a trailing space
|
||||
} else if s.len() == 3 || (s.len() == 4 && s.slice(3,4) == " ") {
|
||||
match s.slice_to(3).parse::<u16>() {
|
||||
Some(code) => Some(Response{
|
||||
} else if s.len() == 3 || (s.len() == 4 && &s[3..4] == " ") {
|
||||
match s[..3].parse::<u16>() {
|
||||
Ok(code) => Ok(Response{
|
||||
code: code,
|
||||
message: None
|
||||
}),
|
||||
None => None,
|
||||
Err(_) => Err("Can't parse the code"),
|
||||
}
|
||||
// If we have a code and a message
|
||||
} else {
|
||||
match (
|
||||
s.slice_to(3).parse::<u16>(),
|
||||
vec![" ", "-"].contains(&s.slice(3,4)),
|
||||
(remove_trailing_crlf(s.slice_from(4)))
|
||||
s[..3].parse::<u16>(),
|
||||
vec![" ", "-"].contains(&&s[3..4]),
|
||||
(remove_trailing_crlf(&s[4..]))
|
||||
) {
|
||||
(Some(code), true, message) => Some(Response{
|
||||
(Ok(code), true, message) => Ok(Response{
|
||||
code: code,
|
||||
message: Some(message.to_string())
|
||||
}),
|
||||
_ => None,
|
||||
_ => Err("Error parsing a code with a message"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,9 @@ use common::{CR, LF, CRLF};
|
||||
#[inline]
|
||||
pub fn remove_trailing_crlf(string: &str) -> &str {
|
||||
if string.ends_with(CRLF) {
|
||||
string.slice_to(string.len() - 2)
|
||||
&string[.. string.len() - 2]
|
||||
} else if string.ends_with(CR) {
|
||||
string.slice_to(string.len() - 1)
|
||||
&string[.. string.len() - 1]
|
||||
} else {
|
||||
string
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pub fn remove_trailing_crlf(string: &str) -> &str {
|
||||
/// Returns the first word of a string, or the string if it contains no space
|
||||
#[inline]
|
||||
pub fn get_first_word(string: &str) -> &str {
|
||||
string.split_str(CRLF).next().unwrap().splitn(1, ' ').next().unwrap()
|
||||
string.split(CRLF).next().unwrap().splitn(1, ' ').next().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the string replacing all the CRLF with "\<CRLF\>"
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
#![unstable]
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Show, Formatter};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
use command::Command;
|
||||
use self::TransactionState::{Unconnected, Connected, HelloSent, MailSent, RecipientSent, DataSent};
|
||||
|
||||
/// Contains the state of the current transaction
|
||||
#[deriving(PartialEq,Eq,Copy)]
|
||||
#[derive(PartialEq,Eq,Copy,Debug)]
|
||||
pub enum TransactionState {
|
||||
/// No connection was established
|
||||
Unconnected,
|
||||
@@ -34,9 +34,9 @@ pub enum TransactionState {
|
||||
DataSent,
|
||||
}
|
||||
|
||||
impl Show for TransactionState {
|
||||
impl Display for TransactionState {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.write(
|
||||
write! (f, "{}",
|
||||
match *self {
|
||||
Unconnected => "Unconnected",
|
||||
Connected => "Connected",
|
||||
@@ -44,7 +44,7 @@ impl Show for TransactionState {
|
||||
MailSent => "MailSent",
|
||||
RecipientSent => "RecipientSent",
|
||||
DataSent => "DataSent",
|
||||
}.as_bytes()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user