From 0346dd15d9e6051e2fb70748ab8961291cf5c148 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 14 Sep 2019 23:24:15 +0200 Subject: [PATCH 1/4] refactor(mimeparser): some more sanity --- src/dc_mimeparser.rs | 2263 +++++++++++++++++++---------------------- src/dc_receive_imf.rs | 192 ++-- src/e2ee.rs | 49 +- src/securejoin.rs | 23 +- 4 files changed, 1178 insertions(+), 1349 deletions(-) diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 02aed946d..2c01e5b69 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -25,27 +25,10 @@ use crate::param::*; use crate::stock::StockMessage; use crate::x::*; -/* Parse MIME body; this is the text part of an IMF, see https://tools.ietf.org/html/rfc5322 -dc_mimeparser_t has no deep dependencies to Context or to the database -(Context is used for logging only). */ -#[derive(Debug, Clone)] -#[repr(C)] -pub struct dc_mimepart_t { - pub type_0: Viewtype, - pub is_meta: bool, - pub int_mimetype: libc::c_int, - pub msg: Option, - pub msg_raw: Option, - pub bytes: libc::c_int, - pub param: Params, -} - -/* * - * @class dc_mimeparser_t - */ -#[allow(non_camel_case_types, missing_debug_implementations)] -pub struct dc_mimeparser_t<'a> { - pub parts: Vec, +#[derive(Debug)] +pub struct MimeParser<'a> { + pub context: &'a Context, + pub parts: Vec, pub mimeroot: *mut mailmime, pub header: HashMap, pub header_root: *mut mailimf_fields, @@ -55,454 +38,535 @@ pub struct dc_mimeparser_t<'a> { pub decrypting_failed: bool, pub e2ee_helper: E2eeHelper, pub is_forwarded: bool, - pub context: &'a Context, pub reports: Vec<*mut mailmime>, pub is_system_message: libc::c_int, pub location_kml: Option, pub message_kml: Option, } -pub fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t { - dc_mimeparser_t { - parts: Vec::new(), - mimeroot: std::ptr::null_mut(), - header: Default::default(), - header_root: std::ptr::null_mut(), - header_protected: std::ptr::null_mut(), - subject: None, - is_send_by_messenger: false, - decrypting_failed: false, - e2ee_helper: Default::default(), - is_forwarded: false, - context, - reports: Vec::new(), - is_system_message: 0, - location_kml: None, - message_kml: None, - } -} - -pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) { - dc_mimeparser_empty(mimeparser); -} - -unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) { - mimeparser.parts = vec![]; - mimeparser.header_root = ptr::null_mut(); - mimeparser.header.clear(); - if !mimeparser.header_protected.is_null() { - mailimf_fields_free(mimeparser.header_protected); - mimeparser.header_protected = ptr::null_mut() - } - mimeparser.is_send_by_messenger = false; - mimeparser.is_system_message = 0i32; - mimeparser.subject = None; - if !mimeparser.mimeroot.is_null() { - mailmime_free(mimeparser.mimeroot); - mimeparser.mimeroot = ptr::null_mut() - } - mimeparser.is_forwarded = false; - mimeparser.reports.clear(); - mimeparser.decrypting_failed = false; - mimeparser.e2ee_helper.thanks(); - - mimeparser.location_kml = None; - mimeparser.message_kml = None; -} - -const DC_MIMETYPE_AC_SETUP_FILE: i32 = 111; - -pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_mimeparser_t<'a> { - let r: libc::c_int; - let mut index: libc::size_t = 0; - let optional_field: *mut mailimf_optional_field; - let mut mimeparser = dc_mimeparser_new(context); - - r = mailmime_parse( - body.as_ptr() as *const libc::c_char, - body.len(), - &mut index, - &mut mimeparser.mimeroot, - ); - if r == MAILIMF_NO_ERROR as libc::c_int && !mimeparser.mimeroot.is_null() { - mimeparser - .e2ee_helper - .decrypt(mimeparser.context, mimeparser.mimeroot); - let mimeparser_ref = &mut mimeparser; - dc_mimeparser_parse_mime_recursive(mimeparser_ref, mimeparser_ref.mimeroot); - let field: *mut mailimf_field = dc_mimeparser_lookup_field(&mimeparser, "Subject"); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int { - let subj = (*(*field).fld_data.fld_subject).sbj_value; - - mimeparser.subject = as_opt_str(subj).map(dc_decode_header_words_safe); +impl<'a> MimeParser<'a> { + pub fn new(context: &'a Context) -> Self { + MimeParser { + parts: Vec::new(), + mimeroot: std::ptr::null_mut(), + header: Default::default(), + header_root: std::ptr::null_mut(), + header_protected: std::ptr::null_mut(), + subject: None, + is_send_by_messenger: false, + decrypting_failed: false, + e2ee_helper: Default::default(), + is_forwarded: false, + context, + reports: Vec::new(), + is_system_message: 0, + location_kml: None, + message_kml: None, } - if !dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Version").is_null() { - mimeparser.is_send_by_messenger = true - } - if !dc_mimeparser_lookup_field(&mut mimeparser, "Autocrypt-Setup-Message").is_null() { - let has_setup_file = mimeparser - .parts - .iter() - .any(|p| p.int_mimetype == DC_MIMETYPE_AC_SETUP_FILE); + } - if has_setup_file { - mimeparser.is_system_message = 6i32; + pub unsafe fn parse(&mut self, body: &[u8]) { + let mut index = 0; - // TODO: replace the following code with this - // once drain_filter stabilizes. - // - // See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter - // and https://github.com/rust-lang/rust/issues/43244 - // - // mimeparser - // .parts - // .drain_filter(|part| part.int_mimetype != 111) - // .for_each(|part| dc_mimepart_unref(part)); + let r = mailmime_parse( + body.as_ptr() as *const libc::c_char, + body.len(), + &mut index, + &mut self.mimeroot, + ); - let mut i = 0; - while i != mimeparser.parts.len() { - if mimeparser.parts[i].int_mimetype != 111 { - mimeparser.parts.remove(i); - } else { - i += 1; + if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() { + self.e2ee_helper.decrypt(self.context, self.mimeroot); + self.parse_mime_recursive(self.mimeroot); + + if let Some(field) = self.lookup_field("Subject") { + if (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int { + let subj = (*(*field).fld_data.fld_subject).sbj_value; + + self.subject = as_opt_str(subj).map(dc_decode_header_words_safe); + } + } + + if self.lookup_optional_field("Chat-Version").is_some() { + self.is_send_by_messenger = true + } + if self.lookup_field("Autocrypt-Setup-Message").is_some() { + let has_setup_file = self + .parts + .iter() + .any(|p| p.mimetype == DC_MIMETYPE_AC_SETUP_FILE); + + if has_setup_file { + self.is_system_message = 6i32; + + // TODO: replace the following code with this + // once drain_filter stabilizes. + // + // See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter + // and https://github.com/rust-lang/rust/issues/43244 + // + // mimeparser + // .parts + // .drain_filter(|part| part.int_mimetype != 111) + // .for_each(|part| dc_mimepart_unref(part)); + + let mut i = 0; + while i != self.parts.len() { + if self.parts[i].mimetype != 111 { + self.parts.remove(i); + } else { + i += 1; + } } } - } - } else { - optional_field = dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Content"); - if !optional_field.is_null() && !(*optional_field).fld_value.is_null() { - if strcmp( - (*optional_field).fld_value, - b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - mimeparser.is_system_message = 8i32 - } - } - } - if !dc_mimeparser_lookup_field(&mut mimeparser, "Chat-Group-Image").is_null() - && !mimeparser.parts.is_empty() - { - let textpart = &mimeparser.parts[0]; - if textpart.type_0 == Viewtype::Text { - if mimeparser.parts.len() >= 2 { - let imgpart = &mut mimeparser.parts[1]; - if imgpart.type_0 == Viewtype::Image { - imgpart.is_meta = true; - } - } - } - } - if mimeparser.is_send_by_messenger && mimeparser.parts.len() == 2 { - let need_drop = { - let textpart = &mimeparser.parts[0]; - let filepart = &mimeparser.parts[1]; - textpart.type_0 == Viewtype::Text - && (filepart.type_0 == Viewtype::Image - || filepart.type_0 == Viewtype::Gif - || filepart.type_0 == Viewtype::Audio - || filepart.type_0 == Viewtype::Voice - || filepart.type_0 == Viewtype::Video - || filepart.type_0 == Viewtype::File) - && !filepart.is_meta - }; - - if need_drop { - let mut filepart = mimeparser.parts.swap_remove(1); - - // insert new one - filepart.msg = mimeparser.parts[0].msg.as_ref().map(|s| s.to_string()); - - // forget the one we use now - mimeparser.parts[0].msg = None; - - // swap new with old - std::mem::replace(&mut mimeparser.parts[0], filepart); - } - } - if let Some(ref subject) = mimeparser.subject { - let mut prepend_subject: libc::c_int = 1i32; - if !mimeparser.decrypting_failed { - let colon = subject.find(':'); - if colon == Some(2) - || colon == Some(3) - || mimeparser.is_send_by_messenger - || subject.contains("Chat:") - { - prepend_subject = 0i32 - } - } - if 0 != prepend_subject { - let subj = if let Some(n) = subject.find('[') { - &subject[0..n] - } else { - subject - } - .trim(); - - if !subj.is_empty() { - let subj_c = CString::yolo(subj); - for part in mimeparser.parts.iter_mut() { - if part.type_0 == Viewtype::Text { - let msg_c = part.msg.as_ref().unwrap().strdup(); - let new_txt: *mut libc::c_char = dc_mprintf( - b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char, - subj_c.as_ptr(), - msg_c, - ); - free(msg_c.cast()); - part.msg = Some(to_string_lossy(new_txt)); - free(new_txt.cast()); - break; + } else { + if let Some(optional_field) = self.lookup_optional_field("Chat-Content") { + if !(*optional_field).fld_value.is_null() { + if strcmp( + (*optional_field).fld_value, + b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, + ) == 0i32 + { + self.is_system_message = 8i32; } } } } - } - if mimeparser.is_forwarded { - for part in mimeparser.parts.iter_mut() { - part.param.set_int(Param::Forwarded, 1); - } - } - if mimeparser.parts.len() == 1 { - if mimeparser.parts[0].type_0 == Viewtype::Audio { - if !dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Voice-Message").is_null() - { - let part_mut = &mut mimeparser.parts[0]; - part_mut.type_0 = Viewtype::Voice; - } - } - let part = &mimeparser.parts[0]; - if part.type_0 == Viewtype::Audio - || part.type_0 == Viewtype::Voice - || part.type_0 == Viewtype::Video - { - let field_0 = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Duration"); - if !field_0.is_null() { - let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value); - if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 { - let part_mut = &mut mimeparser.parts[0]; - part_mut.param.set_int(Param::Duration, duration_ms); + if self.lookup_field("Chat-Group-Image").is_some() && !self.parts.is_empty() { + let textpart = &self.parts[0]; + if textpart.typ == Viewtype::Text { + if self.parts.len() >= 2 { + let imgpart = &mut self.parts[1]; + if imgpart.typ == Viewtype::Image { + imgpart.is_meta = true; + } } } } - } - if !mimeparser.decrypting_failed { - let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field( - &mimeparser, - "Chat-Disposition-Notification-To", - ); - if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(&mut mimeparser).is_some() { - let mut mb_list: *mut mailimf_mailbox_list = ptr::null_mut(); - let mut index_0: libc::size_t = 0; - if mailimf_mailbox_list_parse( - (*dn_field).fld_value, - strlen((*dn_field).fld_value), - &mut index_0, - &mut mb_list, - ) == MAILIMF_NO_ERROR as libc::c_int - && !mb_list.is_null() - { - let dn_to_addr: *mut libc::c_char = mailimf_find_first_addr(mb_list); - if !dn_to_addr.is_null() { - let from_field: *mut mailimf_field = - dc_mimeparser_lookup_field(&mimeparser, "From"); - if !from_field.is_null() - && (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int - && !(*from_field).fld_data.fld_from.is_null() - { - let from_addr: *mut libc::c_char = mailimf_find_first_addr( - (*(*from_field).fld_data.fld_from).frm_mb_list, - ); - if !from_addr.is_null() { - if strcmp(from_addr, dn_to_addr) == 0i32 { - if let Some(part_4) = - dc_mimeparser_get_last_nonmeta(&mut mimeparser) - { - part_4.param.set_int(Param::WantsMdn, 1); - } - } - free(from_addr as *mut libc::c_void); + if self.is_send_by_messenger && self.parts.len() == 2 { + let need_drop = { + let textpart = &self.parts[0]; + let filepart = &self.parts[1]; + textpart.typ == Viewtype::Text + && (filepart.typ == Viewtype::Image + || filepart.typ == Viewtype::Gif + || filepart.typ == Viewtype::Audio + || filepart.typ == Viewtype::Voice + || filepart.typ == Viewtype::Video + || filepart.typ == Viewtype::File) + && !filepart.is_meta + }; + + if need_drop { + let mut filepart = self.parts.swap_remove(1); + + // insert new one + filepart.msg = self.parts[0].msg.as_ref().map(|s| s.to_string()); + + // forget the one we use now + self.parts[0].msg = None; + + // swap new with old + std::mem::replace(&mut self.parts[0], filepart); + } + } + if let Some(ref subject) = self.subject { + let mut prepend_subject: libc::c_int = 1i32; + if !self.decrypting_failed { + let colon = subject.find(':'); + if colon == Some(2) + || colon == Some(3) + || self.is_send_by_messenger + || subject.contains("Chat:") + { + prepend_subject = 0i32 + } + } + if 0 != prepend_subject { + let subj = if let Some(n) = subject.find('[') { + &subject[0..n] + } else { + subject + } + .trim(); + + if !subj.is_empty() { + let subj_c = CString::yolo(subj); + for part in self.parts.iter_mut() { + if part.typ == Viewtype::Text { + let msg_c = part.msg.as_ref().unwrap().strdup(); + let new_txt: *mut libc::c_char = dc_mprintf( + b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char, + subj_c.as_ptr(), + msg_c, + ); + free(msg_c.cast()); + part.msg = Some(to_string_lossy(new_txt)); + free(new_txt.cast()); + break; } } - free(dn_to_addr as *mut libc::c_void); } - mailimf_mailbox_list_free(mb_list); + } + } + if self.is_forwarded { + for part in self.parts.iter_mut() { + part.param.set_int(Param::Forwarded, 1); + } + } + if self.parts.len() == 1 { + if self.parts[0].typ == Viewtype::Audio { + if self.lookup_optional_field("Chat-Voice-Message").is_some() { + let part_mut = &mut self.parts[0]; + part_mut.typ = Viewtype::Voice; + } + } + let part = &self.parts[0]; + if part.typ == Viewtype::Audio + || part.typ == Viewtype::Voice + || part.typ == Viewtype::Video + { + if let Some(field_0) = self.lookup_optional_field("Chat-Duration") { + let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value); + if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 { + let part_mut = &mut self.parts[0]; + part_mut.param.set_int(Param::Duration, duration_ms); + } + } + } + } + if !self.decrypting_failed { + if let Some(dn_field) = + self.lookup_optional_field("Chat-Disposition-Notification-To") + { + if self.get_last_nonmeta().is_some() { + let mut mb_list: *mut mailimf_mailbox_list = ptr::null_mut(); + let mut index_0 = 0; + if mailimf_mailbox_list_parse( + (*dn_field).fld_value, + strlen((*dn_field).fld_value), + &mut index_0, + &mut mb_list, + ) == 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(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( + (*(*from_field).fld_data.fld_from).frm_mb_list, + ); + if let Some(from_addr) = from_addr { + if from_addr == dn_to_addr { + if let Some(part_4) = self.get_last_nonmeta() { + part_4.param.set_int(Param::WantsMdn, 1); + } + } + } + } + } + } + mailimf_mailbox_list_free(mb_list); + } + } } } } - } - /* Cleanup - and try to create at least an empty part if there are no parts yet */ - if dc_mimeparser_get_last_nonmeta(&mut mimeparser).is_none() && mimeparser.reports.is_empty() { - let mut part_5 = dc_mimepart_new(); - part_5.type_0 = Viewtype::Text; - part_5.msg = Some("".into()); + /* Cleanup - and try to create at least an empty part if there are no parts yet */ + if self.get_last_nonmeta().is_none() && self.reports.is_empty() { + let mut part_5 = Part::default(); + part_5.typ = Viewtype::Text; + part_5.msg = Some("".into()); - if let Some(ref subject) = mimeparser.subject { - if !mimeparser.is_send_by_messenger { - part_5.msg = Some(subject.to_string()) + if let Some(ref subject) = self.subject { + if !self.is_send_by_messenger { + part_5.msg = Some(subject.to_string()) + } + } + self.parts.push(part_5); + } + } + + pub fn get_last_nonmeta(&mut self) -> Option<&mut Part> { + self.parts.iter_mut().rev().find(|part| !part.is_meta) + } + + /* the following functions can be used only after a call to dc_mimeparser_parse() */ + + pub fn lookup_field(&self, field_name: &str) -> Option<*mut mailimf_field> { + match self.header.get(field_name) { + Some(v) => { + if v.is_null() { + None + } else { + Some(*v) + } + } + None => None, + } + } + + pub fn lookup_optional_field(&self, field_name: &str) -> Option<*mut mailimf_optional_field> { + if let Some(field) = self.lookup_field(field_name) { + if unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { + let val = unsafe { (*field).fld_data.fld_optional_field }; + if val.is_null() { + return None; + } else { + return Some(val); + } } } - mimeparser.parts.push(part_5); - }; - mimeparser -} -/******************************************************************************* - * a MIME part - ******************************************************************************/ -unsafe fn dc_mimepart_new() -> dc_mimepart_t { - dc_mimepart_t { - type_0: Viewtype::Unknown, - is_meta: false, - int_mimetype: 0, - msg: None, - msg_raw: None, - bytes: 0, - param: Params::new(), + None } -} -pub fn dc_mimeparser_get_last_nonmeta<'a>( - mimeparser: &'a mut dc_mimeparser_t, -) -> Option<&'a mut dc_mimepart_t> { - mimeparser.parts.iter_mut().rev().find(|part| !part.is_meta) -} - -/*the result must be freed*/ -pub unsafe fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> *mut libc::c_char { - if mb_list.is_null() { - return ptr::null_mut(); - } - let mut cur: *mut clistiter = (*(*mb_list).mb_list).first; - while !cur.is_null() { - let mb: *mut mailimf_mailbox = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailimf_mailbox; - if !mb.is_null() && !(*mb).mb_addr_spec.is_null() { - return addr_normalize(as_str((*mb).mb_addr_spec)).strdup(); + unsafe fn parse_mime_recursive(&mut self, mime: *mut mailmime) -> libc::c_int { + if mime.is_null() { + return 0; } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } - } + let mut any_part_added = 0; + let mut cur: *mut clistiter; - ptr::null_mut() -} - -/* the following functions can be used only after a call to dc_mimeparser_parse() */ - -pub fn dc_mimeparser_lookup_field( - mimeparser: &dc_mimeparser_t, - field_name: &str, -) -> *mut mailimf_field { - mimeparser - .header - .get(field_name) - .map(|v| *v) - .unwrap_or_else(|| std::ptr::null_mut()) -} - -pub unsafe fn dc_mimeparser_lookup_optional_field( - mimeparser: &dc_mimeparser_t, - field_name: &str, -) -> *mut mailimf_optional_field { - let field = mimeparser - .header - .get(field_name) - .map(|v| *v) - .unwrap_or_else(|| std::ptr::null_mut()); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { - return (*field).fld_data.fld_optional_field; - } - - ptr::null_mut() -} - -unsafe fn dc_mimeparser_parse_mime_recursive( - mimeparser: &mut dc_mimeparser_t, - mime: *mut mailmime, -) -> libc::c_int { - let mut any_part_added: libc::c_int = 0i32; - let mut cur: *mut clistiter; - if mime.is_null() { - return 0i32; - } - if !mailmime_find_ct_parameter( - mime, - b"protected-headers\x00" as *const u8 as *const libc::c_char, - ) - .is_null() - { - if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int - && (*(*(*mime).mm_content_type).ct_type).tp_type - == MAILMIME_TYPE_DISCRETE_TYPE as libc::c_int - && (*(*(*(*mime).mm_content_type).ct_type) - .tp_data - .tp_discrete_type) - .dt_type - == MAILMIME_DISCRETE_TYPE_TEXT as libc::c_int - && !(*(*mime).mm_content_type).ct_subtype.is_null() - && strcmp( - (*(*mime).mm_content_type).ct_subtype, - b"rfc822-headers\x00" as *const u8 as *const libc::c_char, - ) == 0i32 + if !mailmime_find_ct_parameter( + mime, + b"protected-headers\x00" as *const u8 as *const libc::c_char, + ) + .is_null() { - info!( - mimeparser.context, - "Protected headers found in text/rfc822-headers attachment: Will be ignored.", - ); - return 0i32; - } - if mimeparser.header_protected.is_null() { - let mut dummy: libc::size_t = 0; - if mailimf_envelope_and_optional_fields_parse( - (*mime).mm_mime_start, - (*mime).mm_length, - &mut dummy, - &mut mimeparser.header_protected, - ) != MAILIMF_NO_ERROR as libc::c_int - || mimeparser.header_protected.is_null() + if (*mime).mm_type == MAILMIME_SINGLE as libc::c_int + && (*(*(*mime).mm_content_type).ct_type).tp_type + == MAILMIME_TYPE_DISCRETE_TYPE as libc::c_int + && (*(*(*(*mime).mm_content_type).ct_type) + .tp_data + .tp_discrete_type) + .dt_type + == MAILMIME_DISCRETE_TYPE_TEXT as libc::c_int + && !(*(*mime).mm_content_type).ct_subtype.is_null() + && strcmp( + (*(*mime).mm_content_type).ct_subtype, + b"rfc822-headers\x00" as *const u8 as *const libc::c_char, + ) == 0i32 { - warn!(mimeparser.context, "Protected headers parsing error.",); - } else { - hash_header( - &mut mimeparser.header, - mimeparser.header_protected, - mimeparser.context, + info!( + self.context, + "Protected headers found in text/rfc822-headers attachment: Will be ignored.", ); + return 0i32; } - } else { - info!( - mimeparser.context, + if self.header_protected.is_null() { + let mut dummy = 0; + if mailimf_envelope_and_optional_fields_parse( + (*mime).mm_mime_start, + (*mime).mm_length, + &mut dummy, + &mut self.header_protected, + ) != MAILIMF_NO_ERROR as libc::c_int + || self.header_protected.is_null() + { + warn!(self.context, "Protected headers parsing error.",); + } else { + hash_header(&mut self.header, self.header_protected); + } + } else { + info!( + self.context, "Protected headers found in MIME header: Will be ignored as we already found an outer one." ); + } } - } - match (*mime).mm_type { - // TODO match on enums /rtn - 1 => any_part_added = dc_mimeparser_add_single_part_if_known(mimeparser, mime), - 2 => { - match mailmime_get_mime_type(mime, ptr::null_mut(), ptr::null_mut()) { - 10 => { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - let childmime: *mut mailmime = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime; - if mailmime_get_mime_type(childmime, ptr::null_mut(), ptr::null_mut()) - == 30i32 - { - any_part_added = - dc_mimeparser_parse_mime_recursive(mimeparser, childmime); - break; - } else { + match (*mime).mm_type { + // TODO match on enums /rtn + 1 => any_part_added = self.add_single_part_if_known(mime), + 2 => { + match mailmime_get_mime_type(mime, ptr::null_mut(), ptr::null_mut()) { + 10 => { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + let childmime: *mut mailmime = (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) + as *mut mailmime; + if mailmime_get_mime_type(childmime, ptr::null_mut(), ptr::null_mut()) + == 30i32 + { + any_part_added = self.parse_mime_recursive(childmime); + break; + } else { + cur = if !cur.is_null() { + (*cur).next + } else { + ptr::null_mut() + } + } + } + if 0 == any_part_added { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + let childmime_0: *mut mailmime = (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) + as *mut mailmime; + if mailmime_get_mime_type( + childmime_0, + ptr::null_mut(), + ptr::null_mut(), + ) == 60i32 + { + any_part_added = self.parse_mime_recursive(childmime_0); + break; + } else { + cur = if !cur.is_null() { + (*cur).next + } else { + ptr::null_mut() + } + } + } + } + if 0 == any_part_added { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + if 0 != self.parse_mime_recursive( + (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) as *mut mailmime, + ) { + any_part_added = 1i32; + /* out of for() */ + break; + } else { + cur = if !cur.is_null() { + (*cur).next + } else { + ptr::null_mut() + } + } + } + } + } + 20 => { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + if !cur.is_null() { + any_part_added = self.parse_mime_recursive( + (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) as *mut mailmime, + ) + } + } + 40 => { + let mut part = Part::default(); + part.typ = Viewtype::Text; + let msg_body = self.context.stock_str(StockMessage::CantDecryptMsgBody); + + let txt = format!("[{}]", msg_body); + part.msg_raw = Some(txt.clone()); + part.msg = Some(txt); + + self.parts.push(part); + any_part_added = 1i32; + self.decrypting_failed = true; + } + 46 => { + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + if !cur.is_null() { + any_part_added = self.parse_mime_recursive( + (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) as *mut mailmime, + ) + } + } + 45 => { + if (*(*mime).mm_data.mm_multipart.mm_mp_list).count >= 2i32 { + let report_type = mailmime_find_ct_parameter( + mime, + b"report-type\x00" as *const u8 as *const libc::c_char, + ); + if !report_type.is_null() + && !(*report_type).pa_value.is_null() + && strcmp( + (*report_type).pa_value, + b"disposition-notification\x00" as *const u8 + as *const libc::c_char, + ) == 0i32 + { + self.reports.push(mime); + } else { + any_part_added = self.parse_mime_recursive( + (if !(*(*mime).mm_data.mm_multipart.mm_mp_list).first.is_null() + { + (*(*(*mime).mm_data.mm_multipart.mm_mp_list).first).data + } else { + ptr::null_mut() + }) as *mut mailmime, + ) + } + } + } + _ => { + let mut skip_part = ptr::null_mut(); + let mut html_part = ptr::null_mut(); + let mut plain_cnt = 0i32; + let mut html_cnt = 0i32; + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + let childmime_1 = (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) as *mut mailmime; + if mailmime_get_mime_type(childmime_1, ptr::null_mut(), ptr::null_mut()) + == 60i32 + { + plain_cnt += 1 + } else if mailmime_get_mime_type( + childmime_1, + ptr::null_mut(), + ptr::null_mut(), + ) == 70i32 + { + html_part = childmime_1; + html_cnt += 1 + } + cur = if !cur.is_null() { + (*cur).next + } else { + ptr::null_mut() + } + } + if plain_cnt == 1i32 && html_cnt == 1i32 { + warn!( + self.context, + "HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted." + ); + skip_part = html_part + } + cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + while !cur.is_null() { + let childmime_2 = (if !cur.is_null() { + (*cur).data + } else { + ptr::null_mut() + }) as *mut mailmime; + if childmime_2 != skip_part { + if 0 != self.parse_mime_recursive(childmime_2) { + any_part_added = 1i32 + } + } cur = if !cur.is_null() { (*cur).next } else { @@ -510,262 +574,511 @@ unsafe fn dc_mimeparser_parse_mime_recursive( } } } - if 0 == any_part_added { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - let childmime_0: *mut mailmime = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) - as *mut mailmime; - if mailmime_get_mime_type(childmime_0, ptr::null_mut(), ptr::null_mut()) - == 60i32 - { - any_part_added = - dc_mimeparser_parse_mime_recursive(mimeparser, childmime_0); - break; - } else { - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } - } - } - } - if 0 == any_part_added { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - if 0 != dc_mimeparser_parse_mime_recursive( - mimeparser, - (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime, - ) { - any_part_added = 1i32; - /* out of for() */ - break; - } else { - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } - } - } - } - } - 20 => { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - if !cur.is_null() { - any_part_added = dc_mimeparser_parse_mime_recursive( - mimeparser, - (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime, - ) - } - } - 40 => { - let mut part = dc_mimepart_new(); - part.type_0 = Viewtype::Text; - let msg_body = mimeparser - .context - .stock_str(StockMessage::CantDecryptMsgBody); - - let txt = format!("[{}]", msg_body); - part.msg_raw = Some(txt.clone()); - part.msg = Some(txt); - - mimeparser.parts.push(part); - any_part_added = 1i32; - mimeparser.decrypting_failed = true; - } - 46 => { - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - if !cur.is_null() { - any_part_added = dc_mimeparser_parse_mime_recursive( - mimeparser, - (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime, - ) - } - } - 45 => { - if (*(*mime).mm_data.mm_multipart.mm_mp_list).count >= 2i32 { - let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter( - mime, - b"report-type\x00" as *const u8 as *const libc::c_char, - ); - if !report_type.is_null() - && !(*report_type).pa_value.is_null() - && strcmp( - (*report_type).pa_value, - b"disposition-notification\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - mimeparser.reports.push(mime); - } else { - any_part_added = dc_mimeparser_parse_mime_recursive( - mimeparser, - (if !(*(*mime).mm_data.mm_multipart.mm_mp_list).first.is_null() { - (*(*(*mime).mm_data.mm_multipart.mm_mp_list).first).data - } else { - ptr::null_mut() - }) as *mut mailmime, - ) - } - } - } - _ => { - let mut skip_part: *mut mailmime = ptr::null_mut(); - let mut html_part: *mut mailmime = ptr::null_mut(); - let mut plain_cnt: libc::c_int = 0i32; - let mut html_cnt: libc::c_int = 0i32; - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - let childmime_1: *mut mailmime = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) - as *mut mailmime; - if mailmime_get_mime_type(childmime_1, ptr::null_mut(), ptr::null_mut()) - == 60i32 - { - plain_cnt += 1 - } else if mailmime_get_mime_type( - childmime_1, - ptr::null_mut(), - ptr::null_mut(), - ) == 70i32 - { - html_part = childmime_1; - html_cnt += 1 - } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } - } - if plain_cnt == 1i32 && html_cnt == 1i32 { - warn!( - mimeparser.context, - "HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted." - ); - skip_part = html_part - } - cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - while !cur.is_null() { - let childmime_2: *mut mailmime = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) - as *mut mailmime; - if childmime_2 != skip_part { - if 0 != dc_mimeparser_parse_mime_recursive(mimeparser, childmime_2) { - any_part_added = 1i32 - } - } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } - } } } - } - 3 => { - if mimeparser.header_root.is_null() { - mimeparser.header_root = (*mime).mm_data.mm_message.mm_fields; - hash_header( - &mut mimeparser.header, - mimeparser.header_root, - mimeparser.context, - ); - } - if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() { - any_part_added = dc_mimeparser_parse_mime_recursive( - mimeparser, - (*mime).mm_data.mm_message.mm_msg_mime, - ) - } - } - _ => {} - } - - any_part_added -} - -unsafe fn hash_header( - out: &mut HashMap, - in_0: *const mailimf_fields, - _context: &Context, -) { - if in_0.is_null() { - return; - } - let mut cur1: *mut clistiter = (*(*in_0).fld_list).first; - while !cur1.is_null() { - let field: *mut mailimf_field = (if !cur1.is_null() { - (*cur1).data - } else { - ptr::null_mut() - }) as *mut mailimf_field; - let mut key: *const libc::c_char = ptr::null(); - // TODO match on enums /rtn - match (*field).fld_type { - 1 => key = b"Return-Path\x00" as *const u8 as *const libc::c_char, - 9 => key = b"Date\x00" as *const u8 as *const libc::c_char, - 10 => key = b"From\x00" as *const u8 as *const libc::c_char, - 11 => key = b"Sender\x00" as *const u8 as *const libc::c_char, - 12 => key = b"Reply-To\x00" as *const u8 as *const libc::c_char, - 13 => key = b"To\x00" as *const u8 as *const libc::c_char, - 14 => key = b"Cc\x00" as *const u8 as *const libc::c_char, - 15 => key = b"Bcc\x00" as *const u8 as *const libc::c_char, - 16 => key = b"Message-ID\x00" as *const u8 as *const libc::c_char, - 17 => key = b"In-Reply-To\x00" as *const u8 as *const libc::c_char, - 18 => key = b"References\x00" as *const u8 as *const libc::c_char, - 19 => key = b"Subject\x00" as *const u8 as *const libc::c_char, - 22 => { - // MAILIMF_FIELD_OPTIONAL_FIELD - let optional_field: *const mailimf_optional_field = - (*field).fld_data.fld_optional_field; - if !optional_field.is_null() { - key = (*optional_field).fld_name + 3 => { + if self.header_root.is_null() { + self.header_root = (*mime).mm_data.mm_message.mm_fields; + hash_header(&mut self.header, self.header_root); + } + if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() { + any_part_added = + self.parse_mime_recursive((*mime).mm_data.mm_message.mm_msg_mime); } } _ => {} } - if !key.is_null() { + + any_part_added + } + + unsafe fn add_single_part_if_known(&mut self, mime: *mut mailmime) -> libc::c_int { + let mut ok_to_continue = true; + let old_part_count = self.parts.len(); + let mime_type: libc::c_int; + let mime_data: *mut mailmime_data; + let file_suffix: *mut libc::c_char = ptr::null_mut(); + let mut desired_filename: *mut libc::c_char = ptr::null_mut(); + let mut msg_type = Viewtype::Unknown; + let mut raw_mime: *mut libc::c_char = ptr::null_mut(); + /* mmap_string_unref()'d if set */ + let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut(); + /* must not be free()'d */ + let mut decoded_data: *const libc::c_char = ptr::null(); + let mut decoded_data_bytes = 0; + let mut simplifier: Option = None; + if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) { + mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime); + 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) + { + /* regard `Content-Transfer-Encoding:` */ + if mailmime_transfer_decode( + mime, + &mut decoded_data, + &mut decoded_data_bytes, + &mut transfer_decoding_buffer, + ) { + /* no always error - but no data */ + match mime_type { + 60 | 70 => { + if simplifier.is_none() { + simplifier = Some(Simplify::new()); + } + /* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */ + let charset = mailmime_content_charset_get((*mime).mm_content_type); + if !charset.is_null() + && strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char) + != 0i32 + && strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char) + != 0i32 + { + if let Some(encoding) = Charset::for_label( + CStr::from_ptr(charset).to_str().unwrap().as_bytes(), + ) { + let data = std::slice::from_raw_parts( + decoded_data as *const u8, + decoded_data_bytes, + ); + + let (res, _, _) = encoding.decode(data); + if res.is_empty() { + /* no error - but nothing to add */ + ok_to_continue = false; + } else { + let b = res.as_bytes(); + decoded_data = b.as_ptr() as *const libc::c_char; + decoded_data_bytes = b.len(); + std::mem::forget(res); + } + } else { + warn!( + self.context, + "Cannot convert {} bytes from \"{}\" to \"utf-8\".", + decoded_data_bytes as libc::c_int, + as_str(charset), + ); + } + } + if ok_to_continue { + /* check header directly as is_send_by_messenger is not yet set up */ + let is_msgrmsg = + self.lookup_optional_field("Chat-Version").is_some(); + + let simplified_txt = if decoded_data_bytes <= 0 + || decoded_data.is_null() + { + "".into() + } else { + let input_c = strndup(decoded_data, decoded_data_bytes as _); + let input = to_string_lossy(input_c); + let is_html = mime_type == 70; + free(input_c as *mut _); + + simplifier.unwrap().simplify(&input, is_html, is_msgrmsg) + }; + if !simplified_txt.is_empty() { + let mut part = Part::default(); + part.typ = Viewtype::Text; + part.mimetype = mime_type; + part.msg = Some(simplified_txt); + part.msg_raw = { + let raw_c = strndup( + decoded_data, + decoded_data_bytes as libc::c_ulong, + ); + let raw = to_string_lossy(raw_c); + free(raw_c.cast()); + Some(raw) + }; + self.do_add_single_part(part); + } + + if simplifier.unwrap().is_forwarded { + self.is_forwarded = true; + } + } + } + 80 | 90 | 100 | 110 | 111 => { + /* try to get file name from + `Content-Disposition: ... filename*=...` + or `Content-Disposition: ... filename*0*=... filename*1*=... filename*2*=...` + or `Content-Disposition: ... filename=...` */ + let mut filename_parts = String::new(); + + for cur1 in (*(*(*mime).mm_mime_fields).fld_list).into_iter() { + let field = cur1 as *mut mailmime_field; + if !field.is_null() + && (*field).fld_type + == MAILMIME_FIELD_DISPOSITION as libc::c_int + && !(*field).fld_data.fld_disposition.is_null() + { + let file_disposition: *mut mailmime_disposition = + (*field).fld_data.fld_disposition; + if !file_disposition.is_null() { + for cur2 in (*(*file_disposition).dsp_parms).into_iter() { + let dsp_param = cur2 as *mut mailmime_disposition_parm; + if !dsp_param.is_null() { + if (*dsp_param).pa_type + == MAILMIME_DISPOSITION_PARM_PARAMETER + as libc::c_int + && !(*dsp_param).pa_data.pa_parameter.is_null() + && !(*(*dsp_param).pa_data.pa_parameter) + .pa_name + .is_null() + && strncmp( + (*(*dsp_param).pa_data.pa_parameter) + .pa_name, + b"filename*\x00" as *const u8 + as *const libc::c_char, + 9, + ) == 0i32 + { + filename_parts += &to_string_lossy( + (*(*dsp_param).pa_data.pa_parameter) + .pa_value, + ); + } else if (*dsp_param).pa_type + == MAILMIME_DISPOSITION_PARM_FILENAME + as libc::c_int + { + desired_filename = dc_decode_header_words( + (*dsp_param).pa_data.pa_filename, + ) + } + } + } + } + break; + } + } + if !filename_parts.is_empty() { + free(desired_filename as *mut libc::c_void); + desired_filename = + dc_decode_ext_header(filename_parts.as_bytes()).strdup(); + } + if desired_filename.is_null() { + let param = mailmime_find_ct_parameter( + mime, + b"name\x00" as *const u8 as *const libc::c_char, + ); + if !param.is_null() + && !(*param).pa_value.is_null() + && 0 != *(*param).pa_value.offset(0isize) as libc::c_int + { + desired_filename = dc_strdup((*param).pa_value) + } + } + /* if there is still no filename, guess one */ + if desired_filename.is_null() { + if !(*mime).mm_content_type.is_null() + && !(*(*mime).mm_content_type).ct_subtype.is_null() + { + desired_filename = dc_mprintf( + b"file.%s\x00" as *const u8 as *const libc::c_char, + (*(*mime).mm_content_type).ct_subtype, + ); + } else { + ok_to_continue = false; + } + } + if ok_to_continue { + if strncmp( + desired_filename, + b"location\x00" as *const u8 as *const libc::c_char, + 8, + ) == 0i32 + && strncmp( + desired_filename + .offset(strlen(desired_filename) as isize) + .offset(-4isize), + b".kml\x00" as *const u8 as *const libc::c_char, + 4, + ) == 0i32 + { + if !decoded_data.is_null() && decoded_data_bytes > 0 { + let d = dc_null_terminate( + decoded_data, + decoded_data_bytes as i32, + ); + self.location_kml = + location::Kml::parse(self.context, as_str(d)).ok(); + free(d.cast()); + } + } else if strncmp( + desired_filename, + b"message\x00" as *const u8 as *const libc::c_char, + 7, + ) == 0i32 + && strncmp( + desired_filename + .offset(strlen(desired_filename) as isize) + .offset(-4isize), + b".kml\x00" as *const u8 as *const libc::c_char, + 4, + ) == 0i32 + { + if !decoded_data.is_null() && decoded_data_bytes > 0 { + let d = dc_null_terminate( + decoded_data, + decoded_data_bytes as i32, + ); + self.message_kml = + location::Kml::parse(self.context, as_str(d)).ok(); + free(d.cast()); + } + } else { + dc_replace_bad_utf8_chars(desired_filename); + self.do_add_single_file_part( + msg_type, + mime_type, + as_str(raw_mime), + decoded_data, + decoded_data_bytes, + desired_filename, + ); + } + } + } + _ => {} + } + } + } + } + /* add object? (we do not add all objects, eg. signatures etc. are ignored) */ + if !transfer_decoding_buffer.is_null() { + mmap_string_unref(transfer_decoding_buffer); + } + free(file_suffix as *mut libc::c_void); + free(desired_filename as *mut libc::c_void); + free(raw_mime as *mut libc::c_void); + (self.parts.len() > old_part_count) as libc::c_int + } + + unsafe fn do_add_single_file_part( + &mut self, + msg_type: Viewtype, + mime_type: libc::c_int, + raw_mime: &str, + decoded_data: *const libc::c_char, + decoded_data_bytes: libc::size_t, + desired_filename: *const libc::c_char, + ) { + /* create a free file name to use */ + let path_n_filename = dc_get_fine_pathNfilename( + self.context, + b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, + desired_filename, + ); + if !path_n_filename.is_null() { + /* copy data to file */ + if dc_write_file( + self.context, + path_n_filename, + decoded_data as *const libc::c_void, + decoded_data_bytes, + ) != 0 + { + let mut part = Part::default(); + part.typ = msg_type; + part.mimetype = mime_type; + part.bytes = decoded_data_bytes as libc::c_int; + part.param.set(Param::File, as_str(path_n_filename)); + part.param.set(Param::MimeType, raw_mime); + + if mime_type == 80 { + assert!(!decoded_data.is_null(), "invalid image data"); + let data = std::slice::from_raw_parts( + decoded_data as *const u8, + decoded_data_bytes as usize, + ); + + if let Ok((width, height)) = dc_get_filemeta(data) { + part.param.set_int(Param::Width, width as i32); + part.param.set_int(Param::Height, height as i32); + } + } + self.do_add_single_part(part); + } + } + + free(path_n_filename as *mut libc::c_void); + } + + fn do_add_single_part(&mut self, mut part: Part) { + if self.e2ee_helper.encrypted && self.e2ee_helper.signatures.len() > 0 { + part.param.set_int(Param::GuranteeE2ee, 1); + } else if self.e2ee_helper.encrypted { + part.param.set_int(Param::ErroneousE2ee, 0x2); + } + self.parts.push(part); + } + + pub unsafe fn is_mailinglist_message(&self) -> bool { + if self.lookup_field("List-Id").is_some() { + return true; + } + + if let Some(precedence) = self.lookup_optional_field("Precedence") { + if strcasecmp( + (*precedence).fld_value, + b"list\x00" as *const u8 as *const libc::c_char, + ) == 0i32 + || strcasecmp( + (*precedence).fld_value, + b"bulk\x00" as *const u8 as *const libc::c_char, + ) == 0i32 + { + return true; + } + } + + false + } + + pub unsafe fn sender_equals_recipient(&self) -> bool { + if self.header_root.is_null() { + return false; + } + + let mut sender_equals_recipient = false; + 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); + if !(fld.is_null() + || { + fld_from = (*fld).fld_data.fld_from; + fld_from.is_null() + } + || (*fld_from).frm_mb_list.is_null() + || (*(*fld_from).frm_mb_list).mb_list.is_null() + || (*(*(*fld_from).frm_mb_list).mb_list).count != 1i32) + { + let mb = (if !(*(*(*fld_from).frm_mb_list).mb_list).first.is_null() { + (*(*(*(*fld_from).frm_mb_list).mb_list).first).data + } else { + ptr::null_mut() + }) as *mut mailimf_mailbox; + + if !mb.is_null() { + let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); + let recipients = mailimf_get_recipients(self.header_root); + if recipients.len() == 1 { + if recipients.contains(from_addr_norm) { + sender_equals_recipient = true; + } + } + } + } + + sender_equals_recipient + } + + pub fn repl_msg_by_error(&mut self, error_msg: impl AsRef) { + if self.parts.is_empty() { + return; + } + + let part = &mut self.parts[0]; + part.typ = Viewtype::Text; + part.msg = Some(format!("[{}]", error_msg.as_ref())); + self.parts.truncate(1); + + assert_eq!(self.parts.len(), 1); + } +} + +impl<'a> Drop for MimeParser<'a> { + fn drop(&mut self) { + if !self.header_protected.is_null() { + unsafe { mailimf_fields_free(self.header_protected) }; + } + if !self.mimeroot.is_null() { + unsafe { mailmime_free(self.mimeroot) }; + } + unsafe { self.e2ee_helper.thanks() }; + } +} + +const DC_MIMETYPE_AC_SETUP_FILE: i32 = 111; + +#[derive(Default, Debug, Clone)] +pub struct Part { + pub typ: Viewtype, + pub is_meta: bool, + pub mimetype: i32, + pub msg: Option, + pub msg_raw: Option, + pub bytes: i32, + 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; + } + + for cur in (*(*in_0).fld_list).into_iter() { + let field = cur as *mut mailimf_field; + // TODO match on enums /rtn + + let key = match (*field).fld_type { + 1 => Some("Return-Path".to_string()), + 9 => Some("Date".to_string()), + 10 => Some("From".to_string()), + 11 => Some("Sender".to_string()), + 12 => Some("Reply-To".to_string()), + 13 => Some("To".to_string()), + 14 => Some("Cc".to_string()), + 15 => Some("Bcc".to_string()), + 16 => Some("Message-ID".to_string()), + 17 => Some("In-Reply-To".to_string()), + 18 => Some("References".to_string()), + 19 => Some("Subject".to_string()), + 22 => { + // MAILIMF_FIELD_OPTIONAL_FIELD + let optional_field = (*field).fld_data.fld_optional_field; + if !optional_field.is_null() { + Some(to_string_lossy((*optional_field).fld_name)) + } else { + None + } + } + _ => None, + }; + if let Some(key) = key { // XXX the optional field sometimes contains invalid UTF8 // which should not happen (according to the mime standard). // This might point to a bug in our mime parsing/processing // logic. As mmime/dc_mimeparser is scheduled fore replacement // anyway we just use a lossy conversion. - let key_r = &to_string_lossy(key); - if !out.contains_key(key_r) || // key already exists, only overwrite known types (protected headers) - (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key_r.starts_with("Chat-") + + if !out.contains_key(&key) || // key already exists, only overwrite known types (protected headers) + (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key.starts_with("Chat-") { - out.insert(key_r.to_string(), field); + out.insert(key, field); } } - cur1 = if !cur1.is_null() { - (*cur1).next - } else { - ptr::null_mut() - } } } @@ -774,20 +1087,22 @@ unsafe fn mailmime_get_mime_type( mut msg_type: *mut Viewtype, raw_mime: *mut *mut libc::c_char, ) -> libc::c_int { - let c: *mut mailmime_content = (*mime).mm_content_type; + let c = (*mime).mm_content_type; let mut dummy = Viewtype::Unknown; + if msg_type.is_null() { msg_type = &mut dummy } *msg_type = Viewtype::Unknown; if c.is_null() || (*c).ct_type.is_null() { - return 0i32; + return 0; } + // TODO match on enums /rtn match (*(*c).ct_type).tp_type { 1 => match (*(*(*c).ct_type).tp_data.tp_discrete_type).dt_type { 1 => { - if !(0 != mailmime_is_attachment_disposition(mime)) { + if !mailmime_is_attachment_disposition(mime) { if strcmp( (*c).ct_subtype, b"plain\x00" as *const u8 as *const libc::c_char, @@ -933,42 +1248,34 @@ unsafe fn mailmime_get_mime_type( 0 } -fn reconcat_mime(type_0: Option<&str>, subtype: Option<&str>) -> String { - let type_0 = type_0.unwrap_or("application"); +fn reconcat_mime(typ: Option<&str>, subtype: Option<&str>) -> String { + let typ = typ.unwrap_or("application"); let subtype = subtype.unwrap_or("octet-stream"); - format!("{}/{}", type_0, subtype) + format!("{}/{}", typ, subtype) } -unsafe fn mailmime_is_attachment_disposition(mime: *mut mailmime) -> libc::c_int { - if !(*mime).mm_mime_fields.is_null() { - let mut cur: *mut clistiter = (*(*(*mime).mm_mime_fields).fld_list).first; - while !cur.is_null() { - let field: *mut mailmime_field = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime_field; - if !field.is_null() - && (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int - && !(*field).fld_data.fld_disposition.is_null() +unsafe fn mailmime_is_attachment_disposition(mime: *mut mailmime) -> bool { + if (*mime).mm_mime_fields.is_null() { + return false; + } + + for cur in (*(*(*mime).mm_mime_fields).fld_list).into_iter() { + let field = cur as *mut mailmime_field; + if !field.is_null() + && (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int + && !(*field).fld_data.fld_disposition.is_null() + { + if !(*(*field).fld_data.fld_disposition).dsp_type.is_null() + && (*(*(*field).fld_data.fld_disposition).dsp_type).dsp_type + == MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int { - if !(*(*field).fld_data.fld_disposition).dsp_type.is_null() - && (*(*(*field).fld_data.fld_disposition).dsp_type).dsp_type - == MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int - { - return 1i32; - } - } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() + return true; } } } - 0 + false } /* low-level-tools for working with mailmime structures directly */ @@ -983,384 +1290,32 @@ pub unsafe fn mailmime_find_ct_parameter( { return ptr::null_mut(); } - let mut cur: *mut clistiter; - cur = (*(*(*mime).mm_content_type).ct_parameters).first; - while !cur.is_null() { - let param: *mut mailmime_parameter = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime_parameter; + + for cur in (*(*(*mime).mm_content_type).ct_parameters).into_iter() { + let param = cur as *mut mailmime_parameter; if !param.is_null() && !(*param).pa_name.is_null() { if strcmp((*param).pa_name, name) == 0i32 { return param; } } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } } ptr::null_mut() } -unsafe fn dc_mimeparser_add_single_part_if_known( - mimeparser: &mut dc_mimeparser_t, - mime: *mut mailmime, -) -> libc::c_int { - let mut ok_to_continue = true; - let old_part_count = mimeparser.parts.len(); - let mime_type: libc::c_int; - let mime_data: *mut mailmime_data; - let file_suffix: *mut libc::c_char = ptr::null_mut(); - let mut desired_filename: *mut libc::c_char = ptr::null_mut(); - let mut msg_type = Viewtype::Unknown; - let mut raw_mime: *mut libc::c_char = ptr::null_mut(); - /* mmap_string_unref()'d if set */ - let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut(); - /* must not be free()'d */ - let mut decoded_data: *const libc::c_char = ptr::null(); - let mut decoded_data_bytes = 0; - let mut simplifier: Option = None; - if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) { - mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime); - 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) - { - /* regard `Content-Transfer-Encoding:` */ - if mailmime_transfer_decode( - mime, - &mut decoded_data, - &mut decoded_data_bytes, - &mut transfer_decoding_buffer, - ) { - /* no always error - but no data */ - match mime_type { - 60 | 70 => { - if simplifier.is_none() { - simplifier = Some(Simplify::new()); - } - /* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */ - let charset = mailmime_content_charset_get((*mime).mm_content_type); - if !charset.is_null() - && strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char) - != 0i32 - && strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char) - != 0i32 - { - if let Some(encoding) = Charset::for_label( - CStr::from_ptr(charset).to_str().unwrap().as_bytes(), - ) { - let data = std::slice::from_raw_parts( - decoded_data as *const u8, - decoded_data_bytes, - ); - - let (res, _, _) = encoding.decode(data); - info!(mimeparser.context, "decoded message: '{}'", res); - if res.is_empty() { - /* no error - but nothing to add */ - ok_to_continue = false; - } else { - let b = res.as_bytes(); - decoded_data = b.as_ptr() as *const libc::c_char; - decoded_data_bytes = b.len(); - std::mem::forget(res); - } - } else { - warn!( - mimeparser.context, - "Cannot convert {} bytes from \"{}\" to \"utf-8\".", - decoded_data_bytes as libc::c_int, - as_str(charset), - ); - } - } - if ok_to_continue { - /* check header directly as is_send_by_messenger is not yet set up */ - let is_msgrmsg = - !dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version") - .is_null(); - - let simplified_txt = - if decoded_data_bytes <= 0 || decoded_data.is_null() { - "".into() - } else { - let input_c = strndup(decoded_data, decoded_data_bytes as _); - let input = to_string_lossy(input_c); - let is_html = mime_type == 70; - free(input_c as *mut _); - - simplifier.unwrap().simplify(&input, is_html, is_msgrmsg) - }; - if !simplified_txt.is_empty() { - let mut part = dc_mimepart_new(); - part.type_0 = Viewtype::Text; - part.int_mimetype = mime_type; - part.msg = Some(simplified_txt); - part.msg_raw = { - let raw_c = - strndup(decoded_data, decoded_data_bytes as libc::c_ulong); - let raw = to_string_lossy(raw_c); - free(raw_c.cast()); - Some(raw) - }; - do_add_single_part(mimeparser, part); - } - - if simplifier.unwrap().is_forwarded { - mimeparser.is_forwarded = true; - } - } - } - 80 | 90 | 100 | 110 | 111 => { - /* try to get file name from - `Content-Disposition: ... filename*=...` - or `Content-Disposition: ... filename*0*=... filename*1*=... filename*2*=...` - or `Content-Disposition: ... filename=...` */ - let mut filename_parts = String::new(); - let mut cur1: *mut clistiter = (*(*(*mime).mm_mime_fields).fld_list).first; - while !cur1.is_null() { - let field: *mut mailmime_field = (if !cur1.is_null() { - (*cur1).data - } else { - ptr::null_mut() - }) - as *mut mailmime_field; - if !field.is_null() - && (*field).fld_type == MAILMIME_FIELD_DISPOSITION as libc::c_int - && !(*field).fld_data.fld_disposition.is_null() - { - let file_disposition: *mut mailmime_disposition = - (*field).fld_data.fld_disposition; - if !file_disposition.is_null() { - let mut cur2: *mut clistiter = - (*(*file_disposition).dsp_parms).first; - while !cur2.is_null() { - let dsp_param: *mut mailmime_disposition_parm = - (if !cur2.is_null() { - (*cur2).data - } else { - ptr::null_mut() - }) - as *mut mailmime_disposition_parm; - if !dsp_param.is_null() { - if (*dsp_param).pa_type - == MAILMIME_DISPOSITION_PARM_PARAMETER - as libc::c_int - && !(*dsp_param).pa_data.pa_parameter.is_null() - && !(*(*dsp_param).pa_data.pa_parameter) - .pa_name - .is_null() - && strncmp( - (*(*dsp_param).pa_data.pa_parameter).pa_name, - b"filename*\x00" as *const u8 - as *const libc::c_char, - 9, - ) == 0i32 - { - filename_parts += &to_string_lossy( - (*(*dsp_param).pa_data.pa_parameter).pa_value, - ); - } else if (*dsp_param).pa_type - == MAILMIME_DISPOSITION_PARM_FILENAME as libc::c_int - { - desired_filename = dc_decode_header_words( - (*dsp_param).pa_data.pa_filename, - ) - } - } - cur2 = if !cur2.is_null() { - (*cur2).next - } else { - ptr::null_mut() - } - } - } - break; - } else { - cur1 = if !cur1.is_null() { - (*cur1).next - } else { - ptr::null_mut() - } - } - } - if !filename_parts.is_empty() { - free(desired_filename as *mut libc::c_void); - desired_filename = - dc_decode_ext_header(filename_parts.as_bytes()).strdup(); - } - if desired_filename.is_null() { - let param = mailmime_find_ct_parameter( - mime, - b"name\x00" as *const u8 as *const libc::c_char, - ); - if !param.is_null() - && !(*param).pa_value.is_null() - && 0 != *(*param).pa_value.offset(0isize) as libc::c_int - { - desired_filename = dc_strdup((*param).pa_value) - } - } - /* if there is still no filename, guess one */ - if desired_filename.is_null() { - if !(*mime).mm_content_type.is_null() - && !(*(*mime).mm_content_type).ct_subtype.is_null() - { - desired_filename = dc_mprintf( - b"file.%s\x00" as *const u8 as *const libc::c_char, - (*(*mime).mm_content_type).ct_subtype, - ); - } else { - ok_to_continue = false; - } - } - if ok_to_continue { - if strncmp( - desired_filename, - b"location\x00" as *const u8 as *const libc::c_char, - 8, - ) == 0i32 - && strncmp( - desired_filename - .offset(strlen(desired_filename) as isize) - .offset(-4isize), - b".kml\x00" as *const u8 as *const libc::c_char, - 4, - ) == 0i32 - { - if !decoded_data.is_null() && decoded_data_bytes > 0 { - let d = - dc_null_terminate(decoded_data, decoded_data_bytes as i32); - mimeparser.location_kml = - location::Kml::parse(mimeparser.context, as_str(d)).ok(); - free(d.cast()); - } - } else if strncmp( - desired_filename, - b"message\x00" as *const u8 as *const libc::c_char, - 7, - ) == 0i32 - && strncmp( - desired_filename - .offset(strlen(desired_filename) as isize) - .offset(-4isize), - b".kml\x00" as *const u8 as *const libc::c_char, - 4, - ) == 0i32 - { - if !decoded_data.is_null() && decoded_data_bytes > 0 { - let d = - dc_null_terminate(decoded_data, decoded_data_bytes as i32); - mimeparser.message_kml = - location::Kml::parse(mimeparser.context, as_str(d)).ok(); - free(d.cast()); - } - } else { - dc_replace_bad_utf8_chars(desired_filename); - do_add_single_file_part( - mimeparser, - msg_type, - mime_type, - as_str(raw_mime), - decoded_data, - decoded_data_bytes, - desired_filename, - ); - } - } - } - _ => {} - } - } - } - } - /* add object? (we do not add all objects, eg. signatures etc. are ignored) */ - if !transfer_decoding_buffer.is_null() { - mmap_string_unref(transfer_decoding_buffer); - } - free(file_suffix as *mut libc::c_void); - free(desired_filename as *mut libc::c_void); - free(raw_mime as *mut libc::c_void); - (mimeparser.parts.len() > old_part_count) as libc::c_int -} - -#[allow(non_snake_case)] -unsafe fn do_add_single_file_part( - parser: &mut dc_mimeparser_t, - msg_type: Viewtype, - mime_type: libc::c_int, - raw_mime: &str, - decoded_data: *const libc::c_char, - decoded_data_bytes: libc::size_t, - desired_filename: *const libc::c_char, -) { - let pathNfilename: *mut libc::c_char; - /* create a free file name to use */ - pathNfilename = dc_get_fine_pathNfilename( - (*parser).context, - b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, - desired_filename, - ); - if !pathNfilename.is_null() { - /* copy data to file */ - if !(dc_write_file( - (*parser).context, - pathNfilename, - decoded_data as *const libc::c_void, - decoded_data_bytes, - ) == 0i32) - { - let mut part = dc_mimepart_new(); - part.type_0 = msg_type; - part.int_mimetype = mime_type; - part.bytes = decoded_data_bytes as libc::c_int; - part.param.set(Param::File, as_str(pathNfilename)); - part.param.set(Param::MimeType, raw_mime); - if mime_type == 80 { - assert!(!decoded_data.is_null(), "invalid image data"); - let data = std::slice::from_raw_parts( - decoded_data as *const u8, - decoded_data_bytes as usize, - ); - - if let Ok((width, height)) = dc_get_filemeta(data) { - part.param.set_int(Param::Width, width as i32); - part.param.set_int(Param::Height, height as i32); - } - } - do_add_single_part(parser, part); - } - } - free(pathNfilename as *mut libc::c_void); -} - -unsafe fn do_add_single_part(parser: &mut dc_mimeparser_t, mut part: dc_mimepart_t) { - if (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 { - part.param.set_int(Param::GuranteeE2ee, 1); - } else if (*parser).e2ee_helper.encrypted { - part.param.set_int(Param::ErroneousE2ee, 0x2); - } - parser.parts.push(part); -} - pub unsafe fn mailmime_transfer_decode( mime: *mut mailmime, ret_decoded_data: *mut *const libc::c_char, ret_decoded_data_bytes: *mut libc::size_t, ret_to_mmap_string_unref: *mut *mut libc::c_char, ) -> bool { - let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int; + let mut mime_transfer_encoding = MAILMIME_MECHANISM_BINARY as libc::c_int; let mime_data: *mut mailmime_data; + /* must not be free()'d */ let decoded_data: *const libc::c_char; - let mut decoded_data_bytes: libc::size_t = 0; + let mut decoded_data_bytes = 0; + /* mmap_string_unref()'d if set */ let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut(); if mime.is_null() @@ -1375,29 +1330,19 @@ pub unsafe fn mailmime_transfer_decode( } mime_data = (*mime).mm_data.mm_single; if !(*mime).mm_mime_fields.is_null() { - let mut cur: *mut clistiter; - cur = (*(*(*mime).mm_mime_fields).fld_list).first; - while !cur.is_null() { - let field: *mut mailmime_field = (if !cur.is_null() { - (*cur).data - } else { - ptr::null_mut() - }) as *mut mailmime_field; + for cur in (*(*(*mime).mm_mime_fields).fld_list).into_iter() { + let field = cur as *mut mailmime_field; + if !field.is_null() && (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int && !(*field).fld_data.fld_encoding.is_null() { mime_transfer_encoding = (*(*field).fld_data.fld_encoding).enc_type; break; - } else { - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() - } } } } + 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 @@ -1408,9 +1353,8 @@ pub unsafe fn mailmime_transfer_decode( return false; } } else { - let r: libc::c_int; let mut current_index = 0; - r = mailmime_part_parse( + let r = mailmime_part_parse( (*mime_data).dt_data.dt_text.dt_data, (*mime_data).dt_data.dt_text.dt_length, &mut current_index, @@ -1424,8 +1368,9 @@ pub unsafe fn mailmime_transfer_decode( { return false; } - decoded_data = transfer_decoding_buffer + decoded_data = transfer_decoding_buffer; } + *ret_decoded_data = decoded_data; *ret_decoded_data_bytes = decoded_data_bytes; *ret_to_mmap_string_unref = transfer_decoding_buffer; @@ -1433,80 +1378,16 @@ pub unsafe fn mailmime_transfer_decode( true } -pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t) -> bool { - if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() { - return true; - } - let precedence: *mut mailimf_optional_field = - dc_mimeparser_lookup_optional_field(mimeparser, "Precedence"); - if !precedence.is_null() { - if strcasecmp( - (*precedence).fld_value, - b"list\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - || strcasecmp( - (*precedence).fld_value, - b"bulk\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - return true; - } - } - - false -} - -pub unsafe fn dc_mimeparser_sender_equals_recipient(mimeparser: &dc_mimeparser_t) -> libc::c_int { - let mut sender_equals_recipient: libc::c_int = 0i32; - let fld: *const mailimf_field; - let mut fld_from: *const mailimf_from = ptr::null(); - let mb: *mut mailimf_mailbox; - - if !mimeparser.header_root.is_null() { - /* get From: and check there is exactly one sender */ - fld = mailimf_find_field(mimeparser.header_root, MAILIMF_FIELD_FROM as libc::c_int); - if !(fld.is_null() - || { - fld_from = (*fld).fld_data.fld_from; - fld_from.is_null() - } - || (*fld_from).frm_mb_list.is_null() - || (*(*fld_from).frm_mb_list).mb_list.is_null() - || (*(*(*fld_from).frm_mb_list).mb_list).count != 1i32) - { - mb = (if !(*(*(*fld_from).frm_mb_list).mb_list).first.is_null() { - (*(*(*(*fld_from).frm_mb_list).mb_list).first).data - } else { - ptr::null_mut() - }) as *mut mailimf_mailbox; - if !mb.is_null() { - let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); - let recipients = mailimf_get_recipients(mimeparser.header_root); - if recipients.len() == 1 { - if recipients.contains(from_addr_norm) { - sender_equals_recipient = 1i32; - } - } - } - } - } - - sender_equals_recipient -} - pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet { /* returned addresses are normalized. */ let mut recipients: HashSet = Default::default(); - let mut cur1: *mut clistiter; - cur1 = (*(*imffields).fld_list).first; - while !cur1.is_null() { - let fld: *mut mailimf_field = (if !cur1.is_null() { - (*cur1).data - } else { - ptr::null_mut() - }) as *mut mailimf_field; + + for cur in (*(*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(); // TODO match on enums /rtn match (*fld).fld_type { @@ -1524,78 +1405,38 @@ pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet< } _ => {} } + if !addr_list.is_null() { - let mut cur2: *mut clistiter; - cur2 = (*(*addr_list).ad_list).first; - while !cur2.is_null() { - let adr: *mut mailimf_address = (if !cur2.is_null() { - (*cur2).data - } else { - ptr::null_mut() - }) as *mut mailimf_address; + for cur2 in (*(*addr_list).ad_list).into_iter() { + let adr = cur2 as *mut mailimf_address; + if !adr.is_null() { if (*adr).ad_type == MAILIMF_ADDRESS_MAILBOX as libc::c_int { - mailimf_get_recipients__add_addr( - &mut recipients, - (*adr).ad_data.ad_mailbox, - ); + mailimf_get_recipients_add_addr(&mut recipients, (*adr).ad_data.ad_mailbox); } else if (*adr).ad_type == MAILIMF_ADDRESS_GROUP as libc::c_int { let group: *mut mailimf_group = (*adr).ad_data.ad_group; if !group.is_null() && !(*group).grp_mb_list.is_null() { - let mut cur3: *mut clistiter; - cur3 = (*(*(*group).grp_mb_list).mb_list).first; - while !cur3.is_null() { - mailimf_get_recipients__add_addr( + for cur3 in (*(*(*group).grp_mb_list).mb_list).into_iter() { + mailimf_get_recipients_add_addr( &mut recipients, - (if !cur3.is_null() { - (*cur3).data - } else { - ptr::null_mut() - }) as *mut mailimf_mailbox, + cur3 as *mut mailimf_mailbox, ); - cur3 = if !cur3.is_null() { - (*cur3).next - } else { - ptr::null_mut() - } } } } } - cur2 = if !cur2.is_null() { - (*cur2).next - } else { - ptr::null_mut() - } } } - cur1 = if !cur1.is_null() { - (*cur1).next - } else { - ptr::null_mut() - } } recipients } -/* ****************************************************************************** - * debug output - ******************************************************************************/ -/* DEBUG_MIME_OUTPUT */ -/* ****************************************************************************** - * low-level-tools for getting a list of all recipients - ******************************************************************************/ - -#[allow(non_snake_case)] -unsafe fn mailimf_get_recipients__add_addr( - recipients: &mut HashSet, - mb: *mut mailimf_mailbox, -) { +fn mailimf_get_recipients_add_addr(recipients: &mut HashSet, mb: *mut mailimf_mailbox) { if !mb.is_null() { - let addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); + 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*/ @@ -1628,20 +1469,6 @@ pub unsafe fn mailimf_find_field( ptr::null_mut() } -pub unsafe fn dc_mimeparser_repl_msg_by_error( - mimeparser: &mut dc_mimeparser_t, - error_msg: *const libc::c_char, -) { - if mimeparser.parts.is_empty() { - return; - } - let part = &mut mimeparser.parts[0]; - part.type_0 = Viewtype::Text; - part.msg = Some(format!("[{}]", to_string_lossy(error_msg))); - mimeparser.parts.truncate(1); - assert_eq!(mimeparser.parts.len(), 1); -} - /*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() { @@ -1789,29 +1616,27 @@ mod tests { } } #[test] - fn test_dc_mimeparser_with_context() { + 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 mut mimeparser = dc_mimeparser_parse(&context.ctx, &raw[..]); + let mut mimeparser = MimeParser::new(&context.ctx); + mimeparser.parse(&raw[..]); assert_eq!(mimeparser.subject, Some("inner-subject".into())); - let mut of: *mut mailimf_optional_field = - dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A"); + let of = mimeparser.lookup_optional_field("X-Special-A").unwrap(); assert_eq!( &to_string((*of).fld_value as *const libc::c_char), "special-a", ); - of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo"); + let of = mimeparser.lookup_optional_field("Foo").unwrap(); assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "Bar",); - of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version"); + let of = mimeparser.lookup_optional_field("Chat-Version").unwrap(); assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "1.0",); assert_eq!(mimeparser.parts.len(), 1); - - dc_mimeparser_unref(&mut mimeparser); } } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index f24fa7d00..1920e0cec 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -63,7 +63,8 @@ pub unsafe fn dc_receive_imf( // somewhen, I did not found out anything that speaks against this approach yet) let body = std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes); - let mut mime_parser = dc_mimeparser_parse(context, body); + let mut mime_parser = MimeParser::new(context); + mime_parser.parse(body); if mime_parser.header.is_empty() { // Error - even adding an empty record won't help as we do not know the message ID @@ -149,7 +150,7 @@ pub unsafe fn dc_receive_imf( ); if 0 != check_self { incoming = 0; - if 0 != dc_mimeparser_sender_equals_recipient(&mime_parser) { + if mime_parser.sender_equals_recipient() { from_id = DC_CONTACT_ID_SELF; } } else if from_list.len() >= 1 { @@ -183,7 +184,7 @@ pub unsafe fn dc_receive_imf( } // Add parts - if dc_mimeparser_get_last_nonmeta(&mut mime_parser).is_some() { + if mime_parser.get_last_nonmeta().is_some() { if let Err(err) = add_parts( context, &mut mime_parser, @@ -278,7 +279,7 @@ pub unsafe fn dc_receive_imf( unsafe fn add_parts( context: &Context, - mut mime_parser: &mut dc_mimeparser_t, + mut mime_parser: &mut MimeParser, imf_raw_not_terminated: *const libc::c_char, imf_raw_bytes: libc::size_t, incoming: i32, @@ -414,7 +415,7 @@ unsafe fn add_parts( *to_id = 1; // handshake messages must be processed _before_ chats are created // (eg. contacs may be marked as verified) - if !dc_mimeparser_lookup_field(mime_parser, "Secure-Join").is_null() { + if mime_parser.lookup_field("Secure-Join").is_some() { // avoid discarding by show_emails setting msgrmsg = 1; *chat_id = 0; @@ -463,7 +464,7 @@ unsafe fn add_parts( if *chat_id == 0 { // check if the message belongs to a mailing list - if dc_mimeparser_is_mailinglist_message(mime_parser) { + if mime_parser.is_mailinglist_message() { *chat_id = 3; info!(context, "Message belongs to a mailing list and is ignored.",); } @@ -656,7 +657,7 @@ unsafe fn add_parts( } } } - if part.type_0 == Viewtype::Text { + if part.typ == Viewtype::Text { let msg_raw = CString::yolo(part.msg_raw.as_ref().cloned().unwrap_or_default()); let subject_c = CString::yolo( @@ -687,7 +688,7 @@ unsafe fn add_parts( sort_timestamp, *sent_timestamp, rcvd_timestamp, - part.type_0, + part.typ, state, msgrmsg, part.msg.as_ref().map_or("", String::as_str), @@ -762,14 +763,13 @@ unsafe fn add_parts( } /// Lookup a mime field given its name and type. -unsafe fn lookup_field( - parser: &dc_mimeparser_t, - name: &str, - typ: u32, -) -> Option<*const mailimf_field> { - let field = dc_mimeparser_lookup_field(parser, name); - if !field.is_null() && (*field).fld_type == typ as libc::c_int { - Some(field) +fn lookup_field(parser: &MimeParser, name: &str, typ: u32) -> Option<*const mailimf_field> { + if let Some(field) = parser.lookup_field(name) { + if unsafe { (*field).fld_type } == typ as libc::c_int { + Some(field) + } else { + None + } } else { None } @@ -778,7 +778,7 @@ unsafe fn lookup_field( // Handle reports (mainly MDNs) unsafe fn handle_reports( context: &Context, - mime_parser: &dc_mimeparser_t, + mime_parser: &MimeParser, from_id: u32, sent_timestamp: i64, rr_event_to_send: &mut Vec<(u32, u32)>, @@ -941,7 +941,7 @@ unsafe fn handle_reports( fn save_locations( context: &Context, - mime_parser: &dc_mimeparser_t, + mime_parser: &MimeParser, chat_id: u32, from_id: u32, insert_msg_id: u32, @@ -1039,7 +1039,7 @@ unsafe fn calc_timestamps( #[allow(non_snake_case)] unsafe fn create_or_lookup_group( context: &Context, - mime_parser: &mut dc_mimeparser_t, + mime_parser: &mut MimeParser, allow_creation: libc::c_int, create_blocked: Blocked, from_id: u32, @@ -1062,16 +1062,13 @@ unsafe fn create_or_lookup_group( let mut X_MrGrpNameChanged = 0; let mut X_MrGrpImageChanged = "".to_string(); let mut better_msg: String = From::from(""); - let mut failure_reason = std::ptr::null_mut(); let cleanup = |grpname: *mut libc::c_char, - failure_reason: *mut libc::c_char, ret_chat_id: *mut u32, ret_chat_id_blocked: &mut Blocked, chat_id: u32, chat_id_blocked: Blocked| { free(grpname.cast()); - free(failure_reason.cast()); if !ret_chat_id.is_null() { *ret_chat_id = chat_id; @@ -1089,10 +1086,10 @@ unsafe fn create_or_lookup_group( } set_better_msg(mime_parser, &better_msg); - let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-ID"); - if !optional_field.is_null() { - grpid = to_string((*optional_field).fld_value) + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-ID") { + grpid = to_string((*optional_field).fld_value); } + if grpid.is_empty() { if let Some(field) = lookup_field(mime_parser, "Message-ID", MAILIMF_FIELD_MESSAGE_ID) { let fld_message_id = (*field).fld_data.fld_message_id; @@ -1141,7 +1138,6 @@ unsafe fn create_or_lookup_group( ); cleanup( grpname, - failure_reason, ret_chat_id, ret_chat_id_blocked, chat_id, @@ -1153,13 +1149,10 @@ unsafe fn create_or_lookup_group( } } - let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Name"); - if !optional_field.is_null() { - grpname = dc_decode_header_words((*optional_field).fld_value) + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Name") { + grpname = dc_decode_header_words((*optional_field).fld_value); } - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Member-Removed"); - if !optional_field.is_null() { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") { X_MrRemoveFromGrp = (*optional_field).fld_value; mime_parser.is_system_message = DC_CMD_MEMBER_REMOVED_FROM_GROUP; let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp)) @@ -1175,14 +1168,10 @@ unsafe fn create_or_lookup_group( from_id as u32, ) } else { - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Member-Added"); - if !optional_field.is_null() { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Added") { X_MrAddToGrp = (*optional_field).fld_value; mime_parser.is_system_message = DC_CMD_MEMBER_ADDED_TO_GROUP; - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image"); - if !optional_field.is_null() { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") { // fld_value is a pointer somewhere into mime_parser, must not be freed X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); } @@ -1193,9 +1182,9 @@ unsafe fn create_or_lookup_group( from_id as u32, ) } else { - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Name-Changed"); - if !optional_field.is_null() { + if let Some(optional_field) = + mime_parser.lookup_optional_field("Chat-Group-Name-Changed") + { X_MrGrpNameChanged = 1; mime_parser.is_system_message = DC_CMD_GROUPNAME_CHANGED; better_msg = context.stock_system_msg( @@ -1205,9 +1194,8 @@ unsafe fn create_or_lookup_group( from_id as u32, ) } else { - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Image"); - if !optional_field.is_null() { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") + { // fld_value is a pointer somewhere into mime_parser, must not be freed X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); mime_parser.is_system_message = DC_CMD_GROUPIMAGE_CHANGED; @@ -1230,6 +1218,8 @@ 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, @@ -1239,8 +1229,10 @@ unsafe fn create_or_lookup_group( &mut failure_reason, ) { - dc_mimeparser_repl_msg_by_error(mime_parser, failure_reason); + mime_parser.repl_msg_by_error(to_string(failure_reason)); } + + free(failure_reason.cast()); } // check if the sender is a member of the existing group - @@ -1257,7 +1249,7 @@ unsafe fn create_or_lookup_group( .get_config(context, "configured_addr") .unwrap_or_default(); if chat_id == 0 - && !dc_mimeparser_is_mailinglist_message(mime_parser) + && !mime_parser.is_mailinglist_message() && !grpid.is_empty() && !grpname.is_null() // otherwise, a pending "quit" message may pop up @@ -1267,8 +1259,10 @@ unsafe fn create_or_lookup_group( || !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp))) { let mut create_verified = VerifiedStatus::Unverified; - if !dc_mimeparser_lookup_field(mime_parser, "Chat-Verified").is_null() { + 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, @@ -1276,13 +1270,13 @@ unsafe fn create_or_lookup_group( to_ids, &mut failure_reason, ) { - dc_mimeparser_repl_msg_by_error(mime_parser, failure_reason); + mime_parser.repl_msg_by_error(to_string(failure_reason)); } + free(failure_reason.cast()); } if 0 == allow_creation { cleanup( grpname, - failure_reason, ret_chat_id, ret_chat_id_blocked, chat_id, @@ -1314,7 +1308,6 @@ unsafe fn create_or_lookup_group( } cleanup( grpname, - failure_reason, ret_chat_id, ret_chat_id_blocked, chat_id, @@ -1350,7 +1343,7 @@ unsafe fn create_or_lookup_group( changed = true; } else { for part in &mut mime_parser.parts { - if part.type_0 == Viewtype::Image { + if part.typ == Viewtype::Image { grpimage = part .param .get(Param::File) @@ -1444,7 +1437,6 @@ unsafe fn create_or_lookup_group( cleanup( grpname, - failure_reason, ret_chat_id, ret_chat_id_blocked, chat_id, @@ -1455,7 +1447,7 @@ unsafe fn create_or_lookup_group( /// Handle groups for received messages unsafe fn create_or_lookup_adhoc_group( context: &Context, - mime_parser: &dc_mimeparser_t, + mime_parser: &MimeParser, allow_creation: libc::c_int, create_blocked: Blocked, from_id: u32, @@ -1483,7 +1475,7 @@ unsafe fn create_or_lookup_adhoc_group( }; // build member list from the given ids - if to_ids.is_empty() || dc_mimeparser_is_mailinglist_message(mime_parser) { + if to_ids.is_empty() || mime_parser.is_mailinglist_message() { // too few contacts or a mailinglist cleanup( grpname, @@ -1744,7 +1736,7 @@ fn search_chat_ids_by_contact_ids(context: &Context, unsorted_contact_ids: &Vec< unsafe fn check_verified_properties( context: &Context, - mimeparser: &dc_mimeparser_t, + mimeparser: &MimeParser, from_id: u32, to_ids: &Vec, failure_reason: *mut *mut libc::c_char, @@ -1844,46 +1836,53 @@ unsafe fn check_verified_properties( 1 } -unsafe fn set_better_msg>(mime_parser: &mut dc_mimeparser_t, better_msg: T) { +fn set_better_msg(mime_parser: &mut MimeParser, better_msg: impl AsRef) { let msg = better_msg.as_ref(); if msg.len() > 0 && !mime_parser.parts.is_empty() { let part = &mut mime_parser.parts[0]; - if (*part).type_0 == Viewtype::Text { + if part.typ == Viewtype::Text { part.msg = Some(msg.to_string()); } }; } -unsafe fn dc_is_reply_to_known_message( - context: &Context, - mime_parser: &dc_mimeparser_t, -) -> libc::c_int { +unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) -> libc::c_int { /* check if the message is a reply to a known message; the replies are identified by the Message-ID from `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in dc_chat.c) */ - let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Predecessor"); - if !optional_field.is_null() { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Predecessor") { if 0 != is_known_rfc724_mid(context, (*optional_field).fld_value) { return 1; } } - let field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { - let fld_in_reply_to: *mut mailimf_in_reply_to = (*field).fld_data.fld_in_reply_to; - if !fld_in_reply_to.is_null() { - if is_known_rfc724_mid_in_list(context, (*(*field).fld_data.fld_in_reply_to).mid_list) { - return 1; + + if let Some(field) = mime_parser.lookup_field("In-Reply-To") { + if (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { + let fld_in_reply_to = (*field).fld_data.fld_in_reply_to; + if !fld_in_reply_to.is_null() { + if is_known_rfc724_mid_in_list( + context, + (*(*field).fld_data.fld_in_reply_to).mid_list, + ) { + return 1; + } } } } - let field = dc_mimeparser_lookup_field(mime_parser, "References"); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { - let fld_references: *mut mailimf_references = (*field).fld_data.fld_references; - if !fld_references.is_null() { - if is_known_rfc724_mid_in_list(context, (*(*field).fld_data.fld_references).mid_list) { - return 1; + + if let Some(field) = mime_parser.lookup_field("References") { + if (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { + let fld_references = (*field).fld_data.fld_references; + if !fld_references.is_null() { + if is_known_rfc724_mid_in_list( + context, + (*(*field).fld_data.fld_references).mid_list, + ) { + return 1; + } } } } + 0 } @@ -1920,37 +1919,42 @@ fn is_known_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> li unsafe fn dc_is_reply_to_messenger_message( context: &Context, - mime_parser: &dc_mimeparser_t, + mime_parser: &MimeParser, ) -> libc::c_int { /* function checks, if the message defined by mime_parser references a message send by us from Delta Chat. This is similar to is_reply_to_known_message() but - checks also if any of the referenced IDs are send by a messenger - it is okay, if the referenced messages are moved to trash here - no check for the Chat-* headers (function is only called if it is no messenger message itself) */ - let field = dc_mimeparser_lookup_field(mime_parser, "In-Reply-To"); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { - let fld_in_reply_to: *mut mailimf_in_reply_to = (*field).fld_data.fld_in_reply_to; - if !fld_in_reply_to.is_null() { - if 0 != is_msgrmsg_rfc724_mid_in_list( - context, - (*(*field).fld_data.fld_in_reply_to).mid_list, - ) { - return 1; + + if let Some(field) = mime_parser.lookup_field("In-Reply-To") { + if (*field).fld_type == MAILIMF_FIELD_IN_REPLY_TO as libc::c_int { + let fld_in_reply_to = (*field).fld_data.fld_in_reply_to; + if !fld_in_reply_to.is_null() { + if 0 != is_msgrmsg_rfc724_mid_in_list( + context, + (*(*field).fld_data.fld_in_reply_to).mid_list, + ) { + return 1; + } } } } - let field = dc_mimeparser_lookup_field(mime_parser, "References"); - if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { - let fld_references: *mut mailimf_references = (*field).fld_data.fld_references; - if !fld_references.is_null() { - if 0 != is_msgrmsg_rfc724_mid_in_list( - context, - (*(*field).fld_data.fld_references).mid_list, - ) { - return 1; + + if let Some(field) = mime_parser.lookup_field("References") { + if (*field).fld_type == MAILIMF_FIELD_REFERENCES as libc::c_int { + let fld_references: *mut mailimf_references = (*field).fld_data.fld_references; + if !fld_references.is_null() { + if 0 != is_msgrmsg_rfc724_mid_in_list( + context, + (*(*field).fld_data.fld_references).mid_list, + ) { + return 1; + } } } } + 0 } diff --git a/src/e2ee.rs b/src/e2ee.rs index dddd24810..8b00c1f1e 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -386,16 +386,17 @@ impl E2eeHelper { /*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; - let mut from: *mut libc::c_char = ptr::null_mut(); + let mut from = None; let mut private_keyring = Keyring::default(); let mut public_keyring_for_validate = Keyring::default(); let mut gossip_headers: *mut mailimf_fields = ptr::null_mut(); if !(in_out_message.is_null() || imffields.is_null()) { - let mut field: *mut mailimf_field = - mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int); + let mut field = mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int); + if !field.is_null() && !(*field).fld_data.fld_from.is_null() { from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list) } + field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int); if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() { let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date; @@ -407,25 +408,28 @@ impl E2eeHelper { } } let mut peerstate = None; - let autocryptheader = - as_opt_str(from).and_then(|from| Aheader::from_imffields(from, imffields)); - if message_time > 0 && !from.is_null() { - peerstate = Peerstate::from_addr(context, &context.sql, as_str(from)); + 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); - } 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); + 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); + } 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); + } + } else if let Some(ref header) = autocryptheader { + let p = Peerstate::from_header(context, header, message_time); + assert!(p.save_to_db(&context.sql, true)); + peerstate = Some(p); } - } else if let Some(ref header) = autocryptheader { - let p = Peerstate::from_header(context, header, message_time); - assert!(p.save_to_db(&context.sql, true)); - peerstate = Some(p); } } /* load private key for decryption */ @@ -437,7 +441,8 @@ impl E2eeHelper { &context.sql, ) { if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 { - peerstate = Peerstate::from_addr(&context, &context.sql, as_str(from)); + peerstate = + Peerstate::from_addr(&context, &context.sql, &from.unwrap_or_default()); } if let Some(ref peerstate) = peerstate { if peerstate.degrade_event.is_some() { @@ -486,8 +491,6 @@ impl E2eeHelper { if !gossip_headers.is_null() { mailimf_fields_free(gossip_headers); } - - free(from as *mut libc::c_void); } } diff --git a/src/securejoin.rs b/src/securejoin.rs index 805788c9d..6a302cd33 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -335,7 +335,7 @@ fn fingerprint_equals_sender( /* library private: secure-join */ pub fn handle_securejoin_handshake( context: &Context, - mimeparser: &dc_mimeparser_t, + mimeparser: &MimeParser, contact_id: u32, ) -> libc::c_int { let own_fingerprint: String; @@ -651,21 +651,21 @@ fn secure_connection_established(context: &Context, contact_chat_id: u32) { emit_event!(context, Event::ChatModified(contact_chat_id)); } -fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> Option { - let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key); - unsafe { +fn lookup_field(mime_parser: &MimeParser, key: &str) -> Option { + if let Some(field) = mime_parser.lookup_field(key) { let mut value: *const libc::c_char = ptr::null(); - if field.is_null() - || (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int - || (*field).fld_data.fld_optional_field.is_null() + if unsafe { (*field).fld_type } != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int + || unsafe { (*field).fld_data.fld_optional_field.is_null() } || { - value = (*(*field).fld_data.fld_optional_field).fld_value; + value = unsafe { (*(*field).fld_data.fld_optional_field).fld_value }; value.is_null() } { return None; } - Some(as_str(value).to_string()) + Some(to_string(value)) + } else { + None } } @@ -706,10 +706,7 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Res * Tools: Misc. ******************************************************************************/ -fn encrypted_and_signed( - mimeparser: &dc_mimeparser_t, - expected_fingerprint: impl AsRef, -) -> bool { +fn encrypted_and_signed(mimeparser: &MimeParser, expected_fingerprint: impl AsRef) -> bool { if !mimeparser.e2ee_helper.encrypted { warn!(mimeparser.context, "Message not encrypted.",); false From c0747bf68d91a8ba566bdc8e2ade56f6057a294a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sat, 14 Sep 2019 23:55:35 +0200 Subject: [PATCH 2/4] refactor: use enum for system messages --- src/chat.rs | 4 +- src/constants.rs | 9 -- src/dc_mimefactory.rs | 342 +++++++++++++++++++++++------------------- src/dc_mimeparser.rs | 29 +++- src/dc_receive_imf.rs | 16 +- 5 files changed, 224 insertions(+), 176 deletions(-) diff --git a/src/chat.rs b/src/chat.rs index ff157df56..078e164dd 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -7,6 +7,7 @@ use crate::config::*; use crate::constants::*; use crate::contact::*; use crate::context::Context; +use crate::dc_mimeparser::SystemMessage; use crate::dc_tools::*; use crate::error::Error; use crate::events::Event; @@ -1669,7 +1670,8 @@ pub fn set_chat_profile_image( if chat.update_param(context).is_ok() { if chat.is_promoted() { let mut msg = dc_msg_new_untyped(); - msg.param.set_int(Param::Cmd, DC_CMD_GROUPIMAGE_CHANGED); + msg.param + .set_int(Param::Cmd, SystemMessage::GroupImageChanged as i32); msg.type_0 = Viewtype::Text; msg.text = Some(context.stock_system_msg( if new_image_rel == "" { diff --git a/src/constants.rs b/src/constants.rs index caaf8bde5..aa6a11a9d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -314,12 +314,3 @@ pub enum KeyType { Public = 0, Private = 1, } - -pub const DC_CMD_GROUPNAME_CHANGED: libc::c_int = 2; -pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3; -pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4; -pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5; -pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6; -pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7; -pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8; -pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9; diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 69627708e..b0bd84468 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -11,11 +11,13 @@ use mmime::mailmime_types_helper::*; use mmime::mailmime_write_mem::*; use mmime::mmapstring::*; use mmime::other::*; +use num_traits::FromPrimitive; use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::{get_version_str, Context}; +use crate::dc_mimeparser::SystemMessage; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::e2ee::*; @@ -529,7 +531,12 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact } /* build header etc. */ - let command = factory.msg.param.get_int(Param::Cmd).unwrap_or_default(); + let command = factory + .msg + .param + .get_int(Param::Cmd) + .and_then(SystemMessage::from_i32) + .unwrap_or_default(); if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, @@ -546,181 +553,202 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact dc_encode_header_words(name.as_ptr()), ), ); - if command == DC_CMD_MEMBER_REMOVED_FROM_GROUP { - let email_to_remove = factory - .msg - .param - .get(Param::Arg) - .unwrap_or_default() - .strdup(); - if strlen(email_to_remove) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Member-Removed\x00" as *const u8 - as *const libc::c_char, - ), - email_to_remove, - ), - ); - } - } else if command == DC_CMD_MEMBER_ADDED_TO_GROUP { - let msg = &factory.msg; - do_gossip = 1; - let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); - if strlen(email_to_add) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Member-Added\x00" as *const u8 - as *const libc::c_char, - ), - email_to_add, - ), - ); - grpimage = chat.param.get(Param::ProfileImage); - } - if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { - info!( - context, - "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", - "vg-member-added", - ); - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), - strdup(b"vg-member-added\x00" as *const u8 as *const libc::c_char), - ), - ); - } - } else if command == DC_CMD_GROUPNAME_CHANGED { - let msg = &factory.msg; - let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char, - ), - value_to_add, - ), - ); - } else if command == DC_CMD_GROUPIMAGE_CHANGED { - let msg = &factory.msg; - grpimage = msg.param.get(Param::Arg); - if grpimage.is_none() { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char), - dc_strdup(b"0\x00" as *const u8 as *const libc::c_char), - ), - ); - } - } - } - if command == DC_CMD_LOCATION_STREAMING_ENABLED { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Chat-Content\x00" as *const u8 as *const libc::c_char), - strdup( - b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, - ), - ), - ); - } - if command == DC_CMD_AUTOCRYPT_SETUP_MESSAGE { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Autocrypt-Setup-Message\x00" as *const u8 as *const libc::c_char), - strdup(b"v1\x00" as *const u8 as *const libc::c_char), - ), - ); - placeholdertext = factory - .context - .stock_str(StockMessage::AcSetupMsgBody) - .strdup(); - } - if command == DC_CMD_SECUREJOIN_MESSAGE { - let msg = &factory.msg; - let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); - if strlen(step) > 0 { - info!( - context, - "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", - as_str(step), - ); - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), - step, - ), - ); - let param2 = msg.param.get(Param::Arg2).unwrap_or_default().strdup(); - if strlen(param2) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - if strcmp( - step, - b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char, - ) == 0 - || strcmp( - step, - b"vc-request-with-auth\x00" as *const u8 - as *const libc::c_char, - ) == 0 - { + match command { + SystemMessage::Unknown | SystemMessage::LocationOnly => {} + SystemMessage::MemberRemovedFromGroup => { + let email_to_remove = factory + .msg + .param + .get(Param::Arg) + .unwrap_or_default() + .strdup(); + if strlen(email_to_remove) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( strdup( - b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char, - ) - } else { - strdup( - b"Secure-Join-Invitenumber\x00" as *const u8 + b"Chat-Group-Member-Removed\x00" as *const u8 as *const libc::c_char, - ) - }, - param2, - ), - ); + ), + email_to_remove, + ), + ); + } } - let fingerprint = msg.param.get(Param::Arg3).unwrap_or_default().strdup(); - if strlen(fingerprint) > 0 { + SystemMessage::MemberAddedToGroup => { + let msg = &factory.msg; + do_gossip = 1; + let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + if strlen(email_to_add) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Chat-Group-Member-Added\x00" as *const u8 + as *const libc::c_char, + ), + email_to_add, + ), + ); + grpimage = chat.param.get(Param::ProfileImage); + } + if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { + info!( + context, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + "vg-member-added", + ); + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), + strdup( + b"vg-member-added\x00" as *const u8 as *const libc::c_char, + ), + ), + ); + } + } + SystemMessage::GroupNameChanged => { + let msg = &factory.msg; + + let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); mailimf_fields_add( imf_fields, mailimf_field_new_custom( strdup( - b"Secure-Join-Fingerprint\x00" as *const u8 + b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char, ), - fingerprint, + value_to_add, ), ); } - let grpid = match msg.param.get(Param::Arg4) { - Some(id) => id.strdup(), - None => std::ptr::null_mut(), - }; - if !grpid.is_null() { + SystemMessage::GroupImageChanged => { + let msg = &factory.msg; + grpimage = msg.param.get(Param::Arg); + if grpimage.is_none() { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char, + ), + dc_strdup(b"0\x00" as *const u8 as *const libc::c_char), + ), + ); + } + } + SystemMessage::LocationStreamingEnabled => { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Chat-Content\x00" as *const u8 as *const libc::c_char), + strdup( + b"location-streaming-enabled\x00" as *const u8 + as *const libc::c_char, + ), + ), + ); + } + SystemMessage::AutocryptSetupMessage => { mailimf_fields_add( imf_fields, mailimf_field_new_custom( strdup( - b"Secure-Join-Group\x00" as *const u8 as *const libc::c_char, + b"Autocrypt-Setup-Message\x00" as *const u8 + as *const libc::c_char, ), - grpid, + strdup(b"v1\x00" as *const u8 as *const libc::c_char), ), ); + placeholdertext = factory + .context + .stock_str(StockMessage::AcSetupMsgBody) + .strdup(); + } + SystemMessage::SecurejoinMessage => { + let msg = &factory.msg; + let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + if strlen(step) > 0 { + info!( + context, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + as_str(step), + ); + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), + step, + ), + ); + let param2 = msg.param.get(Param::Arg2).unwrap_or_default().strdup(); + if strlen(param2) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + if strcmp( + step, + b"vg-request-with-auth\x00" as *const u8 + as *const libc::c_char, + ) == 0 + || strcmp( + step, + b"vc-request-with-auth\x00" as *const u8 + as *const libc::c_char, + ) == 0 + { + strdup( + b"Secure-Join-Auth\x00" as *const u8 + as *const libc::c_char, + ) + } else { + strdup( + b"Secure-Join-Invitenumber\x00" as *const u8 + as *const libc::c_char, + ) + }, + param2, + ), + ); + } + let fingerprint = + msg.param.get(Param::Arg3).unwrap_or_default().strdup(); + if strlen(fingerprint) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Secure-Join-Fingerprint\x00" as *const u8 + as *const libc::c_char, + ), + fingerprint, + ), + ); + } + let grpid = match msg.param.get(Param::Arg4) { + Some(id) => id.strdup(), + None => std::ptr::null_mut(), + }; + if !grpid.is_null() { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Secure-Join-Group\x00" as *const u8 + as *const libc::c_char, + ), + grpid, + ), + ); + } + } } } } + if let Some(grpimage) = grpimage { info!(factory.context, "setting group image '{}'", grpimage); let mut meta = dc_msg_new_untyped(); @@ -1079,7 +1107,13 @@ unsafe fn get_subject( } else { b"\x00" as *const u8 as *const libc::c_char }; - if msg.param.get_int(Param::Cmd).unwrap_or_default() == DC_CMD_AUTOCRYPT_SETUP_MESSAGE { + if msg + .param + .get_int(Param::Cmd) + .and_then(SystemMessage::from_i32) + .unwrap_or_default() + == SystemMessage::AutocryptSetupMessage + { ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup() } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { ret = format!( diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 2c01e5b69..6791d3888 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -3,6 +3,7 @@ use std::ffi::{CStr, CString}; use std::ptr; use charset::Charset; +use deltachat_derive::{FromSql, ToSql}; use mmime::clist::*; use mmime::mailimf::*; use mmime::mailimf_types::*; @@ -39,11 +40,31 @@ pub struct MimeParser<'a> { pub e2ee_helper: E2eeHelper, pub is_forwarded: bool, pub reports: Vec<*mut mailmime>, - pub is_system_message: libc::c_int, + pub is_system_message: SystemMessage, pub location_kml: Option, pub message_kml: Option, } +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)] +#[repr(i32)] +pub enum SystemMessage { + Unknown = 0, + GroupNameChanged = 2, + GroupImageChanged = 3, + MemberAddedToGroup = 4, + MemberRemovedFromGroup = 5, + AutocryptSetupMessage = 6, + SecurejoinMessage = 7, + LocationStreamingEnabled = 8, + LocationOnly = 9, +} + +impl Default for SystemMessage { + fn default() -> Self { + SystemMessage::Unknown + } +} + impl<'a> MimeParser<'a> { pub fn new(context: &'a Context) -> Self { MimeParser { @@ -59,7 +80,7 @@ impl<'a> MimeParser<'a> { is_forwarded: false, context, reports: Vec::new(), - is_system_message: 0, + is_system_message: SystemMessage::Unknown, location_kml: None, message_kml: None, } @@ -97,7 +118,7 @@ impl<'a> MimeParser<'a> { .any(|p| p.mimetype == DC_MIMETYPE_AC_SETUP_FILE); if has_setup_file { - self.is_system_message = 6i32; + self.is_system_message = SystemMessage::AutocryptSetupMessage; // TODO: replace the following code with this // once drain_filter stabilizes. @@ -127,7 +148,7 @@ impl<'a> MimeParser<'a> { b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, ) == 0i32 { - self.is_system_message = 8i32; + self.is_system_message = SystemMessage::LocationStreamingEnabled; } } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 1920e0cec..70794257b 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -389,7 +389,7 @@ unsafe fn add_parts( // incoming non-chat messages may be discarded; // maybe this can be optimized later, by checking the state before the message body is downloaded let mut allow_creation = 1; - if mime_parser.is_system_message != DC_CMD_AUTOCRYPT_SETUP_MESSAGE && msgrmsg == 0 { + if mime_parser.is_system_message != SystemMessage::AutocryptSetupMessage && msgrmsg == 0 { let show_emails = context .sql .get_config_int(context, "show_emails") @@ -673,9 +673,9 @@ unsafe fn add_parts( msg_raw.as_ptr(), ) } - if 0 != mime_parser.is_system_message { + if mime_parser.is_system_message != SystemMessage::Unknown { part.param - .set_int(Param::Cmd, mime_parser.is_system_message); + .set_int(Param::Cmd, mime_parser.is_system_message as i32); } stmt.execute(params![ @@ -1080,7 +1080,7 @@ unsafe fn create_or_lookup_group( }; }; - if mime_parser.is_system_message == DC_CMD_LOCATION_STREAMING_ENABLED { + if mime_parser.is_system_message == SystemMessage::LocationStreamingEnabled { better_msg = context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32) } @@ -1154,7 +1154,7 @@ unsafe fn create_or_lookup_group( } if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") { X_MrRemoveFromGrp = (*optional_field).fld_value; - mime_parser.is_system_message = DC_CMD_MEMBER_REMOVED_FROM_GROUP; + mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup; let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp)) == from_id as u32) as libc::c_int; better_msg = context.stock_system_msg( @@ -1170,7 +1170,7 @@ unsafe fn create_or_lookup_group( } else { if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Added") { X_MrAddToGrp = (*optional_field).fld_value; - mime_parser.is_system_message = DC_CMD_MEMBER_ADDED_TO_GROUP; + mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") { // fld_value is a pointer somewhere into mime_parser, must not be freed X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); @@ -1186,7 +1186,7 @@ unsafe fn create_or_lookup_group( mime_parser.lookup_optional_field("Chat-Group-Name-Changed") { X_MrGrpNameChanged = 1; - mime_parser.is_system_message = DC_CMD_GROUPNAME_CHANGED; + mime_parser.is_system_message = SystemMessage::GroupNameChanged; better_msg = context.stock_system_msg( StockMessage::MsgGrpName, as_str((*optional_field).fld_value), @@ -1198,7 +1198,7 @@ unsafe fn create_or_lookup_group( { // fld_value is a pointer somewhere into mime_parser, must not be freed X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); - mime_parser.is_system_message = DC_CMD_GROUPIMAGE_CHANGED; + mime_parser.is_system_message = SystemMessage::GroupImageChanged; better_msg = context.stock_system_msg( if X_MrGrpImageChanged == "0" { StockMessage::MsgGrpImgDeleted From 6be3c9a48add8d774606a91547466cc605f4a25d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 15 Sep 2019 00:26:32 +0200 Subject: [PATCH 3/4] refactor: improve mime field lookup --- src/dc_mimeparser.rs | 57 +++++----- src/dc_receive_imf.rs | 234 +++++++++++++++--------------------------- src/securejoin.rs | 38 ++----- 3 files changed, 117 insertions(+), 212 deletions(-) diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 6791d3888..32c4cbf86 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -142,14 +142,8 @@ impl<'a> MimeParser<'a> { } } else { if let Some(optional_field) = self.lookup_optional_field("Chat-Content") { - if !(*optional_field).fld_value.is_null() { - if strcmp( - (*optional_field).fld_value, - b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { - self.is_system_message = SystemMessage::LocationStreamingEnabled; - } + if optional_field == "location-streaming-enabled" { + self.is_system_message = SystemMessage::LocationStreamingEnabled; } } } @@ -248,8 +242,8 @@ impl<'a> MimeParser<'a> { || part.typ == Viewtype::Video { if let Some(field_0) = self.lookup_optional_field("Chat-Duration") { - let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value); - if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 { + let duration_ms = field_0.parse().unwrap_or_default(); + if duration_ms > 0 && duration_ms < 24 * 60 * 60 * 1000 { let part_mut = &mut self.parts[0]; part_mut.param.set_int(Param::Duration, duration_ms); } @@ -263,9 +257,11 @@ impl<'a> MimeParser<'a> { if self.get_last_nonmeta().is_some() { let mut mb_list: *mut mailimf_mailbox_list = ptr::null_mut(); let mut index_0 = 0; + let dn_field_c = CString::new(dn_field).unwrap(); + if mailimf_mailbox_list_parse( - (*dn_field).fld_value, - strlen((*dn_field).fld_value), + dn_field_c.as_ptr(), + strlen(dn_field_c.as_ptr()), &mut index_0, &mut mb_list, ) == MAILIMF_NO_ERROR as libc::c_int @@ -329,14 +325,14 @@ impl<'a> MimeParser<'a> { } } - pub fn lookup_optional_field(&self, field_name: &str) -> Option<*mut mailimf_optional_field> { + pub fn lookup_optional_field(&self, field_name: &str) -> Option { if let Some(field) = self.lookup_field(field_name) { if unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { let val = unsafe { (*field).fld_data.fld_optional_field }; if val.is_null() { return None; } else { - return Some(val); + return Some(unsafe { to_string_lossy((*val).fld_value) }); } } } @@ -344,6 +340,18 @@ impl<'a> MimeParser<'a> { None } + pub fn lookup_field_typ(&self, name: &str, typ: u32) -> Option<*const mailimf_field> { + if let Some(field) = self.lookup_field(name) { + if unsafe { (*field).fld_type } == typ as libc::c_int { + Some(field) + } else { + None + } + } else { + None + } + } + unsafe fn parse_mime_recursive(&mut self, mime: *mut mailmime) -> libc::c_int { if mime.is_null() { return 0; @@ -938,21 +946,13 @@ impl<'a> MimeParser<'a> { self.parts.push(part); } - pub unsafe fn is_mailinglist_message(&self) -> bool { + pub fn is_mailinglist_message(&self) -> bool { if self.lookup_field("List-Id").is_some() { return true; } if let Some(precedence) = self.lookup_optional_field("Precedence") { - if strcasecmp( - (*precedence).fld_value, - b"list\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - || strcasecmp( - (*precedence).fld_value, - b"bulk\x00" as *const u8 as *const libc::c_char, - ) == 0i32 - { + if precedence == "list" || precedence == "bulk" { return true; } } @@ -1647,16 +1647,13 @@ mod tests { assert_eq!(mimeparser.subject, Some("inner-subject".into())); let of = mimeparser.lookup_optional_field("X-Special-A").unwrap(); - assert_eq!( - &to_string((*of).fld_value as *const libc::c_char), - "special-a", - ); + assert_eq!(&of, "special-a"); let of = mimeparser.lookup_optional_field("Foo").unwrap(); - assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "Bar",); + assert_eq!(&of, "Bar"); let of = mimeparser.lookup_optional_field("Chat-Version").unwrap(); - assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "1.0",); + assert_eq!(&of, "1.0"); assert_eq!(mimeparser.parts.len(), 1); } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 70794257b..31dc62b20 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -125,7 +125,7 @@ pub unsafe fn dc_receive_imf( } }; - if let Some(field) = lookup_field(&mime_parser, "Date", MAILIMF_FIELD_ORIG_DATE) { + if let Some(field) = mime_parser.lookup_field_typ("Date", MAILIMF_FIELD_ORIG_DATE) { let orig_date = (*field).fld_data.fld_orig_date; if !orig_date.is_null() { // is not yet checked against bad times! we do this later if we have the database information. @@ -136,7 +136,7 @@ pub unsafe fn dc_receive_imf( // get From: and check if it is known (for known From:'s we add the other To:/Cc: in the 3rd pass) // or if From: is equal to SELF (in this case, it is any outgoing messages, // we do not check Return-Path any more as this is unreliable, see issue #150 - if let Some(field) = lookup_field(&mime_parser, "From", MAILIMF_FIELD_FROM) { + if let Some(field) = mime_parser.lookup_field_typ("From", MAILIMF_FIELD_FROM) { let fld_from = (*field).fld_data.fld_from; if !fld_from.is_null() { let mut check_self = 0; @@ -164,7 +164,7 @@ pub unsafe fn dc_receive_imf( } // Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass) - if let Some(field) = lookup_field(&mime_parser, "To", MAILIMF_FIELD_TO) { + if let Some(field) = mime_parser.lookup_field_typ("To", MAILIMF_FIELD_TO) { let fld_to = (*field).fld_data.fld_to; if !fld_to.is_null() { dc_add_or_lookup_contacts_by_address_list( @@ -321,7 +321,7 @@ unsafe fn add_parts( // collect the rest information, CC: is added to the to-list, BCC: is ignored // (we should not add BCC to groups as this would split groups. We could add them as "known contacts", // however, the benefit is very small and this may leak data that is expected to be hidden) - if let Some(field) = lookup_field(mime_parser, "Cc", MAILIMF_FIELD_CC) { + if let Some(field) = mime_parser.lookup_field_typ("Cc", MAILIMF_FIELD_CC) { let fld_cc = (*field).fld_data.fld_cc; if !fld_cc.is_null() { dc_add_or_lookup_contacts_by_address_list( @@ -344,7 +344,7 @@ unsafe fn add_parts( // change. (missing Message-IDs may come if the mail was set from this account with another // client that relies in the SMTP server to generate one. // true eg. for the Webmailer used in all-inkl-KAS) - if let Some(field) = lookup_field(mime_parser, "Message-ID", MAILIMF_FIELD_MESSAGE_ID) { + if let Some(field) = mime_parser.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) { let fld_message_id = (*field).fld_data.fld_message_id; if !fld_message_id.is_null() { rfc724_mid = dc_strdup((*fld_message_id).mid_value) @@ -606,7 +606,7 @@ unsafe fn add_parts( .sql .get_config_int(context, "save_mime_headers") .unwrap_or_default(); - if let Some(field) = lookup_field(mime_parser, "In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) { + if let Some(field) = mime_parser.lookup_field_typ("In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) { let fld_in_reply_to = (*field).fld_data.fld_in_reply_to; if !fld_in_reply_to.is_null() { mime_in_reply_to = dc_str_from_clist( @@ -616,7 +616,7 @@ unsafe fn add_parts( } } - if let Some(field) = lookup_field(mime_parser, "References", MAILIMF_FIELD_REFERENCES) { + if let Some(field) = mime_parser.lookup_field_typ("References", MAILIMF_FIELD_REFERENCES) { let fld_references = (*field).fld_data.fld_references; if !fld_references.is_null() { mime_references = dc_str_from_clist( @@ -762,19 +762,6 @@ unsafe fn add_parts( Ok(()) } -/// Lookup a mime field given its name and type. -fn lookup_field(parser: &MimeParser, name: &str, typ: u32) -> Option<*const mailimf_field> { - if let Some(field) = parser.lookup_field(name) { - if unsafe { (*field).fld_type } == typ as libc::c_int { - Some(field) - } else { - None - } - } else { - None - } -} - // Handle reports (mainly MDNs) unsafe fn handle_reports( context: &Context, @@ -1051,25 +1038,20 @@ unsafe fn create_or_lookup_group( let mut chat_id = 0; let mut chat_id_blocked = Blocked::Not; let mut grpid = "".to_string(); - let mut grpname = std::ptr::null_mut(); + let mut grpname = None; let to_ids_cnt = to_ids.len(); let mut recreate_member_list = 0; let mut send_EVENT_CHAT_MODIFIED = 0; - // pointer somewhere into mime_parser, must not be freed - let mut X_MrRemoveFromGrp = std::ptr::null_mut(); - // pointer somewhere into mime_parser, must not be freed - let mut X_MrAddToGrp = std::ptr::null_mut(); + let mut X_MrRemoveFromGrp = None; + let mut X_MrAddToGrp = None; let mut X_MrGrpNameChanged = 0; let mut X_MrGrpImageChanged = "".to_string(); let mut better_msg: String = From::from(""); - let cleanup = |grpname: *mut libc::c_char, - ret_chat_id: *mut u32, + let cleanup = |ret_chat_id: *mut u32, ret_chat_id_blocked: &mut Blocked, chat_id: u32, chat_id_blocked: Blocked| { - free(grpname.cast()); - if !ret_chat_id.is_null() { *ret_chat_id = chat_id; } @@ -1087,11 +1069,11 @@ unsafe fn create_or_lookup_group( set_better_msg(mime_parser, &better_msg); if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-ID") { - grpid = to_string((*optional_field).fld_value); + grpid = optional_field; } if grpid.is_empty() { - if let Some(field) = lookup_field(mime_parser, "Message-ID", MAILIMF_FIELD_MESSAGE_ID) { + if let Some(field) = mime_parser.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) { let fld_message_id = (*field).fld_data.fld_message_id; if !fld_message_id.is_null() { if let Some(extracted_grpid) = @@ -1104,7 +1086,8 @@ unsafe fn create_or_lookup_group( } } if grpid.is_empty() { - if let Some(field) = lookup_field(mime_parser, "In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) + if let Some(field) = + mime_parser.lookup_field_typ("In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) { let fld_in_reply_to = (*field).fld_data.fld_in_reply_to; if !fld_in_reply_to.is_null() { @@ -1115,7 +1098,7 @@ unsafe fn create_or_lookup_group( } if grpid.is_empty() { if let Some(field) = - lookup_field(mime_parser, "References", MAILIMF_FIELD_REFERENCES) + mime_parser.lookup_field_typ("References", MAILIMF_FIELD_REFERENCES) { let fld_references = (*field).fld_data.fld_references; if !fld_references.is_null() { @@ -1136,13 +1119,7 @@ unsafe fn create_or_lookup_group( &mut chat_id, &mut chat_id_blocked, ); - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } } @@ -1150,12 +1127,12 @@ unsafe fn create_or_lookup_group( } if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Name") { - grpname = dc_decode_header_words((*optional_field).fld_value); + grpname = Some(dc_decode_header_words_safe(&optional_field)); } if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") { - X_MrRemoveFromGrp = (*optional_field).fld_value; + X_MrRemoveFromGrp = Some(optional_field); mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup; - let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp)) + let left_group = (Contact::lookup_id_by_addr(context, X_MrRemoveFromGrp.as_ref().unwrap()) == from_id as u32) as libc::c_int; better_msg = context.stock_system_msg( if 0 != left_group { @@ -1163,21 +1140,20 @@ unsafe fn create_or_lookup_group( } else { StockMessage::MsgDelMember }, - as_str(X_MrRemoveFromGrp), + X_MrRemoveFromGrp.as_ref().unwrap(), "", from_id as u32, ) } else { if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Added") { - X_MrAddToGrp = (*optional_field).fld_value; + X_MrAddToGrp = Some(optional_field); mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") { - // fld_value is a pointer somewhere into mime_parser, must not be freed - X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); + X_MrGrpImageChanged = optional_field; } better_msg = context.stock_system_msg( StockMessage::MsgAddMember, - as_str(X_MrAddToGrp), + X_MrAddToGrp.as_ref().unwrap(), "", from_id as u32, ) @@ -1189,15 +1165,19 @@ unsafe fn create_or_lookup_group( mime_parser.is_system_message = SystemMessage::GroupNameChanged; better_msg = context.stock_system_msg( StockMessage::MsgGrpName, - as_str((*optional_field).fld_value), - as_str(grpname), + &optional_field, + if let Some(ref name) = grpname { + name + } else { + "" + }, from_id as u32, ) } else { if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") { // fld_value is a pointer somewhere into mime_parser, must not be freed - X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); + X_MrGrpImageChanged = optional_field; mime_parser.is_system_message = SystemMessage::GroupImageChanged; better_msg = context.stock_system_msg( if X_MrGrpImageChanged == "0" { @@ -1251,12 +1231,12 @@ unsafe fn create_or_lookup_group( if chat_id == 0 && !mime_parser.is_mailinglist_message() && !grpid.is_empty() - && !grpname.is_null() + && grpname.is_some() // otherwise, a pending "quit" message may pop up - && X_MrRemoveFromGrp.is_null() + && X_MrRemoveFromGrp.is_none() // re-create explicitly left groups only if ourself is re-added && (!group_explicitly_left - || !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp))) + || X_MrAddToGrp.is_some() && addr_cmp(&self_addr, X_MrAddToGrp.as_ref().unwrap())) { let mut create_verified = VerifiedStatus::Unverified; if mime_parser.lookup_field("Chat-Verified").is_some() { @@ -1275,16 +1255,16 @@ unsafe fn create_or_lookup_group( free(failure_reason.cast()); } if 0 == allow_creation { - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } - chat_id = create_group_record(context, &grpid, grpname, create_blocked, create_verified); + chat_id = create_group_record( + context, + &grpid, + grpname.as_ref().unwrap(), + create_blocked, + create_verified, + ); chat_id_blocked = create_blocked; recreate_member_list = 1; } @@ -1306,30 +1286,28 @@ unsafe fn create_or_lookup_group( &mut chat_id_blocked, ); } - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } // execute group commands - if !X_MrAddToGrp.is_null() || !X_MrRemoveFromGrp.is_null() { + if X_MrAddToGrp.is_some() || X_MrRemoveFromGrp.is_some() { recreate_member_list = 1; - } else if 0 != X_MrGrpNameChanged && !grpname.is_null() && strlen(grpname) < 200 { - info!(context, "updating grpname for chat {}", chat_id); - if sql::execute( - context, - &context.sql, - "UPDATE chats SET name=? WHERE id=?;", - params![as_str(grpname), chat_id as i32], - ) - .is_ok() - { - context.call_cb(Event::ChatModified(chat_id)); + } else if 0 != X_MrGrpNameChanged { + if let Some(ref grpname) = grpname { + if grpname.len() < 200 { + info!(context, "updating grpname for chat {}", chat_id); + if sql::execute( + context, + &context.sql, + "UPDATE chats SET name=? WHERE id=?;", + params![grpname, chat_id as i32], + ) + .is_ok() + { + context.call_cb(Event::ChatModified(chat_id)); + } + } } } if !X_MrGrpImageChanged.is_empty() { @@ -1375,11 +1353,7 @@ unsafe fn create_or_lookup_group( // than the one that is responsible for the current member list, see // https://github.com/deltachat/deltachat-core/issues/127 - let skip = if !X_MrRemoveFromGrp.is_null() { - X_MrRemoveFromGrp - } else { - ptr::null_mut() - }; + let skip = X_MrRemoveFromGrp.as_ref(); sql::execute( context, &context.sql, @@ -1387,21 +1361,20 @@ unsafe fn create_or_lookup_group( params![chat_id as i32], ) .ok(); - if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) { + if skip.is_none() || !addr_cmp(&self_addr, skip.unwrap()) { chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF); } if from_id > DC_CHAT_ID_LAST_SPECIAL { if !Contact::addr_equals_contact(context, &self_addr, from_id as u32) - && (skip.is_null() - || !Contact::addr_equals_contact(context, to_string(skip), from_id as u32)) + && (skip.is_none() + || !Contact::addr_equals_contact(context, skip.unwrap(), from_id as u32)) { chat::add_to_chat_contacts_table(context, chat_id, from_id as u32); } } for &to_id in to_ids.iter() { if !Contact::addr_equals_contact(context, &self_addr, to_id) - && (skip.is_null() - || !Contact::addr_equals_contact(context, to_string(skip), to_id)) + && (skip.is_none() || !Contact::addr_equals_contact(context, skip.unwrap(), to_id)) { chat::add_to_chat_contacts_table(context, chat_id, to_id); } @@ -1435,13 +1408,7 @@ unsafe fn create_or_lookup_group( } } - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); } /// Handle groups for received messages @@ -1459,15 +1426,11 @@ unsafe fn create_or_lookup_adhoc_group( // group matching the to-list or if we can create one let mut chat_id = 0; let mut chat_id_blocked = Blocked::Not; - let mut grpname = ptr::null_mut(); - let cleanup = |grpname: *mut libc::c_char, - ret_chat_id: *mut u32, + let cleanup = |ret_chat_id: *mut u32, ret_chat_id_blocked: &mut Blocked, chat_id: u32, chat_id_blocked: Blocked| { - free(grpname as *mut libc::c_void); - if !ret_chat_id.is_null() { *ret_chat_id = chat_id; } @@ -1477,13 +1440,7 @@ unsafe fn create_or_lookup_adhoc_group( // build member list from the given ids if to_ids.is_empty() || mime_parser.is_mailinglist_message() { // too few contacts or a mailinglist - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } @@ -1496,13 +1453,7 @@ unsafe fn create_or_lookup_adhoc_group( } if member_ids.len() < 3 { // too few contacts given - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } @@ -1525,25 +1476,13 @@ unsafe fn create_or_lookup_adhoc_group( chat_id = id as u32; chat_id_blocked = id_blocked; /* success, chat found */ - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } } if 0 == allow_creation { - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } // we do not check if the message is a reply to another group, this may result in @@ -1553,24 +1492,16 @@ unsafe fn create_or_lookup_adhoc_group( // - there is no need to check if this group exists; otherwise we would have caught it above let grpid = create_adhoc_grp_id(context, &member_ids); if grpid.is_empty() { - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); return; } // use subject as initial chat name - if let Some(subject) = mime_parser.subject.as_ref().filter(|s| !s.is_empty()) { - grpname = subject.strdup(); + let grpname = if let Some(subject) = mime_parser.subject.as_ref().filter(|s| !s.is_empty()) { + subject.to_string() } else { - grpname = context - .stock_string_repl_int(StockMessage::Member, member_ids.len() as libc::c_int) - .strdup(); - } + context.stock_string_repl_int(StockMessage::Member, member_ids.len() as libc::c_int) + }; // create group record chat_id = create_group_record( @@ -1587,19 +1518,13 @@ unsafe fn create_or_lookup_adhoc_group( context.call_cb(Event::ChatModified(chat_id)); - cleanup( - grpname, - ret_chat_id, - ret_chat_id_blocked, - chat_id, - chat_id_blocked, - ); + cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked); } fn create_group_record( context: &Context, grpid: impl AsRef, - grpname: *const libc::c_char, + grpname: impl AsRef, create_blocked: Blocked, create_verified: VerifiedStatus, ) -> u32 { @@ -1613,7 +1538,7 @@ fn create_group_record( } else { Chattype::Group }, - as_str(grpname), + grpname.as_ref(), grpid.as_ref(), create_blocked, ], @@ -1850,7 +1775,8 @@ unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimePars /* check if the message is a reply to a known message; the replies are identified by the Message-ID from `In-Reply-To`/`References:` (to support non-Delta-Clients) or from `Chat-Predecessor:` (Delta clients, see comment in dc_chat.c) */ if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Predecessor") { - if 0 != is_known_rfc724_mid(context, (*optional_field).fld_value) { + let optional_field_c = CString::new(optional_field).unwrap(); + if 0 != is_known_rfc724_mid(context, optional_field_c.as_ptr()) { return 1; } } diff --git a/src/securejoin.rs b/src/securejoin.rs index 6a302cd33..660cb4f01 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -1,6 +1,4 @@ -use mmime::mailimf_types::*; use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC}; -use std::ptr; use crate::aheader::EncryptPreference; use crate::chat::{self, Chat}; @@ -10,7 +8,6 @@ use crate::constants::*; use crate::contact::*; use crate::context::Context; use crate::dc_mimeparser::*; -use crate::dc_tools::*; use crate::e2ee::*; use crate::error::Error; use crate::events::Event; @@ -343,7 +340,7 @@ pub fn handle_securejoin_handshake( if contact_id <= DC_CONTACT_ID_LAST_SPECIAL { return 0; } - let step = match lookup_field(mimeparser, "Secure-Join") { + let step = match mimeparser.lookup_optional_field("Secure-Join") { Some(s) => s, None => { return 0; @@ -372,7 +369,7 @@ pub fn handle_securejoin_handshake( // it just ensures, we have Bobs key now. If we do _not_ have the key because eg. MitM has removed it, // send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here. // verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code - let invitenumber = match lookup_field(mimeparser, "Secure-Join-Invitenumber") { + let invitenumber = match mimeparser.lookup_optional_field("Secure-Join-Invitenumber") { Some(n) => n, None => { warn!(context, "Secure-join denied (invitenumber missing).",); @@ -459,7 +456,7 @@ pub fn handle_securejoin_handshake( ==== Step 6 in "Out-of-band verified groups" protocol ==== ============================================================ */ // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob - let fingerprint = match lookup_field(mimeparser, "Secure-Join-Fingerprint") { + let fingerprint = match mimeparser.lookup_optional_field("Secure-Join-Fingerprint") { Some(fp) => fp, None => { could_not_establish_secure_connection( @@ -488,7 +485,7 @@ pub fn handle_securejoin_handshake( } info!(context, "Fingerprint verified.",); // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code - let auth_0 = match lookup_field(mimeparser, "Secure-Join-Auth") { + let auth_0 = match mimeparser.lookup_optional_field("Secure-Join-Auth") { Some(auth) => auth, None => { could_not_establish_secure_connection( @@ -517,7 +514,9 @@ pub fn handle_securejoin_handshake( emit_event!(context, Event::ContactsChanged(Some(contact_id))); inviter_progress!(context, contact_id, 600); if join_vg { - let field_grpid = lookup_field(mimeparser, "Secure-Join-Group").unwrap_or_default(); + let field_grpid = mimeparser + .lookup_optional_field("Secure-Join-Group") + .unwrap_or_default(); let (group_chat_id, _, _) = chat::get_chat_id_by_grpid(context, &field_grpid); if group_chat_id == 0 { error!(context, "Chat {} not found.", &field_grpid); @@ -586,8 +585,9 @@ pub fn handle_securejoin_handshake( } Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); emit_event!(context, Event::ContactsChanged(None)); - let cg_member_added = - lookup_field(mimeparser, "Chat-Group-Member-Added").unwrap_or_default(); + let cg_member_added = mimeparser + .lookup_optional_field("Chat-Group-Member-Added") + .unwrap_or_default(); if join_vg && !addr_equals_self(context, cg_member_added) { info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."); return ret; @@ -651,24 +651,6 @@ fn secure_connection_established(context: &Context, contact_chat_id: u32) { emit_event!(context, Event::ChatModified(contact_chat_id)); } -fn lookup_field(mime_parser: &MimeParser, key: &str) -> Option { - if let Some(field) = mime_parser.lookup_field(key) { - let mut value: *const libc::c_char = ptr::null(); - if unsafe { (*field).fld_type } != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int - || unsafe { (*field).fld_data.fld_optional_field.is_null() } - || { - value = unsafe { (*(*field).fld_data.fld_optional_field).fld_value }; - value.is_null() - } - { - return None; - } - Some(to_string(value)) - } else { - None - } -} - fn could_not_establish_secure_connection(context: &Context, contact_chat_id: u32, details: &str) { let contact_id = chat_id_2_contact_id(context, contact_chat_id); let contact = Contact::get_by_id(context, contact_id); From e7108362763bbe800f474fb736693c1ac87e1408 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Sun, 15 Sep 2019 12:08:07 +0200 Subject: [PATCH 4/4] cleanup and fix earlier introduced scoping error --- src/dc_imex.rs | 3 +- src/dc_mimefactory.rs | 356 +++++++++++++++++++----------------------- src/dc_mimeparser.rs | 55 ++++--- src/message.rs | 11 +- src/param.rs | 8 + 5 files changed, 205 insertions(+), 228 deletions(-) diff --git a/src/dc_imex.rs b/src/dc_imex.rs index a2dc5df0e..cd8236bb6 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -490,10 +490,9 @@ pub unsafe fn dc_normalize_setup_code( pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) { let mut ok_to_continue = true; let mut success: libc::c_int = 0; - let what: libc::c_int; if dc_alloc_ongoing(context) { - what = job.param.get_int(Param::Cmd).unwrap_or_default(); + let what = job.param.get_int(Param::Cmd).unwrap_or_default(); let param1_s = job.param.get(Param::Arg).unwrap_or_default(); let param1 = CString::yolo(param1_s); let _param2 = CString::yolo(job.param.get(Param::Arg2).unwrap_or_default()); diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index b0bd84468..8418b468d 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -11,7 +11,6 @@ use mmime::mailmime_types_helper::*; use mmime::mailmime_write_mem::*; use mmime::mmapstring::*; use mmime::other::*; -use num_traits::FromPrimitive; use crate::chat::{self, Chat}; use crate::constants::*; @@ -172,10 +171,10 @@ pub unsafe fn dc_mimefactory_load_msg( ) .unwrap(); - let command = factory.msg.param.get_int(Param::Cmd).unwrap_or_default(); + let command = factory.msg.param.get_cmd(); let msg = &factory.msg; - if command == 5 { + if command == SystemMessage::MemberRemovedFromGroup { let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default(); let email_to_remove_c = email_to_remove.strdup(); @@ -199,8 +198,8 @@ pub unsafe fn dc_mimefactory_load_msg( } } } - if command != 6 - && command != 7 + if command != SystemMessage::AutocryptSetupMessage + && command != SystemMessage::SecurejoinMessage && 0 != context .sql .get_config_int(context, "mdns_enabled") @@ -531,12 +530,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact } /* build header etc. */ - let command = factory - .msg - .param - .get_int(Param::Cmd) - .and_then(SystemMessage::from_i32) - .unwrap_or_default(); + let command = factory.msg.param.get_cmd(); if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, @@ -554,197 +548,179 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact ), ); - match command { - SystemMessage::Unknown | SystemMessage::LocationOnly => {} - SystemMessage::MemberRemovedFromGroup => { - let email_to_remove = factory - .msg - .param - .get(Param::Arg) - .unwrap_or_default() - .strdup(); - if strlen(email_to_remove) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Member-Removed\x00" as *const u8 - as *const libc::c_char, - ), - email_to_remove, + if command == SystemMessage::MemberRemovedFromGroup { + let email_to_remove = factory + .msg + .param + .get(Param::Arg) + .unwrap_or_default() + .strdup(); + if strlen(email_to_remove) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Chat-Group-Member-Removed\x00" as *const u8 + as *const libc::c_char, ), - ); - } + email_to_remove, + ), + ); } - SystemMessage::MemberAddedToGroup => { - let msg = &factory.msg; - do_gossip = 1; - let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); - if strlen(email_to_add) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Member-Added\x00" as *const u8 - as *const libc::c_char, - ), - email_to_add, + } else if command == SystemMessage::MemberAddedToGroup { + let msg = &factory.msg; + do_gossip = 1; + let email_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + if strlen(email_to_add) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Chat-Group-Member-Added\x00" as *const u8 + as *const libc::c_char, ), - ); - grpimage = chat.param.get(Param::ProfileImage); - } - if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { - info!( - context, - "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", - "vg-member-added", - ); - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), - strdup( - b"vg-member-added\x00" as *const u8 as *const libc::c_char, - ), - ), - ); - } + email_to_add, + ), + ); + grpimage = chat.param.get(Param::ProfileImage); } - SystemMessage::GroupNameChanged => { - let msg = &factory.msg; + if 0 != msg.param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { + info!( + context, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + "vg-member-added", + ); + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), + strdup(b"vg-member-added\x00" as *const u8 as *const libc::c_char), + ), + ); + } + } else if command == SystemMessage::GroupNameChanged { + let msg = &factory.msg; - let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char, + ), + value_to_add, + ), + ); + } else if command == SystemMessage::GroupImageChanged { + let msg = &factory.msg; + grpimage = msg.param.get(Param::Arg); + if grpimage.is_none() { mailimf_fields_add( imf_fields, mailimf_field_new_custom( - strdup( - b"Chat-Group-Name-Changed\x00" as *const u8 - as *const libc::c_char, - ), - value_to_add, + strdup(b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char), + dc_strdup(b"0\x00" as *const u8 as *const libc::c_char), ), ); } - SystemMessage::GroupImageChanged => { - let msg = &factory.msg; - grpimage = msg.param.get(Param::Arg); - if grpimage.is_none() { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char, - ), - dc_strdup(b"0\x00" as *const u8 as *const libc::c_char), - ), - ); - } - } - SystemMessage::LocationStreamingEnabled => { + } + } + + if command == SystemMessage::LocationStreamingEnabled { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Chat-Content\x00" as *const u8 as *const libc::c_char), + strdup( + b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, + ), + ), + ); + } + if command == SystemMessage::AutocryptSetupMessage { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Autocrypt-Setup-Message\x00" as *const u8 as *const libc::c_char), + strdup(b"v1\x00" as *const u8 as *const libc::c_char), + ), + ); + placeholdertext = factory + .context + .stock_str(StockMessage::AcSetupMsgBody) + .strdup(); + } + if command == SystemMessage::SecurejoinMessage { + let msg = &factory.msg; + let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); + if strlen(step) > 0 { + info!( + context, + "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", + as_str(step), + ); + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), + step, + ), + ); + let param2 = msg.param.get(Param::Arg2).unwrap_or_default().strdup(); + if strlen(param2) > 0 { mailimf_fields_add( imf_fields, mailimf_field_new_custom( - strdup(b"Chat-Content\x00" as *const u8 as *const libc::c_char), - strdup( - b"location-streaming-enabled\x00" as *const u8 - as *const libc::c_char, - ), - ), - ); - } - SystemMessage::AutocryptSetupMessage => { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Autocrypt-Setup-Message\x00" as *const u8 - as *const libc::c_char, - ), - strdup(b"v1\x00" as *const u8 as *const libc::c_char), - ), - ); - placeholdertext = factory - .context - .stock_str(StockMessage::AcSetupMsgBody) - .strdup(); - } - SystemMessage::SecurejoinMessage => { - let msg = &factory.msg; - let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); - if strlen(step) > 0 { - info!( - context, - "sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>", - as_str(step), - ); - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup(b"Secure-Join\x00" as *const u8 as *const libc::c_char), + if strcmp( step, + b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char, + ) == 0 + || strcmp( + step, + b"vc-request-with-auth\x00" as *const u8 + as *const libc::c_char, + ) == 0 + { + strdup( + b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char, + ) + } else { + strdup( + b"Secure-Join-Invitenumber\x00" as *const u8 + as *const libc::c_char, + ) + }, + param2, + ), + ); + } + let fingerprint = msg.param.get(Param::Arg3).unwrap_or_default().strdup(); + if strlen(fingerprint) > 0 { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Secure-Join-Fingerprint\x00" as *const u8 + as *const libc::c_char, ), - ); - let param2 = msg.param.get(Param::Arg2).unwrap_or_default().strdup(); - if strlen(param2) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - if strcmp( - step, - b"vg-request-with-auth\x00" as *const u8 - as *const libc::c_char, - ) == 0 - || strcmp( - step, - b"vc-request-with-auth\x00" as *const u8 - as *const libc::c_char, - ) == 0 - { - strdup( - b"Secure-Join-Auth\x00" as *const u8 - as *const libc::c_char, - ) - } else { - strdup( - b"Secure-Join-Invitenumber\x00" as *const u8 - as *const libc::c_char, - ) - }, - param2, - ), - ); - } - let fingerprint = - msg.param.get(Param::Arg3).unwrap_or_default().strdup(); - if strlen(fingerprint) > 0 { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Secure-Join-Fingerprint\x00" as *const u8 - as *const libc::c_char, - ), - fingerprint, - ), - ); - } - let grpid = match msg.param.get(Param::Arg4) { - Some(id) => id.strdup(), - None => std::ptr::null_mut(), - }; - if !grpid.is_null() { - mailimf_fields_add( - imf_fields, - mailimf_field_new_custom( - strdup( - b"Secure-Join-Group\x00" as *const u8 - as *const libc::c_char, - ), - grpid, - ), - ); - } - } + fingerprint, + ), + ); + } + let grpid = match msg.param.get(Param::Arg4) { + Some(id) => id.strdup(), + None => std::ptr::null_mut(), + }; + if !grpid.is_null() { + mailimf_fields_add( + imf_fields, + mailimf_field_new_custom( + strdup( + b"Secure-Join-Group\x00" as *const u8 as *const libc::c_char, + ), + grpid, + ), + ); } } } @@ -1107,13 +1083,7 @@ unsafe fn get_subject( } else { b"\x00" as *const u8 as *const libc::c_char }; - if msg - .param - .get_int(Param::Cmd) - .and_then(SystemMessage::from_i32) - .unwrap_or_default() - == SystemMessage::AutocryptSetupMessage - { + if msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage { ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup() } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { ret = format!( diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 32c4cbf86..0247f340a 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -111,6 +111,7 @@ impl<'a> MimeParser<'a> { if self.lookup_optional_field("Chat-Version").is_some() { self.is_send_by_messenger = true } + if self.lookup_field("Autocrypt-Setup-Message").is_some() { let has_setup_file = self .parts @@ -326,14 +327,12 @@ impl<'a> MimeParser<'a> { } pub fn lookup_optional_field(&self, field_name: &str) -> Option { - if let Some(field) = self.lookup_field(field_name) { - if unsafe { (*field).fld_type } == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int { - let val = unsafe { (*field).fld_data.fld_optional_field }; - if val.is_null() { - return None; - } else { - return Some(unsafe { to_string_lossy((*val).fld_value) }); - } + if let Some(field) = self.lookup_field_typ(field_name, MAILIMF_FIELD_OPTIONAL_FIELD) { + let val = unsafe { (*field).fld_data.fld_optional_field }; + if val.is_null() { + return None; + } else { + return Some(unsafe { to_string_lossy((*val).fld_value) }); } } @@ -1063,22 +1062,28 @@ unsafe fn hash_header(out: &mut HashMap, in_0: *cons let field = cur as *mut mailimf_field; // TODO match on enums /rtn - let key = match (*field).fld_type { - 1 => Some("Return-Path".to_string()), - 9 => Some("Date".to_string()), - 10 => Some("From".to_string()), - 11 => Some("Sender".to_string()), - 12 => Some("Reply-To".to_string()), - 13 => Some("To".to_string()), - 14 => Some("Cc".to_string()), - 15 => Some("Bcc".to_string()), - 16 => Some("Message-ID".to_string()), - 17 => Some("In-Reply-To".to_string()), - 18 => Some("References".to_string()), - 19 => Some("Subject".to_string()), - 22 => { + let key = match (*field).fld_type as libc::c_uint { + MAILIMF_FIELD_RETURN_PATH => Some("Return-Path".to_string()), + MAILIMF_FIELD_ORIG_DATE => Some("Date".to_string()), + MAILIMF_FIELD_FROM => Some("From".to_string()), + MAILIMF_FIELD_SENDER => Some("Sender".to_string()), + MAILIMF_FIELD_REPLY_TO => Some("Reply-To".to_string()), + MAILIMF_FIELD_TO => Some("To".to_string()), + MAILIMF_FIELD_CC => Some("Cc".to_string()), + MAILIMF_FIELD_BCC => Some("Bcc".to_string()), + MAILIMF_FIELD_MESSAGE_ID => Some("Message-ID".to_string()), + MAILIMF_FIELD_IN_REPLY_TO => Some("In-Reply-To".to_string()), + MAILIMF_FIELD_REFERENCES => Some("References".to_string()), + MAILIMF_FIELD_SUBJECT => Some("Subject".to_string()), + MAILIMF_FIELD_OPTIONAL_FIELD => { // MAILIMF_FIELD_OPTIONAL_FIELD let optional_field = (*field).fld_data.fld_optional_field; + // XXX the optional field sometimes contains invalid UTF8 + // which should not happen (according to the mime standard). + // This might point to a bug in our mime parsing/processing + // logic. As mmime/dc_mimeparser is scheduled fore replacement + // anyway we just use a lossy conversion. + if !optional_field.is_null() { Some(to_string_lossy((*optional_field).fld_name)) } else { @@ -1088,12 +1093,6 @@ unsafe fn hash_header(out: &mut HashMap, in_0: *cons _ => None, }; if let Some(key) = key { - // XXX the optional field sometimes contains invalid UTF8 - // which should not happen (according to the mime standard). - // This might point to a bug in our mime parsing/processing - // logic. As mmime/dc_mimeparser is scheduled fore replacement - // anyway we just use a lossy conversion. - if !out.contains_key(&key) || // key already exists, only overwrite known types (protected headers) (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key.starts_with("Chat-") { diff --git a/src/message.rs b/src/message.rs index 021bdbf3e..cc07d9c8d 100644 --- a/src/message.rs +++ b/src/message.rs @@ -9,6 +9,7 @@ use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::*; +use crate::dc_mimeparser::SystemMessage; use crate::dc_tools::*; use crate::error::Error; use crate::events::Event; @@ -767,7 +768,7 @@ pub fn dc_msg_get_summarytext_by_raw( Viewtype::Video => context.stock_str(StockMessage::Video).into_owned(), Viewtype::Voice => context.stock_str(StockMessage::VoiceMessage).into_owned(), Viewtype::Audio | Viewtype::File => { - if param.get_int(Param::Cmd) == Some(6) { + if param.get_cmd() == SystemMessage::AutocryptSetupMessage { append_text = false; context .stock_str(StockMessage::AcSetupMsgSubject) @@ -793,7 +794,7 @@ pub fn dc_msg_get_summarytext_by_raw( } } _ => { - if param.get_int(Param::Cmd) != Some(9) { + if param.get_cmd() != SystemMessage::LocationOnly { "".to_string() } else { append_text = false; @@ -839,10 +840,10 @@ pub fn dc_msg_is_forwarded(msg: &Message) -> bool { } pub fn dc_msg_is_info(msg: &Message) -> bool { - let cmd = msg.param.get_int(Param::Cmd).unwrap_or_default(); + let cmd = msg.param.get_cmd(); msg.from_id == 2i32 as libc::c_uint || msg.to_id == 2i32 as libc::c_uint - || 0 != cmd && cmd != 6i32 + || cmd != SystemMessage::Unknown && cmd != SystemMessage::AutocryptSetupMessage } pub fn dc_msg_is_increation(msg: &Message) -> bool { @@ -854,7 +855,7 @@ pub fn dc_msg_is_setupmessage(msg: &Message) -> bool { return false; } - msg.param.get_int(Param::Cmd) == Some(6) + msg.param.get_cmd() == SystemMessage::AutocryptSetupMessage } pub unsafe fn dc_msg_get_setupcodebegin(context: &Context, msg: &Message) -> *mut libc::c_char { diff --git a/src/param.rs b/src/param.rs index 101aa3549..5b45fec11 100644 --- a/src/param.rs +++ b/src/param.rs @@ -4,6 +4,7 @@ use std::str; use num_traits::FromPrimitive; +use crate::dc_mimeparser::SystemMessage; use crate::error; /// Available param keys. @@ -178,6 +179,13 @@ impl Params { self.get(key).and_then(|s| s.parse().ok()) } + /// Get the parameter behind `Param::Cmd` interpreted as `SystemMessage`. + pub fn get_cmd(&self) -> SystemMessage { + self.get_int(Param::Cmd) + .and_then(SystemMessage::from_i32) + .unwrap_or_default() + } + /// Get the given parameter and parse as `f64`. pub fn get_float(&self, key: Param) -> Option { self.get(key).and_then(|s| s.parse().ok())