diff --git a/src/chat.rs b/src/chat.rs index 05e110df1..13373ed41 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -171,51 +171,20 @@ impl<'a> Chat<'a> { return "Err".into(); } - unsafe fn get_parent_mime_headers( - &self, - parent_rfc724_mid: *mut *mut libc::c_char, - parent_in_reply_to: *mut *mut libc::c_char, - parent_references: *mut *mut libc::c_char, - ) -> Result<(), Error> { - if !(parent_rfc724_mid.is_null() - || parent_in_reply_to.is_null() - || parent_references.is_null()) - { - // prefer a last message that isn't from us - let next = self - .context - .sql - .query_row( - "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT max(timestamp) \ - FROM msgs WHERE chat_id=?1 AND from_id!=?2);", - params![self.id as i32, DC_CONTACT_ID_SELF as i32], - |row| { - *parent_rfc724_mid = row.get::<_, String>(0)?.strdup(); - *parent_in_reply_to = row.get::<_, String>(1)?.strdup(); - *parent_references = row.get::<_, String>(2)?.strdup(); - Ok(()) - }, - ) - .is_ok(); + pub fn get_parent_mime_headers(&self) -> Option<(String, String, String)> { + let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)); + let params = params![self.id as i32, DC_CONTACT_ID_SELF as i32]; + let sql = &self.context.sql; + let main_query = "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT max(timestamp) \ + FROM msgs WHERE chat_id=?1 AND from_id!=?2);"; + let fallback_query = "SELECT rfc724_mid, mime_in_reply_to, mime_references \ + FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT min(timestamp) \ + FROM msgs WHERE chat_id=?1 AND from_id==?2);"; - if !next { - self.context.sql.query_row( - "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT min(timestamp) \ - FROM msgs WHERE chat_id=?1 AND from_id==?2);", - params![self.id as i32, DC_CONTACT_ID_SELF as i32], - |row| { - *parent_rfc724_mid = row.get::<_, String>(0)?.strdup(); - *parent_in_reply_to = row.get::<_, String>(1)?.strdup(); - *parent_references = row.get::<_, String>(2)?.strdup(); - Ok(()) - }, - )?; - } - } - - Ok(()) + sql.query_row(main_query, params, collect) + .or_else(|_| sql.query_row(fallback_query, params, collect)) + .ok() } pub unsafe fn get_profile_image(&self) -> Option { @@ -277,13 +246,8 @@ impl<'a> Chat<'a> { ) -> Result { let mut do_guarantee_e2ee: libc::c_int; let e2ee_enabled: libc::c_int; - let mut OK_TO_CONTINUE = true; - let mut parent_rfc724_mid = ptr::null_mut(); - let mut parent_references = ptr::null_mut(); - let mut parent_in_reply_to = ptr::null_mut(); - let mut new_rfc724_mid = ptr::null_mut(); - let mut new_references = ptr::null_mut(); - let mut new_in_reply_to = ptr::null_mut(); + let mut new_references = "".into(); + let mut new_in_reply_to = "".into(); let mut msg_id = 0; let mut to_id = 0; let mut location_id = 0; @@ -293,7 +257,10 @@ impl<'a> Chat<'a> { || self.typ == Chattype::VerifiedGroup) { error!(context, 0, "Cannot send to chat type #{}.", self.typ,); - } else if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup) + return Ok(0); + } + + if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup) && 0 == is_contact_in_chat(context, self.id, 1 as u32) { log_event!( @@ -302,200 +269,173 @@ impl<'a> Chat<'a> { 0, "Cannot send message; self not in group.", ); - } else { - if let Some(from) = context.sql.get_config(context, "configured_addr") { - let from_c = CString::yolo(from); - new_rfc724_mid = dc_create_outgoing_rfc724_mid( - if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { - self.grpid.strdup() - } else { - ptr::null_mut() - }, - from_c.as_ptr(), - ); + return Ok(0); + } - if self.typ == Chattype::Single { - if let Some(id) = context.sql.query_row_col( - context, - "SELECT contact_id FROM chats_contacts WHERE chat_id=?;", - params![self.id as i32], - 0, - ) { - to_id = id; - } else { - error!( - context, - 0, "Cannot send message, contact for chat #{} not found.", self.id, - ); - OK_TO_CONTINUE = false; - } + if let Some(from) = context.sql.get_config(context, "configured_addr") { + let new_rfc724_mid = { + let grpid = match self.typ { + Chattype::Group | Chattype::VerifiedGroup => Some(self.grpid.as_str()), + _ => None, + }; + dc_create_outgoing_rfc724_mid_safe(grpid, &from) + }; + + if self.typ == Chattype::Single { + if let Some(id) = context.sql.query_row_col( + context, + "SELECT contact_id FROM chats_contacts WHERE chat_id=?;", + params![self.id as i32], + 0, + ) { + to_id = id; } else { - if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { - if self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 { - self.param.remove(Param::Unpromoted); - self.update_param().unwrap(); - } + error!( + context, + 0, "Cannot send message, contact for chat #{} not found.", self.id, + ); + return Ok(0); + } + } else { + if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { + if self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 { + self.param.remove(Param::Unpromoted); + self.update_param().unwrap(); } } - if OK_TO_CONTINUE { - /* check if we can guarantee E2EE for this message. - if we guarantee E2EE, and circumstances change - so that E2EE is no longer available at a later point (reset, changed settings), - we do not send the message out at all */ - do_guarantee_e2ee = 0; - e2ee_enabled = context - .sql - .get_config_int(context, "e2ee_enabled") - .unwrap_or_else(|| 1); - if 0 != e2ee_enabled - && msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0 - { - let mut can_encrypt = 1; - let mut all_mutual = 1; + } - let res = context.sql.query_row( - "SELECT ps.prefer_encrypted, c.addr \ - FROM chats_contacts cc \ - LEFT JOIN contacts c ON cc.contact_id=c.id \ - LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ - WHERE cc.chat_id=? AND cc.contact_id>9;", - params![self.id], - |row| { - let state: String = row.get(1)?; + /* check if we can guarantee E2EE for this message. + if we guarantee E2EE, and circumstances change + so that E2EE is no longer available at a later point (reset, changed settings), + we do not send the message out at all */ + do_guarantee_e2ee = 0; + e2ee_enabled = context + .sql + .get_config_int(context, "e2ee_enabled") + .unwrap_or_else(|| 1); + if 0 != e2ee_enabled + && msg.param.get_int(Param::ForcePlaintext).unwrap_or_default() == 0 + { + let mut can_encrypt = 1; + let mut all_mutual = 1; - if let Some(prefer_encrypted) = row.get::<_, Option>(0)? { - if prefer_encrypted != 1 { - info!( - context, - 0, - "[autocrypt] peerstate for {} is {}", - state, - if prefer_encrypted == 0 { - "NOPREFERENCE" - } else { - "RESET" - }, - ); - all_mutual = 0; - } - } else { - info!(context, 0, "[autocrypt] no peerstate for {}", state,); - can_encrypt = 0; - all_mutual = 0; - } - Ok(()) - }, - ); - match res { - Ok(_) => {} - Err(err) => { - warn!(context, 0, "chat: failed to load peerstates: {:?}", err); - } - } - - if 0 != can_encrypt { - if 0 != all_mutual { - do_guarantee_e2ee = 1; - } else if last_msg_in_chat_encrypted(context, &context.sql, self.id) { - do_guarantee_e2ee = 1; + let res = context.sql.query_row( + "SELECT ps.prefer_encrypted, c.addr \ + FROM chats_contacts cc \ + LEFT JOIN contacts c ON cc.contact_id=c.id \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ + WHERE cc.chat_id=? AND cc.contact_id>9;", + params![self.id], + |row| { + let state: String = row.get(1)?; + + if let Some(prefer_encrypted) = row.get::<_, Option>(0)? { + if prefer_encrypted != 1 { + info!( + context, + 0, + "[autocrypt] peerstate for {} is {}", + state, + if prefer_encrypted == 0 { + "NOPREFERENCE" + } else { + "RESET" + }, + ); + all_mutual = 0; } + } else { + info!(context, 0, "[autocrypt] no peerstate for {}", state,); + can_encrypt = 0; + all_mutual = 0; } + Ok(()) + }, + ); + match res { + Ok(_) => {} + Err(err) => { + warn!(context, 0, "chat: failed to load peerstates: {:?}", err); } - if 0 != do_guarantee_e2ee { - msg.param.set_int(Param::GuranteeE2ee, 1); + } + + if 0 != can_encrypt { + if 0 != all_mutual { + do_guarantee_e2ee = 1; + } else if last_msg_in_chat_encrypted(context, &context.sql, self.id) { + do_guarantee_e2ee = 1; } - msg.param.remove(Param::ErroneousE2ee); - if !self.is_self_talk() - && self - .get_parent_mime_headers( - &mut parent_rfc724_mid, - &mut parent_in_reply_to, - &mut parent_references, - ) - .is_ok() - { - if !parent_rfc724_mid.is_null() - && 0 != *parent_rfc724_mid.offset(0isize) as libc::c_int - { - new_in_reply_to = dc_strdup(parent_rfc724_mid) - } - if !parent_references.is_null() { - let space: *mut libc::c_char; - space = strchr(parent_references, ' ' as i32); - if !space.is_null() { - *space = 0 as libc::c_char - } - } - if !parent_references.is_null() - && 0 != *parent_references.offset(0isize) as libc::c_int - && !parent_rfc724_mid.is_null() - && 0 != *parent_rfc724_mid.offset(0isize) as libc::c_int - { - new_references = dc_mprintf( - b"%s %s\x00" as *const u8 as *const libc::c_char, - parent_references, - parent_rfc724_mid, - ) - } else if !parent_references.is_null() - && 0 != *parent_references.offset(0isize) as libc::c_int - { - new_references = dc_strdup(parent_references) - } else if !parent_in_reply_to.is_null() - && 0 != *parent_in_reply_to.offset(0isize) as libc::c_int - && !parent_rfc724_mid.is_null() - && 0 != *parent_rfc724_mid.offset(0isize) as libc::c_int - { - new_references = dc_mprintf( - b"%s %s\x00" as *const u8 as *const libc::c_char, - parent_in_reply_to, - parent_rfc724_mid, - ) - } else if !parent_in_reply_to.is_null() - && 0 != *parent_in_reply_to.offset(0isize) as libc::c_int - { - new_references = dc_strdup(parent_in_reply_to) - } + } + } + if 0 != do_guarantee_e2ee { + msg.param.set_int(Param::GuranteeE2ee, 1); + } + msg.param.remove(Param::ErroneousE2ee); + if !self.is_self_talk() { + if let Some((parent_rfc724_mid, parent_in_reply_to, parent_references)) = + self.get_parent_mime_headers() + { + if !parent_rfc724_mid.is_empty() { + new_in_reply_to = parent_rfc724_mid.clone(); } + let parent_references = if let Some(n) = parent_references.find(' ') { + &parent_references[0..n] + } else { + &parent_references + }; - // add independent location to database - - if msg.param.exists(Param::SetLatitude) { - if sql::execute( - context, - &context.sql, - "INSERT INTO locations \ - (timestamp,from_id,chat_id, latitude,longitude,independent)\ - VALUES (?,?,?, ?,?,1);", - params![ - timestamp, - DC_CONTACT_ID_SELF, - self.id as i32, - msg.param.get_float(Param::SetLatitude).unwrap_or_default(), - msg.param.get_float(Param::SetLongitude).unwrap_or_default(), - ], - ) - .is_ok() - { - location_id = sql::get_rowid2( - context, - &context.sql, - "locations", - "timestamp", - timestamp, - "from_id", - DC_CONTACT_ID_SELF as i32, - ); - } + if !parent_references.is_empty() && !parent_rfc724_mid.is_empty() { + new_references = format!("{} {}", parent_references, parent_rfc724_mid); + } else if !parent_references.is_empty() { + new_references = parent_references.to_string(); + } else if !parent_in_reply_to.is_empty() && !parent_rfc724_mid.is_empty() { + new_references = format!("{} {}", parent_in_reply_to, parent_rfc724_mid); + } else if !parent_in_reply_to.is_empty() { + new_references = parent_in_reply_to.clone(); } + } + } - // add message to the database + // add independent location to database - if sql::execute( + if msg.param.exists(Param::SetLatitude) { + if sql::execute( + context, + &context.sql, + "INSERT INTO locations \ + (timestamp,from_id,chat_id, latitude,longitude,independent)\ + VALUES (?,?,?, ?,?,1);", + params![ + timestamp, + DC_CONTACT_ID_SELF, + self.id as i32, + msg.param.get_float(Param::SetLatitude).unwrap_or_default(), + msg.param.get_float(Param::SetLongitude).unwrap_or_default(), + ], + ) + .is_ok() + { + location_id = sql::get_rowid2( + context, + &context.sql, + "locations", + "timestamp", + timestamp, + "from_id", + DC_CONTACT_ID_SELF as i32, + ); + } + } + + // add message to the database + + if sql::execute( context, &context.sql, "INSERT INTO msgs (rfc724_mid, chat_id, from_id, to_id, timestamp, type, state, txt, param, hidden, mime_in_reply_to, mime_references, location_id) VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?);", params![ - as_str(new_rfc724_mid), + new_rfc724_mid, self.id as i32, 1i32, to_id as i32, @@ -505,8 +445,8 @@ impl<'a> Chat<'a> { msg.text.as_ref().map_or("", String::as_str), msg.param.to_string(), msg.hidden, - to_string(new_in_reply_to), - to_string(new_references), + new_in_reply_to, + new_references, location_id as i32, ] ).is_ok() { @@ -515,7 +455,7 @@ impl<'a> Chat<'a> { &context.sql, "msgs", "rfc724_mid", - as_str(new_rfc724_mid), + new_rfc724_mid, ); } else { error!( @@ -525,19 +465,10 @@ impl<'a> Chat<'a> { self.id, ); } - } - } else { - error!(context, 0, "Cannot send message, not configured.",); - } + } else { + error!(context, 0, "Cannot send message, not configured.",); } - free(parent_rfc724_mid as *mut libc::c_void); - free(parent_in_reply_to as *mut libc::c_void); - free(parent_references as *mut libc::c_void); - free(new_rfc724_mid as *mut libc::c_void); - free(new_in_reply_to as *mut libc::c_void); - free(new_references as *mut libc::c_void); - Ok(msg_id) } } diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index cf9e095a0..e15a3a5e9 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -148,7 +148,7 @@ pub unsafe fn dc_mimefactory_load_msg( for row in rows { let (authname, addr) = row?; let addr_c = addr.strdup(); - if clist_search_string_nocase(factory.recipients_addr, addr_c) == 0 { + if !clist_search_string_nocase(factory.recipients_addr, addr_c) { clist_insert_after( factory.recipients_names, (*factory.recipients_names).last, @@ -183,7 +183,7 @@ pub unsafe fn dc_mimefactory_load_msg( .unwrap_or_default(); if !email_to_remove.is_empty() && email_to_remove != self_addr { - if clist_search_string_nocase(factory.recipients_addr, email_to_remove_c) == 0 { + if !clist_search_string_nocase(factory.recipients_addr, email_to_remove_c) { clist_insert_after( factory.recipients_names, (*factory.recipients_names).last, diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index f2d662571..cf99d24d2 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -32,10 +32,10 @@ dc_mimeparser_t has no deep dependencies to Context or to the database #[repr(C)] pub struct dc_mimepart_t { pub type_0: Viewtype, - pub is_meta: libc::c_int, + pub is_meta: bool, pub int_mimetype: libc::c_int, pub msg: Option, - pub msg_raw: *mut libc::c_char, + pub msg_raw: Option, pub bytes: libc::c_int, pub param: Params, } @@ -50,11 +50,11 @@ pub struct dc_mimeparser_t<'a> { pub header: HashMap, pub header_root: *mut mailimf_fields, pub header_protected: *mut mailimf_fields, - pub subject: *mut libc::c_char, + pub subject: Option, pub is_send_by_messenger: bool, - pub decrypting_failed: libc::c_int, + pub decrypting_failed: bool, pub e2ee_helper: E2eeHelper, - pub is_forwarded: libc::c_int, + pub is_forwarded: bool, pub context: &'a Context, pub reports: Vec<*mut mailmime>, pub is_system_message: libc::c_int, @@ -69,11 +69,11 @@ pub fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t { header: Default::default(), header_root: std::ptr::null_mut(), header_protected: std::ptr::null_mut(), - subject: std::ptr::null_mut(), + subject: None, is_send_by_messenger: false, - decrypting_failed: 0, + decrypting_failed: false, e2ee_helper: Default::default(), - is_forwarded: 0, + is_forwarded: false, context, reports: Vec::new(), is_system_message: 0, @@ -87,10 +87,7 @@ pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) { } unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) { - for part in mimeparser.parts.drain(..) { - dc_mimepart_unref(part); - } - assert!(mimeparser.parts.is_empty()); + mimeparser.parts = vec![]; mimeparser.header_root = ptr::null_mut(); mimeparser.header.clear(); if !mimeparser.header_protected.is_null() { @@ -99,26 +96,20 @@ unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) { } mimeparser.is_send_by_messenger = false; mimeparser.is_system_message = 0i32; - free(mimeparser.subject as *mut libc::c_void); - mimeparser.subject = ptr::null_mut(); + mimeparser.subject = None; if !mimeparser.mimeroot.is_null() { mailmime_free(mimeparser.mimeroot); mimeparser.mimeroot = ptr::null_mut() } - mimeparser.is_forwarded = 0i32; + mimeparser.is_forwarded = false; mimeparser.reports.clear(); - mimeparser.decrypting_failed = 0i32; + mimeparser.decrypting_failed = false; mimeparser.e2ee_helper.thanks(); mimeparser.location_kml = None; mimeparser.message_kml = None; } -unsafe fn dc_mimepart_unref(mut mimepart: dc_mimepart_t) { - mimepart.msg = None; - free(mimepart.msg_raw as *mut libc::c_void); - mimepart.msg_raw = ptr::null_mut(); -} const DC_MIMETYPE_AC_SETUP_FILE: i32 = 111; pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_mimeparser_t<'a> { @@ -141,7 +132,15 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m dc_mimeparser_parse_mime_recursive(mimeparser_ref, mimeparser_ref.mimeroot); let field: *mut mailimf_field = dc_mimeparser_lookup_field(&mimeparser, "Subject"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int { - mimeparser.subject = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value) + let decoded = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value); + if decoded.is_null() + /* XXX: can it happen? */ + { + mimeparser.subject = None + } else { + mimeparser.subject = Some(to_string(decoded)); + free(decoded.cast()); + } } if !dc_mimeparser_lookup_optional_field(&mut mimeparser, "Chat-Version").is_null() { mimeparser.is_send_by_messenger = true @@ -169,8 +168,7 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m let mut i = 0; while i != mimeparser.parts.len() { if mimeparser.parts[i].int_mimetype != 111 { - let part = mimeparser.parts.remove(i); - dc_mimepart_unref(part); + mimeparser.parts.remove(i); } else { i += 1; } @@ -196,7 +194,7 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m if mimeparser.parts.len() >= 2 { let imgpart = &mut mimeparser.parts[1]; if imgpart.type_0 == Viewtype::Image { - imgpart.is_meta = 1i32 + imgpart.is_meta = true; } } } @@ -212,7 +210,7 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m || filepart.type_0 == Viewtype::Voice || filepart.type_0 == Viewtype::Video || filepart.type_0 == Viewtype::File) - && 0 == filepart.is_meta + && !filepart.is_meta }; if need_drop { @@ -225,42 +223,37 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m mimeparser.parts[0].msg = None; // swap new with old - let old = std::mem::replace(&mut mimeparser.parts[0], filepart); - - // unref old one - dc_mimepart_unref(old); + std::mem::replace(&mut mimeparser.parts[0], filepart); } } - if !mimeparser.subject.is_null() { + if let Some(ref subject) = mimeparser.subject { let mut prepend_subject: libc::c_int = 1i32; - if 0 == mimeparser.decrypting_failed { - let p: *mut libc::c_char = strchr(mimeparser.subject, ':' as i32); - if p.wrapping_offset_from(mimeparser.subject) == 2 - || p.wrapping_offset_from(mimeparser.subject) == 3 + if !mimeparser.decrypting_failed { + let colon = subject.find(':'); + if colon == Some(2) + || colon == Some(3) || mimeparser.is_send_by_messenger - || !strstr( - mimeparser.subject, - b"Chat:\x00" as *const u8 as *const libc::c_char, - ) - .is_null() + || subject.contains("Chat:") { prepend_subject = 0i32 } } if 0 != prepend_subject { - let subj: *mut libc::c_char = dc_strdup(mimeparser.subject); - let p_0: *mut libc::c_char = strchr(subj, '[' as i32); - if !p_0.is_null() { - *p_0 = 0i32 as libc::c_char + let subj = if let Some(n) = subject.find('[') { + &subject[0..n] + } else { + subject } - dc_trim(subj); - if 0 != *subj.offset(0isize) { + .trim(); + + if !subj.is_empty() { + let subj_c = CString::yolo(subj); for part in mimeparser.parts.iter_mut() { if part.type_0 == Viewtype::Text { let msg_c = part.msg.as_ref().unwrap().strdup(); let new_txt: *mut libc::c_char = dc_mprintf( b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char, - subj, + subj_c.as_ptr(), msg_c, ); free(msg_c.cast()); @@ -270,10 +263,9 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m } } } - free(subj as *mut libc::c_void); } } - if 0 != mimeparser.is_forwarded { + if mimeparser.is_forwarded { for part in mimeparser.parts.iter_mut() { part.param.set_int(Param::Forwarded, 1); } @@ -301,7 +293,7 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m } } } - if 0 == mimeparser.decrypting_failed { + if !mimeparser.decrypting_failed { let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field( &mimeparser, "Chat-Disposition-Notification-To", @@ -350,10 +342,12 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m if dc_mimeparser_get_last_nonmeta(&mut mimeparser).is_none() && mimeparser.reports.is_empty() { let mut part_5 = dc_mimepart_new(); part_5.type_0 = Viewtype::Text; - if !mimeparser.subject.is_null() && !mimeparser.is_send_by_messenger { - part_5.msg = Some(to_string(mimeparser.subject)); - } else { - part_5.msg = Some("".into()); + part_5.msg = Some("".into()); + + if let Some(ref subject) = mimeparser.subject { + if !mimeparser.is_send_by_messenger { + part_5.msg = Some(subject.to_string()) + } } mimeparser.parts.push(part_5); }; @@ -366,10 +360,10 @@ pub unsafe fn dc_mimeparser_parse<'a>(context: &'a Context, body: &[u8]) -> dc_m unsafe fn dc_mimepart_new() -> dc_mimepart_t { dc_mimepart_t { type_0: Viewtype::Unknown, - is_meta: 0, + is_meta: false, int_mimetype: 0, msg: None, - msg_raw: std::ptr::null_mut(), + msg_raw: None, bytes: 0, param: Params::new(), } @@ -378,11 +372,7 @@ unsafe fn dc_mimepart_new() -> dc_mimepart_t { pub fn dc_mimeparser_get_last_nonmeta<'a>( mimeparser: &'a mut dc_mimeparser_t, ) -> Option<&'a mut dc_mimepart_t> { - mimeparser - .parts - .iter_mut() - .rev() - .find(|part| part.is_meta == 0) + mimeparser.parts.iter_mut().rev().find(|part| !part.is_meta) } /*the result must be freed*/ @@ -596,12 +586,12 @@ unsafe fn dc_mimeparser_parse_mime_recursive( .stock_str(StockMessage::CantDecryptMsgBody); let txt = format!("[{}]", msg_body); - part.msg_raw = txt.strdup(); + part.msg_raw = Some(txt.clone()); part.msg = Some(txt); mimeparser.parts.push(part); any_part_added = 1i32; - mimeparser.decrypting_failed = 1i32 + mimeparser.decrypting_failed = true; } 46 => { cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; @@ -1124,13 +1114,18 @@ unsafe fn dc_mimeparser_add_single_part_if_known( part.type_0 = Viewtype::Text; part.int_mimetype = mime_type; part.msg = Some(simplified_txt); - part.msg_raw = - strndup(decoded_data, decoded_data_bytes as libc::c_ulong); + part.msg_raw = { + let raw_c = + strndup(decoded_data, decoded_data_bytes as libc::c_ulong); + let raw = to_string(raw_c); + free(raw_c.cast()); + Some(raw) + }; do_add_single_part(mimeparser, part); } if simplifier.unwrap().is_forwarded { - mimeparser.is_forwarded = 1i32 + mimeparser.is_forwarded = true; } } } @@ -1284,7 +1279,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( mimeparser, msg_type, mime_type, - raw_mime, + as_str(raw_mime), decoded_data, decoded_data_bytes, desired_filename, @@ -1312,7 +1307,7 @@ unsafe fn do_add_single_file_part( parser: &mut dc_mimeparser_t, msg_type: Viewtype, mime_type: libc::c_int, - raw_mime: *const libc::c_char, + raw_mime: &str, decoded_data: *const libc::c_char, decoded_data_bytes: size_t, desired_filename: *const libc::c_char, @@ -1338,7 +1333,7 @@ unsafe fn do_add_single_file_part( part.int_mimetype = mime_type; part.bytes = decoded_data_bytes as libc::c_int; part.param.set(Param::File, as_str(pathNfilename)); - part.param.set(Param::MimeType, as_str(raw_mime)); + part.param.set(Param::MimeType, raw_mime); if mime_type == 80 { assert!(!decoded_data.is_null(), "invalid image data"); let data = std::slice::from_raw_parts( @@ -1655,9 +1650,7 @@ pub unsafe fn dc_mimeparser_repl_msg_by_error( let part = &mut mimeparser.parts[0]; part.type_0 = Viewtype::Text; part.msg = Some(format!("[{}]", to_string(error_msg))); - for part in mimeparser.parts.drain(1..) { - dc_mimepart_unref(part); - } + mimeparser.parts.truncate(1); assert_eq!(mimeparser.parts.len(), 1); } @@ -1814,10 +1807,7 @@ mod tests { let raw = b"Content-Type: multipart/mixed; boundary=\"==break==\";\nSubject: outer-subject\nX-Special-A: special-a\nFoo: Bar\nChat-Version: 0.0\n\n--==break==\nContent-Type: text/plain; protected-headers=\"v1\";\nSubject: inner-subject\nX-Special-B: special-b\nFoo: Xy\nChat-Version: 1.0\n\ntest1\n\n--==break==--\n\n\x00"; let mut mimeparser = dc_mimeparser_parse(&context.ctx, &raw[..]); - assert_eq!( - &to_string(mimeparser.subject as *const libc::c_char), - "inner-subject", - ); + assert_eq!(mimeparser.subject, Some("inner-subject".into())); let mut of: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A"); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index e4f0e65c4..29bfdd2f9 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1,3 +1,4 @@ +use std::ffi::CString; use std::ptr; use itertools::join; @@ -625,7 +626,7 @@ unsafe fn add_parts( |mut stmt, conn| { for i in 0..icnt { let part = &mut mime_parser.parts[i]; - if part.is_meta != 0 { + if part.is_meta { continue; } @@ -641,14 +642,18 @@ unsafe fn add_parts( } } if part.type_0 == Viewtype::Text { + let msg_raw = CString::yolo(part.msg_raw.as_ref().unwrap().clone()); + let subject_c = CString::yolo( + mime_parser + .subject + .as_ref() + .map(|s| s.to_string()) + .unwrap_or("".into()), + ); txt_raw = dc_mprintf( b"%s\n\n%s\x00" as *const u8 as *const libc::c_char, - if !mime_parser.subject.is_null() { - mime_parser.subject - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - part.msg_raw, + subject_c.as_ptr(), + msg_raw.as_ptr(), ) } if 0 != mime_parser.is_system_message { @@ -1568,8 +1573,8 @@ unsafe fn create_or_lookup_adhoc_group( } // use subject as initial chat name - if !mime_parser.subject.is_null() && 0 != *mime_parser.subject.offset(0isize) as libc::c_int { - grpname = dc_strdup(mime_parser.subject) + if let Some(subject) = mime_parser.subject.as_ref().filter(|s| !s.is_empty()) { + grpname = subject.strdup(); } else { grpname = context .stock_string_repl_int(StockMessage::Member, member_ids.len() as libc::c_int) diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 7a5578141..9ecdb99a1 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -435,21 +435,10 @@ pub unsafe fn clist_free_content(haystack: *const clist) { pub unsafe fn clist_search_string_nocase( haystack: *const clist, needle: *const libc::c_char, -) -> libc::c_int { - let mut iter = (*haystack).first; - - while !iter.is_null() { - if strcasecmp((*iter).data as *const libc::c_char, needle) == 0 { - return 1; - } - iter = if !iter.is_null() { - (*iter).next - } else { - ptr::null_mut() - } - } - - 0 +) -> bool { + (&*haystack) + .into_iter() + .any(|data| strcasecmp(data.cast(), needle) == 0) } /* date/time tools */ @@ -630,6 +619,21 @@ pub unsafe fn dc_create_outgoing_rfc724_mid( ret } +/// Generate globally-unique message-id for a new outgoing message. +/// +/// Note: Do not add a counter or any private data as as this may give +/// unneeded information to the receiver +pub fn dc_create_outgoing_rfc724_mid_safe(grpid: Option<&str>, from_addr: &str) -> String { + let hostname = from_addr + .find('@') + .map(|k| &from_addr[k..]) + .unwrap_or("@nohost"); + match grpid { + Some(grpid) => format!("Gr.{}.{}{}", grpid, dc_create_id(), hostname), + None => format!("Mr.{}.{}{}", dc_create_id(), dc_create_id(), hostname), + } +} + /// Extract the group id (grpid) from a message id (mid) /// /// # Arguments diff --git a/src/job.rs b/src/job.rs index bacb73c1b..066e0eac4 100644 --- a/src/job.rs +++ b/src/job.rs @@ -692,9 +692,7 @@ pub unsafe fn job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int { ); } else { /* unrecoverable */ - if clist_search_string_nocase(mimefactory.recipients_addr, mimefactory.from_addr) - == 0i32 - { + if !clist_search_string_nocase(mimefactory.recipients_addr, mimefactory.from_addr) { clist_insert_after( mimefactory.recipients_names, (*mimefactory.recipients_names).last,