diff --git a/Cargo.lock b/Cargo.lock index 163a27e0c..cd6d60e4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -631,6 +631,7 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.2 (git+https://github.com/deltachat/lettre?branch=feat/rustls)", "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "mailparse 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "mmime 0.1.2", "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1416,6 +1417,16 @@ dependencies = [ "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mailparse" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", + "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "quoted_printable 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "matches" version = "0.1.8" @@ -1906,6 +1917,11 @@ dependencies = [ "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quoted_printable" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "r2d2" version = "0.8.6" @@ -3388,6 +3404,7 @@ dependencies = [ "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" +"checksum mailparse 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "51a60bad00d8aa905d31cf239f207ad4ef16c963ea53cf522d5fd7dc7f3ecfe2" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" @@ -3439,6 +3456,7 @@ dependencies = [ "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" +"checksum quoted_printable 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86cedf331228892e747bb85beb130b6bb23fc628c40dde9ea01eb6becea3c798" "checksum r2d2 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e48fa64898ef0286b6ee4b4d8f61483f9182acf5e44e62a398b1c7f56f2f861d" "checksum r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "806e268035ce9e5a604bf617ac8a073ef28b59ef2e48e8338db0baf530caef33" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" diff --git a/Cargo.toml b/Cargo.toml index 4ba349169..9aee209b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ stop-token = { version = "0.1.1", features = ["unstable"] } rustls = "0.16.0" webpki-roots = "0.18.0" webpki = "0.21.0" +mailparse = "0.9.2" [dev-dependencies] tempfile = "3.0" diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index fc264ace5..920c8656c 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -28,9 +28,10 @@ use crate::stock::StockMessage; use crate::wrapmime; #[derive(Debug)] -pub struct MimeParser<'a> { +pub struct MimeParser<'a, 'b> { pub context: &'a Context, pub parts: Vec, + mail: Option>, pub mimeroot: *mut Mailmime, pub header: HashMap, pub header_root: *mut mailimf_fields, @@ -83,10 +84,11 @@ const DC_MIMETYPE_VIDEO: i32 = 100; const DC_MIMETYPE_FILE: i32 = 110; const DC_MIMETYPE_AC_SETUP_FILE: i32 = 111; -impl<'a> MimeParser<'a> { +impl<'a, 'b> MimeParser<'a, 'b> { pub fn new(context: &'a Context) -> Self { MimeParser { parts: Vec::new(), + mail: None, mimeroot: std::ptr::null_mut(), header: Default::default(), header_root: std::ptr::null_mut(), @@ -106,229 +108,204 @@ impl<'a> MimeParser<'a> { } } - pub unsafe fn parse(&mut self, body: &[u8]) -> Result<(), Error> { + pub fn parse(&mut self, body: &'b [u8]) -> Result<(), Error> { let mut index = 0; - let r = mailmime_parse( - body.as_ptr() as *const libc::c_char, - body.len(), - &mut index, - &mut self.mimeroot, - ); + self.mail = Some(mailparse::parse_mail(body).unwrap()); - if r == MAILIMF_NO_ERROR as libc::c_int && !self.mimeroot.is_null() { - match e2ee::try_decrypt(self.context, self.mimeroot) { - Ok((encrypted, signatures, gossipped_addr)) => { - self.encrypted = encrypted; - self.signatures = signatures; - self.gossipped_addr = gossipped_addr; - } - Err(err) => { - // continue with the current, still encrypted, mime tree. - // unencrypted parts will be replaced by an error message - // that is added as "the message" to the chat then. - // - // if we just return here, the header is missing - // and the caller cannot display the message - // and try to assign the message to a chat - warn!(self.context, "decryption failed: {}", err); - } - } + // TODO: decrypt + // match e2ee::try_decrypt(self.context, self.mimeroot) { + // Ok((encrypted, signatures, gossipped_addr)) => { + // self.encrypted = encrypted; + // self.signatures = signatures; + // self.gossipped_addr = gossipped_addr; + // } + // Err(err) => { + // // continue with the current, still encrypted, mime tree. + // // unencrypted parts will be replaced by an error message + // // that is added as "the message" to the chat then. + // // + // // if we just return here, the header is missing + // // and the caller cannot display the message + // // and try to assign the message to a chat + // warn!(self.context, "decryption failed: {}", err); + // } + // } - self.parse_mime_recursive(self.mimeroot); + self.parse_mime_recursive(None); - 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 = to_opt_string_lossy(subj).map(|x| dc_decode_header_words(&x)); - } - } + if let Some(field) = self.lookup_field("Subject") { + self.subject = Some(field.get_value().unwrap()); + } - if self.lookup_optional_field("Chat-Version").is_some() { - self.is_send_by_messenger = true - } + if self.lookup_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 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; + 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)); + // 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; - } - } - if self.lookup_field("Chat-Group-Image").is_some() && !self.parts.is_empty() { - let textpart = &self.parts[0]; - if textpart.typ == Viewtype::Text && self.parts.len() >= 2 { - let imgpart = &mut self.parts[1]; - if imgpart.typ == Viewtype::Image { - imgpart.is_meta = true; - } - } - } - 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::Sticker - || filepart.typ == Viewtype::Audio - || filepart.typ == Viewtype::Voice - || filepart.typ == Viewtype::Video - || filepart.typ == Viewtype::File) - && !filepart.is_meta - }; - - if need_drop { - let mut filepart = self.parts.swap_remove(1); - - // insert new one - filepart.msg = self.parts[0].msg.as_ref().map(|s| s.to_string()); - - // forget the one we use now - self.parts[0].msg = None; - - // swap new with old - std::mem::replace(&mut self.parts[0], filepart); - } - } - if let Some(ref subject) = self.subject { - let mut prepend_subject: libc::c_int = 1i32; - if !self.decrypting_failed { - let colon = subject.find(':'); - if colon == Some(2) - || colon == Some(3) - || self.is_send_by_messenger - || subject.contains("Chat:") - { - prepend_subject = 0i32 - } - } - if 0 != prepend_subject { - let subj = if let Some(n) = subject.find('[') { - &subject[0..n] + let mut i = 0; + while i != self.parts.len() { + if self.parts[i].mimetype != 111 { + self.parts.remove(i); } else { - subject + i += 1; } - .trim(); + } + } + } else if let Some(optional_field) = self.lookup_field("Chat-Content") { + let value = optional_field.get_value(); + if value.is_ok() && value.as_ref().unwrap() == "location-streaming-enabled" { + self.is_system_message = SystemMessage::LocationStreamingEnabled; + } + } + if self.lookup_field("Chat-Group-Image").is_some() && !self.parts.is_empty() { + let textpart = &self.parts[0]; + if textpart.typ == Viewtype::Text && self.parts.len() >= 2 { + let imgpart = &mut self.parts[1]; + if imgpart.typ == Viewtype::Image { + imgpart.is_meta = true; + } + } + } + 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::Sticker + || filepart.typ == Viewtype::Audio + || filepart.typ == Viewtype::Voice + || filepart.typ == Viewtype::Video + || filepart.typ == Viewtype::File) + && !filepart.is_meta + }; - 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; - } - } - } - } - } - 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; - } - } - if self.parts[0].typ == Viewtype::Image { - if let Some(content_type) = self.lookup_optional_field("Chat-Content") { - if content_type == "sticker" { - let part_mut = &mut self.parts[0]; - part_mut.typ = Viewtype::Sticker; - } - } - } - 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 need_drop { + let mut filepart = self.parts.swap_remove(1); + + // insert new one + filepart.msg = self.parts[0].msg.as_ref().map(|s| s.to_string()); + + // forget the one we use now + self.parts[0].msg = None; + + // swap new with old + std::mem::replace(&mut self.parts[0], filepart); } + } + if let Some(ref subject) = self.subject { + let mut prepend_subject: libc::c_int = 1i32; if !self.decrypting_failed { - if let Some(dn_field) = - self.lookup_optional_field("Chat-Disposition-Notification-To") + let colon = subject.find(':'); + if colon == Some(2) + || colon == Some(3) + || self.is_send_by_messenger + || subject.contains("Chat:") { - 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_or_default(); + prepend_subject = 0i32 + } + } + if 0 != prepend_subject { + let subj = if let Some(n) = subject.find('[') { + &subject[0..n] + } else { + subject + } + .trim(); - 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) = wrapmime::mailimf_find_first_addr(mb_list) { - if let Some(from_field) = self.lookup_field("From") { - if (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int - && !(*from_field).fld_data.fld_from.is_null() - { - let from_addr = wrapmime::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); + 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; } } } } } + 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_field("Chat-Voice-Message").is_some() { + let part_mut = &mut self.parts[0]; + part_mut.typ = Viewtype::Voice; + } + } + if self.parts[0].typ == Viewtype::Image { + if let Some(content_type) = self.lookup_field("Chat-Content") { + let value = content_type.get_value(); + if value.is_ok() && value.as_ref().unwrap() == "sticker" { + let part_mut = &mut self.parts[0]; + part_mut.typ = Viewtype::Sticker; + } + } + } + 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_field("Chat-Duration") { + let duration_ms = field_0.get_value().unwrap().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_field("Chat-Disposition-Notification-To") { + if self.get_last_nonmeta().is_some() { + let addrs = mailparse::addrparse(&dn_field.get_value().unwrap()).unwrap(); + + if let Some(dn_to_addr) = addrs.first() { + if let Some(from_field) = self.lookup_field("From") { + let value = from_field.get_value().unwrap(); + let from_addrs = mailparse::addrparse(&value).unwrap(); + if let Some(from_addr) = from_addrs.first() { + if from_addr == dn_to_addr { + if let Some(part_4) = self.get_last_nonmeta_mut() { + part_4.param.set_int(Param::WantsMdn, 1); + } + } + } + } + } + } + } + } + /* 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(); @@ -345,66 +322,41 @@ impl<'a> MimeParser<'a> { Ok(()) } - pub fn get_last_nonmeta(&mut self) -> Option<&mut Part> { + pub fn get_last_nonmeta(&self) -> Option<&Part> { + self.parts.iter().rev().find(|part| !part.is_meta) + } + + pub fn get_last_nonmeta_mut(&mut self) -> Option<&mut Part> { self.parts.iter_mut().rev().find(|part| !part.is_meta) } /* the following functions can be used only after a call to parse() */ - pub fn lookup_field(&self, field_name: &str) -> Option<*mut mailimf_field> { - match self.header.get(field_name) { - Some(v) => { - if v.is_null() { - None - } else { - Some(*v) - } - } - None => None, + pub fn lookup_field(&self, field_name: &str) -> Option<&'b mailparse::MailHeader<'_>> { + if let Some(ref mail) = self.mail { + return mail + .headers + .iter() + .find(|header| header.get_key().unwrap() == field_name); } - } - - 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 { - 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 { - None - } - } + fn parse_mime_recursive(&mut self, omail: Option>) -> bool { + let mail = omail + .as_ref() + .unwrap_or_else(|| self.mail.as_ref().unwrap()); - unsafe fn parse_mime_recursive(&mut self, mime: *mut Mailmime) -> bool { - if mime.is_null() { - return false; - } + let ctype = mailparse::parse_content_type( + &self + .lookup_field("Content-Type") + .unwrap() + .get_value() + .unwrap(), + ); - if !mailmime_find_ct_parameter(mime, "protected-headers").is_null() { - let mime = *mime; - - 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() - && &to_string_lossy((*mime.mm_content_type).ct_subtype) == "rfc822-headers" - { + if let Some(protected_headers) = ctype.params.get("protected-headers") { + if mail.subparts.is_empty() && ctype.mimetype == "text/rfc822-headers" { info!( self.context, "Protected headers found in text/rfc822-headers attachment: Will be ignored.", @@ -413,21 +365,23 @@ impl<'a> MimeParser<'a> { } if self.header_protected.is_null() { - /* use the most outer protected header - this is typically - created in sync with the normal, unprotected header */ - 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); - } + // TODO: + + // /* use the most outer protected header - this is typically + // created in sync with the normal, unprotected header */ + // 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, @@ -436,331 +390,340 @@ impl<'a> MimeParser<'a> { } } - match (*mime).mm_type as u32 { - MAILMIME_SINGLE => self.add_single_part_if_known(mime), - MAILMIME_MULTIPLE => self.handle_multiple(mime), - MAILMIME_MESSAGE => { - 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() { - return false; - } + // single = multipart/* only one + // multiple = multipart/* multiple + // message = text/rfc822 - self.parse_mime_recursive((*mime).mm_data.mm_message.mm_msg_mime) - } - _ => false, + if mail.ctype.mimetype == "text/rfc822" { + // 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() { + // return false; + // } + + let raw = mail.get_body_raw().unwrap(); + let mail = mailparse::parse_mail(&raw).unwrap(); + + return self.parse_mime_recursive(Some(mail)); } + + if mail.subparts.len() > 1 { + return self.handle_multiple(omail); + } + + if mail.subparts.len() == 1 { + return self.add_single_part_if_known(omail); + } + false } - unsafe fn handle_multiple(&mut self, mime: *mut Mailmime) -> bool { - let mut any_part_added = false; - match mailmime_get_mime_type(mime) { - /* Most times, mutlipart/alternative contains true alternatives - as text/plain and text/html. If we find a multipart/mixed - inside mutlipart/alternative, we use this (happens eg in - apple mail: "plaintext" as an alternative to "html+PDF attachment") */ - (DC_MIMETYPE_MP_ALTERNATIVE, _, _) => { - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - if mailmime_get_mime_type(cur_data as *mut _).0 == DC_MIMETYPE_MP_MIXED { - any_part_added = self.parse_mime_recursive(cur_data as *mut _); - break; - } - } - if !any_part_added { - /* search for text/plain and add this */ - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - if mailmime_get_mime_type(cur_data as *mut _).0 == DC_MIMETYPE_TEXT_PLAIN { - any_part_added = self.parse_mime_recursive(cur_data as *mut _); - break; - } - } - } - if !any_part_added { - /* `text/plain` not found - use the first part */ - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - if self.parse_mime_recursive(cur_data as *mut _) { - any_part_added = true; - break; - } - } - } - } - (DC_MIMETYPE_MP_RELATED, _, _) => { - /* add the "root part" - the other parts may be referenced which is - not interesting for us (eg. embedded images) we assume he "root part" - being the first one, which may not be always true ... - however, most times it seems okay. */ - let cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - if !cur.is_null() { - any_part_added = self.parse_mime_recursive((*cur).data as *mut Mailmime); - } - } - (DC_MIMETYPE_MP_NOT_DECRYPTABLE, _, _) => { - let mut part = Part::default(); - part.typ = Viewtype::Text; - let msg_body = self.context.stock_str(StockMessage::CantDecryptMsgBody); + fn handle_multiple(&mut self, omail: Option>) -> bool { + // let mut any_part_added = false; + // match mailmime_get_mime_type(mime) { + // /* Most times, mutlipart/alternative contains true alternatives + // as text/plain and text/html. If we find a multipart/mixed + // inside mutlipart/alternative, we use this (happens eg in + // apple mail: "plaintext" as an alternative to "html+PDF attachment") */ + // (DC_MIMETYPE_MP_ALTERNATIVE, _, _) => { + // for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + // if mailmime_get_mime_type(cur_data as *mut _).0 == DC_MIMETYPE_MP_MIXED { + // any_part_added = self.parse_mime_recursive(cur_data as *mut _); + // break; + // } + // } + // if !any_part_added { + // /* search for text/plain and add this */ + // for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + // if mailmime_get_mime_type(cur_data as *mut _).0 == DC_MIMETYPE_TEXT_PLAIN { + // any_part_added = self.parse_mime_recursive(cur_data as *mut _); + // break; + // } + // } + // } + // if !any_part_added { + // /* `text/plain` not found - use the first part */ + // for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + // if self.parse_mime_recursive(cur_data as *mut _) { + // any_part_added = true; + // break; + // } + // } + // } + // } + // (DC_MIMETYPE_MP_RELATED, _, _) => { + // /* add the "root part" - the other parts may be referenced which is + // not interesting for us (eg. embedded images) we assume he "root part" + // being the first one, which may not be always true ... + // however, most times it seems okay. */ + // let cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + // if !cur.is_null() { + // any_part_added = self.parse_mime_recursive((*cur).data as *mut Mailmime); + // } + // } + // (DC_MIMETYPE_MP_NOT_DECRYPTABLE, _, _) => { + // 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); + // let txt = format!("[{}]", msg_body); + // part.msg_raw = Some(txt.clone()); + // part.msg = Some(txt); - self.parts.push(part); - any_part_added = true; - self.decrypting_failed = true; - } - (DC_MIMETYPE_MP_SIGNED, _, _) => { - /* RFC 1847: "The multipart/signed content type - contains exactly two body parts. The first body - part is the body part over which the digital signature was created [...] - The second body part contains the control information necessary to - verify the digital signature." We simpliy take the first body part and - skip the rest. (see - https://k9mail.github.io/2016/11/24/OpenPGP-Considerations-Part-I.html - for background information why we use encrypted+signed) */ - let cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; - if !cur.is_null() { - any_part_added = self.parse_mime_recursive((*cur).data as *mut _); - } - } - (DC_MIMETYPE_MP_REPORT, _, _) => { - /* RFC 6522: the first part is for humans, the second for machines */ - if (*(*mime).mm_data.mm_multipart.mm_mp_list).count >= 2 { - let report_type = mailmime_find_ct_parameter(mime, "report-type"); - if !report_type.is_null() - && !(*report_type).pa_value.is_null() - && &to_string_lossy((*report_type).pa_value) == "disposition-notification" - { - self.reports.push(mime); - } else { - /* eg. `report-type=delivery-status`; - maybe we should show them as a little error icon */ - if !(*(*mime).mm_data.mm_multipart.mm_mp_list).first.is_null() { - any_part_added = self.parse_mime_recursive( - (*(*(*mime).mm_data.mm_multipart.mm_mp_list).first).data as *mut _, - ); - } - } - } - } - _ => { - /* eg. DC_MIMETYPE_MP_MIXED - add all parts (in fact, - AddSinglePartIfKnown() later check if the parts are really supported) - HACK: the following lines are a hack for clients who use - multipart/mixed instead of multipart/alternative for - combined text/html messages (eg. Stock Android "Mail" does so). - So, if we detect such a message below, we skip the HTML - part. However, not sure, if there are useful situations to use - plain+html in multipart/mixed - if so, we should disable the hack. */ - let mut skip_part = ptr::null_mut(); - let mut html_part = ptr::null_mut(); - let mut plain_cnt = 0; - let mut html_cnt = 0; + // self.parts.push(part); + // any_part_added = true; + // self.decrypting_failed = true; + // } + // (DC_MIMETYPE_MP_SIGNED, _, _) => { + // /* RFC 1847: "The multipart/signed content type + // contains exactly two body parts. The first body + // part is the body part over which the digital signature was created [...] + // The second body part contains the control information necessary to + // verify the digital signature." We simpliy take the first body part and + // skip the rest. (see + // https://k9mail.github.io/2016/11/24/OpenPGP-Considerations-Part-I.html + // for background information why we use encrypted+signed) */ + // let cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; + // if !cur.is_null() { + // any_part_added = self.parse_mime_recursive((*cur).data as *mut _); + // } + // } + // (DC_MIMETYPE_MP_REPORT, _, _) => { + // /* RFC 6522: the first part is for humans, the second for machines */ + // if (*(*mime).mm_data.mm_multipart.mm_mp_list).count >= 2 { + // let report_type = mailmime_find_ct_parameter(mime, "report-type"); + // if !report_type.is_null() + // && !(*report_type).pa_value.is_null() + // && &to_string_lossy((*report_type).pa_value) == "disposition-notification" + // { + // self.reports.push(mime); + // } else { + // /* eg. `report-type=delivery-status`; + // maybe we should show them as a little error icon */ + // if !(*(*mime).mm_data.mm_multipart.mm_mp_list).first.is_null() { + // any_part_added = self.parse_mime_recursive( + // (*(*(*mime).mm_data.mm_multipart.mm_mp_list).first).data as *mut _, + // ); + // } + // } + // } + // } + // _ => { + // /* eg. DC_MIMETYPE_MP_MIXED - add all parts (in fact, + // AddSinglePartIfKnown() later check if the parts are really supported) + // HACK: the following lines are a hack for clients who use + // multipart/mixed instead of multipart/alternative for + // combined text/html messages (eg. Stock Android "Mail" does so). + // So, if we detect such a message below, we skip the HTML + // part. However, not sure, if there are useful situations to use + // plain+html in multipart/mixed - if so, we should disable the hack. */ + // let mut skip_part = ptr::null_mut(); + // let mut html_part = ptr::null_mut(); + // let mut plain_cnt = 0; + // let mut html_cnt = 0; - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - match mailmime_get_mime_type(cur_data as *mut _) { - (DC_MIMETYPE_TEXT_PLAIN, _, _) => { - plain_cnt += 1; - } - (DC_MIMETYPE_TEXT_HTML, _, _) => { - html_part = cur_data as *mut Mailmime; - html_cnt += 1; - } - _ => {} - } - } - if plain_cnt == 1 && html_cnt == 1 { - 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; - } + // for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + // match mailmime_get_mime_type(cur_data as *mut _) { + // (DC_MIMETYPE_TEXT_PLAIN, _, _) => { + // plain_cnt += 1; + // } + // (DC_MIMETYPE_TEXT_HTML, _, _) => { + // html_part = cur_data as *mut Mailmime; + // html_cnt += 1; + // } + // _ => {} + // } + // } + // if plain_cnt == 1 && html_cnt == 1 { + // 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; + // } - for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { - if cur_data as *mut _ != skip_part { - if self.parse_mime_recursive(cur_data as *mut _) { - any_part_added = true; - } - } - } - } - } + // for cur_data in (*(*mime).mm_data.mm_multipart.mm_mp_list).into_iter() { + // if cur_data as *mut _ != skip_part { + // if self.parse_mime_recursive(cur_data as *mut _) { + // any_part_added = true; + // } + // } + // } + // } + // } - any_part_added + // any_part_added + unimplemented!() } - unsafe fn add_single_part_if_known(&mut self, mime: *mut Mailmime) -> bool { - // return true if a part was added - if mime.is_null() || (*mime).mm_data.mm_single.is_null() { - return false; - } + fn add_single_part_if_known(&mut self, omail: Option>) -> bool { + unimplemented!() + // // return true if a part was added + // let (mime_type, msg_type, raw_mime) = mailmime_get_mime_type(mime); - let (mime_type, msg_type, raw_mime) = mailmime_get_mime_type(mime); + // let mime_data = (*mime).mm_data.mm_single; + // if (*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int + // /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */ + // || (*mime_data).dt_data.dt_text.dt_data.is_null() + // || (*mime_data).dt_data.dt_text.dt_length <= 0 + // { + // return false; + // } - let mime_data = (*mime).mm_data.mm_single; - if (*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int - /* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */ - || (*mime_data).dt_data.dt_text.dt_data.is_null() - || (*mime_data).dt_data.dt_text.dt_length <= 0 - { - return false; - } + // let mut decoded_data = match wrapmime::mailmime_transfer_decode(mime) { + // Ok(decoded_data) => decoded_data, + // Err(_) => { + // // Note that it's not always an error - might be no data + // return false; + // } + // }; - let mut decoded_data = match wrapmime::mailmime_transfer_decode(mime) { - Ok(decoded_data) => decoded_data, - Err(_) => { - // Note that it's not always an error - might be no data - return false; - } - }; + // let old_part_count = self.parts.len(); - let old_part_count = self.parts.len(); + // /* regard `Content-Transfer-Encoding:` */ + // let mut desired_filename = String::default(); + // match mime_type { + // DC_MIMETYPE_TEXT_PLAIN | DC_MIMETYPE_TEXT_HTML => { + // /* 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_string_lossy().as_bytes()) + // { + // let (res, _, _) = encoding.decode(&decoded_data); + // if res.is_empty() { + // /* no error - but nothing to add */ + // return false; + // } + // decoded_data = res.as_bytes().to_vec() + // } else { + // warn!( + // self.context, + // "Cannot convert {} bytes from \"{}\" to \"utf-8\".", + // decoded_data.len(), + // to_string_lossy(charset), + // ); + // } + // } + // /* check header directly as is_send_by_messenger is not yet set up */ + // let is_msgrmsg = self.lookup_field(mail, "Chat-Version").is_some(); - /* regard `Content-Transfer-Encoding:` */ - let mut desired_filename = String::default(); - match mime_type { - DC_MIMETYPE_TEXT_PLAIN | DC_MIMETYPE_TEXT_HTML => { - /* 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_string_lossy().as_bytes()) - { - let (res, _, _) = encoding.decode(&decoded_data); - if res.is_empty() { - /* no error - but nothing to add */ - return false; - } - decoded_data = res.as_bytes().to_vec() - } else { - warn!( - self.context, - "Cannot convert {} bytes from \"{}\" to \"utf-8\".", - decoded_data.len(), - to_string_lossy(charset), - ); - } - } - /* 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 mut simplifier = Simplify::new(); + // let simplified_txt = if decoded_data.is_empty() { + // "".into() + // } else { + // let input = std::string::String::from_utf8_lossy(&decoded_data); + // let is_html = mime_type == 70; - let mut simplifier = Simplify::new(); - let simplified_txt = if decoded_data.is_empty() { - "".into() - } else { - let input = std::string::String::from_utf8_lossy(&decoded_data); - let is_html = mime_type == 70; + // simplifier.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 = + // Some(std::string::String::from_utf8_lossy(&decoded_data).to_string()); + // self.do_add_single_part(part); + // } - simplifier.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 = - Some(std::string::String::from_utf8_lossy(&decoded_data).to_string()); - self.do_add_single_part(part); - } + // if simplifier.is_forwarded { + // self.is_forwarded = true; + // } + // } + // DC_MIMETYPE_IMAGE + // | DC_MIMETYPE_AUDIO + // | DC_MIMETYPE_VIDEO + // | DC_MIMETYPE_FILE + // | DC_MIMETYPE_AC_SETUP_FILE => { + // /* 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(); - if simplifier.is_forwarded { - self.is_forwarded = true; - } - } - DC_MIMETYPE_IMAGE - | DC_MIMETYPE_AUDIO - | DC_MIMETYPE_VIDEO - | DC_MIMETYPE_FILE - | DC_MIMETYPE_AC_SETUP_FILE => { - /* 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 - { - // we assume the filename*?* parts are in order, not seen anything else yet - 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 - { - // might be a wrongly encoded filename - let s = to_string_lossy((*dsp_param).pa_data.pa_filename); - // this is used only if the parts buffer stays empty - desired_filename = dc_decode_header_words(&s) - } - } - } - } - break; - } - } - if !filename_parts.is_empty() { - desired_filename = dc_decode_ext_header(filename_parts.as_bytes()).into_owned(); - } - if desired_filename.is_empty() { - let param = mailmime_find_ct_parameter(mime, "name"); - if !param.is_null() - && !(*param).pa_value.is_null() - && 0 != *(*param).pa_value.offset(0isize) as libc::c_int - { - // might be a wrongly encoded filename - desired_filename = to_string_lossy((*param).pa_value); - } - } - /* if there is still no filename, guess one */ - if desired_filename.is_empty() { - if !(*mime).mm_content_type.is_null() - && !(*(*mime).mm_content_type).ct_subtype.is_null() - { - desired_filename = format!( - "file.{}", - to_string_lossy((*(*mime).mm_content_type).ct_subtype) - ); - } else { - return false; - } - } - self.do_add_single_file_part( - msg_type, - mime_type, - raw_mime.as_ref(), - &decoded_data, - &desired_filename, - ); - } - _ => {} - } - /* add object? (we do not add all objects, eg. signatures etc. are ignored) */ - self.parts.len() > old_part_count + // 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 + // { + // // we assume the filename*?* parts are in order, not seen anything else yet + // 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 + // { + // // might be a wrongly encoded filename + // let s = to_string_lossy((*dsp_param).pa_data.pa_filename); + // // this is used only if the parts buffer stays empty + // desired_filename = dc_decode_header_words(&s) + // } + // } + // } + // } + // break; + // } + // } + // if !filename_parts.is_empty() { + // desired_filename = dc_decode_ext_header(filename_parts.as_bytes()).into_owned(); + // } + // if desired_filename.is_empty() { + // let param = mailmime_find_ct_parameter(mime, "name"); + // if !param.is_null() + // && !(*param).pa_value.is_null() + // && 0 != *(*param).pa_value.offset(0isize) as libc::c_int + // { + // // might be a wrongly encoded filename + // desired_filename = to_string_lossy((*param).pa_value); + // } + // } + // /* if there is still no filename, guess one */ + // if desired_filename.is_empty() { + // if !(*mime).mm_content_type.is_null() + // && !(*(*mime).mm_content_type).ct_subtype.is_null() + // { + // desired_filename = format!( + // "file.{}", + // to_string_lossy((*(*mime).mm_content_type).ct_subtype) + // ); + // } else { + // return false; + // } + // } + // self.do_add_single_file_part( + // msg_type, + // mime_type, + // raw_mime.as_ref(), + // &decoded_data, + // &desired_filename, + // ); + // } + // _ => {} + // } + // /* add object? (we do not add all objects, eg. signatures etc. are ignored) */ + // self.parts.len() > old_part_count } unsafe fn do_add_single_file_part( @@ -844,8 +807,10 @@ impl<'a> MimeParser<'a> { return true; } - if let Some(precedence) = self.lookup_optional_field("Precedence") { - if precedence == "list" || precedence == "bulk" { + if let Some(precedence) = self.lookup_field("Precedence") { + if precedence.get_value().unwrap() == "list" + || precedence.get_value().unwrap() == "bulk" + { return true; } } @@ -906,19 +871,14 @@ impl<'a> MimeParser<'a> { pub fn get_rfc724_mid(&mut self) -> Option { // get Message-ID from header - if let Some(field) = self.lookup_field_typ("Message-ID", MAILIMF_FIELD_MESSAGE_ID) { - unsafe { - let fld_message_id = (*field).fld_data.fld_message_id; - if !fld_message_id.is_null() { - return Some(to_string_lossy((*fld_message_id).mid_value)); - } - } + if let Some(field) = self.lookup_field("Message-ID") { + return field.get_value().ok(); } None } } -impl<'a> Drop for MimeParser<'a> { +impl<'a, 'b> Drop for MimeParser<'a, 'b> { fn drop(&mut self) { if !self.header_protected.is_null() { unsafe { mailimf_fields_free(self.header_protected) }; @@ -1298,13 +1258,21 @@ mod tests { assert_eq!(mimeparser.subject, Some("inner-subject".into())); - let of = mimeparser.lookup_optional_field("X-Special-A").unwrap(); + let of = mimeparser + .lookup_field("X-Special-A") + .unwrap() + .get_value() + .unwrap(); assert_eq!(&of, "special-a"); - let of = mimeparser.lookup_optional_field("Foo").unwrap(); + let of = mimeparser.lookup_field("Foo").unwrap().get_value().unwrap(); assert_eq!(&of, "Bar"); - let of = mimeparser.lookup_optional_field("Chat-Version").unwrap(); + let of = mimeparser + .lookup_field("Chat-Version") + .unwrap() + .get_value() + .unwrap(); assert_eq!(&of, "1.0"); assert_eq!(mimeparser.parts.len(), 1); } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 466698dac..3168cd3e5 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -122,61 +122,59 @@ pub unsafe fn dc_receive_imf( } }; - 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() { + if let Some(field) = mime_parser.lookup_field("Date") { + if let Ok(value) = field.get_value() { // is not yet checked against bad times! we do this later if we have the database information. - sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time) + // sent_timestamp = dc_timestamp_from_date((*orig_date).dt_date_time) + // TODO } } // 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) = mime_parser.lookup_field_typ("From", MAILIMF_FIELD_FROM) { - let fld_from = (*field).fld_data.fld_from; - if !fld_from.is_null() { + if let Some(field) = mime_parser.lookup_field("From") { + if let Ok(fld_from) = field.get_value() { let mut check_self = 0; - let mut from_list = Vec::with_capacity(16); - dc_add_or_lookup_contacts_by_mailbox_list( - context, - (*fld_from).frm_mb_list, - Origin::IncomingUnknownFrom, - &mut from_list, - &mut check_self, - ); - if 0 != check_self { - incoming = 0; - if mime_parser.sender_equals_recipient() { - from_id = DC_CONTACT_ID_SELF; - } - } else if !from_list.is_empty() { - // if there is no from given, from_id stays 0 which is just fine. These messages - // are very rare, however, we have to add them to the database (they go to the - // "deaddrop" chat) to avoid a re-download from the server. See also [**] - from_id = from_list[0]; - incoming_origin = Contact::get_origin_by_id(context, from_id, &mut from_id_blocked) - } + // let mut from_list = Vec::with_capacity(16); + // dc_add_or_lookup_contacts_by_mailbox_list( + // context, + // (*fld_from).frm_mb_list, + // Origin::IncomingUnknownFrom, + // &mut from_list, + // &mut check_self, + // ); + // if 0 != check_self { + // incoming = 0; + // if mime_parser.sender_equals_recipient() { + // from_id = DC_CONTACT_ID_SELF; + // } + // } else if !from_list.is_empty() { + // // if there is no from given, from_id stays 0 which is just fine. These messages + // // are very rare, however, we have to add them to the database (they go to the + // // "deaddrop" chat) to avoid a re-download from the server. See also [**] + // from_id = from_list[0]; + // incoming_origin = Contact::get_origin_by_id(context, from_id, &mut from_id_blocked) + // } } } // Make sure, to_ids starts with the first To:-address (Cc: is added in the loop below pass) - 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( - context, - (*fld_to).to_addr_list, - if 0 == incoming { - Origin::OutgoingTo - } else if incoming_origin.is_verified() { - Origin::IncomingTo - } else { - Origin::IncomingUnknownTo - }, - &mut to_ids, - &mut to_self, - ); + if let Some(field) = mime_parser.lookup_field("To") { + if let Ok(fld_to) = field.get_value() { + // dc_add_or_lookup_contacts_by_address_list( + // context, + // (*fld_to).to_addr_list, + // if 0 == incoming { + // Origin::OutgoingTo + // } else if incoming_origin.is_verified() { + // Origin::IncomingTo + // } else { + // Origin::IncomingUnknownTo + // }, + // &mut to_ids, + // &mut to_self, + // ); } } @@ -328,22 +326,21 @@ 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) = 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( - context, - (*fld_cc).cc_addr_list, - if 0 == incoming { - Origin::OutgoingCc - } else if incoming_origin.is_verified() { - Origin::IncomingCc - } else { - Origin::IncomingUnknownCc - }, - to_ids, - std::ptr::null_mut(), - ); + if let Some(field) = mime_parser.lookup_field("Cc") { + if let Ok(fld_cc) = field.get_value() { + // dc_add_or_lookup_contacts_by_address_list( + // context, + // (*fld_cc).cc_addr_list, + // if 0 == incoming { + // Origin::OutgoingCc + // } else if incoming_origin.is_verified() { + // Origin::IncomingCc + // } else { + // Origin::IncomingUnknownCc + // }, + // to_ids, + // std::ptr::null_mut(), + // ); } } @@ -608,17 +605,15 @@ unsafe fn add_parts( // if the mime-headers should be saved, find out its size // (the mime-header ends with an empty line) let save_mime_headers = context.get_config_bool(Config::SaveMimeHeaders); - 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((*(*field).fld_data.fld_in_reply_to).mid_list, " ") + if let Some(field) = mime_parser.lookup_field("In-Reply-To") { + if let Ok(raw) = field.get_value() { + mime_in_reply_to = raw; } } - 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((*(*field).fld_data.fld_references).mid_list, " ") + if let Some(field) = mime_parser.lookup_field("References") { + if let Ok(raw) = field.get_value() { + mime_references = raw; } } @@ -1002,43 +997,32 @@ unsafe fn create_or_lookup_group( } set_better_msg(mime_parser, &better_msg); - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-ID") { - grpid = optional_field; + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-ID") { + grpid = optional_field.get_value().unwrap_or_default(); } if grpid.is_empty() { - 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) = - dc_extract_grpid_from_rfc724_mid(&to_string_lossy((*fld_message_id).mid_value)) - { - grpid = extracted_grpid.to_string(); - } else { - grpid = "".to_string(); - } + if let Some(field) = mime_parser.lookup_field("Message-ID") { + if let Ok(value) = field.get_value() { + // if let Some(extracted_grpid) = + // dc_extract_grpid_from_rfc724_mid(&to_string_lossy((*fld_message_id).mid_value)) + // { + // grpid = extracted_grpid.to_string(); + // } else { + // grpid = "".to_string(); + // } } } if grpid.is_empty() { - 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() { - grpid = to_string_lossy(dc_extract_grpid_from_rfc724_mid_list( - (*fld_in_reply_to).mid_list, - )); + if let Some(field) = mime_parser.lookup_field("In-Reply-To") { + if let Ok(value) = field.get_value() { + grpid = value; } } if grpid.is_empty() { - 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() { - grpid = to_string_lossy(dc_extract_grpid_from_rfc724_mid_list( - (*fld_references).mid_list, - )); + if let Some(field) = mime_parser.lookup_field("References") { + if let Ok(value) = field.get_value() { + grpid = value; } } @@ -1060,11 +1044,11 @@ unsafe fn create_or_lookup_group( } } - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Name") { - grpname = Some(dc_decode_header_words(&optional_field)); + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Name") { + grpname = Some(dc_decode_header_words(&optional_field.get_value().unwrap())); } - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Removed") { - X_MrRemoveFromGrp = Some(optional_field); + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Member-Removed") { + X_MrRemoveFromGrp = optional_field.get_value().ok(); 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; @@ -1079,11 +1063,11 @@ unsafe fn create_or_lookup_group( from_id as u32, ) } else { - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Member-Added") { - X_MrAddToGrp = Some(optional_field); + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Member-Added") { + X_MrAddToGrp = optional_field.get_value().ok(); mime_parser.is_system_message = SystemMessage::MemberAddedToGroup; - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") { - X_MrGrpImageChanged = optional_field; + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image") { + X_MrGrpImageChanged = optional_field.get_value().unwrap(); } better_msg = context.stock_system_msg( StockMessage::MsgAddMember, @@ -1092,26 +1076,25 @@ unsafe fn create_or_lookup_group( from_id as u32, ) } else { - if let Some(optional_field) = - mime_parser.lookup_optional_field("Chat-Group-Name-Changed") - { + let field = mime_parser.lookup_field("Chat-Group-Name-Changed"); + if field.is_some() { X_MrGrpNameChanged = 1; - mime_parser.is_system_message = SystemMessage::GroupNameChanged; better_msg = context.stock_system_msg( StockMessage::MsgGrpName, - &optional_field, + &field.unwrap().get_value().unwrap(), if let Some(ref name) = grpname { name } else { "" }, from_id as u32, - ) + ); + drop(field); + mime_parser.is_system_message = SystemMessage::GroupNameChanged; } else { - if let Some(optional_field) = mime_parser.lookup_optional_field("Chat-Group-Image") - { + if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-Image") { // fld_value is a pointer somewhere into mime_parser, must not be freed - X_MrGrpImageChanged = optional_field; + X_MrGrpImageChanged = optional_field.get_value().unwrap(); mime_parser.is_system_message = SystemMessage::GroupImageChanged; better_msg = context.stock_system_msg( if X_MrGrpImageChanged == "0" { @@ -1693,35 +1676,33 @@ fn set_better_msg(mime_parser: &mut MimeParser, better_msg: impl AsRef) { }; } -unsafe fn dc_is_reply_to_known_message(context: &Context, mime_parser: &MimeParser) -> libc::c_int { +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) */ 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; - } - } + if let Ok(value) = field.get_value() { + // 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("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() - && is_known_rfc724_mid_in_list( - context, - (*(*field).fld_data.fld_references).mid_list, - ) - { - return 1; - } + if let Ok(value) = field.get_value() { + // if !fld_references.is_null() + // && is_known_rfc724_mid_in_list( + // context, + // (*(*field).fld_data.fld_references).mid_list, + // ) + // { + // return 1; + // } } } @@ -1770,30 +1751,24 @@ unsafe fn dc_is_reply_to_messenger_message( - no check for the Chat-* headers (function is only called if it is no messenger message itself) */ 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; - } - } + if let Ok(value) = field.get_value() { + // 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("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; - } - } + if let Ok(value) = field.get_value() { + // if 0 != is_msgrmsg_rfc724_mid_in_list( + // context, + // (*(*field).fld_data.fld_references).mid_list, + // ) { + // return 1; + // } } } diff --git a/src/securejoin.rs b/src/securejoin.rs index d252bb811..59ba70e46 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -343,318 +343,319 @@ pub(crate) fn handle_securejoin_handshake( mimeparser: &MimeParser, contact_id: u32, ) -> Result { - let own_fingerprint: String; + unimplemented!() + // let own_fingerprint: String; - ensure!( - contact_id > DC_CONTACT_ID_LAST_SPECIAL, - "handle_securejoin_handshake(): called with special contact id" - ); - let step = match mimeparser.lookup_optional_field("Secure-Join") { - Some(s) => s, - None => { - bail!("This message is not a Secure-Join message"); - } - }; - info!( - context, - ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step, - ); - let (contact_chat_id, contact_chat_id_blocked) = - chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default(); + // ensure!( + // contact_id > DC_CONTACT_ID_LAST_SPECIAL, + // "handle_securejoin_handshake(): called with special contact id" + // ); + // let step = match mimeparser.lookup_optional_field("Secure-Join") { + // Some(s) => s, + // None => { + // bail!("This message is not a Secure-Join message"); + // } + // }; + // info!( + // context, + // ">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received", step, + // ); + // let (contact_chat_id, contact_chat_id_blocked) = + // chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not).unwrap_or_default(); - if contact_chat_id_blocked != Blocked::Not { - chat::unblock(context, contact_chat_id); - } - let join_vg = step.starts_with("vg-"); - let mut ret = HandshakeMessageStatus::default(); + // if contact_chat_id_blocked != Blocked::Not { + // chat::unblock(context, contact_chat_id); + // } + // let join_vg = step.starts_with("vg-"); + // let mut ret = HandshakeMessageStatus::default(); - match step.as_str() { - "vg-request" | "vc-request" => { - /* ========================================================= - ==== Alice - the inviter side ==== - ==== Step 3 in "Setup verified contact" protocol ==== - ========================================================= */ - // this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet) - // 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 mimeparser.lookup_optional_field("Secure-Join-Invitenumber") { - Some(n) => n, - None => { - warn!(context, "Secure-join denied (invitenumber missing).",); - return Ok(ret); - } - }; - if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) { - warn!(context, "Secure-join denied (bad invitenumber).",); - return Ok(ret); - } - info!(context, "Secure-join requested.",); + // match step.as_str() { + // "vg-request" | "vc-request" => { + // /* ========================================================= + // ==== Alice - the inviter side ==== + // ==== Step 3 in "Setup verified contact" protocol ==== + // ========================================================= */ + // // this message may be unencrypted (Bob, the joinder and the sender, might not have Alice's key yet) + // // 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 mimeparser.lookup_optional_field("Secure-Join-Invitenumber") { + // Some(n) => n, + // None => { + // warn!(context, "Secure-join denied (invitenumber missing).",); + // return Ok(ret); + // } + // }; + // if !token::exists(context, token::Namespace::InviteNumber, &invitenumber) { + // warn!(context, "Secure-join denied (bad invitenumber).",); + // return Ok(ret); + // } + // info!(context, "Secure-join requested.",); - inviter_progress!(context, contact_id, 300); - send_handshake_msg( - context, - contact_chat_id, - &format!("{}-auth-required", &step[..2]), - "", - None, - "", - ); - } - "vg-auth-required" | "vc-auth-required" => { - let cond = { - let bob = context.bob.read().unwrap(); - let scan = bob.qr_scan.as_ref(); - scan.is_none() - || bob.expects != DC_VC_AUTH_REQUIRED - || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup - }; + // inviter_progress!(context, contact_id, 300); + // send_handshake_msg( + // context, + // contact_chat_id, + // &format!("{}-auth-required", &step[..2]), + // "", + // None, + // "", + // ); + // } + // "vg-auth-required" | "vc-auth-required" => { + // let cond = { + // let bob = context.bob.read().unwrap(); + // let scan = bob.qr_scan.as_ref(); + // scan.is_none() + // || bob.expects != DC_VC_AUTH_REQUIRED + // || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup + // }; - if cond { - warn!(context, "auth-required message out of sync.",); - // no error, just aborted somehow or a mail from another handshake - return Ok(ret); - } - let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); - let auth = get_qr_attr!(context, auth).to_string(); + // if cond { + // warn!(context, "auth-required message out of sync.",); + // // no error, just aborted somehow or a mail from another handshake + // return Ok(ret); + // } + // let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); + // let auth = get_qr_attr!(context, auth).to_string(); - if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) { - could_not_establish_secure_connection( - context, - contact_chat_id, - if mimeparser.encrypted { - "No valid signature." - } else { - "Not encrypted." - }, - ); - ret.stop_ongoing_process = true; - ret.bob_securejoin_success = Some(false); - return Ok(ret); - } - if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on joiner-side.", - ); - ret.stop_ongoing_process = true; - ret.bob_securejoin_success = Some(false); - return Ok(ret); - } - info!(context, "Fingerprint verified.",); - own_fingerprint = get_self_fingerprint(context).unwrap(); - joiner_progress!(context, contact_id, 400); - context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; + // if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // if mimeparser.encrypted { + // "No valid signature." + // } else { + // "Not encrypted." + // }, + // ); + // ret.stop_ongoing_process = true; + // ret.bob_securejoin_success = Some(false); + // return Ok(ret); + // } + // if !fingerprint_equals_sender(context, &scanned_fingerprint_of_alice, contact_chat_id) { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Fingerprint mismatch on joiner-side.", + // ); + // ret.stop_ongoing_process = true; + // ret.bob_securejoin_success = Some(false); + // return Ok(ret); + // } + // info!(context, "Fingerprint verified.",); + // own_fingerprint = get_self_fingerprint(context).unwrap(); + // joiner_progress!(context, contact_id, 400); + // context.bob.write().unwrap().expects = DC_VC_CONTACT_CONFIRM; - send_handshake_msg( - context, - contact_chat_id, - &format!("{}-request-with-auth", &step[..2]), - auth, - Some(own_fingerprint), - if join_vg { - get_qr_attr!(context, text2).to_string() - } else { - "".to_string() - }, - ); - } - "vg-request-with-auth" | "vc-request-with-auth" => { - /* ============================================================ - ==== Alice - the inviter side ==== - ==== Steps 5+6 in "Setup verified contact" protocol ==== - ==== Step 6 in "Out-of-band verified groups" protocol ==== - ============================================================ */ - // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob - let fingerprint = match mimeparser.lookup_optional_field("Secure-Join-Fingerprint") { - Some(fp) => fp, - None => { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint not provided.", - ); - return Ok(ret); - } - }; - if !encrypted_and_signed(mimeparser, &fingerprint) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Auth not encrypted.", - ); - return Ok(ret); - } - if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on inviter-side.", - ); - return Ok(ret); - } - info!(context, "Fingerprint verified.",); - // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code - let auth_0 = match mimeparser.lookup_optional_field("Secure-Join-Auth") { - Some(auth) => auth, - None => { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Auth not provided.", - ); - return Ok(ret); - } - }; - if !token::exists(context, token::Namespace::Auth, &auth_0) { - could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid."); - return Ok(ret); - } - if mark_peer_as_verified(context, fingerprint).is_err() { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on inviter-side.", - ); - return Ok(ret); - } - Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited); - info!(context, "Auth verified.",); - secure_connection_established(context, contact_chat_id); - emit_event!(context, Event::ContactsChanged(Some(contact_id))); - inviter_progress!(context, contact_id, 600); - if join_vg { - 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); - return Ok(ret); - } else { - if let Err(err) = - chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true) - { - error!(context, "failed to add contact: {}", err); - } - } - } else { - send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, ""); - inviter_progress!(context, contact_id, 1000); - } - } - "vg-member-added" | "vc-contact-confirm" => { - if join_vg { - ret.hide_this_msg = false; - } - if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM { - info!(context, "Message belongs to a different handshake.",); - return Ok(ret); - } - let cond = { - let bob = context.bob.read().unwrap(); - let scan = bob.qr_scan.as_ref(); - scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup - }; - if cond { - warn!( - context, - "Message out of sync or belongs to a different handshake.", - ); - return Ok(ret); - } - let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); + // send_handshake_msg( + // context, + // contact_chat_id, + // &format!("{}-request-with-auth", &step[..2]), + // auth, + // Some(own_fingerprint), + // if join_vg { + // get_qr_attr!(context, text2).to_string() + // } else { + // "".to_string() + // }, + // ); + // } + // "vg-request-with-auth" | "vc-request-with-auth" => { + // /* ============================================================ + // ==== Alice - the inviter side ==== + // ==== Steps 5+6 in "Setup verified contact" protocol ==== + // ==== Step 6 in "Out-of-band verified groups" protocol ==== + // ============================================================ */ + // // verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob + // let fingerprint = match mimeparser.lookup_optional_field("Secure-Join-Fingerprint") { + // Some(fp) => fp, + // None => { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Fingerprint not provided.", + // ); + // return Ok(ret); + // } + // }; + // if !encrypted_and_signed(mimeparser, &fingerprint) { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Auth not encrypted.", + // ); + // return Ok(ret); + // } + // if !fingerprint_equals_sender(context, &fingerprint, contact_chat_id) { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Fingerprint mismatch on inviter-side.", + // ); + // return Ok(ret); + // } + // info!(context, "Fingerprint verified.",); + // // verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code + // let auth_0 = match mimeparser.lookup_optional_field("Secure-Join-Auth") { + // Some(auth) => auth, + // None => { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Auth not provided.", + // ); + // return Ok(ret); + // } + // }; + // if !token::exists(context, token::Namespace::Auth, &auth_0) { + // could_not_establish_secure_connection(context, contact_chat_id, "Auth invalid."); + // return Ok(ret); + // } + // if mark_peer_as_verified(context, fingerprint).is_err() { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Fingerprint mismatch on inviter-side.", + // ); + // return Ok(ret); + // } + // Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinInvited); + // info!(context, "Auth verified.",); + // secure_connection_established(context, contact_chat_id); + // emit_event!(context, Event::ContactsChanged(Some(contact_id))); + // inviter_progress!(context, contact_id, 600); + // if join_vg { + // 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); + // return Ok(ret); + // } else { + // if let Err(err) = + // chat::add_contact_to_chat_ex(context, group_chat_id, contact_id, true) + // { + // error!(context, "failed to add contact: {}", err); + // } + // } + // } else { + // send_handshake_msg(context, contact_chat_id, "vc-contact-confirm", "", None, ""); + // inviter_progress!(context, contact_id, 1000); + // } + // } + // "vg-member-added" | "vc-contact-confirm" => { + // if join_vg { + // ret.hide_this_msg = false; + // } + // if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM { + // info!(context, "Message belongs to a different handshake.",); + // return Ok(ret); + // } + // let cond = { + // let bob = context.bob.read().unwrap(); + // let scan = bob.qr_scan.as_ref(); + // scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup + // }; + // if cond { + // warn!( + // context, + // "Message out of sync or belongs to a different handshake.", + // ); + // return Ok(ret); + // } + // let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string(); - let vg_expect_encrypted = if join_vg { - let group_id = get_qr_attr!(context, text2).to_string(); - let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, group_id); - // when joining a non-verified group - // the vg-member-added message may be unencrypted - // when not all group members have keys or prefer encryption. - // So only expect encryption if this is a verified group - is_verified_group - } else { - // setup contact is always encrypted - true - }; - if vg_expect_encrypted - && !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) - { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Contact confirm message not encrypted.", - ); - ret.bob_securejoin_success = Some(false); - return Ok(ret); - } + // let vg_expect_encrypted = if join_vg { + // let group_id = get_qr_attr!(context, text2).to_string(); + // let (_, is_verified_group, _) = chat::get_chat_id_by_grpid(context, group_id); + // // when joining a non-verified group + // // the vg-member-added message may be unencrypted + // // when not all group members have keys or prefer encryption. + // // So only expect encryption if this is a verified group + // is_verified_group + // } else { + // // setup contact is always encrypted + // true + // }; + // if vg_expect_encrypted + // && !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) + // { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Contact confirm message not encrypted.", + // ); + // ret.bob_securejoin_success = Some(false); + // return Ok(ret); + // } - if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() { - could_not_establish_secure_connection( - context, - contact_chat_id, - "Fingerprint mismatch on joiner-side.", - ); - return Ok(ret); - } - Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); - emit_event!(context, Event::ContactsChanged(None)); - 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 Ok(ret); - } - secure_connection_established(context, contact_chat_id); - context.bob.write().unwrap().expects = 0; - if join_vg { - send_handshake_msg( - context, - contact_chat_id, - "vg-member-added-received", - "", - None, - "", - ); - } - ret.stop_ongoing_process = true; - ret.bob_securejoin_success = Some(true); - } - "vg-member-added-received" => { - /* ============================================================ - ==== Alice - the inviter side ==== - ==== Step 8 in "Out-of-band verified groups" protocol ==== - ============================================================ */ - if let Ok(contact) = Contact::get_by_id(context, contact_id) { - if contact.is_verified(context) == VerifiedStatus::Unverified { - warn!(context, "vg-member-added-received invalid.",); - return Ok(ret); - } - inviter_progress!(context, contact_id, 800); - inviter_progress!(context, contact_id, 1000); - 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); - context.call_cb(Event::SecurejoinMemberAdded { - chat_id: group_chat_id, - contact_id: contact_id, - }); - } else { - warn!(context, "vg-member-added-received invalid.",); - return Ok(ret); - } - } - _ => { - warn!(context, "invalid step: {}", step); - } - } - if ret.hide_this_msg { - ret.delete_this_msg = true; - } - Ok(ret) + // if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() { + // could_not_establish_secure_connection( + // context, + // contact_chat_id, + // "Fingerprint mismatch on joiner-side.", + // ); + // return Ok(ret); + // } + // Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined); + // emit_event!(context, Event::ContactsChanged(None)); + // 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 Ok(ret); + // } + // secure_connection_established(context, contact_chat_id); + // context.bob.write().unwrap().expects = 0; + // if join_vg { + // send_handshake_msg( + // context, + // contact_chat_id, + // "vg-member-added-received", + // "", + // None, + // "", + // ); + // } + // ret.stop_ongoing_process = true; + // ret.bob_securejoin_success = Some(true); + // } + // "vg-member-added-received" => { + // /* ============================================================ + // ==== Alice - the inviter side ==== + // ==== Step 8 in "Out-of-band verified groups" protocol ==== + // ============================================================ */ + // if let Ok(contact) = Contact::get_by_id(context, contact_id) { + // if contact.is_verified(context) == VerifiedStatus::Unverified { + // warn!(context, "vg-member-added-received invalid.",); + // return Ok(ret); + // } + // inviter_progress!(context, contact_id, 800); + // inviter_progress!(context, contact_id, 1000); + // 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); + // context.call_cb(Event::SecurejoinMemberAdded { + // chat_id: group_chat_id, + // contact_id: contact_id, + // }); + // } else { + // warn!(context, "vg-member-added-received invalid.",); + // return Ok(ret); + // } + // } + // _ => { + // warn!(context, "invalid step: {}", step); + // } + // } + // if ret.hide_this_msg { + // ret.delete_this_msg = true; + // } + // Ok(ret) } fn secure_connection_established(context: &Context, contact_chat_id: u32) {