summaryrefslogtreecommitdiffstats
path: root/headers/src/headers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'headers/src/headers.rs')
-rw-r--r--headers/src/headers.rs276
1 files changed, 276 insertions, 0 deletions
diff --git a/headers/src/headers.rs b/headers/src/headers.rs
new file mode 100644
index 0000000..4cb862e
--- /dev/null
+++ b/headers/src/headers.rs
@@ -0,0 +1,276 @@
+
+
+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,
+ /// (rfc5322)
+ Date, unchecked { "Date" }, DateTime, maxOne, None,
+ /// (rfc5322)
+ _From, unchecked { "From" }, MailboxList, maxOne, validator_from,
+ /// (rfc5322)
+ Sender, unchecked { "Sender" }, Mailbox, maxOne, None,
+ /// (rfc5322)
+ ReplyTo, unchecked { "Reply-To" }, MailboxList, maxOne, None,
+ /// (rfc5322)
+ _To, unchecked { "To" }, MailboxList, maxOne, None,
+ /// (rfc5322)
+ Cc, unchecked { "Cc" }, MailboxList, maxOne, None,
+ /// (rfc5322)
+ Bcc, unchecked { "Bcc" }, MailboxList, maxOne, None,
+ /// (rfc5322)
+ MessageId, unchecked { "Message-Id" }, MessageId, maxOne, None,
+ /// (rfc5322)
+ InReplyTo, unchecked { "In-Reply-To" }, MessageIdList, maxOne, None,
+ /// (rfc5322)
+ References, unchecked { "References" }, MessageIdList, maxOne, None,
+ /// (rfc5322)
+ Subject, unchecked { "Subject" }, Unstructured, maxOne, None,
+ /// (rfc5322)
+ Comments, unchecked { "Comments" }, Unstructured, multi, None,
+ /// (rfc5322)
+ Keywords, unchecked { "Keywords" }, PhraseList, multi, None,
+ /// (rfc5322)
+ ResentDate, unchecked { "Resent-Date" }, DateTime, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentFrom, unchecked { "Resent-From" }, MailboxList, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentSender, unchecked { "Resent-Sender" }, Mailbox, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentTo, unchecked { "Resent-To" }, MailboxList, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentCc, unchecked { "Resent-Cc" }, MailboxList, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentBcc, unchecked { "Resent-Bcc" }, OptMailboxList, multi, validator_resent_any,
+ /// (rfc5322)
+ ResentMsgId, unchecked { "Resent-Msg-Id" }, MessageId, multi, validator_resent_any,
+ /// (rfc5322)
+ ReturnPath, unchecked { "Return-Path" }, Path, multi, None,
+ /// (rfc5322)
+ Received, unchecked { "Received" }, ReceivedToken, multi, None,
+
+ /// (rfc2045)
+ ContentType, unchecked { "Content-Type" }, MediaType, maxOne, None,
+
+ /// (rfc2045)
+ ContentId, unchecked { "Content-Id" }, ContentId, maxOne, None,
+
+ /// The transfer encoding used to (transfer) encode the body (rfc2045)
+ ///
+ /// This should either be:
+ ///
+ /// - `7bit`: Us-ascii only text, default value if header filed is not present
+ /// - `quoted-printable`: Data encoded with quoted-printable encoding).
+ /// - `base64`: Data encoded with base64 encoding.
+ ///
+ /// Through other defined values include:
+ ///
+ /// - `8bit`: Data which is not encoded but still considers lines and line length,
+ /// i.e. has no more then 998 bytes between two CRLF (or the start/end of data).
+ /// Bodies of this kind can still be send if the server supports the 8bit
+ /// mime extension.
+ ///
+ /// - `binary`: Data which is not encoded and can be any kind of arbitrary binary data.
+ /// To send binary bodies the `CHUNKING` smpt extension (rfc3030) needs to be
+ /// supported using BDATA instead of DATA to send the content. Note that the
+ /// extension does not fix the potential but rare problem of accendentall
+ /// multipart boundary collisions.
+ ///
+ ///
+ /// Nevertheless this encodings are mainly meant to be used for defining the
+ /// domain of data in a system before it is encoded.
+ ContentTransferEncoding, unchecked { "Content-Transfer-Encoding" }, TransferEncoding, maxOne, None,
+
+ /// A description of the content of the body (rfc2045)
+ ///
+ /// This is mainly usefull for multipart body parts, e.g.
+ /// to add an description to a inlined/attached image.
+ ContentDescription, unchecked { "Content-Description" }, Unstructured, maxOne, None,
+
+ /// Defines the disposition of a multipart part it is used on (rfc2183)
+ ///
+ /// This is meant to be used as a header for a multipart body part, which
+ /// was created from a resource, mainly a file.
+ ///
+ /// Examples are attachments like images, etc.
+ ///
+ /// Possible Dispositions are:
+ /// - Inline
+ /// - Attachment
+ ///
+ /// Additional it is used to provide following information as parameters:
+ /// - `filename`: the file name associated with the resource this body is based on
+ /// - `creation-date`: when the resource this body is based on was created
+ /// - `modification-date`: when the resource this body is based on was last modified
+ /// - `read-date`: when the resource this body is based on was read (to create the body)
+ /// - `size`: the size this resource should have, note that `Content-Size` is NOT a mail
+ /// related header but specific to http.
+ 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> {
+ // Note: we do not care about the quantity of From bodies,
+ // nor "other" From bodies
+ // (which do not use a MailboxList and we could
+ // therefore not cast to it,
+ // whatever header put them in has also put in
+ // this bit of validation )
+ let needs_sender =
+ map.get(_From)
+ .filter_map(|res| res.ok())
+ .any(|list| list.len() > 1);
+
+ if needs_sender && !map.contains(Sender) {
+ //this is the wrong bail...
+ header_validation_bail!(kind: MultiMailboxFromWithoutSender);
+ }
+ Ok(())
+ }
+
+ fn validate_resent_block<'a>(
+ block: &HashMap<HeaderName, &'a HeaderObj>
+ ) -> Result<(), HeaderValidationError> {
+ if !block.contains_key(&ResentDate::name()) {
+ //this is the wrong bail...
+ header_validation_bail!(kind: ResentDateFieldMissing);
+ }
+ let needs_sender =
+ //no Resend-From? => no problem
+ block.get(&ResentFrom::name())
+ //can't cast? => not my problem/responsibility
+ .and_then(|tobj| tobj.downcast_ref::<ResentFrom>())
+ .map(|list| list.len() > 1)
+ .unwrap_or(false);
+
+ if needs_sender && !block.contains_key(&ResentSender::name()) {
+ //this is the wrong bail...
+ 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)?;
+ //create new 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());
+ });
+
+} \ No newline at end of file