aboutsummaryrefslogtreecommitdiff
path: root/src/imap
diff options
context:
space:
mode:
Diffstat (limited to 'src/imap')
-rw-r--r--src/imap/mailbox_view.rs221
1 files changed, 101 insertions, 120 deletions
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs
index b0ad95a..70dedf0 100644
--- a/src/imap/mailbox_view.rs
+++ b/src/imap/mailbox_view.rs
@@ -1,4 +1,5 @@
use std::borrow::Borrow;
+use std::collections::HashMap;
use std::num::NonZeroU32;
use std::sync::Arc;
@@ -278,64 +279,12 @@ impl MailboxView {
FetchAttribute::Envelope => {
attributes.push(MessageAttribute::Envelope(message_envelope(&parsed)))
}
- FetchAttribute::Body => {
- /*
- * CAPTURE:
- b fetch 29878:29879 (BODY)
- * 29878 FETCH (BODY (("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 3264 82)("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 31834 643) "alternative"))
- * 29879 FETCH (BODY ("text" "html" ("charset" "us-ascii") NIL NIL "7bit" 4107 131))
- ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^^^ ^^^
- | | | | | | number of lines
- | | | | | size
- | | | | content transfer encoding
- | | | description
- | | id
- | parameter list
- b OK Fetch completed (0.001 + 0.000 secs).
- *
- */
-
- /*match parsed.structure {
- Part(part_id) => {
- match parsed.parts.get(part_id)? {
- Text(
- let fb = FetchBody {
- parameter_list: vec![],
- id: NString(None),
- descritpion: NString(None),
- // Default value is 7bit"
- // https://datatracker.ietf.org/doc/html/rfc2045#section-6.1
- content_transfer_encoding: IString::try_from("7bit").unwrap(),
- };
- }
- }
- List(part_list) => todo!(),
- MultiPart((first, rest)) => todo!(),
- }*/
-
- // @TODO This is a stub
- let is = IString::try_from("test").unwrap();
- let b = BodyStructure::Single {
- body: FetchBody {
- basic: BasicFields {
- parameter_list: vec![],
- id: NString(Some(is.clone())),
- description: NString(Some(is.clone())),
- content_transfer_encoding: is.clone(),
- size: 1,
- },
- specific: SpecificFields::Text {
- // @FIXME I do not understand yet how this part works
- subtype: is,
- number_of_lines: 1,
- },
- },
- // Always None for Body, can be populated for BodyStructure
- extension: None,
- };
-
- attributes.push(MessageAttribute::Body(b));
- }
+ FetchAttribute::Body => attributes.push(MessageAttribute::Body(
+ build_imap_email_struct(&parsed, &parsed.structure)?,
+ )),
+ FetchAttribute::BodyStructure => attributes.push(MessageAttribute::Body(
+ build_imap_email_struct(&parsed, &parsed.structure)?,
+ )),
FetchAttribute::BodyExt {
section,
partial,
@@ -350,30 +299,6 @@ impl MailboxView {
data: NString(Some(is)),
})
}
- FetchAttribute::BodyStructure => {
- // @TODO This is a stub
- let is = IString::try_from("test").unwrap();
- let b = BodyStructure::Single {
- body: FetchBody {
- basic: BasicFields {
- parameter_list: vec![],
- id: NString(Some(is.clone())),
- description: NString(Some(is.clone())),
- content_transfer_encoding: is.clone(),
- size: 1,
- },
- specific: SpecificFields::Text {
- // @FIXME I do not understand yet how this part works
- subtype: is,
- number_of_lines: 1,
- },
- },
- // Always None for Body, can be populated for BodyStructure
- extension: None,
- };
-
- attributes.push(MessageAttribute::BodyStructure(b));
- }
FetchAttribute::InternalDate => {
attributes.push(MessageAttribute::InternalDate(MyDateTime(
Utc.fix()
@@ -564,6 +489,20 @@ fn convert_address(a: &mail_parser::Addr<'_>) -> Address {
)
}
+/*
+--CAPTURE--
+b fetch 29878:29879 (BODY)
+* 29878 FETCH (BODY (("text" "plain" ("charset" "utf-8") NIL NIL "quoted-printable" 3264 82)("text" "html" ("charset" "utf-8") NIL NIL "quoted-printable" 31834 643) "alternative"))
+* 29879 FETCH (BODY ("text" "html" ("charset" "us-ascii") NIL NIL "7bit" 4107 131))
+ ^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^ ^^^^^^ ^^^^ ^^^
+ | | | | | | number of lines
+ | | | | | size
+ | | | | content transfer encoding
+ | | | description
+ | | id
+ | parameter list
+b OK Fetch completed (0.001 + 0.000 secs).
+*/
fn build_imap_email_struct<'a>(
msg: &Message<'a>,
node: &MessageStructure,
@@ -574,47 +513,89 @@ fn build_imap_email_struct<'a>(
"Email part referenced in email structure is missing"
))?;
match part {
- MessagePart::Text(bp) => Ok(BodyStructure::Single {
- body: FetchBody {
- basic: BasicFields {
- parameter_list: vec![], //@TODO
- id: match bp.headers_rfc.get(&RfcHeader::ContentId) {
- Some(HeaderValue::Text(v)) => {
- NString(IString::try_from(v.clone().into_owned()).ok())
- }
- _ => NString(None),
- },
- description: NString(None), //@TODO
- content_transfer_encoding: match bp
- .headers_rfc
- .get(&RfcHeader::ContentTransferEncoding)
- {
- Some(HeaderValue::Text(v)) => {
- IString::try_from(v.clone().into_owned())
- .unwrap_or(unchecked_istring("7bit"))
- }
- _ => unchecked_istring("7bit"),
- },
- size: u32::try_from(bp.len())?,
- },
- specific: SpecificFields::Text {
- subtype: match bp.headers_rfc.get(&RfcHeader::ContentType) {
- Some(HeaderValue::ContentType(ContentType {
- c_subtype: Some(st),
- ..
- })) => IString::try_from(st.clone().into_owned())
- .unwrap_or(unchecked_istring("plain")),
- _ => unchecked_istring("plain"),
- },
- number_of_lines: u32::try_from(bp.get_text_contents().lines().count())?,
- },
- },
- extension: None,
- }),
MessagePart::Multipart(_) => {
unreachable!("A multipart entry can not be found here.")
}
- _ => todo!(),
+ MessagePart::Text(bp) => {
+ // Extract subtype+attributes from Content-Type header
+ let (st, ct_attrs) = match bp.headers_rfc.get(&RfcHeader::ContentType) {
+ Some(HeaderValue::ContentType(c)) => {
+ (c.c_subtype.as_ref(), c.attributes.as_ref())
+ }
+ _ => (None, None),
+ };
+
+ // Transform the Content-Type attributes into IMAP's parameter list
+ // Also check if there is a charset defined.
+ let (charset_found, mut parameter_list) = ct_attrs
+ .map(|attr| {
+ attr.iter().fold(
+ (false, vec![]),
+ |(charset_found, mut param_list), (k, v)| {
+ let nk = k.to_lowercase();
+ match (
+ IString::try_from(k.as_ref()),
+ IString::try_from(v.as_ref()),
+ ) {
+ (Ok(ik), Ok(iv)) => param_list.push((ik, iv)),
+ _ => (),
+ };
+
+ (charset_found || nk == "charset", param_list)
+ },
+ )
+ })
+ .unwrap_or((false, vec![]));
+
+ // If the charset is not defined, set it to "us-ascii"
+ if !charset_found {
+ parameter_list
+ .push((unchecked_istring("charset"), unchecked_istring("us-ascii")));
+ }
+
+ // If the subtype is not defined, set it to "plain"
+ let subtype = st
+ .map(|st| IString::try_from(st.clone().into_owned()).ok())
+ .flatten()
+ .unwrap_or(unchecked_istring("plain"));
+
+ Ok(BodyStructure::Single {
+ body: FetchBody {
+ basic: BasicFields {
+ parameter_list,
+ id: match bp.headers_rfc.get(&RfcHeader::ContentId) {
+ Some(HeaderValue::Text(v)) => {
+ NString(IString::try_from(v.clone().into_owned()).ok())
+ }
+ _ => NString(None),
+ },
+ description: NString(None), //@TODO
+ content_transfer_encoding: match bp
+ .headers_rfc
+ .get(&RfcHeader::ContentTransferEncoding)
+ {
+ Some(HeaderValue::Text(v)) => {
+ IString::try_from(v.clone().into_owned())
+ .unwrap_or(unchecked_istring("7bit"))
+ }
+ _ => unchecked_istring("7bit"),
+ },
+ size: u32::try_from(bp.len())?,
+ },
+ specific: SpecificFields::Text {
+ subtype,
+ number_of_lines: u32::try_from(
+ bp.get_text_contents().lines().count(),
+ )?,
+ },
+ },
+ extension: None,
+ })
+ },
+ MessagePart::Html(_) => todo!(),
+ MessagePart::Binary(_) => todo!(),
+ MessagePart::InlineBinary(_) => todo!(),
+ MessagePart::Message(_) => todo!(),
}
}
MessageStructure::List(l) => todo!(),