From ddfd067e97c2ac4abecbbd19300fd818a5a41380 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 14 Aug 2019 23:36:16 +0200 Subject: [PATCH] refactor(chat): rust based memory management --- deltachat-ffi/src/lib.rs | 21 +- examples/repl/cmdline.rs | 136 ++++--- examples/simple.rs | 2 +- src/chat.rs | 821 ++++++++++++++++++--------------------- src/chatlist.rs | 29 +- src/dc_job.rs | 10 +- src/dc_lot.rs | 9 +- src/dc_mimefactory.rs | 288 +++++++------- src/dc_msg.rs | 50 +-- src/dc_receive_imf.rs | 18 +- src/dc_securejoin.rs | 41 +- tests/stress.rs | 8 +- 12 files changed, 694 insertions(+), 739 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 7d78fcb37..d3c53f556 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -570,7 +570,10 @@ pub unsafe extern "C" fn dc_get_chat<'a>( assert!(!context.is_null()); let context = &*context; - chat::dc_get_chat(context, chat_id) + match chat::dc_get_chat(context, chat_id) { + Ok(chat) => Box::into_raw(Box::new(chat)), + Err(_) => std::ptr::null_mut(), + } } #[no_mangle] @@ -1239,7 +1242,9 @@ pub unsafe extern "C" fn dc_chatlist_get_summary<'a>( ) -> *mut dc_lot::dc_lot_t { assert!(!chatlist.is_null()); + let chat = if chat.is_null() { None } else { Some(&*chat) }; let list = &*chatlist; + list.get_summary(index as usize, chat) } @@ -1262,12 +1267,13 @@ pub type dc_chat_t<'a> = chat::Chat<'a>; pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) { assert!(!chat.is_null()); - chat::dc_chat_unref(chat) + Box::from_raw(chat); } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_id(chat) } @@ -1275,6 +1281,7 @@ pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { #[no_mangle] pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_type(chat) as libc::c_int } @@ -1282,6 +1289,7 @@ pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { #[no_mangle] pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_name(chat) } @@ -1289,6 +1297,7 @@ pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_ #[no_mangle] pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_subtitle(chat) } @@ -1296,6 +1305,7 @@ pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc #[no_mangle] pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_profile_image(chat) } @@ -1303,6 +1313,7 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut #[no_mangle] pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_color(chat) } @@ -1310,6 +1321,7 @@ pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { #[no_mangle] pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_get_archived(chat) as libc::c_int } @@ -1317,6 +1329,7 @@ pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_i #[no_mangle] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_is_unpromoted(chat) } @@ -1324,6 +1337,7 @@ pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_ #[no_mangle] pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_is_self_talk(chat) } @@ -1331,6 +1345,7 @@ pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_i #[no_mangle] pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_is_verified(chat) } @@ -1338,6 +1353,7 @@ pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_in #[no_mangle] pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); + let chat = &*chat; chat::dc_chat_is_sending_locations(chat) as libc::c_int } @@ -1500,6 +1516,7 @@ pub unsafe extern "C" fn dc_msg_get_summary<'a>( chat: *mut dc_chat_t<'a>, ) -> *mut dc_lot::dc_lot_t { assert!(!msg.is_null()); + let chat = if chat.is_null() { None } else { Some(&*chat) }; dc_msg::dc_msg_get_summary(msg, chat) } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 2b3c9afb1..066d13ba3 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -349,16 +349,16 @@ pub unsafe fn dc_cmdline_skip_auth() { S_IS_AUTH = 1; } -unsafe fn chat_prefix(chat: *const Chat) -> &'static str { - (*chat).typ.into() +fn chat_prefix(chat: &Chat) -> &'static str { + chat.typ.into() } pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> { let chat_id = *context.cmdline_sel_chat_id.read().unwrap(); let mut sel_chat = if chat_id > 0 { - dc_get_chat(context, chat_id) + dc_get_chat(context, chat_id).ok() } else { - std::ptr::null_mut() + None }; let mut args = line.splitn(3, ' '); @@ -606,23 +606,23 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ); for i in (0..cnt).rev() { - let chat = dc_get_chat(context, chatlist.get_chat_id(i)); - let temp_subtitle = dc_chat_get_subtitle(chat); - let temp_name = dc_chat_get_name(chat); + let chat = dc_get_chat(context, chatlist.get_chat_id(i))?; + let temp_subtitle = dc_chat_get_subtitle(&chat); + let temp_name = dc_chat_get_name(&chat); info!( context, 0, "{}#{}: {} [{}] [{} fresh]", - chat_prefix(chat), - dc_chat_get_id(chat) as libc::c_int, + chat_prefix(&chat), + dc_chat_get_id(&chat) as libc::c_int, as_str(temp_name), as_str(temp_subtitle), - dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int, + dc_get_fresh_msg_cnt(context, dc_chat_get_id(&chat)) as libc::c_int, ); free(temp_subtitle as *mut libc::c_void); free(temp_name as *mut libc::c_void); - let lot = chatlist.get_summary(i, chat); - let statestr = if dc_chat_get_archived(chat) { + let lot = chatlist.get_summary(i, Some(&chat)); + let statestr = if dc_chat_get_archived(&chat) { " [Archived]" } else { match dc_lot_get_state(lot) { @@ -645,7 +645,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E to_string(text2), statestr, ×tr, - if dc_chat_is_sending_locations(chat) { + if dc_chat_is_sending_locations(&chat) { "📍" } else { "" @@ -654,7 +654,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E free(text1 as *mut libc::c_void); free(text2 as *mut libc::c_void); dc_lot_unref(lot); - dc_chat_unref(chat); info!( context, 0, "================================================================================" @@ -667,20 +666,18 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E println!("{} chats", cnt); } "chat" => { - if sel_chat.is_null() && arg1.is_empty() { + if sel_chat.is_none() && arg1.is_empty() { bail!("Argument [chat-id] is missing."); } - if !sel_chat.is_null() && !arg1.is_empty() { - dc_chat_unref(sel_chat); - } if !arg1.is_empty() { let chat_id = arg1.parse()?; println!("Selecting chat #{}", chat_id); - sel_chat = dc_get_chat(context, chat_id); + sel_chat = Some(dc_get_chat(context, chat_id)?); *context.cmdline_sel_chat_id.write().unwrap() = chat_id; } - ensure!(!sel_chat.is_null(), "Failed to select chat"); + ensure!(sel_chat.is_some(), "Failed to select chat"); + let sel_chat = sel_chat.as_ref().unwrap(); let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0); let temp2 = dc_chat_get_subtitle(sel_chat); @@ -730,16 +727,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "createchatbymsg" => { ensure!(!arg1.is_empty(), "Argument missing"); let msg_id_0: libc::c_int = arg1.parse()?; - let chat_id_0: libc::c_int = - dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int; + let chat_id_0 = dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int; if chat_id_0 != 0 { - let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t); - println!( - "{}#{} created successfully.", - chat_prefix(chat_0), - chat_id_0, - ); - dc_chat_unref(chat_0); + let chat = dc_get_chat(context, chat_id_0 as uint32_t)?; + println!("{}#{} created successfully.", chat_prefix(&chat), chat_id_0,); } else { bail!(""); } @@ -763,13 +754,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "addmember" => { - ensure!(!sel_chat.is_null(), "No chat selected"); + ensure!(sel_chat.is_some(), "No chat selected"); ensure!(!arg1.is_empty(), "Argument missing."); let contact_id_0: libc::c_int = arg1.parse()?; if 0 != dc_add_contact_to_chat( context, - dc_chat_get_id(sel_chat), + dc_chat_get_id(sel_chat.as_ref().unwrap()), contact_id_0 as uint32_t, ) { println!("Contact added to chat."); @@ -778,12 +769,12 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "removemember" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); let contact_id_1: libc::c_int = arg1.parse()?; if 0 != dc_remove_contact_from_chat( context, - dc_chat_get_id(sel_chat), + dc_chat_get_id(sel_chat.as_ref().unwrap()), contact_id_1 as uint32_t, ) { println!("Contact added to chat."); @@ -792,21 +783,21 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "groupname" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) { + if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), arg1_c) { println!("Chat name set"); } else { bail!("Failed to set chat name"); } } "groupimage" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); if 0 != dc_set_chat_profile_image( context, - dc_chat_get_id(sel_chat), + dc_chat_get_id(sel_chat.as_ref().unwrap()), if !arg1.is_empty() { arg1_c } else { @@ -819,21 +810,33 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "chatinfo" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); - let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat)); + let contacts = + dc_get_chat_contacts(context, dc_chat_get_id(sel_chat.as_ref().unwrap())); info!(context, 0, "Memberlist:"); log_contactlist(context, &contacts); println!( "{} contacts\nLocation streaming: {}", contacts.len(), - dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)), + dc_is_sending_locations_to_chat( + context, + dc_chat_get_id(sel_chat.as_ref().unwrap()) + ), ); } "getlocations" => { + ensure!(sel_chat.is_some(), "No chat selected."); + let contact_id = arg1.parse().unwrap_or_default(); - let locations = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0); + let locations = dc_get_locations( + context, + dc_chat_get_id(sel_chat.as_ref().unwrap()), + contact_id, + 0, + 0, + ); let default_marker = "-".to_string(); for location in &locations { let marker = location.marker.as_ref().unwrap_or(&default_marker); @@ -857,12 +860,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "sendlocations" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "No timeout given."); let seconds = arg1.parse()?; - dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds); - println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation ' to play around.", dc_chat_get_id(sel_chat), seconds); + dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), seconds); + println!( + "Locations will be sent to Chat#{} for {} seconds. Use 'setlocation ' to play around.", + dc_chat_get_id(sel_chat.as_ref().unwrap()), + seconds + ); } "setlocation" => { ensure!( @@ -883,27 +890,31 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E dc_delete_all_locations(context); } "send" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "No message text given."); let msg = format!("{} {}", arg1, arg2); - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) { + if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), msg) { println!("Message sent."); } else { bail!("Sending failed."); } } "sendempty" => { - ensure!(!sel_chat.is_null(), "No chat selected."); - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) { + ensure!(sel_chat.is_some(), "No chat selected."); + if 0 != dc_send_text_msg( + context, + dc_chat_get_id(sel_chat.as_ref().unwrap()), + "".into(), + ) { println!("Message sent."); } else { bail!("Sending failed."); } } "sendimage" | "sendfile" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given."); let msg_0 = dc_msg_new( @@ -916,13 +927,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ); dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char); dc_msg_set_text(msg_0, arg2_c); - dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); + dc_send_msg(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), msg_0); dc_msg_unref(msg_0); } "listmsgs" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat = if !sel_chat.is_null() { + let chat = if let Some(ref sel_chat) = sel_chat { dc_chat_get_id(sel_chat) } else { 0 as libc::c_uint @@ -937,25 +948,29 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } } "draft" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); if !arg1.is_empty() { let draft_0 = dc_msg_new(context, Viewtype::Text); dc_msg_set_text(draft_0, arg1_c); - dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0); + dc_set_draft(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), draft_0); dc_msg_unref(draft_0); println!("Draft saved."); } else { - dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t); + dc_set_draft( + context, + dc_chat_get_id(sel_chat.as_ref().unwrap()), + 0 as *mut dc_msg_t, + ); println!("Draft deleted."); } } "listmedia" => { - ensure!(!sel_chat.is_null(), "No chat selected."); + ensure!(sel_chat.is_some(), "No chat selected."); let images = dc_get_chat_media( context, - dc_chat_get_id(sel_chat), + dc_chat_get_id(sel_chat.as_ref().unwrap()), Viewtype::Image, Viewtype::Gif, Viewtype::Video, @@ -1076,9 +1091,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if 0 != i { res += ", "; } - let chat = dc_get_chat(context, chatlist.get_chat_id(i)); - res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat)); - dc_chat_unref(chat); + let chat = dc_get_chat(context, chatlist.get_chat_id(i))?; + res += &format!("{}#{}", chat_prefix(&chat), dc_chat_get_id(&chat)); } } @@ -1124,10 +1138,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E _ => bail!("Unknown command: \"{}\" type ? for help.", arg0), } - if !sel_chat.is_null() { - dc_chat_unref(sel_chat); - } - free(arg1_c as *mut _); free(arg2_c as *mut _); diff --git a/examples/simple.rs b/examples/simple.rs index 48c7db7d8..261e4eb28 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -103,7 +103,7 @@ fn main() { let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap(); for i in 0..chats.len() { - let summary = chats.get_summary(0, std::ptr::null_mut()); + let summary = chats.get_summary(0, None); let text1 = dc_lot_get_text1(summary); let text2 = dc_lot_get_text2(summary); diff --git a/src/chat.rs b/src/chat.rs index 22eacab72..5e66f7016 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -8,6 +8,7 @@ use crate::dc_array::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_tools::*; +use crate::error::Error; use crate::param::*; use crate::sql::{self, Sql}; use crate::stock::StockMessage; @@ -21,11 +22,11 @@ use std::ptr; /// if you want an update, you have to recreate the object. #[derive(Clone)] pub struct Chat<'a> { + pub context: &'a Context, pub id: u32, pub typ: Chattype, pub name: *mut libc::c_char, archived: bool, - pub context: &'a Context, pub grpid: *mut libc::c_char, blocked: Blocked, pub param: Params, @@ -33,72 +34,58 @@ pub struct Chat<'a> { is_sending_locations: bool, } +impl<'a> Chat<'a> { + pub fn new(context: &'a Context) -> Self { + Chat { + context, + id: 0, + typ: Chattype::Undefined, + name: std::ptr::null_mut(), + archived: false, + grpid: std::ptr::null_mut(), + blocked: Blocked::Not, + param: Params::new(), + gossiped_timestamp: 0, + is_sending_locations: false, + } + } +} +impl<'a> Drop for Chat<'a> { + fn drop(&mut self) { + unsafe { + free(self.name.cast()); + free(self.grpid.cast()); + } + } +} + // handle chats pub unsafe fn dc_create_chat_by_msg_id(context: &Context, msg_id: u32) -> u32 { let mut chat_id: u32 = 0i32 as u32; let mut send_event: libc::c_int = 0i32; - let msg: *mut dc_msg_t = dc_msg_new_untyped(context); - let chat: *mut Chat = dc_chat_new(context); - if dc_msg_load_from_db(msg, context, msg_id) - && dc_chat_load_from_db(chat, (*msg).chat_id) - && (*chat).id > 9i32 as libc::c_uint - { - chat_id = (*chat).id; - if (*chat).blocked != Blocked::Not { - dc_unblock_chat(context, (*chat).id); - send_event = 1i32 + let msg = dc_msg_new_untyped(context); + + if dc_msg_load_from_db(msg, context, msg_id) { + if let Ok(chat) = dc_chat_load_from_db(context, (*msg).chat_id) { + if chat.id > 9 { + chat_id = chat.id; + if chat.blocked != Blocked::Not { + dc_unblock_chat(context, chat.id); + send_event = 1; + } + Contact::scaleup_origin_by_id(context, (*msg).from_id, Origin::CreateChat); + } } - Contact::scaleup_origin_by_id(context, (*msg).from_id, Origin::CreateChat); } dc_msg_unref(msg); - dc_chat_unref(chat); + if 0 != send_event { context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); } chat_id } -pub unsafe fn dc_chat_new<'a>(context: &'a Context) -> *mut Chat<'a> { - let chat = Chat { - id: 0, - typ: Chattype::Undefined, - name: std::ptr::null_mut(), - archived: false, - context, - grpid: std::ptr::null_mut(), - blocked: Blocked::Not, - param: Params::new(), - gossiped_timestamp: 0, - is_sending_locations: false, - }; - - Box::into_raw(Box::new(chat)) -} - -pub unsafe fn dc_chat_unref(chat: *mut Chat) { - if chat.is_null() { - return; - } - dc_chat_empty(chat); - Box::from_raw(chat); -} - -unsafe fn dc_chat_empty(mut chat: *mut Chat) { - if chat.is_null() { - return; - } - free((*chat).name as *mut libc::c_void); - (*chat).name = ptr::null_mut(); - (*chat).typ = Chattype::Undefined; - (*chat).id = 0i32 as u32; - free((*chat).grpid as *mut libc::c_void); - (*chat).grpid = ptr::null_mut(); - (*chat).blocked = Blocked::Not; - (*chat).gossiped_timestamp = 0; - (*chat).param = Params::new(); -} - pub unsafe fn dc_unblock_chat(context: &Context, chat_id: u32) { dc_block_chat(context, chat_id, 0i32); } @@ -113,21 +100,14 @@ fn dc_block_chat(context: &Context, chat_id: u32, new_blocking: libc::c_int) -> .is_ok() } -pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { - if chat.is_null() { - return false; - } - unsafe { dc_chat_empty(chat) }; - - let context = unsafe { (*chat).context }; - +pub fn dc_chat_load_from_db(context: &Context, chat_id: u32) -> Result { let res = context.sql.query_row( "SELECT c.id,c.type,c.name, c.grpid,c.param,c.archived, \ c.blocked, c.gossiped_timestamp, c.locations_send_until \ FROM chats c WHERE c.id=?;", params![chat_id as i32], |row| { - let c = unsafe { &mut *chat }; + let mut c = Chat::new(context); c.id = row.get(0)?; c.typ = row.get(1)?; @@ -140,68 +120,63 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { c.gossiped_timestamp = row.get(7)?; c.is_sending_locations = row.get(8)?; - Ok(()) + Ok(c) }, ); match res { - Err(crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => false, + Err(err @ crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => Err(err), Err(err) => match err { _ => { error!( context, 0, "chat: failed to load from db {}: {:?}", chat_id, err ); - false + Err(err) } }, - Ok(_) => { - let c = unsafe { &mut *chat }; - match c.id { + Ok(mut chat) => { + match chat.id { 1 => unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = (*chat).context.stock_str(StockMessage::DeadDrop).strdup(); + free(chat.name.cast()); + chat.name = chat.context.stock_str(StockMessage::DeadDrop).strdup(); }, 6 => unsafe { - free((*chat).name as *mut libc::c_void); - let tempname = (*chat).context.stock_str(StockMessage::ArchivedChats); - let cnt = dc_get_archived_cnt((*chat).context); - (*chat).name = format!("{} ({})", tempname, cnt).strdup(); + free(chat.name.cast()); + let tempname = chat.context.stock_str(StockMessage::ArchivedChats); + let cnt = dc_get_archived_cnt(chat.context); + chat.name = format!("{} ({})", tempname, cnt).strdup(); }, 5 => unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = (*chat) - .context - .stock_str(StockMessage::StarredMsgs) - .strdup(); + free(chat.name.cast()); + chat.name = chat.context.stock_str(StockMessage::StarredMsgs).strdup(); }, _ => { unsafe { - if (*chat).typ == Chattype::Single { - free((*chat).name as *mut libc::c_void); - let contacts = dc_get_chat_contacts((*chat).context, (*chat).id); + if chat.typ == Chattype::Single { + free(chat.name.cast()); + let contacts = dc_get_chat_contacts(chat.context, chat.id); let mut chat_name = "Err [Name not found]".to_owned(); + if !(*contacts).is_empty() { - if let Ok(contact) = - Contact::get_by_id((*chat).context, contacts[0]) - { + if let Ok(contact) = Contact::get_by_id(chat.context, contacts[0]) { chat_name = contact.get_display_name().to_owned(); } } - (*chat).name = (&chat_name).strdup(); + + chat.name = (&chat_name).strdup(); } } - if unsafe { &(*chat).param }.exists(Param::Selftalk) { + if chat.param.exists(Param::Selftalk) { unsafe { - free((*chat).name as *mut libc::c_void); - (*chat).name = - (*chat).context.stock_str(StockMessage::SelfMsg).strdup(); + free(chat.name as *mut libc::c_void); + chat.name = chat.context.stock_str(StockMessage::SelfMsg).strdup(); } } } } - true + Ok(chat) } } } @@ -458,15 +433,18 @@ unsafe fn prepare_msg_common<'a>( } if OK_TO_CONTINUE { dc_unarchive_chat(context, chat_id); - let chat = dc_chat_new(context); - if dc_chat_load_from_db(chat, chat_id) { + if let Ok(mut chat) = dc_chat_load_from_db(context, chat_id) { if (*msg).state != DC_STATE_OUT_PREPARING { (*msg).state = DC_STATE_OUT_PENDING } - (*msg).id = prepare_msg_raw(context, chat, msg, dc_create_smeared_timestamp(context)); + (*msg).id = prepare_msg_raw( + context, + &mut chat, + msg, + dc_create_smeared_timestamp(context), + ); (*msg).chat_id = chat_id } - dc_chat_unref(chat); } (*msg).id @@ -475,7 +453,7 @@ unsafe fn prepare_msg_common<'a>( #[allow(non_snake_case)] unsafe fn prepare_msg_raw( context: &Context, - chat: *mut Chat, + chat: &mut Chat, msg: *mut dc_msg_t, timestamp: i64, ) -> u32 { @@ -492,13 +470,13 @@ unsafe fn prepare_msg_raw( let mut to_id = 0; let mut location_id = 0; - if !((*chat).typ == Chattype::Single - || (*chat).typ == Chattype::Group - || (*chat).typ == Chattype::VerifiedGroup) + if !(chat.typ == Chattype::Single + || chat.typ == Chattype::Group + || chat.typ == Chattype::VerifiedGroup) { - error!(context, 0, "Cannot send to chat type #{}.", (*chat).typ,); - } else if ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) - && 0 == dc_is_contact_in_chat(context, (*chat).id, 1 as u32) + error!(context, 0, "Cannot send to chat type #{}.", chat.typ,); + } else if (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup) + && 0 == dc_is_contact_in_chat(context, chat.id, 1 as u32) { log_event!( context, @@ -513,35 +491,33 @@ unsafe fn prepare_msg_raw( } else { let from_c = CString::yolo(from.unwrap()); new_rfc724_mid = dc_create_outgoing_rfc724_mid( - if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { - (*chat).grpid + if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { + chat.grpid } else { ptr::null_mut() }, from_c.as_ptr(), ); - if (*chat).typ == Chattype::Single { + if chat.typ == Chattype::Single { if let Some(id) = context.sql.query_row_col( context, "SELECT contact_id FROM chats_contacts WHERE chat_id=?;", - params![(*chat).id as i32], + params![chat.id as i32], 0, ) { to_id = id; } else { error!( context, - 0, - "Cannot send message, contact for chat #{} not found.", - (*chat).id, + 0, "Cannot send message, contact for chat #{} not found.", chat.id, ); OK_TO_CONTINUE = false; } } else { - if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { - if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 1 { - (*chat).param.remove(Param::Unpromoted); + if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 { + chat.param.remove(Param::Unpromoted); dc_chat_update_param(chat); } } @@ -572,7 +548,7 @@ unsafe fn prepare_msg_raw( 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![(*chat).id], + params![chat.id], |row| { let state: String = row.get(1)?; @@ -609,8 +585,7 @@ unsafe fn prepare_msg_raw( if 0 != can_encrypt { if 0 != all_mutual { do_guarantee_e2ee = 1; - } else if 0 != last_msg_in_chat_encrypted(context, &context.sql, (*chat).id) - { + } else if 0 != last_msg_in_chat_encrypted(context, &context.sql, chat.id) { do_guarantee_e2ee = 1; } } @@ -682,7 +657,7 @@ unsafe fn prepare_msg_raw( params![ timestamp, DC_CONTACT_ID_SELF as i32, - (*chat).id as i32, + chat.id as i32, (*msg) .param .get_float(Param::SetLatitude) @@ -715,7 +690,7 @@ unsafe fn prepare_msg_raw( "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), - (*chat).id as i32, + chat.id as i32, 1i32, to_id as i32, timestamp, @@ -741,7 +716,7 @@ unsafe fn prepare_msg_raw( context, 0, "Cannot send message, cannot insert to database (chat #{}).", - (*chat).id, + chat.id, ); } } @@ -760,27 +735,24 @@ unsafe fn prepare_msg_raw( // TODO should return bool /rtn unsafe fn get_parent_mime_headers( - chat: *const Chat, + chat: &Chat, parent_rfc724_mid: *mut *mut libc::c_char, parent_in_reply_to: *mut *mut libc::c_char, parent_references: *mut *mut libc::c_char, ) -> libc::c_int { let mut success = 0; - if !(chat.is_null() - || parent_rfc724_mid.is_null() - || parent_in_reply_to.is_null() - || parent_references.is_null()) + 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 - success = (*chat) + success = chat .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![(*chat).id as i32, DC_CONTACT_ID_SELF as i32], + params![chat.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(); @@ -791,14 +763,14 @@ unsafe fn get_parent_mime_headers( .is_ok() as libc::c_int; if 0 == success { - success = (*chat) + success = chat .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![(*chat).id as i32, DC_CONTACT_ID_SELF as i32], + params![chat.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(); @@ -812,11 +784,8 @@ unsafe fn get_parent_mime_headers( success } -pub unsafe fn dc_chat_is_self_talk(chat: *const Chat) -> libc::c_int { - if chat.is_null() { - return 0; - } - (*chat).param.exists(Param::Selftalk) as libc::c_int +pub unsafe fn dc_chat_is_self_talk(chat: &Chat) -> libc::c_int { + chat.param.exists(Param::Selftalk) as libc::c_int } /******************************************************************************* @@ -847,12 +816,12 @@ unsafe fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) } // TODO should return bool /rtn -pub unsafe fn dc_chat_update_param(chat: *mut Chat) -> libc::c_int { +pub unsafe fn dc_chat_update_param(chat: &mut Chat) -> libc::c_int { sql::execute( - (*chat).context, - &(*chat).context.sql, + chat.context, + &chat.context.sql, "UPDATE chats SET param=? WHERE id=?", - params![(*chat).param.to_string(), (*chat).id as i32], + params![chat.param.to_string(), chat.id as i32], ) .is_ok() as libc::c_int } @@ -1344,11 +1313,10 @@ pub fn dc_delete_chat(context: &Context, chat_id: u32) -> bool { if chat_id <= 9 { return false; } - let obj = unsafe { dc_chat_new(context) }; - if !dc_chat_load_from_db(obj, chat_id) { + + if dc_chat_load_from_db(context, chat_id).is_err() { return false; } - unsafe { dc_chat_unref(obj) }; if sql::execute( context, @@ -1423,20 +1391,8 @@ pub fn dc_get_chat_contacts(context: &Context, chat_id: u32) -> Vec { .unwrap_or_default() } -pub unsafe fn dc_get_chat(context: &Context, chat_id: u32) -> *mut Chat { - let mut success: libc::c_int = 0i32; - let obj: *mut Chat = dc_chat_new(context); - - if dc_chat_load_from_db(obj, chat_id) { - success = 1i32 - } - - if 0 != success { - obj - } else { - dc_chat_unref(obj); - ptr::null_mut() - } +pub fn dc_get_chat(context: &Context, chat_id: u32) -> Result { + dc_chat_load_from_db(context, chat_id) } // handle group chats @@ -1521,91 +1477,94 @@ pub unsafe fn dc_add_contact_to_chat_ex( let mut OK_TO_CONTINUE = true; let mut success: libc::c_int = 0; let contact = Contact::get_by_id(context, contact_id); - let chat: *mut Chat = dc_chat_new(context); - let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - if !(contact.is_err() || chat_id <= 9 as libc::c_uint) { - dc_reset_gossiped_timestamp(context, chat_id); - let contact = contact.unwrap(); + if contact.is_err() || chat_id <= 9 { + return 0; + } + let mut msg = dc_msg_new_untyped(context); - /*this also makes sure, not contacts are added to special or normal chats*/ - if !(0 == real_group_exists(context, chat_id) - || !Contact::real_exists_by_id(context, contact_id) - && contact_id != DC_CONTACT_ID_SELF as u32 - || !dc_chat_load_from_db(chat, chat_id)) - { - if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { - log_event!( - context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot add contact to group; self not in group.", - ); - } else { - /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if 0 != flags & 0x1 - && (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 1 - { - (*chat).param.remove(Param::Unpromoted); - dc_chat_update_param(chat); - } - let self_addr = context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(); - if contact.get_addr() != &self_addr { - // ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly. - // if SELF is not in the group, members cannot be added at all. + dc_reset_gossiped_timestamp(context, chat_id); + let contact = contact.unwrap(); - if 0 != dc_is_contact_in_chat(context, chat_id, contact_id) { - if 0 == flags & 0x1 { - success = 1; - OK_TO_CONTINUE = false; - } - } else { - // else continue and send status mail - if (*chat).typ == Chattype::VerifiedGroup { - if contact.is_verified() != VerifiedStatus::BidirectVerified { - error!( + /*this also makes sure, not contacts are added to special or normal chats*/ + let chat = dc_chat_load_from_db(context, chat_id); + + if !(0 == real_group_exists(context, chat_id) + || !Contact::real_exists_by_id(context, contact_id) + && contact_id != DC_CONTACT_ID_SELF as u32 + || chat.is_err()) + { + let mut chat = chat.unwrap(); + + if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { + log_event!( + context, + Event::ERROR_SELF_NOT_IN_GROUP, + 0, + "Cannot add contact to group; self not in group.", + ); + } else { + /* we should respect this - whatever we send to the group, it gets discarded anyway! */ + if 0 != flags & 0x1 && chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 { + chat.param.remove(Param::Unpromoted); + dc_chat_update_param(&mut chat); + } + let self_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default(); + if contact.get_addr() != &self_addr { + // ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly. + // if SELF is not in the group, members cannot be added at all. + + if 0 != dc_is_contact_in_chat(context, chat_id, contact_id) { + if 0 == flags & 0x1 { + success = 1; + OK_TO_CONTINUE = false; + } + } else { + // else continue and send status mail + if chat.typ == Chattype::VerifiedGroup { + if contact.is_verified() != VerifiedStatus::BidirectVerified { + error!( context, 0, "Only bidirectional verified contacts can be added to verified groups." ); - OK_TO_CONTINUE = false; - } - } - if OK_TO_CONTINUE { - if 0 == dc_add_to_chat_contacts_table(context, chat_id, contact_id) { - OK_TO_CONTINUE = false; - } + OK_TO_CONTINUE = false; } } if OK_TO_CONTINUE { - if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = Viewtype::Text; - (*msg).text = Some(context.stock_system_msg( - StockMessage::MsgAddMember, - contact.get_addr(), - "", - DC_CONTACT_ID_SELF as u32, - )); - (*msg).param.set_int(Param::Cmd, 4); - (*msg).param.set(Param::Arg, contact.get_addr()); - (*msg).param.set_int(Param::Arg2, flags); - (*msg).id = dc_send_msg(context, chat_id, msg); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); + if 0 == dc_add_to_chat_contacts_table(context, chat_id, contact_id) { + OK_TO_CONTINUE = false; } - context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0 as uintptr_t); - success = 1; } } + if OK_TO_CONTINUE { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( + StockMessage::MsgAddMember, + contact.get_addr(), + "", + DC_CONTACT_ID_SELF as u32, + )); + (*msg).param.set_int(Param::Cmd, 4); + (*msg).param.set(Param::Arg, contact.get_addr()); + (*msg).param.set_int(Param::Arg2, flags); + (*msg).id = dc_send_msg(context, chat_id, msg); + context.call_cb( + Event::MSGS_CHANGED, + chat_id as uintptr_t, + (*msg).id as uintptr_t, + ); + } + context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0 as uintptr_t); + success = 1; + } } } } - dc_chat_unref(chat); + dc_msg_unref(msg); success @@ -1668,69 +1627,71 @@ pub unsafe fn dc_remove_contact_from_chat( contact_id: u32, ) -> libc::c_int { let mut success = 0; - let chat: *mut Chat = dc_chat_new(context); - let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - if !(chat_id <= 9 as libc::c_uint - || contact_id <= 9 as libc::c_uint && contact_id != DC_CONTACT_ID_SELF as u32) - { - /* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */ - /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */ - if !(0 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { - if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { - log_event!( - context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot remove contact from chat; self not in group.", - ); - } else { - /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if let Ok(contact) = Contact::get_by_id(context, contact_id) { - if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = Viewtype::Text; - if contact.id == DC_CONTACT_ID_SELF as u32 { - dc_set_group_explicitly_left(context, (*chat).grpid); - (*msg).text = Some(context.stock_system_msg( - StockMessage::MsgGroupLeft, - "", - "", - DC_CONTACT_ID_SELF as u32, - )); - } else { - (*msg).text = Some(context.stock_system_msg( - StockMessage::MsgDelMember, - contact.get_addr(), - "", - DC_CONTACT_ID_SELF as u32, - )); - } - (*msg).param.set_int(Param::Cmd, 5); - (*msg).param.set(Param::Arg, contact.get_addr()); - (*msg).id = dc_send_msg(context, chat_id, msg); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); + if chat_id <= 9 || contact_id <= 9 && contact_id != DC_CONTACT_ID_SELF as u32 { + return 0; + } + + let mut msg = dc_msg_new_untyped(context); + + /* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */ + /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */ + let chat = dc_chat_load_from_db(context, chat_id); + + if !(0 == real_group_exists(context, chat_id) || chat.is_err()) { + let chat = chat.unwrap(); + if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { + log_event!( + context, + Event::ERROR_SELF_NOT_IN_GROUP, + 0, + "Cannot remove contact from chat; self not in group.", + ); + } else { + /* we should respect this - whatever we send to the group, it gets discarded anyway! */ + if let Ok(contact) = Contact::get_by_id(context, contact_id) { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { + (*msg).type_0 = Viewtype::Text; + if contact.id == DC_CONTACT_ID_SELF as u32 { + dc_set_group_explicitly_left(context, chat.grpid); + (*msg).text = Some(context.stock_system_msg( + StockMessage::MsgGroupLeft, + "", + "", + DC_CONTACT_ID_SELF as u32, + )); + } else { + (*msg).text = Some(context.stock_system_msg( + StockMessage::MsgDelMember, + contact.get_addr(), + "", + DC_CONTACT_ID_SELF as u32, + )); } + (*msg).param.set_int(Param::Cmd, 5); + (*msg).param.set(Param::Arg, contact.get_addr()); + (*msg).id = dc_send_msg(context, chat_id, msg); + context.call_cb( + Event::MSGS_CHANGED, + chat_id as uintptr_t, + (*msg).id as uintptr_t, + ); } - if sql::execute( - context, - &context.sql, - "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?;", - params![chat_id as i32, contact_id as i32], - ) - .is_ok() - { - context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); - success = 1; - } + } + if sql::execute( + context, + &context.sql, + "DELETE FROM chats_contacts WHERE chat_id=? AND contact_id=?;", + params![chat_id as i32, contact_id as i32], + ) + .is_ok() + { + context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); + success = 1; } } } - dc_chat_unref(chat); dc_msg_unref(msg); success @@ -1768,68 +1729,68 @@ pub unsafe fn dc_set_chat_name( ) -> libc::c_int { /* the function only sets the names of group chats; normal chats get their names from the contacts */ let mut success: libc::c_int = 0i32; - let chat: *mut Chat = dc_chat_new(context); - let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); + let mut msg = dc_msg_new_untyped(context); - if !(new_name.is_null() - || *new_name.offset(0isize) as libc::c_int == 0i32 - || chat_id <= 9i32 as libc::c_uint) - { - if !(0i32 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { - if strcmp((*chat).name, new_name) == 0i32 { - success = 1i32 - } else if !(dc_is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { - log_event!( - context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot set chat name; self not in group", - ); - } else { - /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if sql::execute( - context, - &context.sql, - format!( - "UPDATE chats SET name='{}' WHERE id={};", + if new_name.is_null() || *new_name.offset(0isize) as libc::c_int == 0 || chat_id <= 9 { + return 0; + } + + let chat = dc_chat_load_from_db(context, chat_id); + + if !(0i32 == real_group_exists(context, chat_id) || chat.is_err()) { + let chat = chat.unwrap(); + if strcmp(chat.name, new_name) == 0i32 { + success = 1i32 + } else if !(dc_is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { + log_event!( + context, + Event::ERROR_SELF_NOT_IN_GROUP, + 0, + "Cannot set chat name; self not in group", + ); + } else { + /* we should respect this - whatever we send to the group, it gets discarded anyway! */ + if sql::execute( + context, + &context.sql, + format!( + "UPDATE chats SET name='{}' WHERE id={};", + as_str(new_name), + chat_id as i32 + ), + params![], + ) + .is_ok() + { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( + StockMessage::MsgGrpName, + as_str(chat.name), as_str(new_name), - chat_id as i32 - ), - params![], - ) - .is_ok() - { - if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = Viewtype::Text; - (*msg).text = Some(context.stock_system_msg( - StockMessage::MsgGrpName, - as_str((*chat).name), - as_str(new_name), - DC_CONTACT_ID_SELF as u32, - )); - (*msg).param.set_int(Param::Cmd, 2); - if !(*chat).name.is_null() { - (*msg).param.set(Param::Arg, as_str((*chat).name)); - } - (*msg).id = dc_send_msg(context, chat_id, msg); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); + DC_CONTACT_ID_SELF as u32, + )); + (*msg).param.set_int(Param::Cmd, 2); + if !chat.name.is_null() { + (*msg).param.set(Param::Arg, as_str(chat.name)); } + (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( - Event::CHAT_MODIFIED, + Event::MSGS_CHANGED, chat_id as uintptr_t, - 0i32 as uintptr_t, + (*msg).id as uintptr_t, ); - success = 1i32 } + context.call_cb( + Event::CHAT_MODIFIED, + chat_id as uintptr_t, + 0i32 as uintptr_t, + ); + success = 1i32; } } } - dc_chat_unref(chat); dc_msg_unref(msg); success @@ -1844,11 +1805,17 @@ pub unsafe fn dc_set_chat_profile_image( ) -> libc::c_int { let mut OK_TO_CONTINUE = true; let mut success: libc::c_int = 0i32; - let chat: *mut Chat = dc_chat_new(context); - let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); + + if chat_id <= 9 { + return 0; + } + + let mut msg = dc_msg_new_untyped(context); let mut new_image_rel: *mut libc::c_char = ptr::null_mut(); if !(chat_id <= 9i32 as libc::c_uint) { - if !(0i32 == real_group_exists(context, chat_id) || !dc_chat_load_from_db(chat, chat_id)) { + let chat = dc_chat_load_from_db(context, chat_id); + if !(0i32 == real_group_exists(context, chat_id) || chat.is_err()) { + let mut chat = chat.unwrap(); if !(dc_is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { log_event!( context, @@ -1866,45 +1833,42 @@ pub unsafe fn dc_set_chat_profile_image( } else { OK_TO_CONTINUE = false; } - if OK_TO_CONTINUE { - (*chat) - .param - .set(Param::ProfileImage, as_str(new_image_rel)); - if !(0 == dc_chat_update_param(chat)) { - if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).param.set_int(Param::Cmd, 3); - (*msg).param.set(Param::Arg, as_str(new_image_rel)); - (*msg).type_0 = Viewtype::Text; - (*msg).text = Some(context.stock_system_msg( - if !new_image_rel.is_null() { - StockMessage::MsgGrpImgChanged - } else { - StockMessage::MsgGrpImgDeleted - }, - "", - "", - DC_CONTACT_ID_SELF as u32, - )); - (*msg).id = dc_send_msg(context, chat_id, msg); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); - } + } + if OK_TO_CONTINUE { + chat.param.set(Param::ProfileImage, as_str(new_image_rel)); + if !(0 == dc_chat_update_param(&mut chat)) { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { + (*msg).param.set_int(Param::Cmd, 3); + (*msg).param.set(Param::Arg, as_str(new_image_rel)); + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( + if !new_image_rel.is_null() { + StockMessage::MsgGrpImgChanged + } else { + StockMessage::MsgGrpImgDeleted + }, + "", + "", + DC_CONTACT_ID_SELF as u32, + )); + (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( - Event::CHAT_MODIFIED, + Event::MSGS_CHANGED, chat_id as uintptr_t, - 0i32 as uintptr_t, + (*msg).id as uintptr_t, ); - success = 1i32 } + context.call_cb( + Event::CHAT_MODIFIED, + chat_id as uintptr_t, + 0i32 as uintptr_t, + ); + success = 1i32; } } } } - dc_chat_unref(chat); dc_msg_unref(msg); free(new_image_rel as *mut libc::c_void); @@ -1922,12 +1886,11 @@ pub unsafe fn dc_forward_msgs( } let msg = dc_msg_new_untyped(context); - let chat = dc_chat_new(context); let mut created_db_entries = Vec::new(); let mut curr_timestamp: i64; dc_unarchive_chat(context, chat_id); - if dc_chat_load_from_db(chat, chat_id) { + if let Ok(mut chat) = dc_chat_load_from_db(context, chat_id) { curr_timestamp = dc_create_smeared_timestamps(context, msg_cnt); let idsstr = std::slice::from_raw_parts(msg_ids, msg_cnt as usize) .iter() @@ -1967,7 +1930,7 @@ pub unsafe fn dc_forward_msgs( if (*msg).state == DC_STATE_OUT_PREPARING { let fresh9 = curr_timestamp; curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh9); + new_msg_id = prepare_msg_raw(context, &mut chat, msg, fresh9); let save_param = (*msg).param.clone(); (*msg).param = original_param; (*msg).id = src_msg_id as u32; @@ -1987,7 +1950,7 @@ pub unsafe fn dc_forward_msgs( (*msg).state = DC_STATE_OUT_PENDING; let fresh10 = curr_timestamp; curr_timestamp = curr_timestamp + 1; - new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); + new_msg_id = prepare_msg_raw(context, &mut chat, msg, fresh10); dc_job_send_msg(context, new_msg_id); } created_db_entries.push(chat_id); @@ -2003,60 +1966,49 @@ pub unsafe fn dc_forward_msgs( ); } dc_msg_unref(msg); - dc_chat_unref(chat); } -pub unsafe fn dc_chat_get_id(chat: *const Chat) -> u32 { - if chat.is_null() { - return 0i32 as u32; - } - (*chat).id +pub unsafe fn dc_chat_get_id(chat: &Chat) -> u32 { + chat.id } -pub unsafe fn dc_chat_get_type(chat: *const Chat) -> Chattype { - assert!(!chat.is_null()); - (*chat).typ +pub unsafe fn dc_chat_get_type(chat: &Chat) -> Chattype { + chat.typ } -pub unsafe fn dc_chat_get_name(chat: *const Chat) -> *mut libc::c_char { - if chat.is_null() { - return dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char); - } - dc_strdup((*chat).name) +pub unsafe fn dc_chat_get_name(chat: &Chat) -> *mut libc::c_char { + dc_strdup(chat.name) } -pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { +pub unsafe fn dc_chat_get_subtitle(chat: &Chat) -> *mut libc::c_char { /* returns either the address or the number of chat members */ - if chat.is_null() { - return dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char); - } let mut ret: *mut libc::c_char = std::ptr::null_mut(); - if (*chat).typ == Chattype::Single && (*chat).param.exists(Param::Selftalk) { - ret = (*chat) + if chat.typ == Chattype::Single && chat.param.exists(Param::Selftalk) { + ret = chat .context .stock_str(StockMessage::SelfTalkSubTitle) .strdup(); - } else if (*chat).typ == Chattype::Single { - let ret_raw: String = (*chat) + } else if chat.typ == Chattype::Single { + let ret_raw: String = chat .context .sql .query_row_col( - (*chat).context, + chat.context, "SELECT c.addr FROM chats_contacts cc \ LEFT JOIN contacts c ON c.id=cc.contact_id \ WHERE cc.chat_id=?;", - params![(*chat).id as i32], + params![chat.id as i32], 0, ) .unwrap_or_else(|| "Err".into()); ret = ret_raw.strdup(); - } else if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { - if (*chat).id == 1 { - ret = (*chat).context.stock_str(StockMessage::DeadDrop).strdup(); + } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { + if chat.id == 1 { + ret = chat.context.stock_str(StockMessage::DeadDrop).strdup(); } else { - let cnt = dc_get_chat_contact_cnt((*chat).context, (*chat).id); - ret = (*chat) + let cnt = dc_get_chat_contact_cnt(chat.context, chat.id); + ret = chat .context .stock_string_repl_int(StockMessage::Member, cnt) .strdup(); @@ -2081,25 +2033,22 @@ pub fn dc_get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int { .unwrap_or_default() } -pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char { - let mut image_rel: *mut libc::c_char = 0 as *mut libc::c_char; +pub unsafe fn dc_chat_get_profile_image(chat: &Chat) -> *mut libc::c_char { let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; - if !chat.is_null() { - image_rel = (*chat) - .param - .get(Param::ProfileImage) - .unwrap_or_default() - .strdup(); - if !image_rel.is_null() && 0 != *image_rel.offset(0isize) as libc::c_int { - image_abs = dc_get_abs_path((*chat).context, image_rel) - } else if (*chat).typ == Chattype::Single { - let contacts = dc_get_chat_contacts((*chat).context, (*chat).id); - if !contacts.is_empty() { - if let Ok(contact) = Contact::get_by_id((*chat).context, contacts[0]) { - if let Some(img) = contact.get_profile_image() { - image_abs = img.strdup(); - } + let image_rel = chat + .param + .get(Param::ProfileImage) + .unwrap_or_default() + .strdup(); + if !image_rel.is_null() && 0 != *image_rel.offset(0isize) as libc::c_int { + image_abs = dc_get_abs_path(chat.context, image_rel) + } else if chat.typ == Chattype::Single { + let contacts = dc_get_chat_contacts(chat.context, chat.id); + if !contacts.is_empty() { + if let Ok(contact) = Contact::get_by_id(chat.context, contacts[0]) { + if let Some(img) = contact.get_profile_image() { + image_abs = img.strdup(); } } } @@ -2110,55 +2059,41 @@ pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char image_abs } -pub unsafe fn dc_chat_get_color(chat: *const Chat) -> u32 { +pub unsafe fn dc_chat_get_color(chat: &Chat) -> u32 { let mut color: u32 = 0i32 as u32; - if !chat.is_null() { - if (*chat).typ == Chattype::Single { - let contacts = dc_get_chat_contacts((*chat).context, (*chat).id); - if !contacts.is_empty() { - if let Ok(contact) = Contact::get_by_id((*chat).context, contacts[0]) { - color = contact.get_color(); - } + if chat.typ == Chattype::Single { + let contacts = dc_get_chat_contacts(chat.context, chat.id); + if !contacts.is_empty() { + if let Ok(contact) = Contact::get_by_id(chat.context, contacts[0]) { + color = contact.get_color(); } - } else { - color = dc_str_to_color((*chat).name) as u32 } + } else { + color = dc_str_to_color(chat.name) as u32 } color } // TODO should return bool /rtn -pub unsafe fn dc_chat_get_archived(chat: *const Chat) -> bool { - if chat.is_null() { - return false; - } - (*chat).archived +pub unsafe fn dc_chat_get_archived(chat: &Chat) -> bool { + chat.archived } // TODO should return bool /rtn -pub unsafe fn dc_chat_is_unpromoted(chat: *const Chat) -> libc::c_int { - if chat.is_null() { - return 0; - } - (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() as libc::c_int +pub unsafe fn dc_chat_is_unpromoted(chat: &Chat) -> libc::c_int { + chat.param.get_int(Param::Unpromoted).unwrap_or_default() as libc::c_int } // TODO should return bool /rtn -pub unsafe fn dc_chat_is_verified(chat: *const Chat) -> libc::c_int { - if chat.is_null() { - return 0i32; - } - ((*chat).typ == Chattype::VerifiedGroup) as libc::c_int +pub unsafe fn dc_chat_is_verified(chat: &Chat) -> libc::c_int { + (chat.typ == Chattype::VerifiedGroup) as libc::c_int } // TODO should return bool /rtn -pub unsafe fn dc_chat_is_sending_locations(chat: *const Chat) -> bool { - if chat.is_null() { - return false; - } - (*chat).is_sending_locations +pub unsafe fn dc_chat_is_sending_locations(chat: &Chat) -> bool { + chat.is_sending_locations } pub fn dc_get_chat_cnt(context: &Context) -> usize { diff --git a/src/chatlist.rs b/src/chatlist.rs index 631177ef1..b8e6ac2a9 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -249,7 +249,7 @@ impl<'a> Chatlist<'a> { /// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable. /// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()). // 0 if not applicable. - pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t { + pub unsafe fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> *mut dc_lot_t { // The summary is created by the chat, not by the last message. // This is because we may want to display drafts here or stuff as // "is typing". @@ -261,26 +261,27 @@ impl<'a> Chatlist<'a> { return ret; } - let lastmsg_id = self.ids[index].1; - let mut lastcontact = None; - - if chat.is_null() { - chat = dc_chat_new(self.context); - let chat_to_delete = chat; - if !dc_chat_load_from_db(chat, self.ids[index].0) { - (*ret).text2 = "ErrCannotReadChat".strdup(); - dc_chat_unref(chat_to_delete); - + let chat_loaded: Chat; + let chat = if let Some(chat) = chat { + chat + } else { + if let Ok(chat) = dc_get_chat(self.context, self.ids[index].0) { + chat_loaded = chat; + &chat_loaded + } else { return ret; } - } + }; + + let lastmsg_id = self.ids[index].1; + let mut lastcontact = None; let lastmsg = if 0 != lastmsg_id { let lastmsg = dc_msg_new_untyped(self.context); dc_msg_load_from_db(lastmsg, self.context, lastmsg_id); if (*lastmsg).from_id != 1 as libc::c_uint - && ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) + && (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup) { lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok(); } @@ -289,7 +290,7 @@ impl<'a> Chatlist<'a> { std::ptr::null_mut() }; - if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 { + if chat.id == DC_CHAT_ID_ARCHIVED_LINK as u32 { (*ret).text2 = dc_strdup(ptr::null()) } else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 { (*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup(); diff --git a/src/dc_job.rs b/src/dc_job.rs index 5b7f9bdd3..2a26142e0 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -565,7 +565,7 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_ dc_msg_unref(msg); } unsafe fn dc_send_mdn(context: &Context, msg_id: uint32_t) { - let mut mimefactory: dc_mimefactory_t = dc_mimefactory_t { + let mut mimefactory = dc_mimefactory_t { from_addr: ptr::null_mut(), from_displayname: ptr::null_mut(), selfstatus: ptr::null_mut(), @@ -575,7 +575,7 @@ unsafe fn dc_send_mdn(context: &Context, msg_id: uint32_t) { rfc724_mid: ptr::null_mut(), loaded: DC_MF_NOTHING_LOADED, msg: ptr::null_mut(), - chat: ptr::null_mut(), + chat: None, increation: 0, in_reply_to: ptr::null_mut(), references: ptr::null_mut(), @@ -587,7 +587,7 @@ unsafe fn dc_send_mdn(context: &Context, msg_id: uint32_t) { error: ptr::null_mut(), context, }; - dc_mimefactory_init(&mut mimefactory, context); + if !(0 == dc_mimefactory_load_mdn(&mut mimefactory, msg_id) || 0 == dc_mimefactory_render(&mut mimefactory)) { @@ -1014,7 +1014,7 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in rfc724_mid: 0 as *mut libc::c_char, loaded: DC_MF_NOTHING_LOADED, msg: 0 as *mut dc_msg_t, - chat: 0 as *mut Chat, + chat: None, increation: 0, in_reply_to: 0 as *mut libc::c_char, references: 0 as *mut libc::c_char, @@ -1026,7 +1026,7 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in error: 0 as *mut libc::c_char, context, }; - dc_mimefactory_init(&mut mimefactory, context); + /* load message data */ if 0 == dc_mimefactory_load_msg(&mut mimefactory, msg_id) || mimefactory.from_addr.is_null() { warn!( diff --git a/src/dc_lot.rs b/src/dc_lot.rs index 8b914aa17..b740804a9 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -124,7 +124,7 @@ pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 { pub unsafe fn dc_lot_fill( mut lot: *mut dc_lot_t, msg: *mut dc_msg_t, - chat: *const Chat, + chat: &Chat, contact: Option<&Contact>, context: &Context, ) { @@ -142,15 +142,12 @@ pub unsafe fn dc_lot_fill( (*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup(); (*lot).text1_meaning = 3i32 } - } else if chat.is_null() { - (*lot).text1 = 0 as *mut libc::c_char; - (*lot).text1_meaning = 0i32 - } else if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { + } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { if 0 != dc_msg_is_info(msg) || contact.is_none() { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else { - if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint { + if chat.id == 1 { if let Some(contact) = contact { (*lot).text1 = contact.get_display_name().strdup(); } else { diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index bc6f8ab7f..013eed860 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -24,8 +24,7 @@ use crate::stock::StockMessage; use crate::types::*; use crate::x::*; -#[derive(Copy, Clone)] -#[repr(C)] +#[derive(Clone)] #[allow(non_camel_case_types)] pub struct dc_mimefactory_t<'a> { pub from_addr: *mut libc::c_char, @@ -37,7 +36,7 @@ pub struct dc_mimefactory_t<'a> { pub rfc724_mid: *mut libc::c_char, pub loaded: dc_mimefactory_loaded_t, pub msg: *mut dc_msg_t<'a>, - pub chat: *mut Chat<'a>, + pub chat: Option>, pub increation: libc::c_int, pub in_reply_to: *mut libc::c_char, pub references: *mut libc::c_char, @@ -56,18 +55,6 @@ const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2; pub const DC_MF_MSG_LOADED: dc_mimefactory_loaded_t = 1; pub const DC_MF_NOTHING_LOADED: dc_mimefactory_loaded_t = 0; -pub unsafe fn dc_mimefactory_init<'a>(factory: *mut dc_mimefactory_t<'a>, context: &'a Context) { - if factory.is_null() { - return; - } - memset( - factory as *mut libc::c_void, - 0, - ::std::mem::size_of::(), - ); - (*factory).context = context; -} - pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) { if factory.is_null() { return; @@ -92,8 +79,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) { } dc_msg_unref((*factory).msg); (*factory).msg = 0 as *mut dc_msg_t; - dc_chat_unref((*factory).chat); - (*factory).chat = 0 as *mut Chat; + (*factory).chat = None; free((*factory).in_reply_to as *mut libc::c_void); (*factory).in_reply_to = 0 as *mut libc::c_char; free((*factory).references as *mut libc::c_void); @@ -124,131 +110,139 @@ pub unsafe fn dc_mimefactory_load_msg( (*factory).recipients_names = clist_new(); (*factory).recipients_addr = clist_new(); (*factory).msg = dc_msg_new_untyped(context); - (*factory).chat = dc_chat_new(context); - if dc_msg_load_from_db((*factory).msg, context, msg_id) - && dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id) - { - load_from(factory); - (*factory).req_mdn = 0; - if 0 != dc_chat_is_self_talk((*factory).chat) { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void, - ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - dc_strdup((*factory).from_addr) as *mut libc::c_void, - ); - } else { - context - .sql - .query_map( - "SELECT c.authname, c.addr \ - FROM chats_contacts cc \ - LEFT JOIN contacts c ON cc.contact_id=c.id \ - WHERE cc.chat_id=? AND cc.contact_id>9;", - params![(*(*factory).msg).chat_id as i32], - |row| { - let authname: String = row.get(0)?; - let addr: String = row.get(1)?; - Ok((authname, addr)) - }, - |rows| { - for row in rows { - let (authname, addr) = row?; - let addr_c = addr.strdup(); - if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - if !authname.is_empty() { - authname.strdup() - } else { - std::ptr::null_mut() - } as *mut libc::c_void, - ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - addr_c as *mut libc::c_void, - ); - } - } - Ok(()) - }, - ) - .unwrap(); - let command = (*(*factory).msg) - .param - .get_int(Param::Cmd) - .unwrap_or_default(); + if dc_msg_load_from_db((*factory).msg, context, msg_id) { + if let Ok(chat) = dc_chat_load_from_db(context, (*(*factory).msg).chat_id) { + (*factory).chat = Some(chat); - if command == 5 { - let email_to_remove = (*(*factory).msg).param.get(Param::Arg).unwrap_or_default(); - let email_to_remove_c = email_to_remove.strdup(); + let chat = (*factory).chat.as_ref().unwrap(); - let self_addr = context + load_from(factory); + (*factory).req_mdn = 0; + if 0 != dc_chat_is_self_talk(chat) { + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + dc_strdup((*factory).from_addr) as *mut libc::c_void, + ); + } else { + context .sql - .get_config(context, "configured_addr") + .query_map( + "SELECT c.authname, c.addr \ + FROM chats_contacts cc \ + LEFT JOIN contacts c ON cc.contact_id=c.id \ + WHERE cc.chat_id=? AND cc.contact_id>9;", + params![(*(*factory).msg).chat_id as i32], + |row| { + let authname: String = row.get(0)?; + let addr: String = row.get(1)?; + Ok((authname, addr)) + }, + |rows| { + for row in rows { + let (authname, addr) = row?; + let addr_c = addr.strdup(); + if clist_search_string_nocase((*factory).recipients_addr, addr_c) + == 0 + { + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + if !authname.is_empty() { + authname.strdup() + } else { + std::ptr::null_mut() + } + as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + addr_c as *mut libc::c_void, + ); + } + } + Ok(()) + }, + ) + .unwrap(); + + let command = (*(*factory).msg) + .param + .get_int(Param::Cmd) .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 - { - clist_insert_after( - (*factory).recipients_names, - (*(*factory).recipients_names).last, - 0 as *mut libc::c_void, - ); - clist_insert_after( - (*factory).recipients_addr, - (*(*factory).recipients_addr).last, - email_to_remove_c as *mut libc::c_void, - ); + if command == 5 { + let email_to_remove = + (*(*factory).msg).param.get(Param::Arg).unwrap_or_default(); + let email_to_remove_c = email_to_remove.strdup(); + + let self_addr = context + .sql + .get_config(context, "configured_addr") + .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 + { + clist_insert_after( + (*factory).recipients_names, + (*(*factory).recipients_names).last, + 0 as *mut libc::c_void, + ); + clist_insert_after( + (*factory).recipients_addr, + (*(*factory).recipients_addr).last, + email_to_remove_c as *mut libc::c_void, + ); + } } } + if command != 6 + && command != 7 + && 0 != context + .sql + .get_config_int(context, "mdns_enabled") + .unwrap_or_else(|| 1) + { + (*factory).req_mdn = 1 + } } - if command != 6 - && command != 7 - && 0 != context - .sql - .get_config_int(context, "mdns_enabled") - .unwrap_or_else(|| 1) - { - (*factory).req_mdn = 1 - } - } - let row = context.sql.query_row( - "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", - params![(*(*factory).msg).id as i32], - |row| { - let in_reply_to: String = row.get(0)?; - let references: String = row.get(1)?; + let row = context.sql.query_row( + "SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?", + params![(*(*factory).msg).id as i32], + |row| { + let in_reply_to: String = row.get(0)?; + let references: String = row.get(1)?; - Ok((in_reply_to, references)) - }, - ); - match row { - Ok((in_reply_to, references)) => { - (*factory).in_reply_to = in_reply_to.strdup(); - (*factory).references = references.strdup(); + Ok((in_reply_to, references)) + }, + ); + match row { + Ok((in_reply_to, references)) => { + (*factory).in_reply_to = in_reply_to.strdup(); + (*factory).references = references.strdup(); + } + Err(err) => { + error!( + context, + 0, "mimefactory: failed to load mime_in_reply_to: {:?}", err + ); + } } - Err(err) => { - error!( - context, - 0, "mimefactory: failed to load mime_in_reply_to: {:?}", err - ); - } - } - success = 1; - (*factory).loaded = DC_MF_MSG_LOADED; - (*factory).timestamp = (*(*factory).msg).timestamp_sort; - (*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid) + success = 1; + (*factory).loaded = DC_MF_MSG_LOADED; + (*factory).timestamp = (*(*factory).msg).timestamp_sort; + (*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid) + } } if 0 != success { (*factory).increation = dc_msg_is_increation((*factory).msg) @@ -514,11 +508,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: if (*factory).loaded as libc::c_uint == DC_MF_MSG_LOADED as libc::c_int as libc::c_uint { /* Render a normal message *********************************************************************/ - let chat: *mut Chat = (*factory).chat; - let msg: *mut dc_msg_t = (*factory).msg; + let chat = (*factory).chat.as_ref().unwrap(); + let msg = (*factory).msg; let mut meta_part: *mut mailmime = 0 as *mut mailmime; let mut placeholdertext: *mut libc::c_char = 0 as *mut libc::c_char; - if (*chat).typ == Chattype::VerifiedGroup { + if chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -541,27 +535,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: .unwrap_or_default() } } - if (*chat).gossiped_timestamp == 0 - || ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time() + if chat.gossiped_timestamp == 0 + || (chat.gossiped_timestamp + (2 * 24 * 60 * 60)) < time() { do_gossip = 1 } /* build header etc. */ let command = (*msg).param.get_int(Param::Cmd).unwrap_or_default(); - if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { + if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, mailimf_field_new_custom( strdup(b"Chat-Group-ID\x00" as *const u8 as *const libc::c_char), - dc_strdup((*chat).grpid), + dc_strdup(chat.grpid), ), ); mailimf_fields_add( imf_fields, mailimf_field_new_custom( strdup(b"Chat-Group-Name\x00" as *const u8 as *const libc::c_char), - dc_encode_header_words((*chat).name), + dc_encode_header_words(chat.name), ), ); if command == 5 { @@ -592,7 +586,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: email_to_add, ), ); - grpimage = (*chat).param.get(Param::ProfileImage); + grpimage = chat.param.get(Param::ProfileImage); } if 0 != (*msg).param.get_int(Param::Arg2).unwrap_or_default() & 0x1 { info!( @@ -998,7 +992,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: e.as_ptr(), ); } else { - subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email) + subject_str = get_subject((*factory).chat.as_ref(), (*factory).msg, afwd_email) } subject = mailimf_subject_new(dc_encode_header_words(subject_str)); mailimf_fields_add( @@ -1052,6 +1046,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: success = 1 } } + if !message.is_null() { mailmime_free(message); } @@ -1064,11 +1059,16 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } unsafe fn get_subject( - chat: *const Chat, + chat: Option<&Chat>, msg: *mut dc_msg_t, afwd_email: libc::c_int, ) -> *mut libc::c_char { - let context = (*chat).context; + if chat.is_none() { + return std::ptr::null_mut(); + } + + let chat = chat.unwrap(); + let context = chat.context; let ret: *mut libc::c_char; let raw_subject = { @@ -1089,10 +1089,10 @@ unsafe fn get_subject( }; if (*msg).param.get_int(Param::Cmd).unwrap_or_default() == 6 { ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup() - } else if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { + } else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup { ret = dc_mprintf( b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char, - (*chat).name, + chat.name, fwd, raw_subject, ) diff --git a/src/dc_msg.rs b/src/dc_msg.rs index ec043f6c2..c263ee82d 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -745,35 +745,35 @@ pub unsafe fn dc_msg_get_showpadlock(msg: *const dc_msg_t) -> libc::c_int { pub unsafe fn dc_msg_get_summary<'a>( msg: *mut dc_msg_t<'a>, - mut chat: *const Chat<'a>, + chat: Option<&Chat<'a>>, ) -> *mut dc_lot_t { - let mut ok_to_continue = true; - let ret: *mut dc_lot_t = dc_lot_new(); - let mut chat_to_delete: *mut Chat = 0 as *mut Chat; + let ret = dc_lot_new(); - if !msg.is_null() { - if chat.is_null() { - chat_to_delete = dc_get_chat((*msg).context, (*msg).chat_id); - if chat_to_delete.is_null() { - ok_to_continue = false; - } else { - chat = chat_to_delete; - } - } - if ok_to_continue { - let contact = if (*msg).from_id != DC_CONTACT_ID_SELF as libc::c_uint - && ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) - { - Contact::get_by_id((*chat).context, (*msg).from_id).ok() - } else { - None - }; - - dc_lot_fill(ret, msg, chat, contact.as_ref(), (*msg).context); - } + if msg.is_null() { + return ret; } - dc_chat_unref(chat_to_delete); + let chat_loaded: Chat; + let chat = if let Some(chat) = chat { + chat + } else { + if let Ok(chat) = dc_get_chat((*msg).context, (*msg).chat_id) { + chat_loaded = chat; + &chat_loaded + } else { + return ret; + } + }; + + let contact = if (*msg).from_id != DC_CONTACT_ID_SELF as libc::c_uint + && ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) + { + Contact::get_by_id((*chat).context, (*msg).from_id).ok() + } else { + None + }; + + dc_lot_fill(ret, msg, chat, contact.as_ref(), (*msg).context); ret } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index f6a53c2eb..de26cef40 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1375,7 +1375,6 @@ unsafe fn create_or_lookup_group( } } if 0 != ok { - let chat = dc_chat_new(context); info!( context, 0, @@ -1386,16 +1385,17 @@ unsafe fn create_or_lookup_group( to_string(grpimage) }, ); - dc_chat_load_from_db(chat, chat_id); - if grpimage.is_null() { - (*chat).param.remove(Param::ProfileImage); - } else { - (*chat).param.set(Param::ProfileImage, as_str(grpimage)); + if let Ok(mut chat) = dc_chat_load_from_db(context, chat_id) { + if grpimage.is_null() { + chat.param.remove(Param::ProfileImage); + } else { + chat.param.set(Param::ProfileImage, as_str(grpimage)); + } + dc_chat_update_param(&mut chat); + send_EVENT_CHAT_MODIFIED = 1; } - dc_chat_update_param(chat); - dc_chat_unref(chat); + free(grpimage as *mut libc::c_void); - send_EVENT_CHAT_MODIFIED = 1 } } diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 5ee3b9da8..88d67f34f 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -36,7 +36,6 @@ pub unsafe fn dc_get_securejoin_qr( let mut fingerprint = 0 as *mut libc::c_char; let mut invitenumber: *mut libc::c_char; let mut auth: *mut libc::c_char; - let mut chat = 0 as *mut Chat; let mut group_name = 0 as *mut libc::c_char; let mut group_name_urlencoded = 0 as *mut libc::c_char; let mut qr: Option = None; @@ -54,11 +53,10 @@ pub unsafe fn dc_get_securejoin_qr( } let self_addr = context.sql.get_config(context, "configured_addr"); - let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| { + let cleanup = |fingerprint, group_name, group_name_urlencoded| { free(fingerprint as *mut libc::c_void); free(invitenumber as *mut libc::c_void); free(auth as *mut libc::c_void); - dc_chat_unref(chat); free(group_name as *mut libc::c_void); free(group_name_urlencoded as *mut libc::c_void); @@ -71,7 +69,7 @@ pub unsafe fn dc_get_securejoin_qr( if self_addr.is_none() { error!(context, 0, "Not configured, cannot generate QR code.",); - return cleanup(fingerprint, chat, group_name, group_name_urlencoded); + return cleanup(fingerprint, group_name, group_name_urlencoded); } let self_addr = self_addr.unwrap(); @@ -83,34 +81,33 @@ pub unsafe fn dc_get_securejoin_qr( fingerprint = get_self_fingerprint(context); if fingerprint.is_null() { - return cleanup(fingerprint, chat, group_name, group_name_urlencoded); + return cleanup(fingerprint, group_name, group_name_urlencoded); } let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string(); let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string(); qr = if 0 != group_chat_id { - chat = dc_get_chat(context, group_chat_id); - if chat.is_null() { + if let Ok(chat) = dc_get_chat(context, group_chat_id) { + group_name = dc_chat_get_name(&chat); + group_name_urlencoded = dc_urlencode(group_name); + + Some(format!( + "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", + as_str(fingerprint), + self_addr_urlencoded, + as_str(group_name_urlencoded), + as_str(chat.grpid), + as_str(invitenumber), + as_str(auth), + )) + } else { error!( context, 0, "Cannot get QR-code for chat-id {}", group_chat_id, ); - return cleanup(fingerprint, chat, group_name, group_name_urlencoded); + return cleanup(fingerprint, group_name, group_name_urlencoded); } - - group_name = dc_chat_get_name(chat); - group_name_urlencoded = dc_urlencode(group_name); - - Some(format!( - "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", - as_str(fingerprint), - self_addr_urlencoded, - as_str(group_name_urlencoded), - as_str((*chat).grpid), - as_str(invitenumber), - as_str(auth), - )) } else { Some(format!( "OPENPGP4FPR:{}#a={}&n={}&i={}&s={}", @@ -124,7 +121,7 @@ pub unsafe fn dc_get_securejoin_qr( info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap()); - cleanup(fingerprint, chat, group_name, group_name_urlencoded) + cleanup(fingerprint, group_name, group_name_urlencoded) } fn get_self_fingerprint(context: &Context) -> *mut libc::c_char { diff --git a/tests/stress.rs b/tests/stress.rs index 351d09637..f1e4d0d90 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -767,15 +767,13 @@ fn test_chat() { let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1); assert!(chat_id > 9, "chat_id too small {}", chat_id); - let chat = dc_chat_new(&context.ctx); - assert!(dc_chat_load_from_db(chat, chat_id)); + let chat = dc_chat_load_from_db(&context.ctx, chat_id).unwrap(); let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1); assert_eq!(chat2_id, chat_id); - let chat2 = dc_chat_new(&context.ctx); - assert!(dc_chat_load_from_db(chat2, chat2_id)); + let chat2 = dc_chat_load_from_db(&context.ctx, chat2_id).unwrap(); - assert_eq!(as_str((*chat2).name), as_str((*chat).name)); + assert_eq!(as_str(chat2.name), as_str(chat.name)); } }