diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 571acd30c..2614d18ca 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -244,7 +244,7 @@ pub fn dc_receive_imf( cleanup(context, &create_event_to_send, &created_db_entries); - mime_parser.handle_reports(from_id, sent_timestamp, &server_folder, server_uid); + mime_parser.handle_reports(context, from_id, sent_timestamp, &server_folder, server_uid); Ok(()) } @@ -1054,7 +1054,7 @@ fn create_or_lookup_group( } /// try extract a grpid from a message-id list header value -fn extract_grpid<'a>(mime_parser: &'a MimeMessage, headerdef: HeaderDef) -> Option<&'a str> { +fn extract_grpid(mime_parser: &MimeMessage, headerdef: HeaderDef) -> Option<&str> { let header = mime_parser.get(headerdef)?; let parts = header .split(',') diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 16ab7aba6..c94ea6b57 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -36,8 +36,7 @@ use crate::{bail, ensure}; /// It is created by parsing the raw data of an actual MIME message /// using the [MimeMessage::from_bytes] constructor. #[derive(Debug)] -pub struct MimeMessage<'a> { - pub context: &'a Context, +pub struct MimeMessage { pub parts: Vec, header: HashMap, pub decrypting_failed: bool, @@ -91,8 +90,8 @@ impl Default for SystemMessage { const MIME_AC_SETUP_FILE: &str = "application/autocrypt-setup"; -impl<'a> MimeMessage<'a> { - pub fn from_bytes(context: &'a Context, body: &[u8]) -> Result { +impl MimeMessage { + pub fn from_bytes(context: &Context, body: &[u8]) -> Result { let mail = mailparse::parse_mail(body)?; let message_time = mail @@ -160,7 +159,6 @@ impl<'a> MimeMessage<'a> { signatures, gossipped_addr, is_forwarded: false, - context, reports: Vec::new(), is_system_message: SystemMessage::Unknown, location_kml: None, @@ -168,14 +166,14 @@ impl<'a> MimeMessage<'a> { user_avatar: AvatarAction::None, group_avatar: AvatarAction::None, }; - parser.parse_mime_recursive(&mail)?; - parser.parse_headers()?; + parser.parse_mime_recursive(context, &mail)?; + parser.parse_headers(context)?; Ok(parser) } /// Parses system messages. - fn parse_system_message_headers(&mut self) -> Result<()> { + fn parse_system_message_headers(&mut self, context: &Context) -> Result<()> { if self.get(HeaderDef::AutocryptSetupMessage).is_some() { self.parts = self .parts @@ -190,7 +188,7 @@ impl<'a> MimeMessage<'a> { if self.parts.len() == 1 { self.is_system_message = SystemMessage::AutocryptSetupMessage; } else { - warn!(self.context, "could not determine ASM mime-part"); + warn!(context, "could not determine ASM mime-part"); } } else if let Some(value) = self.get(HeaderDef::ChatContent) { if value == "location-streaming-enabled" { @@ -284,8 +282,8 @@ impl<'a> MimeMessage<'a> { } } - fn parse_headers(&mut self) -> Result<()> { - self.parse_system_message_headers()?; + fn parse_headers(&mut self, context: &Context) -> Result<()> { + self.parse_system_message_headers(context)?; self.parse_avatar_headers(); self.squash_attachment_parts(); @@ -330,9 +328,9 @@ impl<'a> MimeMessage<'a> { // See if an MDN is requested from the other side if !self.decrypting_failed && !self.parts.is_empty() { if let Some(ref dn_to_addr) = - self.parse_first_addr(HeaderDef::ChatDispositionNotificationTo) + self.parse_first_addr(context, HeaderDef::ChatDispositionNotificationTo) { - if let Some(ref from_addr) = self.parse_first_addr(HeaderDef::From_) { + if let Some(ref from_addr) = self.parse_first_addr(context, HeaderDef::From_) { if compare_addrs(from_addr, dn_to_addr) { if let Some(part) = self.parts.last_mut() { part.param.set_int(Param::WantsMdn, 1); @@ -407,31 +405,35 @@ impl<'a> MimeMessage<'a> { self.header.get(&headerdef.get_headername()) } - fn parse_first_addr(&self, headerdef: HeaderDef) -> Option { + fn parse_first_addr(&self, context: &Context, headerdef: HeaderDef) -> Option { if let Some(value) = self.get(headerdef.clone()) { match mailparse::addrparse(&value) { Ok(ref addrs) => { return addrs.first().cloned(); } Err(err) => { - warn!(self.context, "header {} parse error: {:?}", headerdef, err); + warn!(context, "header {} parse error: {:?}", headerdef, err); } } } None } - fn parse_mime_recursive(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result { + fn parse_mime_recursive( + &mut self, + context: &Context, + mail: &mailparse::ParsedMail<'_>, + ) -> Result { if mail.ctype.params.get("protected-headers").is_some() { if mail.ctype.mimetype == "text/rfc822-headers" { warn!( - self.context, + context, "Protected headers found in text/rfc822-headers attachment: Will be ignored.", ); return Ok(false); } - warn!(self.context, "Ignoring nested protected headers"); + warn!(context, "Ignoring nested protected headers"); } enum MimeS { @@ -459,7 +461,7 @@ impl<'a> MimeMessage<'a> { }; match m { - MimeS::Multiple => self.handle_multiple(mail), + MimeS::Multiple => self.handle_multiple(context, mail), MimeS::Message => { let raw = mail.get_body_raw()?; if raw.is_empty() { @@ -467,13 +469,17 @@ impl<'a> MimeMessage<'a> { } let mail = mailparse::parse_mail(&raw).unwrap(); - self.parse_mime_recursive(&mail) + self.parse_mime_recursive(context, &mail) } - MimeS::Single => self.add_single_part_if_known(mail), + MimeS::Single => self.add_single_part_if_known(context, mail), } } - fn handle_multiple(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result { + fn handle_multiple( + &mut self, + context: &Context, + mail: &mailparse::ParsedMail<'_>, + ) -> Result { let mut any_part_added = false; let mimetype = get_mime_type(mail)?.0; match (mimetype.type_(), mimetype.subtype().as_str()) { @@ -484,7 +490,7 @@ impl<'a> MimeMessage<'a> { (mime::MULTIPART, "alternative") => { for cur_data in &mail.subparts { if get_mime_type(cur_data)?.0 == "multipart/mixed" { - any_part_added = self.parse_mime_recursive(cur_data)?; + any_part_added = self.parse_mime_recursive(context, cur_data)?; break; } } @@ -492,7 +498,7 @@ impl<'a> MimeMessage<'a> { /* search for text/plain and add this */ for cur_data in &mail.subparts { if get_mime_type(cur_data)?.0.type_() == mime::TEXT { - any_part_added = self.parse_mime_recursive(cur_data)?; + any_part_added = self.parse_mime_recursive(context, cur_data)?; break; } } @@ -500,7 +506,7 @@ impl<'a> MimeMessage<'a> { if !any_part_added { /* `text/plain` not found - use the first part */ for cur_part in &mail.subparts { - if self.parse_mime_recursive(cur_part)? { + if self.parse_mime_recursive(context, cur_part)? { any_part_added = true; break; } @@ -513,14 +519,14 @@ impl<'a> MimeMessage<'a> { being the first one, which may not be always true ... however, most times it seems okay. */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(first)?; + any_part_added = self.parse_mime_recursive(context, first)?; } } (mime::MULTIPART, "encrypted") => { // we currently do not try to decrypt non-autocrypt messages // at all. If we see an encrypted part, we set // decrypting_failed. - let msg_body = self.context.stock_str(StockMessage::CantDecryptMsgBody); + let msg_body = context.stock_str(StockMessage::CantDecryptMsgBody); let txt = format!("[{}]", msg_body); let mut part = Part::default(); @@ -543,7 +549,7 @@ impl<'a> MimeMessage<'a> { https://k9mail.github.io/2016/11/24/OpenPGP-Considerations-Part-I.html for background information why we use encrypted+signed) */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(first)?; + any_part_added = self.parse_mime_recursive(context, first)?; } } (mime::MULTIPART, "report") => { @@ -551,14 +557,14 @@ impl<'a> MimeMessage<'a> { if mail.subparts.len() >= 2 { if let Some(report_type) = mail.ctype.params.get("report-type") { if report_type == "disposition-notification" { - if let Some(report) = self.process_report(mail)? { + if let Some(report) = self.process_report(context, mail)? { self.reports.push(report); } } else { /* eg. `report-type=delivery-status`; maybe we should show them as a little error icon */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(first)?; + any_part_added = self.parse_mime_recursive(context, first)?; } } } @@ -568,7 +574,7 @@ impl<'a> MimeMessage<'a> { // Add all parts (in fact, AddSinglePartIfKnown() later check if // the parts are really supported) for cur_data in mail.subparts.iter() { - if self.parse_mime_recursive(cur_data)? { + if self.parse_mime_recursive(context, cur_data)? { any_part_added = true; } } @@ -578,7 +584,11 @@ impl<'a> MimeMessage<'a> { Ok(any_part_added) } - fn add_single_part_if_known(&mut self, mail: &mailparse::ParsedMail<'_>) -> Result { + fn add_single_part_if_known( + &mut self, + context: &Context, + mail: &mailparse::ParsedMail<'_>, + ) -> Result { // return true if a part was added let (mime_type, msg_type) = get_mime_type(mail)?; let raw_mime = mail.ctype.mimetype.to_lowercase(); @@ -589,6 +599,7 @@ impl<'a> MimeMessage<'a> { if let Ok(filename) = filename { self.do_add_single_file_part( + context, msg_type, mime_type, &raw_mime, @@ -604,7 +615,7 @@ impl<'a> MimeMessage<'a> { let decoded_data = match mail.get_body() { Ok(decoded_data) => decoded_data, Err(err) => { - warn!(self.context, "Invalid body parsed {:?}", err); + warn!(context, "Invalid body parsed {:?}", err); // Note that it's not always an error - might be no data return Ok(false); } @@ -645,6 +656,7 @@ impl<'a> MimeMessage<'a> { fn do_add_single_file_part( &mut self, + context: &Context, msg_type: Viewtype, mime_type: Mime, raw_mime: &str, @@ -659,9 +671,9 @@ impl<'a> MimeMessage<'a> { // XXX what if somebody sends eg an "location-highlights.kml" // attachment unrelated to location streaming? if filename.starts_with("location") || filename.starts_with("message") { - let parsed = location::Kml::parse(self.context, decoded_data) + let parsed = location::Kml::parse(context, decoded_data) .map_err(|err| { - warn!(self.context, "failed to parse kml part: {}", err); + warn!(context, "failed to parse kml part: {}", err); }) .ok(); if filename.starts_with("location") { @@ -675,17 +687,17 @@ impl<'a> MimeMessage<'a> { /* we have a regular file attachment, write decoded data to new blob object */ - let blob = match BlobObject::create(self.context, filename, decoded_data) { + let blob = match BlobObject::create(context, filename, decoded_data) { Ok(blob) => blob, Err(err) => { error!( - self.context, + context, "Could not add blob for mime part {}, error {}", filename, err ); return; } }; - info!(self.context, "added blobfile: {:?}", blob.as_name()); + info!(context, "added blobfile: {:?}", blob.as_name()); /* create and register Mime part referencing the new Blob object */ let mut part = Part::default(); @@ -759,7 +771,11 @@ impl<'a> MimeMessage<'a> { } } - fn process_report(&self, report: &mailparse::ParsedMail<'_>) -> Result> { + fn process_report( + &self, + context: &Context, + report: &mailparse::ParsedMail<'_>, + ) -> Result> { // parse as mailheaders let report_body = report.subparts[1].get_body_raw()?; let (report_fields, _) = mailparse::parse_headers(&report_body)?; @@ -788,7 +804,7 @@ impl<'a> MimeMessage<'a> { } } warn!( - self.context, + context, "ignoring unknown disposition-notification, Message-Id: {:?}", report_fields.get_first_value("Message-ID").ok() ); @@ -799,6 +815,7 @@ impl<'a> MimeMessage<'a> { /// Handle reports (only MDNs for now) pub fn handle_reports( &self, + context: &Context, from_id: u32, sent_timestamp: i64, server_folder: impl AsRef, @@ -813,13 +830,10 @@ impl<'a> MimeMessage<'a> { for original_message_id in std::iter::once(&report.original_message_id).chain(&report.additional_message_ids) { - if let Some((chat_id, msg_id)) = message::mdn_from_ext( - self.context, - from_id, - original_message_id, - sent_timestamp, - ) { - self.context.call_cb(Event::MsgRead { chat_id, msg_id }); + if let Some((chat_id, msg_id)) = + message::mdn_from_ext(context, from_id, original_message_id, sent_timestamp) + { + context.call_cb(Event::MsgRead { chat_id, msg_id }); mdn_recognized = true; } } @@ -829,10 +843,10 @@ impl<'a> MimeMessage<'a> { let mut param = Params::new(); param.set(Param::ServerFolder, server_folder.as_ref()); param.set_int(Param::ServerUid, server_uid as i32); - if self.has_chat_version() && self.context.get_config_bool(Config::MvboxMove) { + if self.has_chat_version() && context.get_config_bool(Config::MvboxMove) { param.set_int(Param::AlsoMove, 1); } - job_add(self.context, Action::MarkseenMdnOnImap, 0, param, 0); + job_add(context, Action::MarkseenMdnOnImap, 0, param, 0); } } } @@ -1166,10 +1180,13 @@ mod tests { let mimeparser = MimeMessage::from_bytes(&context.ctx, &raw[..]).unwrap(); - let of = mimeparser.parse_first_addr(HeaderDef::From_).unwrap(); + let of = mimeparser + .parse_first_addr(&context.ctx, HeaderDef::From_) + .unwrap(); assert_eq!(of, mailparse::addrparse("hello@one.org").unwrap()[0]); - let of = mimeparser.parse_first_addr(HeaderDef::ChatDispositionNotificationTo); + let of = + mimeparser.parse_first_addr(&context.ctx, HeaderDef::ChatDispositionNotificationTo); assert!(of.is_none()); } diff --git a/src/securejoin.rs b/src/securejoin.rs index 2936e4cd8..e19b5d1a6 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -485,7 +485,7 @@ pub(crate) fn handle_securejoin_handshake( 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(mime_message, &scanned_fingerprint_of_alice) { + if !encrypted_and_signed(context, mime_message, &scanned_fingerprint_of_alice) { could_not_establish_secure_connection( context, contact_chat_id, @@ -548,7 +548,7 @@ pub(crate) fn handle_securejoin_handshake( return Ok(HandshakeMessage::Ignore); } }; - if !encrypted_and_signed(mime_message, &fingerprint) { + if !encrypted_and_signed(context, mime_message, &fingerprint) { could_not_establish_secure_connection( context, contact_chat_id, @@ -675,7 +675,7 @@ pub(crate) fn handle_securejoin_handshake( true }; if vg_expect_encrypted - && !encrypted_and_signed(mime_message, &scanned_fingerprint_of_alice) + && !encrypted_and_signed(context, mime_message, &scanned_fingerprint_of_alice) { could_not_establish_secure_connection( context, @@ -830,22 +830,26 @@ fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef) -> Res * Tools: Misc. ******************************************************************************/ -fn encrypted_and_signed(mimeparser: &MimeMessage, expected_fingerprint: impl AsRef) -> bool { +fn encrypted_and_signed( + context: &Context, + mimeparser: &MimeMessage, + expected_fingerprint: impl AsRef, +) -> bool { if !mimeparser.was_encrypted() { - warn!(mimeparser.context, "Message not encrypted.",); + warn!(context, "Message not encrypted.",); false } else if mimeparser.signatures.is_empty() { - warn!(mimeparser.context, "Message not signed.",); + warn!(context, "Message not signed.",); false } else if expected_fingerprint.as_ref().is_empty() { - warn!(mimeparser.context, "Fingerprint for comparison missing.",); + warn!(context, "Fingerprint for comparison missing.",); false } else if !mimeparser .signatures .contains(expected_fingerprint.as_ref()) { warn!( - mimeparser.context, + context, "Message does not match expected fingerprint {}.", expected_fingerprint.as_ref(), );