diff --git a/examples/client.rs b/examples/client.rs index 3f381a4..372e37a 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -63,10 +63,12 @@ fn main() { optflag("h", "help", "print this help menu"), optflag("v", "verbose", "display the transaction details"), ]; + let matches = match getopts(args_string.tail(), opts) { Ok(m) => { m } Err(f) => { panic!("{}", f) } }; + if matches.opt_present("h") { print_usage(description, opts); return; @@ -94,7 +96,6 @@ fn main() { let port = match matches.opt_str("p") { Some(port) => from_str::(port.as_slice()), None => None - }; let recipients_str: &str = if !matches.free.is_empty() { @@ -103,9 +104,10 @@ fn main() { print_usage(description, opts); return; }; + let mut recipients = Vec::new(); for recipient in recipients_str.split(' ') { - recipients.push(recipient) + recipients.push(recipient); } let mut message = String::new(); diff --git a/src/client/connecter.rs b/src/client/connecter.rs index 8b305ce..dd8090a 100644 --- a/src/client/connecter.rs +++ b/src/client/connecter.rs @@ -16,10 +16,6 @@ use std::io::net::ip::SocketAddr; use std::io::net::tcp::TcpStream; /// A trait for the concept of opening a stream connected to a IP socket address. -/// -/// Why is this here? So that we can implement things which must make -/// connections in terms of *anything* that can make such a connection rather -/// than in terms of `TcpStream` only. This is handy for testing and for SSL. pub trait Connecter { /// TODO fn connect(host: &str, port: u16) -> IoResult; diff --git a/src/client/mod.rs b/src/client/mod.rs index 8f84f9c..03d8259 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -66,6 +66,8 @@ macro_rules! fail_with_err ( impl Client { /// Creates a new SMTP client + /// + /// It does not connects to the server, but only create the `Client` pub fn new(host: &str, port: Option, my_hostname: Option<&str>) -> Client { Client{ stream: None, @@ -79,7 +81,7 @@ impl Client { } impl Client { - /// Closes the SMTP trclose_on_error if possible, and then closes the TCP session + /// Closes the SMTP transaction if possible, and then closes the TCP session fn close_on_error(&mut self) { if self.is_connected::() { let _ = self.quit::(); @@ -139,7 +141,6 @@ impl Client { } /// Sends an SMTP command - // TODO : ensure this is an ASCII string fn send_command(&mut self, command: Command) -> SmtpResult { // for now we do not support SMTPUTF8 if !command.is_ascii() { @@ -153,7 +154,11 @@ impl Client { self.send_server(Command::Message, Some(message)) } - /// TODO + /// Sends content to the server, after checking the command sequence, and then + /// updates the transaction state + /// + /// * If `message` is `None`, the given command will be formatted and sent to the server + /// * If `message` is `Some(str)`, the `str` string will be sent to the server fn send_server(&mut self, command: Command, message: Option<&str>) -> SmtpResult { if !self.state.is_command_possible(command.clone()) { fail_with_err!(Response{code: 503, message: Some("Bad sequence of commands".to_string())} self); @@ -177,6 +182,8 @@ impl Client { /// Connects to the configured server pub fn connect(&mut self) -> SmtpResult { + let command = command::Connect; + // connect should not be called when the client is already connected if !self.stream.is_none() { fail_with_err!("The connection is already established" self); @@ -189,13 +196,15 @@ impl Client { info!("Connection established to {}[{}]:{}", self.host, self.stream.clone().unwrap().peer_name().unwrap().ip, self.port); - let response = try!(self.stream.clone().unwrap().get_reply()); - let result = try!(response.with_code(vec![220])); - self.state = self.state.next_state(Command::Connect).unwrap(); - Ok(result) + let result = try!(self.stream.clone().unwrap().get_reply()); + + let checked_result = try!(command.test_success(result)); + + self.state = self.state.next_state(command).unwrap(); + Ok(checked_result) } - /// Checks if the server is connected + /// Checks if the server is connected using the NOOP SMTP command pub fn is_connected(&mut self) -> bool { self.noop::().is_ok() } @@ -210,7 +219,7 @@ impl Client { self.server_info = None; } - /// Send a HELO command + /// Send a HELO command and fills `server_info` pub fn helo(&mut self, my_hostname: &str) -> SmtpResult { let result = try!(self.send_command(command::Hello(my_hostname.to_string()))); self.server_info = Some( @@ -222,7 +231,7 @@ impl Client { Ok(result) } - /// Sends a EHLO command + /// Sends a EHLO command and fills `server_info` pub fn ehlo(&mut self, my_hostname: &str) -> SmtpResult { let result = try!(self.send_command(command::ExtendedHello(my_hostname.to_string()))); self.server_info = Some( @@ -273,8 +282,16 @@ impl Client { /// Sends the message content pub fn message(&mut self, message_content: &str) -> SmtpResult { - let server_info = self.server_info.clone().expect("Bad command sequence"); + let server_info = match self.server_info.clone() { + Some(info) => info, + None => fail_with_err!(Response{ + code: 503, + message: Some("Bad sequence of commands".to_string()) + } self) + }; + + // Check message encoding if !server_info.supports_feature(extension::EightBitMime).is_some() { if !message_content.clone().is_ascii() { fail_with_err!("Server does not accepts UTF-8 strings" self); diff --git a/src/client/server_info.rs b/src/client/server_info.rs index 331c085..09e961c 100644 --- a/src/client/server_info.rs +++ b/src/client/server_info.rs @@ -7,20 +7,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! TODO +//! Store the information about a server use std::fmt; use std::fmt::{Show, Formatter}; use extension::Extension; -/// Information about an SMTP server +/// Contains information about an SMTP server #[deriving(Clone)] pub struct ServerInfo { /// Server name + /// + /// The name given in the server banner pub name: String, /// ESMTP features supported by the server - /// The `None` value means the server does not support ESMTP + /// + /// It contains the features supported by the server and known by the `Extension` module. + /// The `None` value means the server does not support ESMTP. pub esmtp_features: Option> } diff --git a/src/client/stream.rs b/src/client/stream.rs index 1f1aab9..dbdc686 100644 --- a/src/client/stream.rs +++ b/src/client/stream.rs @@ -32,7 +32,7 @@ pub trait ClientStream { } impl ClientStream for TcpStream { - /// Sends a complete message or a command to the server and get the response + /// Sends a string to the server and get the response fn send_and_get_response(&mut self, string: &str, end: &str) -> SmtpResult { try!(self.write_str(format!("{}{}", escape_dot(string), end).as_slice())); diff --git a/src/command.rs b/src/command.rs index ff0d70e..cb82a90 100644 --- a/src/command.rs +++ b/src/command.rs @@ -100,7 +100,10 @@ impl Command { } } - /// TODO + /// Tests if the command was successful + /// + /// Returns `Ok` if the given response is considered successful for the `Command`, + /// `Err` otherwise pub fn test_success(&self, response: Response) -> SmtpResult { let success = match *self { Connect => vec![220], diff --git a/src/common.rs b/src/common.rs index f42caad..c4eb4e4 100644 --- a/src/common.rs +++ b/src/common.rs @@ -15,13 +15,13 @@ use std::io::net::ip::Port; use std::string::String; use std::str::replace; -/// Default SMTP port +/// Default smtp port pub static SMTP_PORT: Port = 25; -/// Default SMTPS port +/// Default smtps port pub static SMTPS_PORT: Port = 465; -/// Default SUBMISSION port +/// Default submission port pub static SUBMISSION_PORT: Port = 587; // Maximum length of an SMTP command line @@ -83,6 +83,7 @@ pub fn escape_crlf(string: &str) -> String { } /// Returns the string after adding a dot at the beginning of each line starting with a dot +/// /// Reference : https://tools.ietf.org/html/rfc5321#page-62 (4.5.2. Transparency) #[inline] pub fn escape_dot(string: &str) -> String { diff --git a/src/extension.rs b/src/extension.rs index 6d24808..9ee672d 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -7,7 +7,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! SMTP commands and ESMTP features library +//! ESMTP features library #![unstable] diff --git a/src/response.rs b/src/response.rs index be06ad6..1a46fb7 100644 --- a/src/response.rs +++ b/src/response.rs @@ -7,17 +7,14 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! SMTP responses, containing a mandatory return code, and an optional text message +//! SMTP response, containing a mandatory return code, and an optional text message #![unstable] use std::from_str::FromStr; use std::fmt::{Show, Formatter, Result}; -use std::result; -use std::error::FromError; use common::remove_trailing_crlf; -use error::SmtpError; /// Contains an SMTP reply, with separed code and message /// @@ -26,7 +23,7 @@ use error::SmtpError; pub struct Response { /// Server response code pub code: u16, - /// Server response string + /// Server response string (optionnal) pub message: Option } @@ -41,7 +38,6 @@ impl Show for Response { } } -// FromStr ? impl FromStr for Response { fn from_str(s: &str) -> Option { // If the string is too short to be a response code @@ -75,25 +71,9 @@ impl FromStr for Response { } } -impl Response { - /// Checks the presence of the response code in the array of expected codes. - pub fn with_code(&self, - expected_codes: Vec) -> result::Result { - let response = self.clone(); - if expected_codes.contains(&self.code) { - Ok(response) - } else { - Err(FromError::from_error(response)) - } - } -} - - - #[cfg(test)] mod test { use super::Response; - use std::error::FromError; #[test] fn test_fmt() { @@ -140,23 +120,4 @@ mod test { assert_eq!(from_str::("2"), None); assert_eq!(from_str::(""), None); } - - #[test] - fn test_with_code() { - assert_eq!( - Response{code: 200, message: Some("message".to_string())}.with_code(vec![200]), - Ok(Response{code: 200, message: Some("message".to_string())}) - ); - assert_eq!( - Response{code: 400, message: Some("message".to_string())}.with_code(vec![200]), - Err(FromError::from_error(Response{code: 400, message: Some("message".to_string())})) - ); - assert_eq!( - Response{ - code: 200, - message: Some("message".to_string()) - }.with_code(vec![200, 300]), - Ok(Response{code: 200, message: Some("message".to_string())}) - ); - } }