diff --git a/src/client/mod.rs b/src/client/mod.rs index 3290f9b..2274298 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -10,10 +10,8 @@ //! SMTP client use std::fmt::Show; -use std::str::from_utf8; use std::result::Result; use std::string::String; -use std::io::{IoResult, Reader, Writer}; use std::io::net::ip::Port; use common::{get_first_word, unquote_email_address}; @@ -26,9 +24,11 @@ use transaction; use transaction::TransactionState; use client::connecter::Connecter; use client::server_info::ServerInfo; +use client::stream::ClientStream; mod connecter; mod server_info; +mod stream; /// Structure that implements the SMTP client pub struct Client { @@ -63,7 +63,7 @@ impl Client { } // T : String ou String, selon le support -impl Client { +impl Client { /// TODO fn smtp_fail_if_err(&mut self, response: Result) { @@ -92,7 +92,7 @@ impl Client { info!("Connection established to {}[{}]:{}", self.host, self.stream.clone().unwrap().peer_name().unwrap().ip, self.port); - match self.get_reply() { + match self.stream.clone().unwrap().get_reply() { Some(response) => match response.with_code(vec!(220)) { Ok(response) => { self.state = transaction::Connected; @@ -182,35 +182,12 @@ impl Client { if !self.state.is_command_possible(command.clone()) { panic!("Bad command sequence"); } - self.send_and_get_response(format!("{}", command).as_slice()) + self.stream.clone().unwrap().send_and_get_response(format!("{}", command).as_slice()) } /// Sends an email fn send_message(&mut self, message: String) -> Response { - self.send_and_get_response(format!("{}{:s}.", message, CRLF).as_slice()) - } - - /// Sends a complete message or a command to the server and get the response - fn send_and_get_response(&mut self, string: &str) -> Response { - match (&mut self.stream.clone().unwrap() as &mut Writer) - .write_str(format!("{}", string).as_slice()) { // TODO improve this - Ok(..) => debug!("Wrote: {}", string), - Err(..) => panic!("Could not write to stream") - } - - match self.get_reply() { - Some(response) => {debug!("Read: {}", response); response}, - None => panic!("No answer on {}", self.host) - } - } - - /// Gets the SMTP response - fn get_reply(&mut self) -> Option { - let response = match self.read_to_string() { - Ok(string) => string, - Err(..) => panic!("No answer") - }; - from_str::(response.as_slice()) + self.stream.clone().unwrap().send_and_get_response(format!("{}{}.{}", message, CRLF, CRLF).as_slice()) } /// Closes the connection and fail with a given messgage @@ -373,35 +350,3 @@ impl Client { self.send_command(command::Verify(to_address, None)).with_code(vec!(250)) } } - -impl Reader for Client { - /// Reads a string from the client socket - fn read(&mut self, buf: &mut [u8]) -> IoResult { - self.stream.clone().unwrap().read(buf) - } - - /// Reads a string from the client socket - // TODO: Size of response ?. - fn read_to_string(&mut self) -> IoResult { - let mut buf = [0u8, ..1034]; - - let response = match self.read(buf) { - Ok(bytes_read) => from_utf8(buf.slice_to(bytes_read - 1)).unwrap(), - Err(..) => panic!("Read error") - }; - - return Ok(response.to_string()); - } -} - -impl Writer for Client { - /// Sends a string on the client socket - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - self.stream.clone().unwrap().write(buf) - } - - /// Sends a string on the client socket - fn write_str(&mut self, string: &str) -> IoResult<()> { - self.stream.clone().unwrap().write_str(string) - } -} diff --git a/src/client/stream.rs b/src/client/stream.rs new file mode 100644 index 0000000..c7a984f --- /dev/null +++ b/src/client/stream.rs @@ -0,0 +1,70 @@ +//! TODO + +use response::Response; +use std::io::net::tcp::TcpStream; +use std::io::IoResult; +use std::str::from_utf8; +use std::vec::Vec; + +static BUFFER_SIZE: uint = 1024; + +/// TODO +pub trait ClientStream { + /// TODO + fn send_and_get_response(&mut self, string: &str) -> Response; + /// TODO + fn get_reply(&mut self) -> Option; + /// TODO + fn read_into_string(&mut self) -> IoResult; +} + +impl ClientStream for TcpStream { + /// Sends a complete message or a command to the server and get the response + fn send_and_get_response(&mut self, string: &str) -> Response { + match self.write_str(format!("{}", string).as_slice()) { + Ok(..) => debug!("Wrote: {}", string), + Err(..) => panic!("Could not write to stream") + } + + match self.get_reply() { + Some(response) => {debug!("Read: {}", response); response}, + None => panic!("No answer") + } + } + + /// Reads on the stream into a string + fn read_into_string(&mut self) -> IoResult { + let mut more = true; + let mut result = String::new(); + // TODO: Set appropriate timeouts + self.set_timeout(Some(1000)); + + while more { + let mut buf: Vec = Vec::with_capacity(BUFFER_SIZE); + let response = match self.push(BUFFER_SIZE, &mut buf) { + Ok(bytes_read) => { + more = bytes_read == BUFFER_SIZE; + if bytes_read > 0 { + from_utf8(buf.slice_to(bytes_read)).unwrap() + } else { + "" + } + }, + // TODO: Manage error kinds + Err(..) => {more = false; ""}, + }; + result.push_str(response); + } + debug!("Read: {}", result); + return Ok(result); + } + + /// Gets the SMTP response + fn get_reply(&mut self) -> Option { + let response = match self.read_into_string() { + Ok(string) => string, + Err(..) => panic!("No answer") + }; + from_str::(response.as_slice()) + } +} diff --git a/src/common.rs b/src/common.rs index 8125b32..831da8a 100644 --- a/src/common.rs +++ b/src/common.rs @@ -16,8 +16,9 @@ use std::string::String; /// Default SMTP port pub static SMTP_PORT: Port = 25; -//pub static SMTPS_PORT: Port = 465; -//pub static SUBMISSION_PORT: Port = 587; + +// Maximum length of an SMTP command line +//pub static MAX_SMTP_LINE_LENGTH: uint = 1034; /// The word separator for SMTP transactions pub static SP: &'static str = " ";