diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 35e889ace..50041c9cf 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -287,12 +287,12 @@ impl<'a> MimeParser<'a> { ) == MAILIMF_NO_ERROR as libc::c_int && !mb_list.is_null() { - if let Some(dn_to_addr) = mailimf_find_first_addr(mb_list) { + if let Some(dn_to_addr) = wrapmime::mailimf_find_first_addr(mb_list) { if let Some(from_field) = self.lookup_field("From") { if (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int && !(*from_field).fld_data.fld_from.is_null() { - let from_addr = mailimf_find_first_addr( + let from_addr = wrapmime::mailimf_find_first_addr( (*(*from_field).fld_data.fld_from).frm_mb_list, ); if let Some(from_addr) = from_addr { @@ -590,7 +590,7 @@ impl<'a> MimeParser<'a> { return false; } - let mut decoded_data = match mailmime_transfer_decode(mime) { + let mut decoded_data = match wrapmime::mailmime_transfer_decode(mime) { Ok(decoded_data) => decoded_data, Err(_) => { // Note that it's now always an error - might be no data @@ -838,7 +838,7 @@ impl<'a> MimeParser<'a> { let mut fld_from: *const mailimf_from = ptr::null(); /* get From: and check there is exactly one sender */ - let fld = mailimf_find_field(self.header_root, MAILIMF_FIELD_FROM as libc::c_int); + let fld = wrapmime::mailimf_find_field(self.header_root, MAILIMF_FIELD_FROM as libc::c_int); if !(fld.is_null() || { fld_from = (*fld).fld_data.fld_from; @@ -856,7 +856,7 @@ impl<'a> MimeParser<'a> { if !mb.is_null() { let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); - let recipients = mailimf_get_recipients(self.header_root); + let recipients = wrapmime::mailimf_get_recipients(self.header_root); if recipients.len() == 1 { if recipients.contains(from_addr_norm) { sender_equals_recipient = true; @@ -917,22 +917,6 @@ pub struct Part { pub param: Params, } -pub fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> Option { - if mb_list.is_null() { - return None; - } - - for cur in unsafe { (*(*mb_list).mb_list).into_iter() } { - let mb = cur as *mut mailimf_mailbox; - if !mb.is_null() && !unsafe { (*mb).mb_addr_spec.is_null() } { - let addr = unsafe { as_str((*mb).mb_addr_spec) }; - return Some(addr_normalize(addr).to_string()); - } - } - - None -} - unsafe fn hash_header(out: &mut HashMap, in_0: *const mailimf_fields) { if in_0.is_null() { return; @@ -1154,162 +1138,6 @@ pub unsafe fn mailmime_find_ct_parameter( ptr::null_mut() } -pub fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result, Error> { - ensure!(!mime.is_null(), "invalid inputs"); - - let mime_transfer_encoding = - wrapmime::get_mime_transfer_encoding(mime).unwrap_or(MAILMIME_MECHANISM_BINARY as i32); - - let mime_data = unsafe { (*mime).mm_data.mm_single }; - - wrapmime::decode_dt_data(mime_data, mime_transfer_encoding) -} - -pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet { - /* returned addresses are normalized. */ - let mut recipients: HashSet = Default::default(); - - for cur in unsafe { (*(*imffields).fld_list).into_iter() } { - let fld = cur as *mut mailimf_field; - - let fld_to: *mut mailimf_to; - let fld_cc: *mut mailimf_cc; - - let mut addr_list: *mut mailimf_address_list = ptr::null_mut(); - if fld.is_null() { - continue; - } - - let fld = unsafe { *fld }; - - // TODO match on enums /rtn - match fld.fld_type { - 13 => { - fld_to = unsafe { fld.fld_data.fld_to }; - if !fld_to.is_null() { - addr_list = unsafe { (*fld_to).to_addr_list }; - } - } - 14 => { - fld_cc = unsafe { fld.fld_data.fld_cc }; - if !fld_cc.is_null() { - addr_list = unsafe { (*fld_cc).cc_addr_list }; - } - } - _ => {} - } - - if !addr_list.is_null() { - for cur2 in unsafe { &(*(*addr_list).ad_list) } { - let adr = cur2 as *mut mailimf_address; - - if adr.is_null() { - continue; - } - let adr = unsafe { *adr }; - - if adr.ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int { - mailimf_get_recipients_add_addr(&mut recipients, unsafe { - adr.ad_data.ad_mailbox - }); - } else if adr.ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int { - let group = unsafe { adr.ad_data.ad_group }; - if !group.is_null() && unsafe { !(*group).grp_mb_list.is_null() } { - for cur3 in unsafe { &(*(*(*group).grp_mb_list).mb_list) } { - mailimf_get_recipients_add_addr( - &mut recipients, - cur3 as *mut mailimf_mailbox, - ); - } - } - } - } - } - } - - recipients -} - -fn mailimf_get_recipients_add_addr(recipients: &mut HashSet, mb: *mut mailimf_mailbox) { - if !mb.is_null() { - let addr_norm = addr_normalize(as_str(unsafe { (*mb).mb_addr_spec })); - recipients.insert(addr_norm.into()); - } -} - -/*the result is a pointer to mime, must not be freed*/ -pub fn mailimf_find_field( - header: *mut mailimf_fields, - wanted_fld_type: libc::c_int, -) -> *mut mailimf_field { - if header.is_null() { - return ptr::null_mut(); - } - - let header = unsafe { (*header) }; - if header.fld_list.is_null() { - return ptr::null_mut(); - } - - for cur in unsafe { &(*header.fld_list) } { - let field = cur as *mut mailimf_field; - if !field.is_null() { - if unsafe { (*field).fld_type } == wanted_fld_type { - return field; - } - } - } - - ptr::null_mut() -} - -/*the result is a pointer to mime, must not be freed*/ -pub unsafe fn mailmime_find_mailimf_fields(mime: *mut Mailmime) -> *mut mailimf_fields { - if mime.is_null() { - return ptr::null_mut(); - } - - match (*mime).mm_type as _ { - MAILMIME_MULTIPLE => { - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - let header = mailmime_find_mailimf_fields(cur_data as *mut _); - if !header.is_null() { - return header; - } - } - } - MAILMIME_MESSAGE => return (*mime).mm_data.mm_message.mm_fields, - _ => {} - } - - ptr::null_mut() -} - -pub unsafe fn mailimf_find_optional_field( - header: *mut mailimf_fields, - wanted_fld_name: *const libc::c_char, -) -> *mut mailimf_optional_field { - if header.is_null() || (*header).fld_list.is_null() { - return ptr::null_mut(); - } - for cur_data in (*(*header).fld_list).into_iter() { - let field: *mut mailimf_field = cur_data as *mut _; - - if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { - let optional_field: *mut mailimf_optional_field = (*field).fld_data.fld_optional_field; - if !optional_field.is_null() - && !(*optional_field).fld_name.is_null() - && !(*optional_field).fld_value.is_null() - && strcasecmp((*optional_field).fld_name, wanted_fld_name) == 0i32 - { - return optional_field; - } - } - } - - ptr::null_mut() -} - #[cfg(test)] mod tests { use super::*; @@ -1325,14 +1153,13 @@ mod tests { let mut mime: *mut Mailmime = ptr::null_mut(); let mut dummy = 0; let res = mailmime_parse(txt, strlen(txt), &mut dummy, &mut mime); - assert_eq!(res, MAIL_NO_ERROR as libc::c_int); assert!(!mime.is_null()); - let fields: *mut mailimf_fields = mailmime_find_mailimf_fields(mime); + let fields: *mut mailimf_fields = wrapmime::mailmime_find_mailimf_fields(mime); assert!(!fields.is_null()); - let mut of_a: *mut mailimf_optional_field = mailimf_find_optional_field( + let mut of_a: *mut mailimf_optional_field = wrapmime::mailimf_find_optional_field( fields, b"fielda\x00" as *const u8 as *const libc::c_char, ); @@ -1352,7 +1179,7 @@ mod tests { "ValueA", ); - of_a = mailimf_find_optional_field( + of_a = wrapmime::mailimf_find_optional_field( fields, b"FIELDA\x00" as *const u8 as *const libc::c_char, ); @@ -1372,7 +1199,7 @@ mod tests { "ValueA", ); - let of_b: *mut mailimf_optional_field = mailimf_find_optional_field( + let of_b: *mut mailimf_optional_field = wrapmime::mailimf_find_optional_field( fields, b"FieldB\x00" as *const u8 as *const libc::c_char, ); @@ -1395,7 +1222,9 @@ mod tests { let context = dummy_context(); let raw = include_bytes!("../test-data/message/issue_523.txt"); let mut mimeparser = MimeParser::new(&context.ctx); - unsafe { mimeparser.parse(&raw[..]).unwrap() }; + unsafe { + mimeparser.parse(&raw[..]).unwrap(); + }; assert_eq!(mimeparser.subject, None); assert_eq!(mimeparser.parts.len(), 1); } @@ -1403,9 +1232,13 @@ mod tests { proptest! { #[test] fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") { + // this test doesn't exercise much of dc_mimeparser anymore + // because a missing From-field early aborts parsing let context = dummy_context(); let mut mimeparser = MimeParser::new(&context.ctx); - unsafe { mimeparser.parse(data.as_bytes()).unwrap() }; + unsafe { + assert!(mimeparser.parse(data.as_bytes()).is_err()); + } } } @@ -1434,7 +1267,7 @@ mod tests { fn test_mimeparser_with_context() { unsafe { let context = dummy_context(); - let raw = b"Content-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00"; + let raw = b"From: hello\nContent-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00"; let mut mimeparser = MimeParser::new(&context.ctx); mimeparser.parse(&raw[..]).unwrap(); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 866e9cbba..5a6a40e8d 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -29,6 +29,7 @@ use crate::peerstate::*; use crate::securejoin::handle_securejoin_handshake; use crate::sql; use crate::stock::StockMessage; +use crate::wrapmime; #[derive(Debug, PartialEq, Eq)] enum CreateEvent { @@ -795,7 +796,7 @@ unsafe fn handle_reports( b"disposition-notification\x00" as *const u8 as *const libc::c_char, ) == 0 { - if let Ok(report_body) = mailmime_transfer_decode(report_data) { + if let Ok(report_body) = wrapmime::mailmime_transfer_decode(report_data) { let mut report_parsed = std::ptr::null_mut(); let mut dummy = 0; @@ -807,13 +808,14 @@ unsafe fn handle_reports( ) == MAIL_NO_ERROR as libc::c_int && !report_parsed.is_null() { - let report_fields = mailmime_find_mailimf_fields(report_parsed); + let report_fields = + wrapmime::mailmime_find_mailimf_fields(report_parsed); if !report_fields.is_null() { - let of_disposition = mailimf_find_optional_field( + let of_disposition = wrapmime::mailimf_find_optional_field( report_fields, b"Disposition\x00" as *const u8 as *const libc::c_char, ); - let of_org_msgid = mailimf_find_optional_field( + let of_org_msgid = wrapmime::mailimf_find_optional_field( report_fields, b"Original-Message-ID\x00" as *const u8 as *const libc::c_char, ); diff --git a/src/e2ee.rs b/src/e2ee.rs index 5c702fb6c..e5d78e3a8 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -22,7 +22,6 @@ use num_traits::FromPrimitive; use crate::aheader::*; use crate::config::Config; use crate::context::Context; -use crate::dc_mimeparser::*; use crate::dc_tools::*; use crate::error::*; use crate::key::*; @@ -266,103 +265,80 @@ pub fn try_decrypt( context: &Context, in_out_message: *mut Mailmime, ) -> Result<(bool, HashSet, HashSet)> { + // just a pointer into mailmime structure, must not be freed + let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) }; + ensure!( + !in_out_message.is_null() && !imffields.is_null(), + "corrupt invalid mime inputs" + ); + + let from = wrapmime::get_field_from(imffields)?; + let message_time = wrapmime::get_field_date(imffields)?; + + let mut peerstate = None; + let autocryptheader = Aheader::from_imffields(&from, imffields); + + if message_time > 0 { + peerstate = Peerstate::from_addr(context, &context.sql, &from); + + if let Some(ref mut peerstate) = peerstate { + if let Some(ref header) = autocryptheader { + peerstate.apply_header(&header, message_time); + peerstate.save_to_db(&context.sql, false).unwrap(); + } else if message_time > peerstate.last_seen_autocrypt + && !contains_report(in_out_message) + { + peerstate.degrade_encryption(message_time); + peerstate.save_to_db(&context.sql, false).unwrap(); + } + } else if let Some(ref header) = autocryptheader { + let p = Peerstate::from_header(context, header, message_time); + p.save_to_db(&context.sql, true).unwrap(); + peerstate = Some(p); + } + } + /* possibly perform decryption */ + let mut private_keyring = Keyring::default(); + let mut public_keyring_for_validate = Keyring::default(); let mut encrypted = false; let mut signatures = HashSet::default(); let mut gossipped_addr = HashSet::default(); - // just a pointer into mailmime structure, must not be freed - let imffields = unsafe { mailmime_find_mailimf_fields(in_out_message) }; - let mut message_time = 0; - let mut from = None; - let mut private_keyring = Keyring::default(); - let mut public_keyring_for_validate = Keyring::default(); - let mut gossip_headers = ptr::null_mut(); + let self_addr = context.get_config(Config::ConfiguredAddr); - // XXX do wrapmime:: helper for the next block - if !(in_out_message.is_null() || imffields.is_null()) { - let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int); - - if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } { - let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list }; - from = mailimf_find_first_addr(mb_list); - } - - field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int); - if !field.is_null() && unsafe { !(*field).fld_data.fld_orig_date.is_null() } { - let orig_date = unsafe { (*field).fld_data.fld_orig_date }; - - if !orig_date.is_null() { - let dt = unsafe { (*orig_date).dt_date_time }; - message_time = dc_timestamp_from_date(dt); - if message_time != 0 && message_time > time() { - message_time = time() + if let Some(self_addr) = self_addr { + if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) { + if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 { + peerstate = Peerstate::from_addr(&context, &context.sql, &from); + } + if let Some(ref peerstate) = peerstate { + if peerstate.degrade_event.is_some() { + handle_degrade_event(context, &peerstate)?; + } + if let Some(ref key) = peerstate.gossip_key { + public_keyring_for_validate.add_ref(key); + } + if let Some(ref key) = peerstate.public_key { + public_keyring_for_validate.add_ref(key); } } - } - let mut peerstate = None; - let autocryptheader = from - .as_ref() - .and_then(|from| Aheader::from_imffields(from, imffields)); - if message_time > 0 { - if let Some(ref from) = from { - peerstate = Peerstate::from_addr(context, &context.sql, from); - if let Some(ref mut peerstate) = peerstate { - if let Some(ref header) = autocryptheader { - peerstate.apply_header(&header, message_time); - peerstate.save_to_db(&context.sql, false).unwrap(); - } else if message_time > peerstate.last_seen_autocrypt - && !contains_report(in_out_message) - { - peerstate.degrade_encryption(message_time); - peerstate.save_to_db(&context.sql, false).unwrap(); - } - } else if let Some(ref header) = autocryptheader { - let p = Peerstate::from_header(context, header, message_time); - p.save_to_db(&context.sql, true).unwrap(); - peerstate = Some(p); - } - } - } - /* load private key for decryption */ - let self_addr = context.get_config(Config::ConfiguredAddr); - if let Some(self_addr) = self_addr { - if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) { - if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 { - peerstate = - Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default()); - } - if let Some(ref peerstate) = peerstate { - if peerstate.degrade_event.is_some() { - handle_degrade_event(context, &peerstate)?; - } - if let Some(ref key) = peerstate.gossip_key { - public_keyring_for_validate.add_ref(key); - } - if let Some(ref key) = peerstate.public_key { - public_keyring_for_validate.add_ref(key); - } - } - - encrypted = decrypt_if_autocrypt_message( - context, - in_out_message, - &private_keyring, - &public_keyring_for_validate, - &mut signatures, - &mut gossip_headers, - )?; - if !gossip_headers.is_null() { - gossipped_addr = - update_gossip_peerstates(context, message_time, imffields, gossip_headers)?; - } + let mut gossip_headers = ptr::null_mut(); + encrypted = decrypt_if_autocrypt_message( + context, + in_out_message, + &private_keyring, + &public_keyring_for_validate, + &mut signatures, + &mut gossip_headers, + )?; + if !gossip_headers.is_null() { + gossipped_addr = + update_gossip_peerstates(context, message_time, imffields, gossip_headers)?; + unsafe { mailimf_fields_free(gossip_headers) }; } } } - if !gossip_headers.is_null() { - unsafe { mailimf_fields_free(gossip_headers) }; - } - Ok((encrypted, signatures, gossipped_addr)) } diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 626b290a8..7eec8f925 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -16,7 +16,7 @@ use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::{get_version_str, Context}; -use crate::dc_mimeparser::{mailmime_find_mailimf_fields, SystemMessage}; +use crate::dc_mimeparser::SystemMessage; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::e2ee::*; @@ -645,7 +645,7 @@ impl<'a> MimeFactory<'a> { ); /*just a pointer into mailmime structure, must not be freed*/ - let imffields_unprotected = mailmime_find_mailimf_fields(message); + let imffields_unprotected = wrapmime::mailmime_find_mailimf_fields(message); ensure!( !imffields_unprotected.is_null(), "could not find mime fields" diff --git a/src/wrapmime.rs b/src/wrapmime.rs index 07afb1e75..ae303f699 100644 --- a/src/wrapmime.rs +++ b/src/wrapmime.rs @@ -1,10 +1,12 @@ +use std::collections::HashSet; use std::ffi::CString; use std::ptr; +use crate::contact::addr_normalize; use crate::dc_tools::*; use crate::error::Error; use mmime::clist::*; -use mmime::display::*; +// use mmime::display::*; use mmime::mailimf::types::*; use mmime::mailimf::types_helper::*; use mmime::mailmime::content::*; @@ -90,6 +92,192 @@ pub fn has_decryptable_data(mime_data: *mut mailmime_data) -> bool { } } +pub fn get_field_from(imffields: *mut mailimf_fields) -> Result { + let field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int); + if !field.is_null() && unsafe { !(*field).fld_data.fld_from.is_null() } { + let mb_list = unsafe { (*(*field).fld_data.fld_from).frm_mb_list }; + if let Some(addr) = mailimf_find_first_addr(mb_list) { + return Ok(addr); + } + } + bail!("not From field found"); +} + +pub fn get_field_date(imffields: *mut mailimf_fields) -> Result { + let field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int); + let mut message_time = 0; + + if !field.is_null() && unsafe { !(*field).fld_data.fld_orig_date.is_null() } { + let orig_date = unsafe { (*field).fld_data.fld_orig_date }; + + if !orig_date.is_null() { + let dt = unsafe { (*orig_date).dt_date_time }; + message_time = dc_timestamp_from_date(dt); + if message_time != 0 && message_time > time() { + message_time = time() + } + } + } + + Ok(message_time) +} + +fn mailimf_get_recipients_add_addr(recipients: &mut HashSet, mb: *mut mailimf_mailbox) { + if !mb.is_null() { + let addr_norm = addr_normalize(as_str(unsafe { (*mb).mb_addr_spec })); + recipients.insert(addr_norm.into()); + } +} + +/*the result is a pointer to mime, must not be freed*/ +pub fn mailimf_find_field( + header: *mut mailimf_fields, + wanted_fld_type: libc::c_int, +) -> *mut mailimf_field { + if header.is_null() { + return ptr::null_mut(); + } + + let header = unsafe { (*header) }; + if header.fld_list.is_null() { + return ptr::null_mut(); + } + + for cur in unsafe { &(*header.fld_list) } { + let field = cur as *mut mailimf_field; + if !field.is_null() { + if unsafe { (*field).fld_type } == wanted_fld_type { + return field; + } + } + } + + ptr::null_mut() +} + +/*the result is a pointer to mime, must not be freed*/ +pub unsafe fn mailmime_find_mailimf_fields(mime: *mut Mailmime) -> *mut mailimf_fields { + if mime.is_null() { + return ptr::null_mut(); + } + + match (*mime).mm_type as _ { + MAILMIME_MULTIPLE => { + for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + let header = mailmime_find_mailimf_fields(cur_data as *mut _); + if !header.is_null() { + return header; + } + } + } + MAILMIME_MESSAGE => return (*mime).mm_data.mm_message.mm_fields, + _ => {} + } + + ptr::null_mut() +} + +pub unsafe fn mailimf_find_optional_field( + header: *mut mailimf_fields, + wanted_fld_name: *const libc::c_char, +) -> *mut mailimf_optional_field { + if header.is_null() || (*header).fld_list.is_null() { + return ptr::null_mut(); + } + for cur_data in (*(*header).fld_list).into_iter() { + let field: *mut mailimf_field = cur_data as *mut _; + + if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { + let optional_field: *mut mailimf_optional_field = (*field).fld_data.fld_optional_field; + if !optional_field.is_null() + && !(*optional_field).fld_name.is_null() + && !(*optional_field).fld_value.is_null() + && strcasecmp((*optional_field).fld_name, wanted_fld_name) == 0i32 + { + return optional_field; + } + } + } + + ptr::null_mut() +} + +pub fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet { + /* returned addresses are normalized. */ + let mut recipients: HashSet = Default::default(); + + for cur in unsafe { (*(*imffields).fld_list).into_iter() } { + let fld = cur as *mut mailimf_field; + + let fld_to: *mut mailimf_to; + let fld_cc: *mut mailimf_cc; + + let mut addr_list: *mut mailimf_address_list = ptr::null_mut(); + if fld.is_null() { + continue; + } + + let fld = unsafe { *fld }; + + // TODO match on enums /rtn + match fld.fld_type { + 13 => { + fld_to = unsafe { fld.fld_data.fld_to }; + if !fld_to.is_null() { + addr_list = unsafe { (*fld_to).to_addr_list }; + } + } + 14 => { + fld_cc = unsafe { fld.fld_data.fld_cc }; + if !fld_cc.is_null() { + addr_list = unsafe { (*fld_cc).cc_addr_list }; + } + } + _ => {} + } + + if !addr_list.is_null() { + for cur2 in unsafe { &(*(*addr_list).ad_list) } { + let adr = cur2 as *mut mailimf_address; + + if adr.is_null() { + continue; + } + let adr = unsafe { *adr }; + + if adr.ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int { + mailimf_get_recipients_add_addr(&mut recipients, unsafe { + adr.ad_data.ad_mailbox + }); + } else if adr.ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int { + let group = unsafe { adr.ad_data.ad_group }; + if !group.is_null() && unsafe { !(*group).grp_mb_list.is_null() } { + for cur3 in unsafe { &(*(*(*group).grp_mb_list).mb_list) } { + mailimf_get_recipients_add_addr( + &mut recipients, + cur3 as *mut mailimf_mailbox, + ); + } + } + } + } + } + } + + recipients +} + +pub fn mailmime_transfer_decode(mime: *mut Mailmime) -> Result, Error> { + ensure!(!mime.is_null(), "invalid inputs"); + + let mime_transfer_encoding = + get_mime_transfer_encoding(mime).unwrap_or(MAILMIME_MECHANISM_BINARY as i32); + + let mime_data = unsafe { (*mime).mm_data.mm_single }; + + decode_dt_data(mime_data, mime_transfer_encoding) +} + pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option { unsafe { let mm_mime_fields = (*mime).mm_mime_fields; @@ -131,29 +319,30 @@ pub fn decode_dt_data( return Ok(result.to_vec()); } } - unsafe { display_mime_data(mime_data) }; + // unsafe { display_mime_data(mime_data) }; let mut current_index = 0; let mut transfer_decoding_buffer = ptr::null_mut(); let mut decoded_data_bytes = 0; - let r = unsafe { mailmime_part_parse( - (*mime_data).dt_data.dt_text.dt_data, - (*mime_data).dt_data.dt_text.dt_length, - &mut current_index, - mime_transfer_encoding, - &mut transfer_decoding_buffer, - &mut decoded_data_bytes, - ) }; + let r = unsafe { + mailmime_part_parse( + (*mime_data).dt_data.dt_text.dt_data, + (*mime_data).dt_data.dt_text.dt_length, + &mut current_index, + mime_transfer_encoding, + &mut transfer_decoding_buffer, + &mut decoded_data_bytes, + ) + }; if r == MAILIMF_NO_ERROR as libc::c_int && !transfer_decoding_buffer.is_null() && decoded_data_bytes > 0 { - let result = unsafe { std::slice::from_raw_parts( - transfer_decoding_buffer as *const u8, - decoded_data_bytes, - ) } + let result = unsafe { + std::slice::from_raw_parts(transfer_decoding_buffer as *const u8, decoded_data_bytes) + } .to_vec(); // we return a fresh vec and transfer_decoding_buffer is not used or passed anywhere // so it's safe to free it right away, as mailman_part_parse has @@ -166,6 +355,22 @@ pub fn decode_dt_data( Err(format_err!("Failed to to decode")) } +pub fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> Option { + if mb_list.is_null() { + return None; + } + + for cur in unsafe { (*(*mb_list).mb_list).into_iter() } { + let mb = cur as *mut mailimf_mailbox; + if !mb.is_null() && !unsafe { (*mb).mb_addr_spec.is_null() } { + let addr = unsafe { as_str((*mb).mb_addr_spec) }; + return Some(addr_normalize(addr).to_string()); + } + } + + None +} + /************************************** * mime creation API **************************************/