diff options
Diffstat (limited to 'src/rfc822.rs')
-rw-r--r-- | src/rfc822.rs | 365 |
1 files changed, 166 insertions, 199 deletions
diff --git a/src/rfc822.rs b/src/rfc822.rs index 658b167..a8fa5c3 100644 --- a/src/rfc822.rs +++ b/src/rfc822.rs @@ -1,14 +1,15 @@ use chrono::DateTime; -use mailparse::{addrparse_header, body::Body, dateparse, DispositionType, ParsedMail}; - -use crate::error::{Error, Result}; -use crate::pb3::jwebmail::mimeheader::ContentDisposition::*; -use crate::pb3::jwebmail::{ - mail_body::Multipart, mail_header::MailAddr, ListMailHeader, MIMEHeader, MIMEPart, Mail, - MailBody, MailHeader, +use maildir::MailEntryError; +use mailparse::{ + addrparse_header, body::Body, dateparse, DispositionType, MailParseError, ParsedMail, }; -fn parse_mail_addrs(inp: &mailparse::MailHeader) -> Result<Vec<MailAddr>> { +use crate::de_jmhoffmann_jwebmail_mailstorage::MIMEHeader_content_dispo as CD; +use crate::de_jmhoffmann_jwebmail_mailstorage::*; + +fn parse_mail_addrs( + inp: &mailparse::MailHeader, +) -> std::result::Result<Vec<MailAddr>, MailParseError> { let mut mal = addrparse_header(inp)?; Ok(mal @@ -17,17 +18,16 @@ fn parse_mail_addrs(inp: &mailparse::MailHeader) -> Result<Vec<MailAddr>> { mailparse::MailAddr::Group(mut g) => g .addrs .drain(..) - .map(|s| { - let mut r = MailAddr::new(); - r.name = Some(s.display_name.unwrap_or_default()); - r.address = s.addr; - r + .map(|s| MailAddr { + name: Some(s.display_name.unwrap_or_default()), + address: s.addr, }) .collect(), mailparse::MailAddr::Single(s) => { - let mut addr = MailAddr::new(); - addr.name = Some(s.display_name.unwrap_or_default()); - addr.address = s.addr; + let addr = MailAddr { + name: Some(s.display_name.unwrap_or_default()), + address: s.addr, + }; vec![addr] } }) @@ -44,43 +44,44 @@ fn get_received(me: &mut maildir::MailEntry) -> i64 { }) } -pub fn me_to_lmh(mut me: maildir::MailEntry) -> Result<ListMailHeader> { - let mut lmh = ListMailHeader::new(); - lmh.byte_size = me.path().metadata()?.len(); - lmh.unread = !me.is_seen(); - lmh.rec_date = DateTime::from_timestamp(get_received(&mut me), 0) - .unwrap() - .to_rfc3339(); - lmh.mid = me.id().to_owned(); - lmh.header = Some(parse_mail_header(&me.parsed()?)?).into(); +pub fn me_to_lmh( + mut me: maildir::MailEntry, +) -> std::result::Result<ListMailHeader, MailEntryError> { + let lmh = ListMailHeader { + byte_size: me.path().metadata()?.len() as i64, + unread: !me.is_seen(), + rec_date: DateTime::from_timestamp(get_received(&mut me), 0) + .unwrap() + .to_rfc3339(), + mid: me.id().to_owned(), + header: parse_mail_header(&me.parsed()?)?, + }; Ok(lmh) } -pub fn parse_mail_content(v: &ParsedMail) -> Result<MIMEHeader> { - let mut c = MIMEHeader::new(); - - { - let mut val = v.ctype.mimetype.clone(); - if let Some(i) = val.find(';') { - val.truncate(i); - } - let j = val.find('/').unwrap(); - c.subtype = val.split_off(j + 1); - val.pop(); - c.maintype = val; +pub fn parse_mail_content(v: &ParsedMail) -> MIMEHeader { + let mut val = v.ctype.mimetype.clone(); + if let Some(i) = val.find(';') { + val.truncate(i); } + let j = val.find('/').unwrap(); + let subtype = val.split_off(j + 1); + val.pop(); + let maintype = val; + + let mut file_name = None; - match v.get_content_disposition().disposition { - DispositionType::Inline => c.contentdispo = CONTENT_DISPOSITION_INLINE.into(), + let cd = match v.get_content_disposition().disposition { + DispositionType::Inline => CD::inline, DispositionType::Attachment => { - c.contentdispo = CONTENT_DISPOSITION_ATTACHMENT.into(); if let Some(fname) = v.get_content_disposition().params.remove("filename") { - c.file_name = Some(fname); + file_name = Some(fname); } + CD::attachment } - _ => {} - } + _ => CD::none, + }; for h in &v.headers { let mut key = h.get_key(); @@ -89,29 +90,44 @@ pub fn parse_mail_content(v: &ParsedMail) -> Result<MIMEHeader> { key.make_ascii_lowercase(); if key == "filename" { - c.file_name = Some(val); + file_name = Some(val); } } - Ok(c) + MIMEHeader { + mime_type: MIMEHeader_mime_type { + main_type: maintype, + sub_type: subtype, + }, + content_dispo: cd, + file_name, + } } -fn parse_mail_header(pm: &ParsedMail) -> Result<MailHeader> { +fn parse_mail_header(pm: &ParsedMail) -> std::result::Result<MailHeader, MailParseError> { let v = &pm.headers; - let mut mh = MailHeader::new(); - let mut mimeh = MIMEHeader::new(); - - { - let mut val = pm.ctype.mimetype.clone(); - if let Some(i) = val.find(';') { - val.truncate(i); - } - let j = val.find('/').unwrap(); - mimeh.subtype = val.split_off(j + 1); - val.pop(); - mimeh.maintype = val; + let mut val = pm.ctype.mimetype.clone(); + if let Some(i) = val.find(';') { + val.truncate(i); } + let j = val.find('/').unwrap(); + let sub_type = val.split_off(j + 1); + val.pop(); + let main_type = val; + + let mut file_name = None; + let mut content_dispo = CD::none; + let mut cc = vec![]; + let mut bcc = vec![]; + let mut comments = vec![]; + let mut keywords = vec![]; + let mut reply_to = vec![]; + let mut written_from = vec![]; + let mut send_to = vec![]; + let mut subject = String::new(); + let mut sender = None; + let mut send_date = String::new(); let mut key = String::new(); @@ -123,102 +139,130 @@ fn parse_mail_header(pm: &ParsedMail) -> Result<MailHeader> { match key.as_str() { "date" => { - mh.send_date = DateTime::from_timestamp(dateparse(&val)?, 0) + send_date = DateTime::from_timestamp(dateparse(&val)?, 0) .unwrap() .to_rfc3339() } "from" => { - if !mh.written_from.is_empty() { - return Err(Error::SortOrder("from already set".into())); + if !written_from.is_empty() { + const FROM_MULTIPLE_TIMES_ERROR: &str = "from already set"; + return Err(MailParseError::Generic(FROM_MULTIPLE_TIMES_ERROR)); } - mh.written_from = parse_mail_addrs(y)? + written_from = parse_mail_addrs(y)? } - "sender" => mh.sender = parse_mail_addrs(y)?.drain(0..1).next().into(), - "reply-to" => mh.reply_to = parse_mail_addrs(y)?, - "to" => mh.send_to = parse_mail_addrs(y)?, - "cc" => mh.cc = parse_mail_addrs(y)?, - "bcc" => mh.bcc = parse_mail_addrs(y)?, + "sender" => sender = parse_mail_addrs(y)?.drain(0..1).next(), + "reply-to" => reply_to = parse_mail_addrs(y)?, + "to" => send_to = parse_mail_addrs(y)?, + "cc" => cc = parse_mail_addrs(y)?, + "bcc" => bcc = parse_mail_addrs(y)?, "subject" => { - mh.subject = val; + subject = val; } "comments" => { - mh.comments.push(val); + comments.push(val); } "keywords" => { - mh.keywords.push(val); + keywords.push(val); } "mime-version" => { strip_comments(&mut val); if val.trim() != "1.0" { - return Err(Error::MailEntryError(maildir::MailEntryError::DateError( - "unknown mime version", - ))); + const UNKNOWN_MIME_VERSION_ERROR: &str = "unknown mime version"; + return Err(MailParseError::Generic(UNKNOWN_MIME_VERSION_ERROR)); } } "content-disposition" => { let cd = val.to_ascii_lowercase(); match cd.as_ref() { - "inline" => mimeh.contentdispo = CONTENT_DISPOSITION_INLINE.into(), - "attachment" => mimeh.contentdispo = CONTENT_DISPOSITION_ATTACHMENT.into(), + "inline" => content_dispo = CD::inline, + "attachment" => content_dispo = CD::attachment, _ => {} }; } "filename" => { - mimeh.file_name = Some(val); + file_name = Some(val); } _ => {} }; key.clear(); } - mh.mime = Some(mimeh).into(); - Ok(mh) + Ok(MailHeader { + written_from, + sender, + send_to, + cc, + bcc, + reply_to, + subject, + send_date, + keywords, + comments, + mime: MIMEHeader { + mime_type: MIMEHeader_mime_type { + main_type, + sub_type, + }, + content_dispo, + file_name, + }, + }) } -fn parse_mail_body(pm: &ParsedMail) -> Result<MailBody> { +fn parse_mail_body(pm: &ParsedMail) -> std::result::Result<MailBody, MailParseError> { let body = if pm.ctype.mimetype.starts_with("message/") { - let mut mb = MailBody::new(); - mb.set_mail(parsed_mail_to_mail(mailparse::parse_mail( - pm.get_body()?.as_ref(), - )?)?); - mb + MailBody { + mail: Some(Box::new(parsed_mail_to_mail(mailparse::parse_mail( + pm.get_body()?.as_ref(), + )?)?)), + discrete: None, + multipart: None, + } } else if pm.subparts.is_empty() && pm.ctype.mimetype.starts_with("text/") { - let mut mb = MailBody::new(); - mb.set_discrete(pm.get_body()?); - mb + MailBody { + discrete: Some(pm.get_body()?), + multipart: None, + mail: None, + } } else if pm.subparts.is_empty() { - let b = match pm.get_body_encoded() { - Body::Base64(eb) => { - let db = eb.get_raw(); - if db.len() < 512 * 1024 { - String::from_utf8_lossy(db).into_owned() - } else { - String::new() + MailBody { + discrete: Some(match pm.get_body_encoded() { + Body::Base64(eb) => { + let db = eb.get_raw(); + if db.len() < 512 * 1024 { + String::from_utf8_lossy(db).into_owned() + } else { + String::new() + } } - } - Body::SevenBit(eb) => eb.get_as_string()?, - Body::QuotedPrintable(eb) => eb.get_decoded_as_string()?, - _ => todo!(), - }; - let mut mb = MailBody::new(); - mb.set_discrete(b); - mb + Body::SevenBit(eb) => eb.get_as_string()?, + Body::QuotedPrintable(eb) => eb.get_decoded_as_string()?, + _ => todo!(), + }), + multipart: None, + mail: None, + } } else { - let mut mb = MailBody::new(); - let mut mp = Multipart::new(); - mp.parts = pm - .subparts - .iter() - .map(|part| -> Result<MIMEPart> { - let mut mp = MIMEPart::new(); - mp.mime_header = Some(parse_mail_content(part)?).into(); - mp.body = Some(parse_mail_body(part)?).into(); - Ok(mp) - }) - .filter_map(|p| p.ok()) - .collect(); - mb.set_multipart(mp); - mb + let mp = Multipart { + parts: pm + .subparts + .iter() + .map(|part| -> std::result::Result<MIMEPart, MailParseError> { + Ok(MIMEPart { + mime_header: parse_mail_content(part), + body: parse_mail_body(part)?, + }) + }) + .filter_map(|p| p.ok()) + .collect(), + preamble: None, + epilogue: None, + }; + MailBody { + multipart: Some(mp), + discrete: None, + mail: None, + } }; Ok(body) } @@ -316,86 +360,9 @@ fn strip_comments(s: &mut String) { } } -pub fn parsed_mail_to_mail(pm: ParsedMail) -> Result<Mail> { - let mut m = Mail::new(); - m.head = Some(parse_mail_header(&pm)?).into(); - m.body = Some(parse_mail_body(&pm)?).into(); - - Ok(m) -} - -/* -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn comment() { - let mut x = r#"(this is ((some) text)) a "some text with (comment \" in) quotes)(" (example) \( included) (xx)b()"#.to_owned(); - strip_comments(&mut x); - assert_eq!( - &x, - r#" a "some text with (comment \" in) quotes)(" \( included) b"# - ); - } - - #[test] - fn unclosed_comment() { - let mut x = "(this is (some text) example b".to_owned(); - strip_comments(&mut x); - assert_eq!(&x, "(this is example b"); - } - - #[test] - fn find_first_pair() { - let mut r = find_pair(0, "abc def"); - assert_eq!(r, None); - - r = find_pair(0, "abc ( def"); - assert_eq!(r, None); - - r = find_pair(0, "abc ) def"); - assert_eq!(r, None); - - let s = "(abc) def"; - if let Some(i) = find_pair(0, s) { - assert_eq!(i, 0..5); - assert_eq!(&s[i], "(abc)"); - } else { - assert!(false, "Got None expected Some!"); - } - - let s = "abc (def) ghi"; - if let Some(i) = find_pair(0, s) { - assert_eq!(i, 4..9); - assert_eq!(&s[i], "(def)"); - } else { - assert!(false, "Got None expected Some!"); - } - - let s = "(abc (def) ghi"; - if let Some(i) = find_pair(0, s) { - assert_eq!(i, 5..10); - assert_eq!(&s[i], "(def)"); - } else { - assert!(false, "Got None expected Some!"); - } - - let s = "abc ((def) ghi)"; - if let Some(i) = find_pair(0, s) { - assert_eq!(i, 4..15); - assert_eq!(&s[i], "((def) ghi)"); - } else { - assert!(false, "Got None expected Some!"); - } - - let s = r#" a "some text with (comment \" in) quotes)(" (example)"#; - if let Some(i) = find_pair(0, s) { - assert_eq!(i, 45..54); - assert_eq!(&s[i], "(example)"); - } else { - assert!(false, "Got None expected Some!"); - } - } +pub fn parsed_mail_to_mail(pm: ParsedMail) -> std::result::Result<Mail, MailParseError> { + Ok(Mail { + head: parse_mail_header(&pm)?, + body: parse_mail_body(&pm)?, + }) } -*/ |