use ::header_components;
use self::validators::{
from as validator_from,
resent_any as validator_resent_any
};
def_headers! {
test_name: validate_header_names,
scope: header_components,
Date, unchecked { "Date" }, DateTime, maxOne, None,
_From, unchecked { "From" }, MailboxList, maxOne, validator_from,
Sender, unchecked { "Sender" }, Mailbox, maxOne, None,
ReplyTo, unchecked { "Reply-To" }, MailboxList, maxOne, None,
_To, unchecked { "To" }, MailboxList, maxOne, None,
Cc, unchecked { "Cc" }, MailboxList, maxOne, None,
Bcc, unchecked { "Bcc" }, MailboxList, maxOne, None,
MessageId, unchecked { "Message-Id" }, MessageId, maxOne, None,
InReplyTo, unchecked { "In-Reply-To" }, MessageIdList, maxOne, None,
References, unchecked { "References" }, MessageIdList, maxOne, None,
Subject, unchecked { "Subject" }, Unstructured, maxOne, None,
Comments, unchecked { "Comments" }, Unstructured, multi, None,
Keywords, unchecked { "Keywords" }, PhraseList, multi, None,
ResentDate, unchecked { "Resent-Date" }, DateTime, multi, validator_resent_any,
ResentFrom, unchecked { "Resent-From" }, MailboxList, multi, validator_resent_any,
ResentSender, unchecked { "Resent-Sender" }, Mailbox, multi, validator_resent_any,
ResentTo, unchecked { "Resent-To" }, MailboxList, multi, validator_resent_any,
ResentCc, unchecked { "Resent-Cc" }, MailboxList, multi, validator_resent_any,
ResentBcc, unchecked { "Resent-Bcc" }, OptMailboxList, multi, validator_resent_any,
ResentMsgId, unchecked { "Resent-Msg-Id" }, MessageId, multi, validator_resent_any,
ReturnPath, unchecked { "Return-Path" }, Path, multi, None,
Received, unchecked { "Received" }, ReceivedToken, multi, None,
ContentType, unchecked { "Content-Type" }, MediaType, maxOne, None,
ContentId, unchecked { "Content-Id" }, ContentId, maxOne, None,
ContentTransferEncoding, unchecked { "Content-Transfer-Encoding" }, TransferEncoding, maxOne, None,
ContentDescription, unchecked { "Content-Description" }, Unstructured, maxOne, None,
ContentDisposition, unchecked { "Content-Disposition" }, Disposition, maxOne, None
}
mod validators {
use std::collections::HashMap;
use ::{ HeaderMap, HeaderKind, HeaderName, HeaderObj };
use ::error::HeaderValidationError;
use super::{ _From, ResentFrom, Sender, ResentSender, ResentDate };
pub fn from(map: &HeaderMap) -> Result<(), HeaderValidationError> {
let needs_sender =
map.get(_From)
.filter_map(|res| res.ok())
.any(|list| list.len() > 1);
if needs_sender && !map.contains(Sender) {
header_validation_bail!(kind: MultiMailboxFromWithoutSender);
}
Ok(())
}
fn validate_resent_block<'a>(
block: &HashMap<HeaderName, &'a HeaderObj>
) -> Result<(), HeaderValidationError> {
if !block.contains_key(&ResentDate::name()) {
header_validation_bail!(kind: ResentDateFieldMissing);
}
let needs_sender =
block.get(&ResentFrom::name())
.and_then(|tobj| tobj.downcast_ref::<ResentFrom>())
.map(|list| list.len() > 1)
.unwrap_or(false);
if needs_sender && !block.contains_key(&ResentSender::name()) {
header_validation_bail!(kind: MultiMailboxResentFromWithoutResentSender)
}
Ok(())
}
pub fn resent_any(map: &HeaderMap) -> Result<(), HeaderValidationError> {
let resents = map
.iter()
.filter(|&(name, _)| name.as_str().starts_with("Resent-"));
let mut block = HashMap::new();
for (name, content) in resents {
if block.contains_key(&name) {
validate_resent_block(&block)?;
block = HashMap::new();
}
block.insert(name, content);
}
validate_resent_block(&block)
}
}
#[cfg(test)]
mod test {
use ::header_components::DateTime;
use ::{HeaderMap, HeaderKind};
use ::headers::{
_From, ResentFrom, ResentTo, ResentDate,
Sender, ResentSender, Subject
};
test!(from_validation_normal {
let mut map = HeaderMap::new();
map.insert(_From ::auto_body( [("Mr. Peté", "pete@nixmail.example")] )?);
map.insert(Subject ::auto_body( "Ok" )?);
assert_ok!(map.use_contextual_validators());
});
test!(from_validation_multi_err {
let mut map = HeaderMap::new();
map.insert(_From::auto_body((
("Mr. Peté", "nixperson@nixmail.nixdomain"),
"a@b.c"
))?);
map.insert(Subject::auto_body("Ok")?);
assert_err!(map.use_contextual_validators());
});
test!(from_validation_multi_ok {
let mut map = HeaderMap::new();
map.insert(_From::auto_body((
("Mr. Peté", "nixperson@nixmail.nixdomain"),
"a@b.c"
))?);
map.insert(Sender ::auto_body( "abx@d.e" )?);
map.insert(Subject ::auto_body( "Ok" )?);
assert_ok!(map.use_contextual_validators());
});
test!(resent_no_date_err {
let mut map = HeaderMap::new();
map.insert(ResentFrom ::auto_body( ["a@b.c"] )?);
assert_err!(map.use_contextual_validators());
});
test!(resent_with_date {
let mut map = HeaderMap::new();
map.insert(ResentFrom ::auto_body( ["a@b.c"] )?);
map.insert(ResentDate ::auto_body( DateTime::now() )?);
assert_ok!(map.use_contextual_validators());
});
test!(resent_no_date_err_second_block {
let mut map = HeaderMap::new();
map.insert(ResentDate ::auto_body( DateTime::now() )?);
map.insert(ResentFrom ::auto_body( ["a@b.c"] )?);
map.insert(ResentTo ::auto_body( ["e@f.d"] )?);
map.insert(ResentFrom ::auto_body( ["ee@ee.e"] )?);
assert_err!(map.use_contextual_validators());
});
test!(resent_with_date_second_block {
let mut map = HeaderMap::new();
map.insert(ResentDate ::auto_body( DateTime::now() )?);
map.insert(ResentFrom ::auto_body( ["a@b.c"] )?);
map.insert(ResentTo ::auto_body( ["e@f.d"] )?);
map.insert(ResentFrom ::auto_body( ["ee@ee.e"] )?);
map.insert(ResentDate ::auto_body( DateTime::now() )?);
assert_ok!(map.use_contextual_validators());
});
test!(resent_multi_mailbox_from_no_sender {
let mut map = HeaderMap::new();
map.insert(ResentDate ::auto_body( DateTime::now() )?);
map.insert(ResentFrom ::auto_body( ["a@b.c","e@c.d"] )?);
assert_err!(map.use_contextual_validators());
});
test!(resent_multi_mailbox_from_with_sender {
let mut map = HeaderMap::new();
map.insert(ResentDate ::auto_body( DateTime::now() )?);
map.insert(ResentFrom ::auto_body( ["a@b.c","e@c.d"] )?);
map.insert(ResentSender ::auto_body( "a@b.c" )?);
assert_ok!(map.use_contextual_validators());
});
}