Add transparency for <CRLF>.<CRLF>
This commit is contained in:
@@ -19,7 +19,7 @@ use response::Response;
|
||||
use extension;
|
||||
use command;
|
||||
use command::Command;
|
||||
use common::{SMTP_PORT, CRLF};
|
||||
use common::SMTP_PORT;
|
||||
use transaction;
|
||||
use transaction::TransactionState;
|
||||
use client::connecter::Connecter;
|
||||
@@ -175,12 +175,12 @@ impl<S: Connecter + ClientStream + Clone> Client<S> {
|
||||
if !self.state.is_command_possible(command.clone()) {
|
||||
panic!("Bad command sequence");
|
||||
}
|
||||
self.stream.clone().unwrap().send_and_get_response(format!("{}", command).as_slice())
|
||||
self.stream.clone().unwrap().send_and_get_response(format!("{}", command).as_slice(), false)
|
||||
}
|
||||
|
||||
/// Sends the email content
|
||||
fn send_message(&mut self, message: String) -> Response {
|
||||
self.stream.clone().unwrap().send_and_get_response(format!("{}{}.{}", message, CRLF, CRLF).as_slice())
|
||||
self.stream.clone().unwrap().send_and_get_response(format!("{}", message).as_slice(), true)
|
||||
}
|
||||
|
||||
/// Connects to the configured server
|
||||
|
||||
@@ -6,14 +6,14 @@ use std::str::from_utf8;
|
||||
use std::vec::Vec;
|
||||
|
||||
use response::Response;
|
||||
use common::escape_crlf;
|
||||
use common::{escape_crlf, escape_dot, CRLF};
|
||||
|
||||
static BUFFER_SIZE: uint = 1024;
|
||||
|
||||
/// TODO
|
||||
pub trait ClientStream {
|
||||
/// TODO
|
||||
fn send_and_get_response(&mut self, string: &str) -> Response;
|
||||
fn send_and_get_response(&mut self, string: &str, is_message: bool) -> Response;
|
||||
/// TODO
|
||||
fn get_reply(&mut self) -> Option<Response>;
|
||||
/// TODO
|
||||
@@ -22,9 +22,13 @@ pub trait ClientStream {
|
||||
|
||||
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: {}", escape_crlf(string.to_string())),
|
||||
fn send_and_get_response(&mut self, string: &str, is_message: bool) -> Response {
|
||||
let end = match is_message {
|
||||
true => format!("{}.{}", CRLF, CRLF),
|
||||
false => format!("{}", CRLF)
|
||||
};
|
||||
match self.write_str(format!("{}{}", escape_dot(string.to_string()), end).as_slice()) {
|
||||
Ok(..) => debug!("Wrote: {}", escape_crlf(escape_dot(string.to_string()))),
|
||||
Err(..) => panic!("Could not write to stream")
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! Represents a valid complete SMTP command, ready to be sent to a server
|
||||
|
||||
use std::fmt::{Show, Formatter, Result};
|
||||
use common::{SP, CRLF};
|
||||
use common::SP;
|
||||
|
||||
/// Supported SMTP commands
|
||||
///
|
||||
@@ -49,7 +49,7 @@ pub enum Command {
|
||||
|
||||
impl Show for Command {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
let mut line = match *self {
|
||||
f.write( match *self {
|
||||
Connect => "CONNECT".to_string(),
|
||||
ExtendedHello(ref my_hostname) =>
|
||||
format!("EHLO {}", my_hostname.clone()),
|
||||
@@ -78,9 +78,7 @@ impl Show for Command {
|
||||
format!("HELP {}", argument.clone()),
|
||||
Noop => "NOOP".to_string(),
|
||||
Quit => "QUIT".to_string(),
|
||||
};
|
||||
line.push_str(CRLF);
|
||||
f.write(line.as_bytes())
|
||||
}.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,25 +105,24 @@ impl Command {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use command;
|
||||
use common::CRLF;
|
||||
|
||||
#[test]
|
||||
fn test_fmt() {
|
||||
assert_eq!(
|
||||
format!("{}", command::Noop),
|
||||
format!("NOOP{}", CRLF)
|
||||
format!("NOOP")
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", command::ExtendedHello("my_name".to_string())),
|
||||
format!("EHLO my_name{}", CRLF)
|
||||
format!("EHLO my_name")
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", command::Mail("test".to_string(), Some(vec!("option".to_string())))),
|
||||
format!("MAIL FROM:<test> option{}", CRLF)
|
||||
format!("MAIL FROM:<test> option")
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", command::Mail("test".to_string(), Some(vec!("option".to_string(), "option2".to_string())))),
|
||||
format!("MAIL FROM:<test> option option2{}", CRLF)
|
||||
format!("MAIL FROM:<test> option option2")
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ pub static SP: &'static str = " ";
|
||||
|
||||
/// The line ending for SMTP transactions
|
||||
pub static CRLF: &'static str = "\r\n";
|
||||
pub static CR: &'static str = "\r";
|
||||
pub static LF: &'static str = "\n";
|
||||
|
||||
/// Adds quotes to emails if needed
|
||||
pub fn quote_email_address(address: String) -> String {
|
||||
@@ -66,14 +68,27 @@ pub fn get_first_word(string: String) -> String {
|
||||
string.as_slice().split_str(CRLF).next().unwrap().splitn(1, ' ').next().unwrap().to_string()
|
||||
}
|
||||
|
||||
/// TODO
|
||||
/// Returns the string replacing all the CRLF with "<CRLF>"
|
||||
#[inline]
|
||||
pub fn escape_crlf(string: String) -> String {
|
||||
string.replace(CRLF, "<CRLF>")
|
||||
string.replace(CRLF, "<CR><LF>")
|
||||
}
|
||||
|
||||
/// 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: String) -> String {
|
||||
if string.len() > 0 && string.chars().next().unwrap() == '.' {
|
||||
format!(".{}", string)
|
||||
} else {
|
||||
string
|
||||
}.replace(format!("{}.", CR).as_slice(), format!("{}..", CR).as_slice())
|
||||
.replace(format!("{}.", LF).as_slice(), format!("{}..", LF).as_slice())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::CRLF;
|
||||
use super::{CRLF, CR, LF};
|
||||
|
||||
#[test]
|
||||
fn test_quote_email_address() {
|
||||
@@ -122,8 +137,17 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_escape_crlf() {
|
||||
assert_eq!(super::escape_crlf(format!("{}", CRLF)), "<CRLF>".to_string());
|
||||
assert_eq!(super::escape_crlf(format!("EHLO my_name{}", CRLF)), "EHLO my_name<CRLF>".to_string());
|
||||
assert_eq!(super::escape_crlf(format!("EHLO my_name{}SIZE 42{}", CRLF, CRLF)), "EHLO my_name<CRLF>SIZE 42<CRLF>".to_string());
|
||||
assert_eq!(super::escape_crlf(format!("{}", CRLF)), "<CR><LF>".to_string());
|
||||
assert_eq!(super::escape_crlf(format!("EHLO my_name{}", CRLF)), "EHLO my_name<CR><LF>".to_string());
|
||||
assert_eq!(super::escape_crlf(format!("EHLO my_name{}SIZE 42{}", CRLF, CRLF)), "EHLO my_name<CR><LF>SIZE 42<CR><LF>".to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_escape_dot() {
|
||||
assert_eq!(super::escape_dot(".test".to_string()), "..test".to_string());
|
||||
assert_eq!(super::escape_dot(format!("{}.{}.{}", CR, LF, CRLF)), format!("{}..{}..{}", CR, LF, CRLF));
|
||||
assert_eq!(super::escape_dot(format!("{}.{}.{}", CRLF, CRLF, CRLF)), format!("{}..{}..{}", CRLF, CRLF, CRLF));
|
||||
assert_eq!(super::escape_dot(format!("test{}.test{}", CRLF, CRLF)), format!("test{}..test{}", CRLF, CRLF));
|
||||
assert_eq!(super::escape_dot(format!("test{}.{}test", CRLF, CRLF)), format!("test{}..{}test", CRLF, CRLF));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user