use super::utils;
use std::borrow::ToOwned;
#[cfg(test)]
use std::iter::{repeat, FromIterator};
use std::net::IpAddr;
#[cfg(test)]
use std::net::{Ipv4Addr, Ipv6Addr};
use std::string::String;
static MAX_MAILBOX_LOCAL_PART_LEN: usize = 64;
static MAX_MAILBOX_LEN: usize = 254;
static MAX_DOMAIN_LEN: usize = 255;
#[test]
fn test_static_vars() {
assert_eq!(64, MAX_MAILBOX_LOCAL_PART_LEN);
assert_eq!(254, MAX_MAILBOX_LEN);
assert_eq!(255, MAX_DOMAIN_LEN);
}
fn get_mailbox_local_part(s: &str) -> Option<&str> {
utils::get_dot_string(s).or_else(|| utils::get_quoted_string(s))
}
#[test]
fn test_local_part() {
assert_eq!(Some("rust.cool"), get_mailbox_local_part("rust.cool"));
assert_eq!(
Some("\"rust \\a cool\""),
get_mailbox_local_part("\"rust \\a cool\"")
);
assert_eq!(
Some("\"rust.cool\""),
get_mailbox_local_part("\"rust.cool\"")
);
assert_eq!(
Some("\"rust.cool.\""),
get_mailbox_local_part("\"rust.cool.\"")
);
assert_eq!(
Some("\"rust\\\\\\b\\;.c\\\"ool\""),
get_mailbox_local_part("\"rust\\\\\\b\\;.c\\\"ool\"")
);
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum MailboxForeignPart {
Domain(String),
IpAddr(IpAddr),
}
#[test]
fn test_foreign_part() {
let domain_text = "rustastic.org";
let domain = MailboxForeignPart::Domain(domain_text.to_owned());
let ipv4 = MailboxForeignPart::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
let ipv6 = MailboxForeignPart::IpAddr(IpAddr::V6(Ipv6Addr::new(1, 1, 1, 1, 1, 1, 1, 1)));
assert!(domain == domain);
assert!(domain != MailboxForeignPart::Domain(domain_text.to_owned() + "bullshit"));
assert!(domain != ipv4);
assert!(domain != ipv6);
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Mailbox {
local_part: String,
foreign_part: MailboxForeignPart,
}
#[derive(PartialEq, Eq, Clone, Debug, Copy)]
pub enum MailboxParseError {
LocalPartTooLong,
LocalPartUnrecognized,
ForeignPartUnrecognized,
DomainTooLong,
TooLong,
AtNotFound,
}
impl Mailbox {
pub fn parse(s: &str) -> Result<Mailbox, MailboxParseError> {
let mut local_part: String;
let foreign_part: MailboxForeignPart;
let mut offset = utils::get_source_route(s).map_or(0, |s| s.len());
match get_mailbox_local_part(&s[offset..]) {
Some(lp) => {
if lp.len() > MAX_MAILBOX_LOCAL_PART_LEN {
return Err(MailboxParseError::LocalPartTooLong);
}
local_part = lp.to_owned();
offset += lp.len();
}
None => {
return Err(MailboxParseError::LocalPartUnrecognized);
}
}
if offset >= s.len() {
return Err(MailboxParseError::AtNotFound);
}
if s.chars().nth(offset).unwrap() != '@' {
return Err(MailboxParseError::LocalPartUnrecognized);
}
offset += 1;
match utils::get_domain(&s[offset..]) {
Some(d) => {
if d.len() > MAX_DOMAIN_LEN {
return Err(MailboxParseError::DomainTooLong);
}
foreign_part = MailboxForeignPart::Domain(s[offset..offset + d.len()].to_owned());
offset += d.len();
}
None => match utils::get_mailbox_ip(&s[offset..]) {
Some((ip, addr)) => {
foreign_part = MailboxForeignPart::IpAddr(addr);
offset += ip.len();
}
None => {
return Err(MailboxParseError::ForeignPartUnrecognized);
}
},
}
if offset != s.len() {
Err(MailboxParseError::ForeignPartUnrecognized)
} else if offset > MAX_MAILBOX_LEN {
Err(MailboxParseError::TooLong)
} else {
if local_part.as_str().is_ascii() {
let local_part_c = local_part.to_lowercase();
if local_part_c.as_str() == "postmaster" {
local_part = "postmaster".to_owned();
}
}
Ok(Mailbox {
local_part: local_part,
foreign_part: foreign_part,
})
}
}
}
#[test]
fn test_mailbox() {
let mut s = String::from_iter(repeat('a').take(MAX_MAILBOX_LOCAL_PART_LEN));
s.push_str("@t.com");
assert!(Mailbox::parse(s.as_str()).is_ok());
let mut s = String::from_iter(repeat('a').take(MAX_MAILBOX_LOCAL_PART_LEN + 1));
s.push_str("@t.com");
assert_eq!(
Err(MailboxParseError::LocalPartTooLong),
Mailbox::parse(s.as_str())
);
assert_eq!(
Err(MailboxParseError::LocalPartUnrecognized),
Mailbox::parse("t @t.com{")
);
assert_eq!(
Err(MailboxParseError::LocalPartUnrecognized),
Mailbox::parse("t ")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("t@{}")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("t@t.com{")
);
assert_eq!(
Err(MailboxParseError::TooLong),
Mailbox::parse(
("rust@".to_owned() + String::from_iter(repeat('a').take(MAX_DOMAIN_LEN)).as_str())
.as_str()
)
);
assert_eq!(
Err(MailboxParseError::DomainTooLong),
Mailbox::parse(
("rust@".to_owned() + String::from_iter(repeat('a').take(MAX_DOMAIN_LEN + 1)).as_ref())
.as_str()
)
);
assert!(Mailbox::parse(
("rust@".to_owned() + String::from_iter(repeat('a').take(MAX_MAILBOX_LEN - 5)).as_str())
.as_str()
)
.is_ok());
assert_eq!(
Err(MailboxParseError::TooLong),
Mailbox::parse(
("rust@".to_owned()
+ String::from_iter(repeat('a').take(MAX_MAILBOX_LEN - 4)).as_str())
.as_str()
)
);
assert_eq!(Err(MailboxParseError::AtNotFound), Mailbox::parse("t"));
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("rust.is@[127.0.0.1")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("rust.is@[00.0.1]")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("rust.is@[::1]")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("rust.is@[Ipv6: ::1]")
);
assert_eq!(
Err(MailboxParseError::ForeignPartUnrecognized),
Mailbox::parse("rust.is@[Ipv6:::1")
);
let path_1 = Mailbox::parse("rust.is@rustastic.org").unwrap();
let path_2 = Mailbox::parse("rust.is.not@rustastic.org").unwrap();
let path_3 = Mailbox::parse("\"hello\"@rust").unwrap();
assert!(path_1 == path_1.clone());
assert!(path_2 == path_2.clone());
assert!(path_1 != path_2);
assert_eq!(path_3.local_part.as_str(), "\"hello\"");
assert_eq!(
path_3.foreign_part,
MailboxForeignPart::Domain("rust".to_owned())
);
let path_4 = Mailbox::parse("rust.is@[127.0.0.1]").unwrap();
assert_eq!(
path_4.foreign_part,
MailboxForeignPart::IpAddr(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)))
);
let path_5 = Mailbox::parse("rust.is@[Ipv6:::1]").unwrap();
assert_eq!(
path_5.foreign_part,
MailboxForeignPart::IpAddr(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)))
);
let path_6 = Mailbox::parse("rust.is@[Ipv6:2001:db8::ff00:42:8329]").unwrap();
assert_eq!(
path_6.foreign_part,
MailboxForeignPart::IpAddr(IpAddr::V6(Ipv6Addr::new(
0x2001, 0xdb8, 0x0, 0x0, 0x0, 0xff00, 0x42, 0x8329
)))
);
let path_7 = Mailbox::parse("PosTMAster@ok").unwrap();
assert_eq!("postmaster", path_7.local_part.as_str());
let path_8 = Mailbox::parse("postmaster@ok").unwrap();
assert_eq!("postmaster", path_8.local_part.as_str());
let path_9 = Mailbox::parse("#!$%&'*+-/=?^_{}|~@example.org").unwrap();
assert_eq!("#!$%&'*+-/=?^_{}|~", path_9.local_part);
assert_eq!(
MailboxForeignPart::Domain("example.org".to_string()),
path_9.foreign_part
);
}