Remove the with_code method from response
This commit is contained in:
@@ -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>(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();
|
||||
|
||||
@@ -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<Self>;
|
||||
|
||||
@@ -66,6 +66,8 @@ macro_rules! fail_with_err (
|
||||
|
||||
impl<S> Client<S> {
|
||||
/// Creates a new SMTP client
|
||||
///
|
||||
/// It does not connects to the server, but only create the `Client`
|
||||
pub fn new(host: &str, port: Option<Port>, my_hostname: Option<&str>) -> Client<S> {
|
||||
Client{
|
||||
stream: None,
|
||||
@@ -79,7 +81,7 @@ impl<S> Client<S> {
|
||||
}
|
||||
|
||||
impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
/// 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<S>(&mut self) {
|
||||
if self.is_connected::<S>() {
|
||||
let _ = self.quit::<S>();
|
||||
@@ -139,7 +141,6 @@ impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
}
|
||||
|
||||
/// 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<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
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<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
|
||||
/// 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<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
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<S>(&mut self) -> bool {
|
||||
self.noop::<S>().is_ok()
|
||||
}
|
||||
@@ -210,7 +219,7 @@ impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
self.server_info = None;
|
||||
}
|
||||
|
||||
/// Send a HELO command
|
||||
/// Send a HELO command and fills `server_info`
|
||||
pub fn helo<S>(&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<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Sends a EHLO command
|
||||
/// Sends a EHLO command and fills `server_info`
|
||||
pub fn ehlo<S>(&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<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
|
||||
/// Sends the message content
|
||||
pub fn message<S>(&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);
|
||||
|
||||
@@ -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<Vec<Extension>>
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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<String>
|
||||
}
|
||||
|
||||
@@ -41,7 +38,6 @@ impl Show for Response {
|
||||
}
|
||||
}
|
||||
|
||||
// FromStr ?
|
||||
impl FromStr for Response {
|
||||
fn from_str(s: &str) -> Option<Response> {
|
||||
// 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<u16>) -> result::Result<Response, SmtpError> {
|
||||
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::<Response>("2"), None);
|
||||
assert_eq!(from_str::<Response>(""), 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())})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user