1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
use super::{Flag, Seq, Uid}; use chrono::{DateTime, FixedOffset}; use imap_proto::types::{AttributeValue, BodyStructure, Envelope, MessageSection, SectionPath}; /// Format of Date and Time as defined RFC3501. /// See `date-time` element in [Formal Syntax](https://tools.ietf.org/html/rfc3501#section-9) /// chapter of this RFC. const DATE_TIME_FORMAT: &str = "%d-%b-%Y %H:%M:%S %z"; /// An IMAP [`FETCH` response](https://tools.ietf.org/html/rfc3501#section-7.4.2) that contains /// data about a particular message. This response occurs as the result of a `FETCH` or `STORE` /// command, as well as by unilateral server decision (e.g., flag updates). #[derive(Debug, Eq, PartialEq)] pub struct Fetch { /// The ordinal number of this message in its containing mailbox. pub message: Seq, /// A number expressing the unique identifier of the message. /// Only present if `UID` was specified in the query argument to `FETCH` and the server /// supports UIDs. pub uid: Option<Uid>, /// A number expressing the [RFC-2822](https://tools.ietf.org/html/rfc2822) size of the message. /// Only present if `RFC822.SIZE` was specified in the query argument to `FETCH`. pub size: Option<u32>, // Note that none of these fields are *actually* 'static. Rather, they are tied to the lifetime // of the `ZeroCopy` that contains this `Name`. That's also why they can't be public -- we can // only return them with a lifetime tied to self. pub(crate) fetch: Vec<AttributeValue<'static>>, pub(crate) flags: Vec<Flag<'static>>, } impl Fetch { /// A list of flags that are set for this message. pub fn flags(&self) -> &[Flag<'_>] { &self.flags[..] } /// The bytes that make up the header of this message, if `BODY[HEADER]`, `BODY.PEEK[HEADER]`, /// or `RFC822.HEADER` was included in the `query` argument to `FETCH`. pub fn header(&self) -> Option<&[u8]> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::BodySection { section: Some(SectionPath::Full(MessageSection::Header)), data: Some(hdr), .. } | AttributeValue::Rfc822Header(Some(hdr)) => Some(*hdr), _ => None, }) .next() } /// The bytes that make up this message, included if `BODY[]` or `RFC822` was included in the /// `query` argument to `FETCH`. The bytes SHOULD be interpreted by the client according to the /// content transfer encoding, body type, and subtype. pub fn body(&self) -> Option<&[u8]> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::BodySection { section: None, data: Some(body), .. } | AttributeValue::Rfc822(Some(body)) => Some(*body), _ => None, }) .next() } /// The bytes that make up the text of this message, included if `BODY[TEXT]`, `RFC822.TEXT`, /// or `BODY.PEEK[TEXT]` was included in the `query` argument to `FETCH`. The bytes SHOULD be /// interpreted by the client according to the content transfer encoding, body type, and /// subtype. pub fn text(&self) -> Option<&[u8]> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::BodySection { section: Some(SectionPath::Full(MessageSection::Text)), data: Some(body), .. } | AttributeValue::Rfc822Text(Some(body)) => Some(*body), _ => None, }) .next() } /// The envelope of this message, if `ENVELOPE` was included in the `query` argument to /// `FETCH`. This is computed by the server by parsing the /// [RFC-2822](https://tools.ietf.org/html/rfc2822) header into the component parts, defaulting /// various fields as necessary. /// /// The full description of the format of the envelope is given in [RFC 3501 section /// 7.4.2](https://tools.ietf.org/html/rfc3501#section-7.4.2). pub fn envelope(&self) -> Option<&Envelope<'_>> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::Envelope(env) => Some(&**env), _ => None, }) .next() } /// Extract the bytes that makes up the given `BOD[<section>]` of a `FETCH` response. /// /// See [section 7.4.2 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-7.4.2) for /// details. pub fn section(&self, path: &SectionPath) -> Option<&[u8]> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::BodySection { section: Some(sp), data: Some(data), .. } if sp == path => Some(*data), _ => None, }) .next() } /// Extract the `INTERNALDATE` of a `FETCH` response /// /// See [section 2.3.3 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-2.3.3) for /// details. pub fn internal_date(&self) -> Option<DateTime<FixedOffset>> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::InternalDate(date_time) => Some(*date_time), _ => None, }) .next() .and_then( |date_time| match DateTime::parse_from_str(date_time, DATE_TIME_FORMAT) { Ok(date_time) => Some(date_time), Err(_) => None, }, ) } /// Extract the `BODYSTRUCTURE` of a `FETCH` response /// /// See [section 2.3.6 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-2.3.6) for /// details. pub fn bodystructure<'a>(&self) -> Option<&BodyStructure<'a>> { self.fetch .iter() .filter_map(|av| match av { AttributeValue::BodyStructure(bs) => Some(bs), _ => None, }) .next() } }