diff --git a/mmime/src/display.rs b/mmime/src/display.rs new file mode 100644 index 000000000..d274d81be --- /dev/null +++ b/mmime/src/display.rs @@ -0,0 +1,387 @@ + +use crate::clist::*; + +use crate::mailimf::types::*; +use crate::mailmime::types::*; + +use std::ffi::CStr; + +pub unsafe fn display_mime(mut mime: *mut Mailmime) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + println!("{}", (*mime).mm_type); + + match (*mime).mm_type as u32 { + MAILMIME_SINGLE => { + println!("single part"); + } + MAILMIME_MULTIPLE => { + println!("multipart"); + } + MAILMIME_MESSAGE => println!("message"), + _ => {} + } + if !(*mime).mm_mime_fields.is_null() { + if !(*(*(*mime).mm_mime_fields).fld_list).first.is_null() { + print!("MIME headers begin"); + display_mime_fields((*mime).mm_mime_fields); + println!("MIME headers end"); + } + } + display_mime_content((*mime).mm_content_type); + match (*mime).mm_type as u32 { + MAILMIME_SINGLE => { + display_mime_data((*mime).mm_data.mm_single); + } + MAILMIME_MULTIPLE => { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + display_mime( + (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut Mailmime, + ); + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } + } + MAILMIME_MESSAGE => { + if !(*mime).mm_data.mm_message.mm_fields.is_null() { + if !(*(*(*mime).mm_data.mm_message.mm_fields).fld_list) + .first + .is_null() + { + println!("headers begin"); + display_fields((*mime).mm_data.mm_message.mm_fields); + println!("headers end"); + } + if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() { + display_mime((*mime).mm_data.mm_message.mm_msg_mime); + } + } + } + _ => {} + }; +} + +unsafe fn display_mime_content(mut content_type: *mut mailmime_content) { + print!("type: "); + display_mime_type((*content_type).ct_type); + println!( + "/{}", + CStr::from_ptr((*content_type).ct_subtype).to_str().unwrap() + ); +} +unsafe fn display_mime_type(mut type_0: *mut mailmime_type) { + match (*type_0).tp_type { + 1 => { + display_mime_discrete_type((*type_0).tp_data.tp_discrete_type); + } + 2 => { + display_mime_composite_type((*type_0).tp_data.tp_composite_type); + } + _ => {} + }; +} +unsafe fn display_mime_composite_type(mut ct: *mut mailmime_composite_type) { + match (*ct).ct_type { + 1 => { + print!("message"); + } + 2 => { + print!("multipart"); + } + 3 => { + print!("{}", CStr::from_ptr((*ct).ct_token).to_str().unwrap()); + } + _ => {} + }; +} +unsafe fn display_mime_discrete_type(mut discrete_type: *mut mailmime_discrete_type) { + match (*discrete_type).dt_type { + 1 => { + print!("text"); + } + 2 => { + print!("image"); + } + 3 => { + print!("audio"); + } + 4 => { + print!("video"); + } + 5 => { + print!("application"); + } + 6 => { + print!("{}", (*discrete_type).dt_extension as u8 as char); + } + _ => {} + }; +} +unsafe fn display_mime_data(mut data: *mut mailmime_data) { + match (*data).dt_type { + 0 => { + println!( + "data : {} bytes", + (*data).dt_data.dt_text.dt_length as libc::c_uint, + ); + } + 1 => { + println!( + "data (file) : {}", + CStr::from_ptr((*data).dt_data.dt_filename) + .to_str() + .unwrap() + ); + } + _ => {} + }; +} +unsafe fn display_mime_dsp_parm(mut param: *mut mailmime_disposition_parm) { + match (*param).pa_type { + 0 => { + println!( + "filename: {}", + CStr::from_ptr((*param).pa_data.pa_filename) + .to_str() + .unwrap() + ); + } + _ => {} + }; +} +unsafe fn display_mime_disposition(mut disposition: *mut mailmime_disposition) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + cur = (*(*disposition).dsp_parms).first; + while !cur.is_null() { + let mut param: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm; + param = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailmime_disposition_parm; + display_mime_dsp_parm(param); + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } +} +unsafe fn display_mime_field(mut field: *mut mailmime_field) { + match (*field).fld_type { + 1 => { + print!("content-type: "); + display_mime_content((*field).fld_data.fld_content); + println!(""); + } + 6 => { + display_mime_disposition((*field).fld_data.fld_disposition); + } + _ => {} + }; +} +unsafe fn display_mime_fields(mut fields: *mut mailmime_fields) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + cur = (*(*fields).fld_list).first; + while !cur.is_null() { + let mut field: *mut mailmime_field = 0 as *mut mailmime_field; + field = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailmime_field; + display_mime_field(field); + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } +} +unsafe fn display_date_time(mut d: *mut mailimf_date_time) { + print!( + "{:02}/{:02}/{:02} {:02}:{:02}:{:02} +{:04}", + (*d).dt_day, + (*d).dt_month, + (*d).dt_year, + (*d).dt_hour, + (*d).dt_min, + (*d).dt_sec, + (*d).dt_zone, + ); +} +unsafe fn display_orig_date(mut orig_date: *mut mailimf_orig_date) { + display_date_time((*orig_date).dt_date_time); +} +unsafe fn display_mailbox(mut mb: *mut mailimf_mailbox) { + if !(*mb).mb_display_name.is_null() { + print!( + "{}", + CStr::from_ptr((*mb).mb_display_name).to_str().unwrap() + ); + } + print!("<{}>", CStr::from_ptr((*mb).mb_addr_spec).to_str().unwrap()); +} +unsafe fn display_mailbox_list(mut mb_list: *mut mailimf_mailbox_list) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + cur = (*(*mb_list).mb_list).first; + while !cur.is_null() { + let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox; + mb = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailimf_mailbox; + display_mailbox(mb); + if !if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + .is_null() + { + print!(", "); + } + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } +} +unsafe fn display_group(mut group: *mut mailimf_group) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + print!( + "{}: ", + CStr::from_ptr((*group).grp_display_name).to_str().unwrap() + ); + cur = (*(*(*group).grp_mb_list).mb_list).first; + while !cur.is_null() { + let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox; + mb = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailimf_mailbox; + display_mailbox(mb); + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } + print!("; "); +} +unsafe fn display_address(mut a: *mut mailimf_address) { + match (*a).ad_type { + 2 => { + display_group((*a).ad_data.ad_group); + } + 1 => { + display_mailbox((*a).ad_data.ad_mailbox); + } + _ => {} + }; +} +unsafe fn display_address_list(mut addr_list: *mut mailimf_address_list) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + cur = (*(*addr_list).ad_list).first; + while !cur.is_null() { + let mut addr: *mut mailimf_address = 0 as *mut mailimf_address; + addr = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailimf_address; + display_address(addr); + if !if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + .is_null() + { + print!(", "); + } + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } +} +unsafe fn display_from(mut from: *mut mailimf_from) { + display_mailbox_list((*from).frm_mb_list); +} +unsafe fn display_to(mut to: *mut mailimf_to) { + display_address_list((*to).to_addr_list); +} +unsafe fn display_cc(mut cc: *mut mailimf_cc) { + display_address_list((*cc).cc_addr_list); +} +unsafe fn display_subject(mut subject: *mut mailimf_subject) { + print!("{}", CStr::from_ptr((*subject).sbj_value).to_str().unwrap()); +} +unsafe fn display_field(mut field: *mut mailimf_field) { + match (*field).fld_type { + 9 => { + print!("Date: "); + display_orig_date((*field).fld_data.fld_orig_date); + println!(""); + } + 10 => { + print!("From: "); + display_from((*field).fld_data.fld_from); + println!(""); + } + 13 => { + print!("To: "); + display_to((*field).fld_data.fld_to); + println!(""); + } + 14 => { + print!("Cc: "); + display_cc((*field).fld_data.fld_cc); + println!(""); + } + 19 => { + print!("Subject: "); + display_subject((*field).fld_data.fld_subject); + println!(""); + } + 16 => { + println!( + "Message-ID: {}", + CStr::from_ptr((*(*field).fld_data.fld_message_id).mid_value) + .to_str() + .unwrap(), + ); + } + _ => {} + }; +} +unsafe fn display_fields(mut fields: *mut mailimf_fields) { + let mut cur: *mut clistiter = 0 as *mut clistiter; + cur = (*(*fields).fld_list).first; + while !cur.is_null() { + let mut f: *mut mailimf_field = 0 as *mut mailimf_field; + f = (if !cur.is_null() { + (*cur).data + } else { + 0 as *mut libc::c_void + }) as *mut mailimf_field; + display_field(f); + cur = if !cur.is_null() { + (*cur).next + } else { + 0 as *mut clistcell + } + } +} diff --git a/mmime/src/lib.rs b/mmime/src/lib.rs index dca70f9ca..c3a868375 100644 --- a/mmime/src/lib.rs +++ b/mmime/src/lib.rs @@ -23,6 +23,7 @@ pub mod mailimf; pub mod mailmime; pub mod mmapstring; pub mod other; +pub mod display; pub use self::charconv::*; pub use self::chash::*; @@ -31,6 +32,8 @@ pub use self::mailimf::*; pub use self::mailmime::*; pub use self::mmapstring::*; pub use self::other::*; +pub use self::display::*; + #[cfg(test)] mod tests { @@ -77,384 +80,4 @@ mod tests { mailmime::types::mailmime_free(mime); } } - - unsafe fn display_mime(mut mime: *mut Mailmime) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - println!("{}", (*mime).mm_type); - - match (*mime).mm_type { - 1 => { - println!("single part"); - } - 2 => { - println!("multipart"); - } - 3 => println!("message"), - _ => {} - } - if !(*mime).mm_mime_fields.is_null() { - if !(*(*(*mime).mm_mime_fields).fld_list).first.is_null() { - print!("MIME headers begin"); - display_mime_fields((*mime).mm_mime_fields); - println!("MIME headers end"); - } - } - display_mime_content((*mime).mm_content_type); - match (*mime).mm_type { - 1 => { - display_mime_data((*mime).mm_data.mm_single); - } - 2 => { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - display_mime( - (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut Mailmime, - ); - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } - 3 => { - if !(*mime).mm_data.mm_message.mm_fields.is_null() { - if !(*(*(*mime).mm_data.mm_message.mm_fields).fld_list) - .first - .is_null() - { - println!("headers begin"); - display_fields((*mime).mm_data.mm_message.mm_fields); - println!("headers end"); - } - if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() { - display_mime((*mime).mm_data.mm_message.mm_msg_mime); - } - } - } - _ => {} - }; - } - - unsafe fn display_mime_content(mut content_type: *mut mailmime_content) { - print!("type: "); - display_mime_type((*content_type).ct_type); - println!( - "/{}", - CStr::from_ptr((*content_type).ct_subtype).to_str().unwrap() - ); - } - unsafe fn display_mime_type(mut type_0: *mut mailmime_type) { - match (*type_0).tp_type { - 1 => { - display_mime_discrete_type((*type_0).tp_data.tp_discrete_type); - } - 2 => { - display_mime_composite_type((*type_0).tp_data.tp_composite_type); - } - _ => {} - }; - } - unsafe fn display_mime_composite_type(mut ct: *mut mailmime_composite_type) { - match (*ct).ct_type { - 1 => { - print!("message"); - } - 2 => { - print!("multipart"); - } - 3 => { - print!("{}", CStr::from_ptr((*ct).ct_token).to_str().unwrap()); - } - _ => {} - }; - } - unsafe fn display_mime_discrete_type(mut discrete_type: *mut mailmime_discrete_type) { - match (*discrete_type).dt_type { - 1 => { - print!("text"); - } - 2 => { - print!("image"); - } - 3 => { - print!("audio"); - } - 4 => { - print!("video"); - } - 5 => { - print!("application"); - } - 6 => { - print!("{}", (*discrete_type).dt_extension as u8 as char); - } - _ => {} - }; - } - unsafe fn display_mime_data(mut data: *mut mailmime_data) { - match (*data).dt_type { - 0 => { - println!( - "data : {} bytes", - (*data).dt_data.dt_text.dt_length as libc::c_uint, - ); - } - 1 => { - println!( - "data (file) : {}", - CStr::from_ptr((*data).dt_data.dt_filename) - .to_str() - .unwrap() - ); - } - _ => {} - }; - } - unsafe fn display_mime_dsp_parm(mut param: *mut mailmime_disposition_parm) { - match (*param).pa_type { - 0 => { - println!( - "filename: {}", - CStr::from_ptr((*param).pa_data.pa_filename) - .to_str() - .unwrap() - ); - } - _ => {} - }; - } - unsafe fn display_mime_disposition(mut disposition: *mut mailmime_disposition) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - cur = (*(*disposition).dsp_parms).first; - while !cur.is_null() { - let mut param: *mut mailmime_disposition_parm = 0 as *mut mailmime_disposition_parm; - param = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailmime_disposition_parm; - display_mime_dsp_parm(param); - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } - unsafe fn display_mime_field(mut field: *mut mailmime_field) { - match (*field).fld_type { - 1 => { - print!("content-type: "); - display_mime_content((*field).fld_data.fld_content); - println!(""); - } - 6 => { - display_mime_disposition((*field).fld_data.fld_disposition); - } - _ => {} - }; - } - unsafe fn display_mime_fields(mut fields: *mut mailmime_fields) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - cur = (*(*fields).fld_list).first; - while !cur.is_null() { - let mut field: *mut mailmime_field = 0 as *mut mailmime_field; - field = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailmime_field; - display_mime_field(field); - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } - unsafe fn display_date_time(mut d: *mut mailimf_date_time) { - print!( - "{:02}/{:02}/{:02} {:02}:{:02}:{:02} +{:04}", - (*d).dt_day, - (*d).dt_month, - (*d).dt_year, - (*d).dt_hour, - (*d).dt_min, - (*d).dt_sec, - (*d).dt_zone, - ); - } - unsafe fn display_orig_date(mut orig_date: *mut mailimf_orig_date) { - display_date_time((*orig_date).dt_date_time); - } - unsafe fn display_mailbox(mut mb: *mut mailimf_mailbox) { - if !(*mb).mb_display_name.is_null() { - print!( - "{}", - CStr::from_ptr((*mb).mb_display_name).to_str().unwrap() - ); - } - print!("<{}>", CStr::from_ptr((*mb).mb_addr_spec).to_str().unwrap()); - } - unsafe fn display_mailbox_list(mut mb_list: *mut mailimf_mailbox_list) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - cur = (*(*mb_list).mb_list).first; - while !cur.is_null() { - let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox; - mb = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailimf_mailbox; - display_mailbox(mb); - if !if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - .is_null() - { - print!(", "); - } - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } - unsafe fn display_group(mut group: *mut mailimf_group) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - print!( - "{}: ", - CStr::from_ptr((*group).grp_display_name).to_str().unwrap() - ); - cur = (*(*(*group).grp_mb_list).mb_list).first; - while !cur.is_null() { - let mut mb: *mut mailimf_mailbox = 0 as *mut mailimf_mailbox; - mb = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailimf_mailbox; - display_mailbox(mb); - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - print!("; "); - } - unsafe fn display_address(mut a: *mut mailimf_address) { - match (*a).ad_type { - 2 => { - display_group((*a).ad_data.ad_group); - } - 1 => { - display_mailbox((*a).ad_data.ad_mailbox); - } - _ => {} - }; - } - unsafe fn display_address_list(mut addr_list: *mut mailimf_address_list) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - cur = (*(*addr_list).ad_list).first; - while !cur.is_null() { - let mut addr: *mut mailimf_address = 0 as *mut mailimf_address; - addr = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailimf_address; - display_address(addr); - if !if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - .is_null() - { - print!(", "); - } - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } - unsafe fn display_from(mut from: *mut mailimf_from) { - display_mailbox_list((*from).frm_mb_list); - } - unsafe fn display_to(mut to: *mut mailimf_to) { - display_address_list((*to).to_addr_list); - } - unsafe fn display_cc(mut cc: *mut mailimf_cc) { - display_address_list((*cc).cc_addr_list); - } - unsafe fn display_subject(mut subject: *mut mailimf_subject) { - print!("{}", CStr::from_ptr((*subject).sbj_value).to_str().unwrap()); - } - unsafe fn display_field(mut field: *mut mailimf_field) { - match (*field).fld_type { - 9 => { - print!("Date: "); - display_orig_date((*field).fld_data.fld_orig_date); - println!(""); - } - 10 => { - print!("From: "); - display_from((*field).fld_data.fld_from); - println!(""); - } - 13 => { - print!("To: "); - display_to((*field).fld_data.fld_to); - println!(""); - } - 14 => { - print!("Cc: "); - display_cc((*field).fld_data.fld_cc); - println!(""); - } - 19 => { - print!("Subject: "); - display_subject((*field).fld_data.fld_subject); - println!(""); - } - 16 => { - println!( - "Message-ID: {}", - CStr::from_ptr((*(*field).fld_data.fld_message_id).mid_value) - .to_str() - .unwrap(), - ); - } - _ => {} - }; - } - unsafe fn display_fields(mut fields: *mut mailimf_fields) { - let mut cur: *mut clistiter = 0 as *mut clistiter; - cur = (*(*fields).fld_list).first; - while !cur.is_null() { - let mut f: *mut mailimf_field = 0 as *mut mailimf_field; - f = (if !cur.is_null() { - (*cur).data - } else { - 0 as *mut libc::c_void - }) as *mut mailimf_field; - display_field(f); - cur = if !cur.is_null() { - (*cur).next - } else { - 0 as *mut clistcell - } - } - } } diff --git a/mmime/src/mailmime/types.rs b/mmime/src/mailmime/types.rs index 0308c8124..a894e0b04 100644 --- a/mmime/src/mailmime/types.rs +++ b/mmime/src/mailmime/types.rs @@ -154,6 +154,7 @@ pub struct unnamed_6 { pub dt_data: *const libc::c_char, pub dt_length: size_t, } + pub type unnamed_7 = libc::c_uint; pub const MAILMIME_MESSAGE: unnamed_7 = 3; pub const MAILMIME_MULTIPLE: unnamed_7 = 2; diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 2aa2ae25c..a958f3338 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -102,7 +102,7 @@ impl<'a> MimeParser<'a> { } } - pub unsafe fn parse(&mut self, body: &[u8]) { + pub unsafe fn parse(&mut self, body: &[u8]) -> Result<(), Error> { let mut index = 0; let r = mailmime_parse( @@ -113,7 +113,7 @@ impl<'a> MimeParser<'a> { ); if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() { - self.e2ee_helper.decrypt(self.context, self.mimeroot); + self.e2ee_helper.try_decrypt(self.context, self.mimeroot)?; self.parse_mime_recursive(self.mimeroot); if let Some(field) = self.lookup_field("Subject") { @@ -317,6 +317,7 @@ impl<'a> MimeParser<'a> { } self.parts.push(part_5); } + Ok(()) } pub fn get_last_nonmeta(&mut self) -> Option<&mut Part> { @@ -1056,7 +1057,7 @@ unsafe fn mailmime_get_mime_type(mime: *mut Mailmime) -> (libc::c_int, Viewtype, Some("alternative") => DC_MIMETYPE_MP_ALTERNATIVE, Some("related") => DC_MIMETYPE_MP_RELATED, Some("encrypted") => { - // decryptable parts are already converted to other mime parts in dc_e2ee_decrypt() + // apparently try_decrypt failed to decrypt DC_MIMETYPE_MP_NOT_DECRYPTABLE } Some("signed") => DC_MIMETYPE_MP_SIGNED, @@ -1426,7 +1427,7 @@ 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[..]) }; + unsafe { mimeparser.parse(&raw[..]).unwrap() }; assert_eq!(mimeparser.subject, None); assert_eq!(mimeparser.parts.len(), 1); } @@ -1436,7 +1437,7 @@ mod tests { fn test_dc_mailmime_parse_crash_fuzzy(data in "[!-~\t ]{2000,}") { let context = dummy_context(); let mut mimeparser = MimeParser::new(&context.ctx); - unsafe { mimeparser.parse(data.as_bytes()) }; + unsafe { mimeparser.parse(data.as_bytes()).unwrap() }; } } @@ -1445,7 +1446,7 @@ mod tests { let context = dummy_context(); let raw = include_bytes!("../test-data/message/mail_with_message_id.txt"); let mut mimeparser = MimeParser::new(&context.ctx); - unsafe { mimeparser.parse(&raw[..]) }; + unsafe { mimeparser.parse(&raw[..]).unwrap() }; assert_eq!( mimeparser.get_rfc724_mid(), Some("2dfdbde7@example.org".into()) @@ -1457,7 +1458,7 @@ 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[..]) }; + unsafe { mimeparser.parse(&raw[..]).unwrap() }; assert_eq!(mimeparser.get_rfc724_mid(), None); } @@ -1467,7 +1468,7 @@ mod tests { 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 mut mimeparser = MimeParser::new(&context.ctx); - mimeparser.parse(&raw[..]); + mimeparser.parse(&raw[..]).unwrap(); assert_eq!(mimeparser.subject, Some("inner-subject".into())); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index e52c1364d..adfbda27a 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -61,7 +61,9 @@ pub unsafe fn dc_receive_imf( // somewhen, I did not found out anything that speaks against this approach yet) let mut mime_parser = MimeParser::new(context); - mime_parser.parse(imf_raw); + if let Err(err) = mime_parser.parse(imf_raw) { + error!(context, "dc_receive_imf parse error: {}", err); + }; if mime_parser.header.is_empty() { // Error - even adding an empty record won't help as we do not know the message ID @@ -1148,21 +1150,15 @@ unsafe fn create_or_lookup_group( // check, if we have a chat with this group ID let (mut chat_id, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid); if chat_id != 0 { - let mut failure_reason = std::ptr::null_mut(); - - if chat_id_verified - && 0 == check_verified_properties( - context, - mime_parser, - from_id as u32, - to_ids, - &mut failure_reason, - ) - { - mime_parser.repl_msg_by_error(to_string(failure_reason)); + if chat_id_verified { + if let Err(err) = + check_verified_properties(context, mime_parser, from_id as u32, to_ids) + { + warn!(context, "verification problem: {}", err); + let s = format!("{}. See 'Info' for more details", err); + mime_parser.repl_msg_by_error(s); + } } - - free(failure_reason.cast()); } // check if the sender is a member of the existing group - @@ -1191,18 +1187,14 @@ unsafe fn create_or_lookup_group( let mut create_verified = VerifiedStatus::Unverified; if mime_parser.lookup_field("Chat-Verified").is_some() { create_verified = VerifiedStatus::Verified; - let mut failure_reason = std::ptr::null_mut(); - if 0 == check_verified_properties( - context, - mime_parser, - from_id as u32, - to_ids, - &mut failure_reason, - ) { - mime_parser.repl_msg_by_error(to_string(failure_reason)); + if let Err(err) = + check_verified_properties(context, mime_parser, from_id as u32, to_ids) + { + warn!(context, "verification problem: {}", err); + let s = format!("{}. See 'Info' for more details", err); + mime_parser.repl_msg_by_error(&s); } - free(failure_reason.cast()); } if 0 == allow_creation { cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); @@ -1609,51 +1601,41 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec< chat_ids } -unsafe fn check_verified_properties( +fn check_verified_properties( context: &Context, mimeparser: &MimeParser, from_id: u32, to_ids: &Vec, - failure_reason: *mut *mut libc::c_char, -) -> libc::c_int { - let verify_fail = |reason: String| { - *failure_reason = format!("{}. See \"Info\" for details.", reason).strdup(); - warn!(context, "{}", reason); - }; +) -> Result<()> { + let contact = Contact::load_from_db(context, from_id)?; - let contact = match Contact::load_from_db(context, from_id) { - Ok(contact) => contact, - Err(_err) => { - verify_fail("Internal Error; cannot load contact".into()); - return 0; - } - }; - - if !mimeparser.e2ee_helper.encrypted { - verify_fail("This message is not encrypted".into()); - return 0; - } + ensure!( + mimeparser.e2ee_helper.encrypted, + "This message is not encrypted." + ); // ensure, the contact is verified // and the message is signed with a verified key of the sender. // this check is skipped for SELF as there is no proper SELF-peerstate // and results in group-splits otherwise. - if from_id != 1 { + if from_id != DC_CONTACT_ID_SELF { let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr()); if peerstate.is_none() || contact.is_verified_ex(context, peerstate.as_ref()) != VerifiedStatus::BidirectVerified { - verify_fail("The sender of this message is not verified.".into()); - return 0; + bail!( + "Sender of this message is not verified: {}", + contact.get_addr() + ); } if let Some(peerstate) = peerstate { - if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) { - verify_fail("The message was sent with non-verified encryption.".into()); - return 0; - } + ensure!( + peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures), + "The message was sent with non-verified encryption." + ); } } @@ -1671,13 +1653,13 @@ unsafe fn check_verified_properties( rows.collect::, _>>() .map_err(Into::into) }, - ); + )?; - if rows.is_err() { - return 0; - } - for (to_addr, mut is_verified) in rows.unwrap().into_iter() { + for (to_addr, _is_verified) in rows.into_iter() { + let mut is_verified = _is_verified != 0; let mut peerstate = Peerstate::from_addr(context, &context.sql, &to_addr); + + // mark gossiped keys (if any) as verified if mimeparser.e2ee_helper.gossipped_addr.contains(&to_addr) && peerstate.is_some() { let peerstate = peerstate.as_mut().unwrap(); @@ -1686,7 +1668,7 @@ unsafe fn check_verified_properties( // - OR if the verified-key does not match public-key or gossip-key // (otherwise a verified key can _only_ be updated through QR scan which might be annoying, // see https://github.com/nextleap-project/countermitm/issues/46 for a discussion about this point) - if 0 == is_verified + if !is_verified || peerstate.verified_key_fingerprint != peerstate.public_key_fingerprint && peerstate.verified_key_fingerprint != peerstate.gossip_key_fingerprint { @@ -1694,21 +1676,19 @@ unsafe fn check_verified_properties( let fp = peerstate.gossip_key_fingerprint.clone(); if let Some(fp) = fp { peerstate.set_verified(0, &fp, 2); - peerstate.save_to_db(&context.sql, false); - is_verified = 1; + peerstate.save_to_db(&context.sql, false).unwrap(); + is_verified = true; } } } - if 0 == is_verified { - verify_fail(format!( + if !is_verified { + bail!( "{} is not a member of this verified group", - to_addr - )); - return 0; + to_addr.to_string() + ); } } - - 1 + Ok(()) } fn set_better_msg(mime_parser: &mut MimeParser, better_msg: impl AsRef) { diff --git a/src/e2ee.rs b/src/e2ee.rs index a4e2cffd7..3ff8a2b0a 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -7,6 +7,7 @@ use std::ptr; use std::str::FromStr; use libc::{free, strcmp, strlen, strncmp}; +use mmime::display::display_mime; use mmime::clist::*; use mmime::mailimf::types::*; use mmime::mailimf::types_helper::*; @@ -305,9 +306,11 @@ impl E2eeHelper { } } - pub unsafe fn decrypt(&mut self, context: &Context, in_out_message: *mut Mailmime) { - /* return values: 0=nothing to decrypt/cannot decrypt, 1=sth. decrypted - (to detect parts that could not be decrypted, simply look for left "multipart/encrypted" MIME types */ + pub unsafe fn try_decrypt( + &mut self, + context: &Context, + in_out_message: *mut Mailmime, + ) -> Result<()> { /*just a pointer into mailmime structure, must not be freed*/ let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message); let mut message_time = 0; @@ -343,16 +346,16 @@ impl E2eeHelper { 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); + 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); + peerstate.save_to_db(&context.sql, false).unwrap(); } } else if let Some(ref header) = autocryptheader { let p = Peerstate::from_header(context, header, message_time); - assert!(p.save_to_db(&context.sql, true)); + p.save_to_db(&context.sql, true).unwrap(); peerstate = Some(p); } } @@ -380,30 +383,22 @@ impl E2eeHelper { public_keyring_for_validate.add_ref(key); } } - for iterations in 0..10 { - let mut has_unencrypted_parts: libc::c_int = 0i32; - if decrypt_recursive( - context, - in_out_message, - &private_keyring, - &public_keyring_for_validate, - &mut self.signatures, - &mut gossip_headers, - &mut has_unencrypted_parts, - ) - .is_err() - { - break; + + match decrypt_if_autocrypt_message( + context, + in_out_message, + &private_keyring, + &public_keyring_for_validate, + &mut self.signatures, + &mut gossip_headers, + ) { + Ok(res) => { + self.encrypted = res; } - /* if we're here, sth. was encrypted. if we're on top-level, - and there are no additional unencrypted parts in the message - the encryption was fine (signature is handled separately and - returned as `signatures`) */ - if iterations == 0 && 0 == has_unencrypted_parts { - self.encrypted = true; + Err(err) => { + bail!("failed to decrypt: {}", err); } } - /* check for Autocrypt-Gossip */ if !gossip_headers.is_null() { self.gossipped_addr = update_gossip_peerstates( context, @@ -419,6 +414,7 @@ impl E2eeHelper { if !gossip_headers.is_null() { mailimf_fields_free(gossip_headers); } + Ok(()) } } @@ -536,10 +532,10 @@ unsafe fn update_gossip_peerstates( Peerstate::from_addr(context, &context.sql, &header.addr); if let Some(ref mut peerstate) = peerstate { peerstate.apply_gossip(header, message_time); - peerstate.save_to_db(&context.sql, false); + peerstate.save_to_db(&context.sql, false).unwrap(); } else { let p = Peerstate::from_gossip(context, header, message_time); - p.save_to_db(&context.sql, true); + p.save_to_db(&context.sql, true).unwrap(); peerstate = Some(p); } if let Some(peerstate) = peerstate { @@ -564,220 +560,165 @@ unsafe fn update_gossip_peerstates( gossipped_addr } -unsafe fn decrypt_recursive( +fn decrypt_if_autocrypt_message( context: &Context, - mime: *mut Mailmime, + mime_undetermined: *mut Mailmime, private_keyring: &Keyring, public_keyring_for_validate: &Keyring, ret_valid_signatures: &mut HashSet, ret_gossip_headers: *mut *mut mailimf_fields, - ret_has_unencrypted_parts: *mut libc::c_int, -) -> Result<()> { - ensure!(!mime.is_null(), "Invalid mime reference"); - let ct: *mut mailmime_content; +) -> Result<(bool)> { + /* The returned bool is true if we detected an Autocrypt-encrypted + message and successfully decrypted it. Decryption modifies the + passed in mime structure in place. The returned bool is false + if it was not an Autocrypt message. + Errors are returned for failures related to decryption. + */ + ensure!(!mime_undetermined.is_null(), "Invalid mime reference"); + let mime: *mut Mailmime; + unsafe { + println!("****** INCOMING MSG BEGIN"); + display_mime(mime_undetermined); + println!("****** INCOMING MSG END"); + + if (*mime_undetermined).mm_type != MAILMIME_MESSAGE as libc::c_int { + return Ok(false); + } + mime = (*mime_undetermined).mm_data.mm_message.mm_msg_mime; + + if (*mime).mm_type != MAILMIME_MULTIPLE as libc::c_int + || "encrypted" != wrapmime::get_ct_subtype(mime).unwrap_or_default() + { + return Ok(false); + } + } + info!(context, "found OpenPGP-encrypted message"); + // we may have a proper Multipart/Encrypted Autocrypt Level 1 message + // XXX: more precise check we have exactly this specified OpenPGP-mime structure + // https://tools.ietf.org/html/rfc3156.html#section-4 - if (*mime).mm_type == MAILMIME_MULTIPLE as libc::c_int { - ct = (*mime).mm_content_type; - if !ct.is_null() - && !(*ct).ct_subtype.is_null() - && strcmp( - (*ct).ct_subtype, - b"encrypted\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - let mut decrypted_mime: *mut Mailmime = ptr::null_mut(); - let decrypted = match decrypt_part( - context, - cur_data as *mut Mailmime, - private_keyring, - public_keyring_for_validate, - ret_valid_signatures, - &mut decrypted_mime, - ) { - Ok(res) => res, - Err(err) => bail!("decrypt_part: {}", err), - }; - if decrypted { - if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 { - let mut dummy: libc::size_t = 0; - let mut test: *mut mailimf_fields = ptr::null_mut(); - if mailimf_envelope_and_optional_fields_parse( - (*decrypted_mime).mm_mime_start, - (*decrypted_mime).mm_length, - &mut dummy, - &mut test, - ) == MAILIMF_NO_ERROR as libc::c_int - && !test.is_null() - { - *ret_gossip_headers = test - } - } - mailmime_substitute(mime, decrypted_mime); - mailmime_free(mime); - return Ok(()); - } - } - *ret_has_unencrypted_parts = 1i32 - } else { - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - if decrypt_recursive( - context, - cur_data as *mut Mailmime, - private_keyring, - public_keyring_for_validate, - ret_valid_signatures, - ret_gossip_headers, - ret_has_unencrypted_parts, - ) - .is_ok() - { - return Ok(()); - } - } - } - } else if (*mime).mm_type == MAILMIME_MESSAGE as libc::c_int { - if decrypt_recursive( - context, - (*mime).mm_data.mm_message.mm_msg_mime, - private_keyring, - public_keyring_for_validate, - ret_valid_signatures, - ret_gossip_headers, - ret_has_unencrypted_parts, - ) - .is_ok() - { - return Ok(()); - } - } else { - *ret_has_unencrypted_parts = 1; + let mut parts: Vec<*mut libc::c_void> = Vec::new(); + unsafe { + parts.extend((*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter()); + } + ensure!(parts.len() == 2, "Invalid Autocrypt Level 1 Mime Parts"); + + info!(context, "decrypt_if_autocrypt_message found AC-encrypted message"); + + // ensure protocol-parameter "application/pgp-encrypted") + // ensure wrapmime::get_content_type(parts[1])) == "application/octetstream" + + let encrypted_mime_payload = parts[1] as *mut mmime::mailmime::types::Mailmime; + + let decrypted_mime = match decrypt_part( + context, + encrypted_mime_payload, + private_keyring, + public_keyring_for_validate, + ret_valid_signatures, + ) { + Ok(res) => res, + Err(err) => bail!("decrypt_part failed: {}", err), + }; + /* decrypted_mime is a dangling pointer which we now put into + mailmime's Ownership */ + unsafe { + mailmime_substitute(mime, decrypted_mime); + mailmime_free(mime); } - Err(format_err!("Failed to decrypt")) + /* finally, let's return any gossip headers */ + unsafe { + if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 { + let mut dummy: libc::size_t = 0; + let mut test: *mut mailimf_fields = ptr::null_mut(); + if mailimf_envelope_and_optional_fields_parse( + (*decrypted_mime).mm_mime_start, + (*decrypted_mime).mm_length, + &mut dummy, + &mut test, + ) == MAILIMF_NO_ERROR as libc::c_int + && !test.is_null() + { + *ret_gossip_headers = test + } + } + } + return Ok(true); } -unsafe fn decrypt_part( +fn decrypt_part( _context: &Context, mime: *mut Mailmime, private_keyring: &Keyring, public_keyring_for_validate: &Keyring, ret_valid_signatures: &mut HashSet, - ret_decrypted_mime: *mut *mut Mailmime, -) -> Result { +) -> Result<*mut Mailmime> { let mime_data: *mut mailmime_data; - let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int; - let mut sth_decrypted = false; + let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int; - let cleanup = |transfer_decoding_buffer: *mut libc::c_char| { - if !transfer_decoding_buffer.is_null() { - mmap_string_unref(transfer_decoding_buffer); - } - }; - - *ret_decrypted_mime = ptr::null_mut(); - mime_data = (*mime).mm_data.mm_single; - /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */ - if (*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int - || (*mime_data).dt_data.dt_text.dt_data.is_null() - || (*mime_data).dt_data.dt_text.dt_length <= 0 - { - return Ok(false); + unsafe { + mime_data = (*mime).mm_data.mm_single; } - if !(*mime).mm_mime_fields.is_null() { - for cur_data in (*(*(*mime).mm_mime_fields).fld_list).into_iter() { - let field: *mut mailmime_field = cur_data as *mut _; - if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int - && !(*field).fld_data.fld_encoding.is_null() + if !wrapmime::has_decryptable_data_(mime_data) { + return Ok(ptr::null_mut()); + } + + if let Some(enc) = wrapmime::get_mime_transfer_encoding(mime) { + mime_transfer_encoding = enc; + } + + let (decoded_data, decoded_data_bytes) = + wrapmime::decode_dt_data(mime_data, mime_transfer_encoding)?; + /* encrypted, non-NULL decoded data in decoded_data now ... + Note that we need to take care of freeing decoded_data ourself. + Once decryption is finished we unref() can do this, so our caller does not + need to care for it. + + */ + let mut ret_decrypted_mime = ptr::null_mut(); + + unsafe { + if has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int) { + /* we should only have one decryption happening */ + assert!(ret_valid_signatures.is_empty(), "corrupted"); + + let plain = match dc_pgp_pk_decrypt( + std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes), + &private_keyring, + &public_keyring_for_validate, + Some(ret_valid_signatures), + ) { + Ok(plain) => plain, + Err(err) => { + mmap_string_unref(decoded_data); + bail!("could not decrypt: {}", err) + } + }; + let plain_bytes = plain.len(); + let plain_buf = plain.as_ptr() as *const libc::c_char; + + let mut index: libc::size_t = 0; + let mut decrypted_mime: *mut Mailmime = ptr::null_mut(); + if mailmime_parse( + plain_buf as *const _, + plain_bytes, + &mut index, + &mut decrypted_mime, + ) != MAIL_NO_ERROR as libc::c_int + || decrypted_mime.is_null() { - mime_transfer_encoding = (*(*field).fld_data.fld_encoding).enc_type + if !decrypted_mime.is_null() { + mailmime_free(decrypted_mime); + } + } else { + ret_decrypted_mime = decrypted_mime; } + std::mem::forget(plain); } + mmap_string_unref(decoded_data); } - /* regarding `Content-Transfer-Encoding:` */ - /* mmap_string_unref()'d if set */ - let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut(); - let decoded_data: *const libc::c_char; - let mut decoded_data_bytes: libc::size_t = 0; - if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int - || mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int - || mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int - { - decoded_data = (*mime_data).dt_data.dt_text.dt_data; - decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length; - if decoded_data.is_null() || decoded_data_bytes <= 0 { - /* no error - but no data */ - return Ok(false); - } - } else { - let r: libc::c_int; - let mut current_index: libc::size_t = 0; - r = 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 - { - cleanup(transfer_decoding_buffer); - bail!("mailmime_part_parse returned error or invalid data"); - } - decoded_data = transfer_decoding_buffer; - } - - /* encrypted, decoded data in decoded_data now ... */ - if has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int) { - let add_signatures = if ret_valid_signatures.is_empty() { - Some(ret_valid_signatures) - } else { - None - }; - - /*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */ - let plain = match dc_pgp_pk_decrypt( - std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes), - &private_keyring, - &public_keyring_for_validate, - add_signatures, - ) { - Ok(plain) => plain, - Err(err) => { - cleanup(transfer_decoding_buffer); - bail!("could not decrypt: {}", err) - } - }; - let plain_bytes = plain.len(); - let plain_buf = plain.as_ptr() as *const libc::c_char; - - let mut index: libc::size_t = 0; - let mut decrypted_mime: *mut Mailmime = ptr::null_mut(); - if mailmime_parse( - plain_buf as *const _, - plain_bytes, - &mut index, - &mut decrypted_mime, - ) != MAIL_NO_ERROR as libc::c_int - || decrypted_mime.is_null() - { - if !decrypted_mime.is_null() { - mailmime_free(decrypted_mime); - } - } else { - *ret_decrypted_mime = decrypted_mime; - sth_decrypted = true; - } - std::mem::forget(plain); - } - //mailmime_substitute(mime, new_mime); - //s. mailprivacy_gnupg.c::pgp_decrypt() - cleanup(transfer_decoding_buffer); - - Ok(sth_decrypted) + Ok(ret_decrypted_mime) } unsafe fn has_decrypted_pgp_armor(str__: *const libc::c_char, mut str_bytes: libc::c_int) -> bool { diff --git a/src/peerstate.rs b/src/peerstate.rs index ec1764e65..9d4dc5123 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -7,6 +7,7 @@ use crate::aheader::*; use crate::chat::*; use crate::constants::*; use crate::context::Context; +use crate::error::*; use crate::key::*; use crate::sql::{self, Sql}; @@ -408,28 +409,19 @@ impl<'a> Peerstate<'a> { success } - pub fn save_to_db(&self, sql: &Sql, create: bool) -> bool { - let mut success = false; - - if self.addr.is_none() { - return success; - } - + pub fn save_to_db(&self, sql: &Sql, create: bool) -> Result<()> { + ensure!(!self.addr.is_none(), "self.addr is not configured"); if create { - if sql::execute( + sql::execute( self.context, sql, "INSERT INTO acpeerstates (addr) VALUES(?);", params![self.addr.as_ref().unwrap()], - ) - .is_err() - { - return false; - } + )?; } if self.to_save == Some(ToSave::All) || create { - success = sql::execute( + sql::execute( self.context, sql, "UPDATE acpeerstates \ @@ -450,10 +442,9 @@ impl<'a> Peerstate<'a> { &self.verified_key_fingerprint, &self.addr, ], - ).is_ok(); - assert_eq!(success, true); + )? } else if self.to_save == Some(ToSave::Timestamps) { - success = sql::execute( + sql::execute( self.context, sql, "UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \ @@ -464,15 +455,14 @@ impl<'a> Peerstate<'a> { self.gossip_timestamp, &self.addr ], - ) - .is_ok(); + )?; } if self.to_save == Some(ToSave::All) || create { reset_gossiped_timestamp(self.context, 0); } - success + Ok(()) } pub fn has_verified_key(&self, fingerprints: &HashSet) -> bool { @@ -522,7 +512,10 @@ mod tests { degrade_event: None, }; - assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save"); + assert!( + peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + "failed to save to db" + ); let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into()) .expect("failed to load peerstate from db"); @@ -564,7 +557,10 @@ mod tests { degrade_event: None, }; - assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save"); + assert!( + peerstate.save_to_db(&ctx.ctx.sql, true).is_ok(), + "failed to save" + ); let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into()) .expect("failed to load peerstate from db"); diff --git a/src/securejoin.rs b/src/securejoin.rs index 8202131b7..788670b28 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -678,7 +678,7 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Res if peerstate.set_verified(1, fingerprint.as_ref(), 2) { peerstate.prefer_encrypt = EncryptPreference::Mutual; peerstate.to_save = Some(ToSave::All); - peerstate.save_to_db(&context.sql, false); + peerstate.save_to_db(&context.sql, false).unwrap(); return Ok(()); } } diff --git a/src/sql.rs b/src/sql.rs index 44f464667..679695a9f 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -777,7 +777,7 @@ fn open( if let Some(ref mut peerstate) = Peerstate::from_addr(context, sql, &addr?) { peerstate.recalc_fingerprint(); - peerstate.save_to_db(sql, false); + peerstate.save_to_db(sql, false).unwrap(); } } Ok(()) diff --git a/src/wrapmime.rs b/src/wrapmime.rs index 855872bd6..f2b75a902 100644 --- a/src/wrapmime.rs +++ b/src/wrapmime.rs @@ -1,10 +1,12 @@ use std::ffi::CString; +use std::ptr; use crate::dc_tools::*; use crate::error::Error; use mmime::clist::*; use mmime::mailimf::types::*; use mmime::mailimf::types_helper::*; +use mmime::mailmime::content::*; use mmime::mailmime::disposition::*; use mmime::mailmime::types::*; use mmime::mailmime::types_helper::*; @@ -25,6 +27,100 @@ macro_rules! clist_append { }; } +/************************************** +* mime parsing API +**************************************/ + +pub fn get_ct_subtype(mime: *mut Mailmime) -> Option { + unsafe { + let ct: *mut mailmime_content = (*mime).mm_content_type; + + if !ct.is_null() && !(*ct).ct_subtype.is_null() { + println!("ct_subtype: {}", to_string((*ct).ct_subtype)); + Some(to_string((*ct).ct_subtype)) + } else { + None + } + } +} + +pub fn has_decryptable_data_(mime_data: *mut mailmime_data) -> bool { + /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */ + unsafe { + (*mime_data).dt_type == MAILMIME_DATA_TEXT as libc::c_int + && !(*mime_data).dt_data.dt_text.dt_data.is_null() + && (*mime_data).dt_data.dt_text.dt_length > 0 + } +} + +pub fn get_mime_transfer_encoding(mime: *mut Mailmime) -> Option { + unsafe { + let mm_mime_fields = (*mime).mm_mime_fields; + if !mm_mime_fields.is_null() { + for cur_data in (*(*mm_mime_fields).fld_list).into_iter() { + let field: *mut mailmime_field = cur_data as *mut _; + if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int + && !(*field).fld_data.fld_encoding.is_null() + { + return Some((*(*field).fld_data.fld_encoding).enc_type); + } + } + } + } + None +} + +pub fn decode_dt_data( + mime_data: *mut mailmime_data, + mime_transfer_encoding: libc::c_int, +) -> Result<(*mut libc::c_char, libc::size_t), Error> { + // Decode data according to mime_transfer_encoding + // returns Ok with a (decoded_data,decoded_data_bytes) pointer + // where the caller must make sure to free it. + // It may return Ok(ptr::null_mut(), 0) + + let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut(); + let decoded_data: *mut libc::c_char; + let mut decoded_data_bytes: libc::size_t = 0; + if mime_transfer_encoding == MAILMIME_MECHANISM_7BIT as libc::c_int + || mime_transfer_encoding == MAILMIME_MECHANISM_8BIT as libc::c_int + || mime_transfer_encoding == MAILMIME_MECHANISM_BINARY as libc::c_int + { + unsafe { + decoded_data = (*mime_data).dt_data.dt_text.dt_data as *mut _; + decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length; + } + ensure!( + !decoded_data.is_null() && decoded_data_bytes > 0, + "could not decode mime message" + ); + } else { + let mut current_index: libc::size_t = 0; + unsafe { + let r = 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 + { + bail!("mailmime_part_parse returned error or invalid data"); + } + decoded_data = transfer_decoding_buffer; + } + } + Ok((decoded_data, decoded_data_bytes)) +} + +/************************************** +* mime creation API +**************************************/ + pub fn add_filename_part( message: *mut Mailmime, basename: &str,