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_imex.rs b/src/dc_imex.rs index 2768b6ad0..d252e7446 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -478,10 +478,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 cb8b34506..c9747e09b 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -16,6 +16,7 @@ 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::*; @@ -169,10 +170,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(); @@ -196,8 +197,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") @@ -525,7 +526,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).unwrap_or_default(); + let command = factory.msg.param.get_cmd(); if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, @@ -541,7 +542,8 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact dc_encode_header_words(&chat.name), ), ); - if command == DC_CMD_MEMBER_REMOVED_FROM_GROUP { + + if command == SystemMessage::MemberRemovedFromGroup { let email_to_remove = factory .msg .param @@ -560,7 +562,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact ), ); } - } else if command == DC_CMD_MEMBER_ADDED_TO_GROUP { + } 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(); @@ -591,7 +593,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact ), ); } - } else if command == DC_CMD_GROUPNAME_CHANGED { + } else if command == SystemMessage::GroupNameChanged { let msg = &factory.msg; let value_to_add = msg.param.get(Param::Arg).unwrap_or_default().strdup(); @@ -604,7 +606,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact value_to_add, ), ); - } else if command == DC_CMD_GROUPIMAGE_CHANGED { + } else if command == SystemMessage::GroupImageChanged { let msg = &factory.msg; grpimage = msg.param.get(Param::Arg); if grpimage.is_none() { @@ -618,7 +620,8 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact } } } - if command == DC_CMD_LOCATION_STREAMING_ENABLED { + + if command == SystemMessage::LocationStreamingEnabled { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -629,7 +632,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact ), ); } - if command == DC_CMD_AUTOCRYPT_SETUP_MESSAGE { + if command == SystemMessage::AutocryptSetupMessage { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -644,8 +647,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact .to_string(), ); } - - if command == DC_CMD_SECUREJOIN_MESSAGE { + if command == SystemMessage::SecurejoinMessage { let msg = &factory.msg; let step = msg.param.get(Param::Arg).unwrap_or_default().strdup(); if strlen(step) > 0 { @@ -719,6 +721,7 @@ pub unsafe fn dc_mimefactory_render(context: &Context, factory: &mut dc_mimefact } } } + if let Some(grpimage) = grpimage { info!(factory.context, "setting group image '{}'", grpimage); let mut meta = dc_msg_new_untyped(); @@ -1061,7 +1064,7 @@ unsafe fn get_subject( dc_msg_get_summarytext_by_raw(msg.type_0, msg.text.as_ref(), &mut msg.param, 32, context); let fwd = if 0 != afwd_email { "Fwd: " } else { "" }; - if msg.param.get_int(Param::Cmd).unwrap_or_default() == DC_CMD_AUTOCRYPT_SETUP_MESSAGE { + 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!("Chat: {}: {}{}", chat.name, fwd, raw_subject,).strdup(); diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index 81882b928..b7b109834 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -1,8 +1,9 @@ use std::collections::{HashMap, HashSet}; -use std::ffi::CStr; +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::*; @@ -25,27 +26,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,450 +39,558 @@ 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 is_system_message: SystemMessage, 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, +#[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 } } -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: SystemMessage::Unknown, + 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 = SystemMessage::AutocryptSetupMessage; + + // 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 { + if let Some(optional_field) = self.lookup_optional_field("Chat-Content") { + if optional_field == "location-streaming-enabled" { + self.is_system_message = SystemMessage::LocationStreamingEnabled; } } } - } 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() { - for part in mimeparser.parts.iter_mut() { - if part.type_0 == Viewtype::Text { - let new_txt = format!( - "{} – {}", - subj, - part.msg.as_ref().expect("missing msg part") - ); - part.msg = Some(new_txt); - break; + 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.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; + 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); } } - 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 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 !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 0 != prepend_subject { + let subj = if let Some(n) = subject.find('[') { + &subject[0..n] + } else { + subject + } + .trim(); + + if !subj.is_empty() { + for part in self.parts.iter_mut() { + if part.typ == Viewtype::Text { + let new_txt = format!( + "{} – {}", + subj, + part.msg.as_ref().expect("missing msg part") + ); + part.msg = Some(new_txt); + 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 = 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); + } + } + } + } + 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; + let dn_field_c = CString::new(dn_field).unwrap(); + + if mailimf_mailbox_list_parse( + dn_field_c.as_ptr(), + strlen(dn_field_c.as_ptr()), + &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()) + } } - } - 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(), - } -} - -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(); - } - cur = if !cur.is_null() { - (*cur).next - } else { - ptr::null_mut() + self.parts.push(part_5); } } - 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; + pub fn get_last_nonmeta(&mut self) -> Option<&mut Part> { + self.parts.iter_mut().rev().find(|part| !part.is_meta) } - ptr::null_mut() -} + /* the following functions can be used only after a call to parse() */ -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 - { - info!( - mimeparser.context, - "Protected headers found in text/rfc822-headers attachment: Will be ignored.", - ); - return 0i32; + 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, } - 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() - { - warn!(mimeparser.context, "Protected headers parsing error.",); + } + + pub fn lookup_optional_field(&self, field_name: &str) -> Option { + 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 { - hash_header( - &mut mimeparser.header, - mimeparser.header_protected, - mimeparser.context, - ); + return Some(unsafe { to_string_lossy((*val).fld_value) }); + } + } + + 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 { - info!( - mimeparser.context, + None + } + } + + unsafe fn parse_mime_recursive(&mut self, mime: *mut mailmime) -> libc::c_int { + if mime.is_null() { + return 0; + } + let mut any_part_added = 0; + let mut cur: *mut clistiter; + + 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 + { + info!( + self.context, + "Protected headers found in text/rfc822-headers attachment: Will be ignored.", + ); + return 0i32; + } + 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 { @@ -506,261 +598,491 @@ 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() { - // 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-") + + 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) { - out.insert(key_r.to_string(), field); + /* 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 = format!( + "file.{}", + as_str((*(*mime).mm_content_type).ct_subtype) + ) + .strdup(); + } 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, + ); + } + } + } + _ => {} + } + } } } - cur1 = if !cur1.is_null() { - (*cur1).next - } else { - ptr::null_mut() + /* 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_filename = + dc_get_fine_path_filename(self.context, "$BLOBDIR", as_str(desired_filename)); + let bytes = std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes); + + /* copy data to file */ + if dc_write_file(self.context, &path_filename, bytes) { + 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, path_filename.to_string_lossy()); + 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); + } + } + + 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 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 precedence == "list" || precedence == "bulk" { + 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 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 { + None + } + } + _ => None, + }; + if let Some(key) = key { + 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, field); + } } } } @@ -770,20 +1092,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, @@ -929,42 +1253,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 */ @@ -979,371 +1295,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 = format!( - "file.{}", - as_str((*(*mime).mm_content_type).ct_subtype) - ) - .strdup(); - } 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 path_filename = - dc_get_fine_path_filename((*parser).context, "$BLOBDIR", as_str(desired_filename)); - let bytes = std::slice::from_raw_parts(decoded_data as *const u8, decoded_data_bytes); - - /* copy data to file */ - if dc_write_file((*parser).context, &path_filename, bytes) { - 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, path_filename.to_string_lossy()); - 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); - } -} - -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() @@ -1358,29 +1335,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 @@ -1391,9 +1358,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, @@ -1407,8 +1373,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; @@ -1416,80 +1383,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 { @@ -1507,78 +1410,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*/ @@ -1611,20 +1474,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() { @@ -1772,29 +1621,24 @@ 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"); - assert_eq!( - &to_string((*of).fld_value as *const libc::c_char), - "special-a", - ); + let of = mimeparser.lookup_optional_field("X-Special-A").unwrap(); + assert_eq!(&of, "special-a"); - of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo"); - assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "Bar",); + let of = mimeparser.lookup_optional_field("Foo").unwrap(); + assert_eq!(&of, "Bar"); - of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version"); - assert_eq!(&to_string((*of).fld_value as *const libc::c_char), "1.0",); + let of = mimeparser.lookup_optional_field("Chat-Version").unwrap(); + assert_eq!(&of, "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 443f2002c..e071dbe0d 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1,3 +1,4 @@ +use std::ffi::CString; use std::ptr; use itertools::join; @@ -62,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 @@ -123,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. @@ -134,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; @@ -148,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 { @@ -162,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( @@ -182,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, @@ -277,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, @@ -315,7 +317,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( @@ -338,7 +340,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) @@ -383,7 +385,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") @@ -409,7 +411,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; @@ -458,7 +460,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.",); } @@ -600,7 +602,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( @@ -610,7 +612,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( @@ -653,7 +655,7 @@ unsafe fn add_parts( } } } - if part.type_0 == Viewtype::Text { + if part.typ == Viewtype::Text { let msg_raw = part.msg_raw.as_ref().cloned().unwrap_or_default(); let subject = mime_parser .subject @@ -662,9 +664,9 @@ unsafe fn add_parts( .unwrap_or("".into()); txt_raw = Some(format!("{}\n\n{}", subject, msg_raw)); } - 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![ @@ -677,7 +679,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), @@ -746,24 +748,10 @@ unsafe fn add_parts( Ok(()) } -/// 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) - } else { - None - } -} - // 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)>, @@ -926,7 +914,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, @@ -1024,7 +1012,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, @@ -1036,28 +1024,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 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, + let cleanup = |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; } @@ -1068,18 +1048,18 @@ 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) } 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 = 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) = @@ -1092,7 +1072,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() { @@ -1103,7 +1084,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() { @@ -1124,30 +1105,20 @@ unsafe fn create_or_lookup_group( &mut chat_id, &mut chat_id_blocked, ); - cleanup( - grpname, - failure_reason, - 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; } } } } - 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 = Some(dc_decode_header_words_safe(&optional_field)); } - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Member-Removed"); - if !optional_field.is_null() { - 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)) + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") { + X_MrRemoveFromGrp = Some(optional_field); + mime_parser.is_system_message = SystemMessage::MemberRemovedFromGroup; + 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 { @@ -1155,47 +1126,45 @@ unsafe fn create_or_lookup_group( } else { StockMessage::MsgDelMember }, - as_str(X_MrRemoveFromGrp), + X_MrRemoveFromGrp.as_ref().unwrap(), "", from_id as u32, ) } else { - let optional_field = - dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-Member-Added"); - if !optional_field.is_null() { - 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() { - // fld_value is a pointer somewhere into mime_parser, must not be freed - X_MrGrpImageChanged = as_str((*optional_field).fld_value).to_string(); + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Added") { + 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") { + 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, ) } 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; + 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 { - 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; + X_MrGrpImageChanged = optional_field; + mime_parser.is_system_message = SystemMessage::GroupImageChanged; better_msg = context.stock_system_msg( if X_MrGrpImageChanged == "0" { StockMessage::MsgGrpImgDeleted @@ -1215,6 +1184,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, @@ -1224,8 +1195,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 - @@ -1242,18 +1215,20 @@ 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() + && 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 !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, @@ -1261,21 +1236,21 @@ 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, - 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; } @@ -1297,31 +1272,28 @@ unsafe fn create_or_lookup_group( &mut chat_id_blocked, ); } - cleanup( - grpname, - failure_reason, - 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() { @@ -1335,7 +1307,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) @@ -1367,11 +1339,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, @@ -1379,21 +1347,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); } @@ -1427,20 +1394,13 @@ unsafe fn create_or_lookup_group( } } - cleanup( - grpname, - failure_reason, - 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 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, @@ -1452,15 +1412,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; } @@ -1468,15 +1424,9 @@ 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, - 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; } @@ -1489,13 +1439,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; } @@ -1518,25 +1462,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 @@ -1546,24 +1478,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( @@ -1580,19 +1504,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 { @@ -1606,7 +1524,7 @@ fn create_group_record( } else { Chattype::Group }, - as_str(grpname), + grpname.as_ref(), grpid.as_ref(), create_blocked, ], @@ -1729,7 +1647,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, @@ -1829,46 +1747,54 @@ 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 0 != is_known_rfc724_mid(context, (*optional_field).fld_value) { + if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Predecessor") { + let optional_field_c = CString::new(optional_field).unwrap(); + if 0 != is_known_rfc724_mid(context, optional_field_c.as_ptr()) { 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 } @@ -1905,37 +1831,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/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()) diff --git a/src/securejoin.rs b/src/securejoin.rs index 805788c9d..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; @@ -335,7 +332,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; @@ -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(mimeparser: &dc_mimeparser_t, key: &str) -> Option { - let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key); - unsafe { - 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() - || { - value = (*(*field).fld_data.fld_optional_field).fld_value; - value.is_null() - } - { - return None; - } - Some(as_str(value).to_string()) - } -} - 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); @@ -706,10 +688,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