From c8ce099f222638fee7c79d59a69ef57b30c8a11d Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 14 Aug 2019 20:37:36 +0200 Subject: [PATCH 1/9] refactor(chat): improve field types by using enums and bools --- deltachat-ffi/src/lib.rs | 6 +- deltachat_derive/src/lib.rs | 1 - examples/repl/cmdline.rs | 14 +- src/chatlist.rs | 3 +- src/constants.rs | 46 +++++- src/dc_chat.rs | 312 +++++++++++++++++------------------- src/dc_lot.rs | 3 +- src/dc_mimefactory.rs | 10 +- src/dc_msg.rs | 4 +- src/dc_qr.rs | 5 +- src/dc_receive_imf.rs | 104 ++++++------ src/dc_securejoin.rs | 30 ++-- 12 files changed, 272 insertions(+), 266 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 94c27c228..08be95a45 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -1276,7 +1276,7 @@ pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_get_type(chat) + dc_chat::dc_chat_get_type(chat) as libc::c_int } #[no_mangle] @@ -1311,7 +1311,7 @@ pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_get_archived(chat) + dc_chat::dc_chat_get_archived(chat) as libc::c_int } #[no_mangle] @@ -1339,7 +1339,7 @@ pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_in pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_is_sending_locations(chat) + dc_chat::dc_chat_is_sending_locations(chat) as libc::c_int } // dc_msg_t diff --git a/deltachat_derive/src/lib.rs b/deltachat_derive/src/lib.rs index 276b2085c..6fde011b7 100644 --- a/deltachat_derive/src/lib.rs +++ b/deltachat_derive/src/lib.rs @@ -20,7 +20,6 @@ pub fn to_sql_derive(input: TokenStream) -> TokenStream { let num = *self as i64; let value = rusqlite::types::Value::Integer(num); let output = rusqlite::types::ToSqlOutput::Owned(value); - std::result::Result::Ok(output) } } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 7939881f9..a3b70fd04 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -350,13 +350,7 @@ pub unsafe fn dc_cmdline_skip_auth() { } unsafe fn chat_prefix(chat: *const Chat) -> &'static str { - if (*chat).type_0 == 120 { - "Group" - } else if (*chat).type_0 == 130 { - "VerifiedGroup" - } else { - "Single" - } + (*chat).typ.into() } pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> { @@ -628,7 +622,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 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 0 != dc_chat_get_archived(chat) { + let statestr = if dc_chat_get_archived(chat) { " [Archived]" } else { match dc_lot_get_state(lot) { @@ -651,7 +645,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E to_string(text2), statestr, ×tr, - if 0 != dc_chat_is_sending_locations(chat) { + if dc_chat_is_sending_locations(chat) { "📍" } else { "" @@ -699,7 +693,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E dc_chat_get_id(sel_chat), as_str(temp_name), as_str(temp2), - if 0 != dc_chat_is_sending_locations(sel_chat) { + if dc_chat_is_sending_locations(sel_chat) { "📍" } else { "" diff --git a/src/chatlist.rs b/src/chatlist.rs index c675f3b64..1d9eb67d1 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -280,8 +280,7 @@ impl<'a> Chatlist<'a> { dc_msg_load_from_db(lastmsg, self.context, lastmsg_id); if (*lastmsg).from_id != 1 as libc::c_uint - && ((*chat).type_0 == DC_CHAT_TYPE_GROUP - || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP) + && ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) { lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok(); } diff --git a/src/constants.rs b/src/constants.rs index b0aca01b7..102636a8c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -26,9 +26,19 @@ const DC_SENTBOX_WATCH_DEFAULT: i32 = 1; const DC_MVBOX_WATCH_DEFAULT: i32 = 1; const DC_MVBOX_MOVE_DEFAULT: i32 = 1; -pub const DC_CHAT_NOT_BLOCKED: i32 = 0; -const DC_CHAT_MANUALLY_BLOCKED: i32 = 1; -pub const DC_CHAT_DEADDROP_BLOCKED: i32 = 2; +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)] +#[repr(u8)] +pub enum Blocked { + Not = 0, + Manually = 1, + Deaddrop = 2, +} + +impl Default for Blocked { + fn default() -> Self { + Blocked::Not + } +} pub const DC_IMAP_SEEN: u32 = 0x1; @@ -88,10 +98,32 @@ pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7; /// larger chat IDs are "real" chats, their messages are "real" messages. pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9; -const DC_CHAT_TYPE_UNDEFINED: i32 = 0; -pub const DC_CHAT_TYPE_SINGLE: i32 = 100; -pub const DC_CHAT_TYPE_GROUP: i32 = 120; -pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130; +#[derive( + Debug, + Display, + Clone, + Copy, + PartialEq, + Eq, + FromPrimitive, + ToPrimitive, + FromSql, + ToSql, + IntoStaticStr, +)] +#[repr(u32)] +pub enum Chattype { + Undefined = 0, + Single = 100, + Group = 120, + VerifiedGroup = 130, +} + +impl Default for Chattype { + fn default() -> Self { + Chattype::Undefined + } +} pub const DC_MSG_ID_MARKER1: usize = 1; const DC_MSG_ID_DAYMARKER: usize = 9; diff --git a/src/dc_chat.rs b/src/dc_chat.rs index 5843b16d3..22eacab72 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -15,31 +15,27 @@ use crate::types::*; use crate::x::*; use std::ptr; -/** - * @class dc_chat_t - * - * An object representing a single chat in memory. - * Chat objects are created using eg. dc_get_chat() - * and are not updated on database changes; - * if you want an update, you have to recreate the object. - */ +/// An object representing a single chat in memory. +/// Chat objects are created using eg. dc_get_chat() +/// and are not updated on database changes; +/// if you want an update, you have to recreate the object. #[derive(Clone)] pub struct Chat<'a> { - pub id: uint32_t, - pub type_0: libc::c_int, + pub id: u32, + pub typ: Chattype, pub name: *mut libc::c_char, - archived: libc::c_int, + archived: bool, pub context: &'a Context, pub grpid: *mut libc::c_char, - blocked: libc::c_int, + blocked: Blocked, pub param: Params, pub gossiped_timestamp: i64, - is_sending_locations: libc::c_int, + is_sending_locations: bool, } // handle chats -pub unsafe fn dc_create_chat_by_msg_id(context: &Context, msg_id: uint32_t) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; +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); @@ -48,7 +44,7 @@ pub unsafe fn dc_create_chat_by_msg_id(context: &Context, msg_id: uint32_t) -> u && (*chat).id > 9i32 as libc::c_uint { chat_id = (*chat).id; - if 0 != (*chat).blocked { + if (*chat).blocked != Blocked::Not { dc_unblock_chat(context, (*chat).id); send_event = 1i32 } @@ -66,15 +62,15 @@ pub unsafe fn dc_create_chat_by_msg_id(context: &Context, msg_id: uint32_t) -> u pub unsafe fn dc_chat_new<'a>(context: &'a Context) -> *mut Chat<'a> { let chat = Chat { id: 0, - type_0: 0, + typ: Chattype::Undefined, name: std::ptr::null_mut(), - archived: 0, + archived: false, context, grpid: std::ptr::null_mut(), - blocked: 0, + blocked: Blocked::Not, param: Params::new(), gossiped_timestamp: 0, - is_sending_locations: 0, + is_sending_locations: false, }; Box::into_raw(Box::new(chat)) @@ -94,16 +90,16 @@ unsafe fn dc_chat_empty(mut chat: *mut Chat) { } free((*chat).name as *mut libc::c_void); (*chat).name = ptr::null_mut(); - (*chat).type_0 = 0i32; - (*chat).id = 0i32 as uint32_t; + (*chat).typ = Chattype::Undefined; + (*chat).id = 0i32 as u32; free((*chat).grpid as *mut libc::c_void); (*chat).grpid = ptr::null_mut(); - (*chat).blocked = 0i32; + (*chat).blocked = Blocked::Not; (*chat).gossiped_timestamp = 0; (*chat).param = Params::new(); } -pub unsafe fn dc_unblock_chat(context: &Context, chat_id: uint32_t) { +pub unsafe fn dc_unblock_chat(context: &Context, chat_id: u32) { dc_block_chat(context, chat_id, 0i32); } @@ -134,13 +130,13 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { let c = unsafe { &mut *chat }; c.id = row.get(0)?; - c.type_0 = row.get(1)?; + c.typ = row.get(1)?; c.name = unsafe { row.get::<_, String>(2)?.strdup() }; c.grpid = unsafe { row.get::<_, String>(3)?.strdup() }; c.param = row.get::<_, String>(4)?.parse().unwrap_or_default(); c.archived = row.get(5)?; - c.blocked = row.get::<_, Option>(6)?.unwrap_or_default(); + c.blocked = row.get::<_, Option<_>>(6)?.unwrap_or_default(); c.gossiped_timestamp = row.get(7)?; c.is_sending_locations = row.get(8)?; @@ -181,7 +177,7 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { }, _ => { unsafe { - if (*chat).type_0 == DC_CHAT_TYPE_SINGLE { + if (*chat).typ == Chattype::Single { free((*chat).name as *mut libc::c_void); let contacts = dc_get_chat_contacts((*chat).context, (*chat).id); let mut chat_name = "Err [Name not found]".to_owned(); @@ -210,15 +206,15 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { } } -pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { +pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: u32) -> u32 { let mut chat_id = 0; - let mut chat_blocked = 0; + let mut chat_blocked = Blocked::Not; let mut send_event = 0; dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); if 0 != chat_id { - if 0 != chat_blocked { + if chat_blocked != Blocked::Not { dc_unblock_chat(context, chat_id); - send_event = 1i32 + send_event = 1i32; } } else if !Contact::real_exists_by_id(context, contact_id) && contact_id != DC_CONTACT_ID_SELF as u32 @@ -231,9 +227,9 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 dc_create_or_lookup_nchat_by_contact_id( context, contact_id, - 0i32, + Blocked::Not, &mut chat_id, - ptr::null_mut(), + None, ); if 0 != chat_id { send_event = 1; @@ -248,20 +244,22 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( context: &Context, - contact_id: uint32_t, - create_blocked: libc::c_int, - ret_chat_id: *mut uint32_t, - ret_chat_blocked: *mut libc::c_int, + contact_id: u32, + create_blocked: Blocked, + ret_chat_id: *mut u32, + mut ret_chat_blocked: Option<&mut Blocked>, ) { let mut chat_id = 0; - let mut chat_blocked = 0; + let mut chat_blocked = Blocked::Not; if !ret_chat_id.is_null() { *ret_chat_id = 0; } - if !ret_chat_blocked.is_null() { - *ret_chat_blocked = 0; + + if let Some(b) = ret_chat_blocked.as_mut() { + **b = Blocked::Not; } + if !context.sql.is_open() { return; } @@ -271,17 +269,18 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); if chat_id != 0 { if !ret_chat_id.is_null() { - *ret_chat_id = chat_id + *ret_chat_id = chat_id; } - if !ret_chat_blocked.is_null() { - *ret_chat_blocked = chat_blocked + + if let Some(b) = ret_chat_blocked.as_mut() { + **b = chat_blocked; } return; } if let Ok(contact) = Contact::load_from_db(context, contact_id) { let chat_name = contact.get_display_name(); - if sql::execute( + match sql::execute( context, &context.sql, format!( @@ -289,49 +288,49 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( 100, chat_name, if contact_id == DC_CONTACT_ID_SELF as u32 { "K=1" } else { "" }, - create_blocked, + create_blocked as u8, contact.get_addr(), ), params![], - ).is_ok() { - chat_id = sql::get_rowid( - context, - &context.sql, - "chats", - "grpid", - contact.get_addr(), - ); + ) { + Ok(_) => { + chat_id = sql::get_rowid( + context, + &context.sql, + "chats", + "grpid", + contact.get_addr(), + ); - sql::execute( - context, - &context.sql, - format!("INSERT INTO chats_contacts (chat_id, contact_id) VALUES({}, {})", chat_id, contact_id), - params![], - ).ok(); + sql::execute( + context, + &context.sql, + format!("INSERT INTO chats_contacts (chat_id, contact_id) VALUES({}, {})", chat_id, contact_id), + params![], + ).ok(); + }, + Err(err) => panic!("{:?}", err), } } if !ret_chat_id.is_null() { *ret_chat_id = chat_id } - if !ret_chat_blocked.is_null() { - *ret_chat_blocked = create_blocked - }; + if let Some(b) = ret_chat_blocked.as_mut() { + **b = create_blocked; + } } pub fn dc_lookup_real_nchat_by_contact_id( context: &Context, - contact_id: uint32_t, - ret_chat_id: *mut uint32_t, - ret_chat_blocked: *mut libc::c_int, + contact_id: u32, + ret_chat_id: *mut u32, + ret_chat_blocked: &mut Blocked, ) { /* checks for "real" chats or self-chat */ if !ret_chat_id.is_null() { unsafe { *ret_chat_id = 0 }; } - if !ret_chat_blocked.is_null() { - unsafe { *ret_chat_blocked = 0 }; - } if !context.sql.is_open() { return; } @@ -339,19 +338,19 @@ pub fn dc_lookup_real_nchat_by_contact_id( if let Ok((id, blocked)) = context.sql.query_row( "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", params![contact_id as i32], - |row| Ok((row.get(0)?, row.get::<_, Option>(1)?.unwrap_or_default())), + |row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())), ) { unsafe { *ret_chat_id = id }; - unsafe { *ret_chat_blocked = blocked }; + *ret_chat_blocked = blocked; } } -pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: uint32_t) -> uint32_t { - let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut chat_id_blocked: libc::c_int = 0i32; +pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: u32) -> u32 { + let mut chat_id: u32 = 0i32 as u32; + let mut chat_id_blocked = Blocked::Not; dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_id_blocked); - if 0 != chat_id_blocked { - 0i32 as libc::c_uint + if chat_id_blocked != Blocked::Not { + 0 } else { chat_id } @@ -359,14 +358,14 @@ pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: uint32 pub unsafe fn dc_prepare_msg<'a>( context: &'a Context, - chat_id: uint32_t, + chat_id: u32, mut msg: *mut dc_msg_t<'a>, -) -> uint32_t { +) -> u32 { if msg.is_null() || chat_id <= 9i32 as libc::c_uint { - return 0i32 as uint32_t; + return 0i32 as u32; } (*msg).state = DC_STATE_OUT_PREPARING; - let msg_id: uint32_t = prepare_msg_common(context, chat_id, msg); + let msg_id: u32 = prepare_msg_common(context, chat_id, msg); context.call_cb( Event::MSGS_CHANGED, (*msg).chat_id as uintptr_t, @@ -390,11 +389,11 @@ pub fn msgtype_has_file(msgtype: Viewtype) -> bool { #[allow(non_snake_case)] unsafe fn prepare_msg_common<'a>( context: &'a Context, - chat_id: uint32_t, + chat_id: u32, mut msg: *mut dc_msg_t<'a>, -) -> uint32_t { +) -> u32 { let mut OK_TO_CONTINUE = true; - (*msg).id = 0i32 as uint32_t; + (*msg).id = 0i32 as u32; (*msg).context = context; if (*msg).type_0 == Viewtype::Text { /* the caller should check if the message text is empty */ @@ -479,7 +478,7 @@ unsafe fn prepare_msg_raw( chat: *mut Chat, msg: *mut dc_msg_t, timestamp: i64, -) -> uint32_t { +) -> u32 { let mut do_guarantee_e2ee: libc::c_int; let e2ee_enabled: libc::c_int; let mut OK_TO_CONTINUE = true; @@ -493,10 +492,13 @@ unsafe fn prepare_msg_raw( let mut to_id = 0; let mut location_id = 0; - if !((*chat).type_0 == 100 || (*chat).type_0 == 120 || (*chat).type_0 == 130) { - error!(context, 0, "Cannot send to chat type #{}.", (*chat).type_0,); - } else if ((*chat).type_0 == 120 || (*chat).type_0 == 130) - && 0 == dc_is_contact_in_chat(context, (*chat).id, 1 as uint32_t) + 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) { log_event!( context, @@ -511,7 +513,7 @@ unsafe fn prepare_msg_raw( } else { let from_c = CString::yolo(from.unwrap()); new_rfc724_mid = dc_create_outgoing_rfc724_mid( - if (*chat).type_0 == 120 || (*chat).type_0 == 130 { + if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { (*chat).grpid } else { ptr::null_mut() @@ -519,7 +521,7 @@ unsafe fn prepare_msg_raw( from_c.as_ptr(), ); - if (*chat).type_0 == DC_CHAT_TYPE_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=?;", @@ -537,9 +539,7 @@ unsafe fn prepare_msg_raw( OK_TO_CONTINUE = false; } } else { - if (*chat).type_0 == DC_CHAT_TYPE_GROUP - || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP - { + 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); @@ -823,11 +823,7 @@ pub unsafe fn dc_chat_is_self_talk(chat: *const Chat) -> libc::c_int { * Sending messages ******************************************************************************/ // TODO should return bool /rtn -unsafe fn last_msg_in_chat_encrypted( - context: &Context, - sql: &Sql, - chat_id: uint32_t, -) -> libc::c_int { +unsafe fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) -> libc::c_int { let packed: Option = sql.query_row_col( context, "SELECT param \ @@ -863,8 +859,8 @@ pub unsafe fn dc_chat_update_param(chat: *mut Chat) -> libc::c_int { pub unsafe fn dc_is_contact_in_chat( context: &Context, - chat_id: uint32_t, - contact_id: uint32_t, + chat_id: u32, + contact_id: u32, ) -> libc::c_int { /* this function works for group and for normal chats, however, it is more useful for group chats. DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group chat (DC_CONTACT_ID_SELF is not added to normal chats) */ @@ -889,11 +885,7 @@ pub fn dc_unarchive_chat(context: &Context, chat_id: u32) { .ok(); } -pub unsafe fn dc_send_msg<'a>( - context: &'a Context, - chat_id: uint32_t, - msg: *mut dc_msg_t<'a>, -) -> uint32_t { +pub unsafe fn dc_send_msg<'a>(context: &'a Context, chat_id: u32, msg: *mut dc_msg_t<'a>) -> u32 { if msg.is_null() { return 0; } @@ -944,11 +936,7 @@ pub unsafe fn dc_send_msg<'a>( (*msg).id } -pub unsafe fn dc_send_text_msg( - context: &Context, - chat_id: uint32_t, - text_to_send: String, -) -> uint32_t { +pub unsafe fn dc_send_text_msg(context: &Context, chat_id: u32, text_to_send: String) -> u32 { if chat_id <= 9 { warn!( context, @@ -964,7 +952,7 @@ pub unsafe fn dc_send_text_msg( ret } -pub unsafe fn dc_set_draft(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t) { +pub unsafe fn dc_set_draft(context: &Context, chat_id: u32, msg: *mut dc_msg_t) { if chat_id <= 9i32 as libc::c_uint { return; } @@ -975,10 +963,10 @@ pub unsafe fn dc_set_draft(context: &Context, chat_id: uint32_t, msg: *mut dc_ms // TODO should return bool /rtn #[allow(non_snake_case)] -unsafe fn set_draft_raw(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t) -> libc::c_int { +unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> libc::c_int { let mut OK_TO_CONTINUE = true; // similar to as dc_set_draft() but does not emit an event - let prev_draft_msg_id: uint32_t; + let prev_draft_msg_id: u32; let mut sth_changed: libc::c_int = 0i32; prev_draft_msg_id = get_draft_msg_id(context, chat_id); if 0 != prev_draft_msg_id { @@ -1050,8 +1038,8 @@ fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 { draft_msg_id as u32 } -pub unsafe fn dc_get_draft(context: &Context, chat_id: uint32_t) -> *mut dc_msg_t { - let draft_msg_id: uint32_t; +pub unsafe fn dc_get_draft(context: &Context, chat_id: u32) -> *mut dc_msg_t { + let draft_msg_id: u32; let draft_msg: *mut dc_msg_t; if chat_id <= 9i32 as libc::c_uint { return 0 as *mut dc_msg_t; @@ -1071,9 +1059,9 @@ pub unsafe fn dc_get_draft(context: &Context, chat_id: uint32_t) -> *mut dc_msg_ pub fn dc_get_chat_msgs( context: &Context, - chat_id: uint32_t, - flags: uint32_t, - marker1before: uint32_t, + chat_id: u32, + flags: u32, + marker1before: u32, ) -> *mut dc_array_t { let mut ret = Vec::new(); @@ -1234,7 +1222,7 @@ pub fn dc_marknoticed_all_chats(context: &Context) -> bool { pub fn dc_get_chat_media( context: &Context, - chat_id: uint32_t, + chat_id: u32, msg_type: Viewtype, msg_type2: Viewtype, msg_type3: Viewtype, @@ -1267,13 +1255,13 @@ pub fn dc_get_chat_media( pub unsafe fn dc_get_next_media( context: &Context, - curr_msg_id: uint32_t, + curr_msg_id: u32, dir: libc::c_int, msg_type: Viewtype, msg_type2: Viewtype, msg_type3: Viewtype, -) -> uint32_t { - let mut ret_msg_id: uint32_t = 0i32 as uint32_t; +) -> u32 { + let mut ret_msg_id: u32 = 0i32 as u32; let msg: *mut dc_msg_t = dc_msg_new_untyped(context); let mut list: *mut dc_array_t = ptr::null_mut(); let mut i: libc::c_int; @@ -1435,7 +1423,7 @@ 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: uint32_t) -> *mut Chat { +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); @@ -1546,7 +1534,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( && 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 uint32_t) == 1) { + if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1576,7 +1564,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( } } else { // else continue and send status mail - if (*chat).type_0 == 130 { + if (*chat).typ == Chattype::VerifiedGroup { if contact.is_verified() != VerifiedStatus::BidirectVerified { error!( context, 0, @@ -1598,7 +1586,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( StockMessage::MsgAddMember, contact.get_addr(), "", - DC_CONTACT_ID_SELF as uint32_t, + DC_CONTACT_ID_SELF as u32, )); (*msg).param.set_int(Param::Cmd, 4); (*msg).param.set(Param::Arg, contact.get_addr()); @@ -1689,7 +1677,7 @@ pub unsafe fn dc_remove_contact_from_chat( /* 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 uint32_t) == 1) { + if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1775,7 +1763,7 @@ pub fn dc_is_group_explicitly_left(context: &Context, grpid: *const libc::c_char // TODO should return bool /rtn pub unsafe fn dc_set_chat_name( context: &Context, - chat_id: uint32_t, + chat_id: u32, new_name: *const libc::c_char, ) -> libc::c_int { /* the function only sets the names of group chats; normal chats get their names from the contacts */ @@ -1790,7 +1778,7 @@ pub unsafe fn dc_set_chat_name( 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 uint32_t) == 1i32) { + } else if !(dc_is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1851,7 +1839,7 @@ pub unsafe fn dc_set_chat_name( #[allow(non_snake_case)] pub unsafe fn dc_set_chat_profile_image( context: &Context, - chat_id: uint32_t, + chat_id: u32, new_image: *const libc::c_char, ) -> libc::c_int { let mut OK_TO_CONTINUE = true; @@ -1861,7 +1849,7 @@ pub unsafe fn dc_set_chat_profile_image( 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)) { - if !(dc_is_contact_in_chat(context, chat_id, 1i32 as uint32_t) == 1i32) { + if !(dc_is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1895,7 +1883,7 @@ pub unsafe fn dc_set_chat_profile_image( }, "", "", - DC_CONTACT_ID_SELF as uint32_t, + DC_CONTACT_ID_SELF as u32, )); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( @@ -1975,14 +1963,14 @@ pub unsafe fn dc_forward_msgs( (*msg).param.remove(Param::ForcePlaintext); (*msg).param.remove(Param::Cmd); - let new_msg_id: uint32_t; + let new_msg_id: u32; 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); let save_param = (*msg).param.clone(); (*msg).param = original_param; - (*msg).id = src_msg_id as uint32_t; + (*msg).id = src_msg_id as u32; if let Some(old_fwd) = (*msg).param.get(Param::PrepForwards) { let new_fwd = format!("{} {}", old_fwd, new_msg_id); @@ -2018,18 +2006,16 @@ pub unsafe fn dc_forward_msgs( dc_chat_unref(chat); } -pub unsafe fn dc_chat_get_id(chat: *const Chat) -> uint32_t { +pub unsafe fn dc_chat_get_id(chat: *const Chat) -> u32 { if chat.is_null() { - return 0i32 as uint32_t; + return 0i32 as u32; } (*chat).id } -pub unsafe fn dc_chat_get_type(chat: *const Chat) -> libc::c_int { - if chat.is_null() { - return 0i32; - } - (*chat).type_0 +pub unsafe fn dc_chat_get_type(chat: *const Chat) -> Chattype { + assert!(!chat.is_null()); + (*chat).typ } pub unsafe fn dc_chat_get_name(chat: *const Chat) -> *mut libc::c_char { @@ -2046,12 +2032,12 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { } let mut ret: *mut libc::c_char = std::ptr::null_mut(); - if (*chat).type_0 == 100 && (*chat).param.exists(Param::Selftalk) { + if (*chat).typ == Chattype::Single && (*chat).param.exists(Param::Selftalk) { ret = (*chat) .context .stock_str(StockMessage::SelfTalkSubTitle) .strdup(); - } else if (*chat).type_0 == 100 { + } else if (*chat).typ == Chattype::Single { let ret_raw: String = (*chat) .context .sql @@ -2065,7 +2051,7 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { ) .unwrap_or_else(|| "Err".into()); ret = ret_raw.strdup(); - } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { + } else if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { if (*chat).id == 1 { ret = (*chat).context.stock_str(StockMessage::DeadDrop).strdup(); } else { @@ -2107,7 +2093,7 @@ pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char .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).type_0 == DC_CHAT_TYPE_SINGLE { + } 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]) { @@ -2124,11 +2110,11 @@ 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) -> uint32_t { - let mut color: uint32_t = 0i32 as uint32_t; +pub unsafe fn dc_chat_get_color(chat: *const Chat) -> u32 { + let mut color: u32 = 0i32 as u32; if !chat.is_null() { - if (*chat).type_0 == DC_CHAT_TYPE_SINGLE { + 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]) { @@ -2136,7 +2122,7 @@ pub unsafe fn dc_chat_get_color(chat: *const Chat) -> uint32_t { } } } else { - color = dc_str_to_color((*chat).name) as uint32_t + color = dc_str_to_color((*chat).name) as u32 } } @@ -2144,9 +2130,9 @@ pub unsafe fn dc_chat_get_color(chat: *const Chat) -> uint32_t { } // TODO should return bool /rtn -pub unsafe fn dc_chat_get_archived(chat: *const Chat) -> libc::c_int { +pub unsafe fn dc_chat_get_archived(chat: *const Chat) -> bool { if chat.is_null() { - return 0i32; + return false; } (*chat).archived } @@ -2164,13 +2150,13 @@ pub unsafe fn dc_chat_is_verified(chat: *const Chat) -> libc::c_int { if chat.is_null() { return 0i32; } - ((*chat).type_0 == 130i32) as 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) -> libc::c_int { +pub unsafe fn dc_chat_is_sending_locations(chat: *const Chat) -> bool { if chat.is_null() { - return 0i32; + return false; } (*chat).is_sending_locations } @@ -2195,12 +2181,9 @@ pub fn dc_get_chat_cnt(context: &Context) -> usize { pub unsafe fn dc_get_chat_id_by_grpid( context: &Context, grpid: *const libc::c_char, - ret_blocked: *mut libc::c_int, + ret_blocked: Option<&mut Blocked>, ret_verified: *mut libc::c_int, ) -> u32 { - if !ret_blocked.is_null() { - *ret_blocked = 0; - } if !ret_verified.is_null() { *ret_verified = 0; } @@ -2212,20 +2195,21 @@ pub unsafe fn dc_get_chat_id_by_grpid( params![as_str(grpid)], |row| { let chat_id = row.get(0)?; - if !ret_blocked.is_null() { - *ret_blocked = row.get(1)?; - } - if !ret_verified.is_null() { - let v: i32 = row.get(2)?; - *ret_verified = (v == 130) as libc::c_int; + + if let Some(b) = ret_blocked { + *b = row.get::<_, Option>(1)?.unwrap_or_default(); } + + let v = row.get::<_, Option>(2)?.unwrap_or_default(); + *ret_verified = (v == Chattype::VerifiedGroup) as libc::c_int; + Ok(chat_id) }, ) .unwrap_or_default() } -pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc::c_char) { +pub fn dc_add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char) { if text.is_null() { return; } diff --git a/src/dc_lot.rs b/src/dc_lot.rs index 8829c9bbd..c9e7d072c 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -1,3 +1,4 @@ +use crate::constants::Chattype; use crate::contact::*; use crate::context::Context; use crate::dc_chat::*; @@ -144,7 +145,7 @@ pub unsafe fn dc_lot_fill( } else if chat.is_null() { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 - } else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { + } 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 diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 17a8830ad..d51aede75 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -518,7 +518,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let msg: *mut dc_msg_t = (*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).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int { + if (*chat).typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -549,9 +549,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: /* build header etc. */ let command = (*msg).param.get_int(Param::Cmd).unwrap_or_default(); - if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int - || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int - { + if (*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -1091,9 +1089,7 @@ 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).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int - || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int - { + } 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, diff --git a/src/dc_msg.rs b/src/dc_msg.rs index 8e8e58699..6f4c11dad 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -762,7 +762,7 @@ pub unsafe fn dc_msg_get_summary<'a>( } if ok_to_continue { let contact = if (*msg).from_id != DC_CONTACT_ID_SELF as libc::c_uint - && ((*chat).type_0 == 120 || (*chat).type_0 == 130) + && ((*chat).typ == Chattype::Group || (*chat).typ == Chattype::VerifiedGroup) { Contact::get_by_id((*chat).context, (*msg).from_id).ok() } else { @@ -1393,7 +1393,7 @@ mod tests { assert!(res.is_ok()); let chat = dc_create_chat_by_contact_id(ctx, contact); - assert!(chat != 0); + assert!(chat != 0, "failed to create chat"); let msg = dc_msg_new(ctx, Viewtype::Text); assert!(!msg.is_null()); diff --git a/src/dc_qr.rs b/src/dc_qr.rs index cb80eccdd..a1ac9535f 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -1,5 +1,6 @@ use percent_encoding::percent_decode_str; +use crate::constants::Blocked; use crate::contact::*; use crate::context::Context; use crate::dc_chat::*; @@ -233,9 +234,9 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc dc_create_or_lookup_nchat_by_contact_id( context, (*qr_parsed).id, - 2i32, + Blocked::Deaddrop, &mut chat_id, - 0 as *mut libc::c_int, + None, ); device_msg = dc_mprintf( b"%s verified.\x00" as *const u8 as *const libc::c_char, diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index ac1b4c50b..1fed9da38 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -284,7 +284,7 @@ unsafe fn add_parts( ) -> Result<()> { let mut state: libc::c_int; let mut msgrmsg: libc::c_int; - let mut chat_id_blocked = 0; + let mut chat_id_blocked = Blocked::Not; let mut sort_timestamp = 0; let mut rcvd_timestamp = 0; let mut mime_in_reply_to = std::ptr::null_mut(); @@ -409,7 +409,7 @@ unsafe fn add_parts( } } let mut test_normal_chat_id = 0; - let mut test_normal_chat_id_blocked = 0; + let mut test_normal_chat_id_blocked = Blocked::Not; dc_lookup_real_nchat_by_contact_id( context, @@ -425,12 +425,12 @@ unsafe fn add_parts( // (groups appear automatically only if the _sender_ is known, see core issue #54) let create_blocked = if 0 != test_normal_chat_id - && test_normal_chat_id_blocked == DC_CHAT_NOT_BLOCKED + && test_normal_chat_id_blocked == Blocked::Not || incoming_origin.is_start_new_chat() { - DC_CHAT_NOT_BLOCKED + Blocked::Not } else { - DC_CHAT_DEADDROP_BLOCKED + Blocked::Deaddrop }; create_or_lookup_group( @@ -443,9 +443,9 @@ unsafe fn add_parts( chat_id, &mut chat_id_blocked, ); - if 0 != *chat_id && 0 != chat_id_blocked && 0 == create_blocked { + if 0 != *chat_id && Blocked::Not != chat_id_blocked && create_blocked == Blocked::Not { dc_unblock_chat(context, *chat_id); - chat_id_blocked = 0; + chat_id_blocked = Blocked::Not; } } @@ -463,27 +463,27 @@ unsafe fn add_parts( if *chat_id == 0 { // try to create a normal chat let create_blocked = if incoming_origin.is_start_new_chat() || *from_id == *to_id { - DC_CHAT_NOT_BLOCKED + Blocked::Not } else { - DC_CHAT_DEADDROP_BLOCKED + Blocked::Deaddrop }; if 0 != test_normal_chat_id { *chat_id = test_normal_chat_id; - chat_id_blocked = test_normal_chat_id_blocked + chat_id_blocked = test_normal_chat_id_blocked; } else if 0 != allow_creation { dc_create_or_lookup_nchat_by_contact_id( context, *from_id, create_blocked, chat_id, - &mut chat_id_blocked, + Some(&mut chat_id_blocked), ); } - if 0 != *chat_id && 0 != chat_id_blocked { - if 0 == create_blocked { + if 0 != *chat_id && Blocked::Not != chat_id_blocked { + if Blocked::Not == create_blocked { dc_unblock_chat(context, *chat_id); - chat_id_blocked = 0; + chat_id_blocked = Blocked::Not; } else if 0 != dc_is_reply_to_known_message(context, mime_parser) { // we do not want any chat to be created implicitly. Because of the origin-scale-up, // the contact requests will pop up and this should be just fine. @@ -506,7 +506,7 @@ unsafe fn add_parts( // if the chat_id is blocked, // for unknown senders and non-delta messages set the state to NOTICED // to not result in a contact request (this would require the state FRESH) - if 0 != chat_id_blocked && state == DC_STATE_IN_FRESH { + if Blocked::Not != chat_id_blocked && state == DC_STATE_IN_FRESH { if !incoming_origin.is_verified() && msgrmsg == 0 { state = DC_STATE_IN_NOTICED; } @@ -525,33 +525,36 @@ unsafe fn add_parts( context, &mut mime_parser, allow_creation, - 0, + Blocked::Not, *from_id, to_ids, chat_id, &mut chat_id_blocked, ); - if 0 != *chat_id && 0 != chat_id_blocked { + if 0 != *chat_id && Blocked::Not != chat_id_blocked { dc_unblock_chat(context, *chat_id); - chat_id_blocked = 0 + chat_id_blocked = Blocked::Not; } } if *chat_id == 0 && 0 != allow_creation { let create_blocked = if 0 != msgrmsg && !Contact::is_blocked_load(context, *to_id) { - DC_CHAT_NOT_BLOCKED + Blocked::Not } else { - DC_CHAT_DEADDROP_BLOCKED + Blocked::Deaddrop }; dc_create_or_lookup_nchat_by_contact_id( context, *to_id, create_blocked, chat_id, - &mut chat_id_blocked, + Some(&mut chat_id_blocked), ); - if 0 != *chat_id && 0 != chat_id_blocked && 0 == create_blocked { + if 0 != *chat_id + && Blocked::Not != chat_id_blocked + && Blocked::Not == create_blocked + { dc_unblock_chat(context, *chat_id); - chat_id_blocked = 0 + chat_id_blocked = Blocked::Not; } } } @@ -562,13 +565,13 @@ unsafe fn add_parts( dc_create_or_lookup_nchat_by_contact_id( context, 1, - 0, + Blocked::Not, chat_id, - &mut chat_id_blocked, + Some(&mut chat_id_blocked), ); - if 0 != *chat_id && 0 != chat_id_blocked { + if 0 != *chat_id && Blocked::Not != chat_id_blocked { dc_unblock_chat(context, *chat_id); - chat_id_blocked = 0; + chat_id_blocked = Blocked::Not; } } } @@ -743,7 +746,7 @@ unsafe fn add_parts( } else if 0 != incoming && state == DC_STATE_IN_FRESH { if 0 != from_id_blocked { *create_event_to_send = None; - } else if 0 != chat_id_blocked { + } else if Blocked::Not != chat_id_blocked { *create_event_to_send = Some(Event::MSGS_CHANGED); } else { *create_event_to_send = Some(Event::INCOMING_MSG); @@ -1043,15 +1046,15 @@ unsafe fn create_or_lookup_group( context: &Context, mime_parser: &mut dc_mimeparser_t, allow_creation: libc::c_int, - create_blocked: libc::c_int, + create_blocked: Blocked, from_id: u32, to_ids: &mut Vec, ret_chat_id: *mut uint32_t, - ret_chat_id_blocked: *mut libc::c_int, + ret_chat_id_blocked: &mut Blocked, ) { let group_explicitly_left: libc::c_int; let mut chat_id = 0; - let mut chat_id_blocked = 0; + let mut chat_id_blocked = Blocked::Not; let mut chat_id_verified = 0; let mut grpid = std::ptr::null_mut(); let mut grpname = std::ptr::null_mut(); @@ -1071,9 +1074,9 @@ unsafe fn create_or_lookup_group( grpname: *mut libc::c_char, failure_reason: *mut libc::c_char, ret_chat_id: *mut uint32_t, - ret_chat_id_blocked: *mut libc::c_int, + ret_chat_id_blocked: &mut Blocked, chat_id: u32, - chat_id_blocked: i32| { + chat_id_blocked: Blocked| { free(grpid.cast()); free(grpname.cast()); free(failure_reason.cast()); @@ -1081,9 +1084,11 @@ unsafe fn create_or_lookup_group( if !ret_chat_id.is_null() { *ret_chat_id = chat_id; } - if !ret_chat_id_blocked.is_null() { - *ret_chat_id_blocked = if 0 != chat_id { chat_id_blocked } else { 0 }; - } + *ret_chat_id_blocked = if 0 != chat_id { + chat_id_blocked + } else { + Blocked::Not + }; }; if mime_parser.is_system_message == DC_CMD_LOCATION_STREAMING_ENABLED { @@ -1231,7 +1236,12 @@ unsafe fn create_or_lookup_group( set_better_msg(mime_parser, &better_msg); // check, if we have a chat with this group ID - chat_id = dc_get_chat_id_by_grpid(context, grpid, &mut chat_id_blocked, &mut chat_id_verified); + chat_id = dc_get_chat_id_by_grpid( + context, + grpid, + Some(&mut chat_id_blocked), + &mut chat_id_verified, + ); if chat_id != 0 { if 0 != chat_id_verified && 0 == check_verified_properties( @@ -1472,34 +1482,32 @@ unsafe fn create_or_lookup_adhoc_group( context: &Context, mime_parser: &dc_mimeparser_t, allow_creation: libc::c_int, - create_blocked: libc::c_int, + create_blocked: Blocked, from_id: u32, to_ids: &mut Vec, ret_chat_id: *mut uint32_t, - ret_chat_id_blocked: *mut libc::c_int, + ret_chat_id_blocked: &mut Blocked, ) { // if we're here, no grpid was found, check there is an existing ad-hoc // group matching the to-list or if we can create one let mut chat_id = 0; - let mut chat_id_blocked = 0; + let mut chat_id_blocked = Blocked::Not; let mut grpid = 0 as *mut libc::c_char; let mut grpname = 0 as *mut libc::c_char; let cleanup = |grpid: *mut libc::c_char, grpname: *mut libc::c_char, ret_chat_id: *mut uint32_t, - ret_chat_id_blocked: *mut libc::c_int, + ret_chat_id_blocked: &mut Blocked, chat_id: u32, - chat_id_blocked: i32| { + chat_id_blocked: Blocked| { free(grpid as *mut libc::c_void); free(grpname as *mut libc::c_void); if !ret_chat_id.is_null() { - *ret_chat_id = chat_id + *ret_chat_id = chat_id; } - if !ret_chat_id_blocked.is_null() { - *ret_chat_id_blocked = chat_id_blocked - }; + *ret_chat_id_blocked = chat_id_blocked; }; // build member list from the given ids @@ -1547,7 +1555,7 @@ unsafe fn create_or_lookup_adhoc_group( ), params![], |row| { - Ok((row.get::<_, i32>(0)?, row.get::<_, Option>(1)?.unwrap_or_default())) + Ok((row.get::<_, i32>(0)?, row.get::<_, Option>(1)?.unwrap_or_default())) } ); @@ -1628,7 +1636,7 @@ fn create_group_record( context: &Context, grpid: *const libc::c_char, grpname: *const libc::c_char, - create_blocked: libc::c_int, + create_blocked: Blocked, create_verified: libc::c_int, ) -> u32 { if sql::execute( diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 0b5fb2a92..afe0d328e 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -234,12 +234,9 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> bob.expects = 0; if bob.status == 1 { if 0 != join_vg { - ret_chat_id = dc_get_chat_id_by_grpid( - context, - (*qr_scan).text2, - 0 as *mut libc::c_int, - 0 as *mut libc::c_int, - ) as libc::c_int + ret_chat_id = + dc_get_chat_id_by_grpid(context, (*qr_scan).text2, None, 0 as *mut libc::c_int) + as libc::c_int } else { ret_chat_id = contact_chat_id as libc::c_int } @@ -346,7 +343,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut auth: *mut libc::c_char = 0 as *mut libc::c_char; let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char; let mut contact_chat_id: uint32_t = 0i32 as uint32_t; - let mut contact_chat_id_blocked: libc::c_int = 0i32; + let mut contact_chat_id_blocked = Blocked::Not; let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut ret: libc::c_int = 0i32; @@ -364,11 +361,11 @@ pub unsafe fn dc_handle_securejoin_handshake( dc_create_or_lookup_nchat_by_contact_id( context, contact_id, - 0i32, + Blocked::Not, &mut contact_chat_id, - &mut contact_chat_id_blocked, + Some(&mut contact_chat_id_blocked), ); - if 0 != contact_chat_id_blocked { + if Blocked::Not != contact_chat_id_blocked { dc_unblock_chat(context, contact_chat_id); } ret = 0x2i32; @@ -583,7 +580,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let group_chat_id: uint32_t = dc_get_chat_id_by_grpid( context, grpid, - 0 as *mut libc::c_int, + None, 0 as *mut libc::c_int, ); if group_chat_id == 0i32 as libc::c_uint { @@ -653,12 +650,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut vg_expect_encrypted: libc::c_int = 1i32; if 0 != join_vg { let mut is_verified_group: libc::c_int = 0i32; - dc_get_chat_id_by_grpid( - context, - grpid, - 0 as *mut libc::c_int, - &mut is_verified_group, - ); + dc_get_chat_id_by_grpid(context, grpid, None, &mut is_verified_group); if 0 == is_verified_group { vg_expect_encrypted = 0i32 } @@ -938,9 +930,9 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) dc_create_or_lookup_nchat_by_contact_id( context, contact_id as u32, - 2, + Blocked::Deaddrop, &mut contact_chat_id, - 0 as *mut libc::c_int, + None, ); let peeraddr: &str = match peerstate.addr { Some(ref addr) => &addr, From 64117c2964bd0593b0358fdf68deff27dcd8c8bf Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 14 Aug 2019 20:50:33 +0200 Subject: [PATCH 2/9] refactor(chat): rename dc_chat to chat --- deltachat-ffi/src/lib.rs | 78 ++++++++++++++++++------------------- examples/repl/cmdline.rs | 2 +- examples/simple.rs | 2 +- src/{dc_chat.rs => chat.rs} | 0 src/chatlist.rs | 2 +- src/context.rs | 2 +- src/dc_imex.rs | 2 +- src/dc_job.rs | 2 +- src/dc_location.rs | 2 +- src/dc_lot.rs | 2 +- src/dc_mimefactory.rs | 2 +- src/dc_msg.rs | 2 +- src/dc_qr.rs | 2 +- src/dc_receive_imf.rs | 6 +-- src/dc_securejoin.rs | 2 +- src/lib.rs | 2 +- src/peerstate.rs | 2 +- tests/stress.rs | 2 +- 18 files changed, 57 insertions(+), 57 deletions(-) rename src/{dc_chat.rs => chat.rs} (100%) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 08be95a45..7d78fcb37 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -322,7 +322,7 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms assert!(!context.is_null()); let context = &*context; - dc_chat::dc_create_chat_by_msg_id(context, msg_id) + chat::dc_create_chat_by_msg_id(context, msg_id) } #[no_mangle] @@ -333,7 +333,7 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_create_chat_by_contact_id(context, contact_id) + chat::dc_create_chat_by_contact_id(context, contact_id) } #[no_mangle] @@ -344,7 +344,7 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_chat_id_by_contact_id(context, contact_id) + chat::dc_get_chat_id_by_contact_id(context, contact_id) } #[no_mangle] @@ -357,7 +357,7 @@ pub unsafe extern "C" fn dc_prepare_msg( assert!(!msg.is_null()); let context = &*context; - dc_chat::dc_prepare_msg(context, chat_id, msg) + chat::dc_prepare_msg(context, chat_id, msg) } #[no_mangle] @@ -370,7 +370,7 @@ pub unsafe extern "C" fn dc_send_msg( assert!(!msg.is_null()); let context = &*context; - dc_chat::dc_send_msg(context, chat_id, msg) + chat::dc_send_msg(context, chat_id, msg) } #[no_mangle] @@ -384,7 +384,7 @@ pub unsafe extern "C" fn dc_send_text_msg( let context = &*context; let text_to_send = dc_tools::to_string_lossy(text_to_send); - dc_chat::dc_send_text_msg(context, chat_id, text_to_send) + chat::dc_send_text_msg(context, chat_id, text_to_send) } #[no_mangle] @@ -396,7 +396,7 @@ pub unsafe extern "C" fn dc_set_draft( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_set_draft(context, chat_id, msg) + chat::dc_set_draft(context, chat_id, msg) } #[no_mangle] @@ -407,7 +407,7 @@ pub unsafe extern "C" fn dc_get_draft<'a>( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_draft(context, chat_id) + chat::dc_get_draft(context, chat_id) } #[no_mangle] @@ -420,7 +420,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_chat_msgs(context, chat_id, flags, marker1before) + chat::dc_get_chat_msgs(context, chat_id, flags, marker1before) } #[no_mangle] @@ -428,7 +428,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32 assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_msg_cnt(context, chat_id) + chat::dc_get_msg_cnt(context, chat_id) } #[no_mangle] @@ -439,7 +439,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_fresh_msg_cnt(context, chat_id) + chat::dc_get_fresh_msg_cnt(context, chat_id) } #[no_mangle] @@ -457,7 +457,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id assert!(!context.is_null()); let context = &*context; - dc_chat::dc_marknoticed_chat(context, chat_id); + chat::dc_marknoticed_chat(context, chat_id); } #[no_mangle] @@ -465,7 +465,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - dc_chat::dc_marknoticed_all_chats(context); + chat::dc_marknoticed_all_chats(context); } fn from_prim(s: S) -> Option @@ -493,7 +493,7 @@ pub unsafe extern "C" fn dc_get_chat_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); - dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3) + chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3) } #[no_mangle] @@ -514,7 +514,7 @@ pub unsafe extern "C" fn dc_get_next_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); - dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3) + chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3) } #[no_mangle] @@ -526,7 +526,7 @@ pub unsafe extern "C" fn dc_archive_chat( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_archive_chat(context, chat_id, archive); + chat::dc_archive_chat(context, chat_id, archive); } #[no_mangle] @@ -535,7 +535,7 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 let context = &*context; // TODO: update to indicate public api success/failure of deletion - dc_chat::dc_delete_chat(context, chat_id); + chat::dc_delete_chat(context, chat_id); } #[no_mangle] @@ -546,7 +546,7 @@ pub unsafe extern "C" fn dc_get_chat_contacts( assert!(!context.is_null()); let context = &*context; - dc_array_t::from(dc_chat::dc_get_chat_contacts(context, chat_id)).into_raw() + dc_array_t::from(chat::dc_get_chat_contacts(context, chat_id)).into_raw() } #[no_mangle] @@ -570,7 +570,7 @@ pub unsafe extern "C" fn dc_get_chat<'a>( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_get_chat(context, chat_id) + chat::dc_get_chat(context, chat_id) } #[no_mangle] @@ -583,7 +583,7 @@ pub unsafe extern "C" fn dc_create_group_chat( assert!(!name.is_null()); let context = &*context; - dc_chat::dc_create_group_chat(context, verified, name) + chat::dc_create_group_chat(context, verified, name) } #[no_mangle] @@ -595,7 +595,7 @@ pub unsafe extern "C" fn dc_is_contact_in_chat( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_is_contact_in_chat(context, chat_id, contact_id) + chat::dc_is_contact_in_chat(context, chat_id, contact_id) } #[no_mangle] @@ -607,7 +607,7 @@ pub unsafe extern "C" fn dc_add_contact_to_chat( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_add_contact_to_chat(context, chat_id, contact_id) + chat::dc_add_contact_to_chat(context, chat_id, contact_id) } #[no_mangle] @@ -619,7 +619,7 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat( assert!(!context.is_null()); let context = &*context; - dc_chat::dc_remove_contact_from_chat(context, chat_id, contact_id) + chat::dc_remove_contact_from_chat(context, chat_id, contact_id) } #[no_mangle] @@ -633,7 +633,7 @@ pub unsafe extern "C" fn dc_set_chat_name( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - dc_chat::dc_set_chat_name(context, chat_id, name) + chat::dc_set_chat_name(context, chat_id, name) } #[no_mangle] @@ -646,7 +646,7 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - dc_chat::dc_set_chat_profile_image(context, chat_id, image) + chat::dc_set_chat_profile_image(context, chat_id, image) } #[no_mangle] @@ -698,7 +698,7 @@ pub unsafe extern "C" fn dc_forward_msgs( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - dc_chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id) + chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id) } #[no_mangle] @@ -1256,90 +1256,90 @@ pub unsafe extern "C" fn dc_chatlist_get_context( // dc_chat_t #[no_mangle] -pub type dc_chat_t<'a> = dc_chat::Chat<'a>; +pub type dc_chat_t<'a> = chat::Chat<'a>; #[no_mangle] pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) { assert!(!chat.is_null()); - dc_chat::dc_chat_unref(chat) + chat::dc_chat_unref(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { assert!(!chat.is_null()); - dc_chat::dc_chat_get_id(chat) + chat::dc_chat_get_id(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_get_type(chat) as libc::c_int + chat::dc_chat_get_type(chat) as 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()); - dc_chat::dc_chat_get_name(chat) + chat::dc_chat_get_name(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char { assert!(!chat.is_null()); - dc_chat::dc_chat_get_subtitle(chat) + chat::dc_chat_get_subtitle(chat) } #[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()); - dc_chat::dc_chat_get_profile_image(chat) + chat::dc_chat_get_profile_image(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { assert!(!chat.is_null()); - dc_chat::dc_chat_get_color(chat) + chat::dc_chat_get_color(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_get_archived(chat) as libc::c_int + chat::dc_chat_get_archived(chat) as libc::c_int } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_is_unpromoted(chat) + chat::dc_chat_is_unpromoted(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_is_self_talk(chat) + chat::dc_chat_is_self_talk(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_is_verified(chat) + chat::dc_chat_is_verified(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int { assert!(!chat.is_null()); - dc_chat::dc_chat_is_sending_locations(chat) as libc::c_int + chat::dc_chat_is_sending_locations(chat) as libc::c_int } // dc_msg_t diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index a3b70fd04..2b3c9afb1 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1,13 +1,13 @@ use std::ffi::CString; use std::str::FromStr; +use deltachat::chat::*; use deltachat::chatlist::*; use deltachat::config; use deltachat::constants::*; use deltachat::contact::*; use deltachat::context::*; use deltachat::dc_array::*; -use deltachat::dc_chat::*; use deltachat::dc_configure::*; use deltachat::dc_imex::*; use deltachat::dc_job::*; diff --git a/examples/simple.rs b/examples/simple.rs index 628414c93..48c7db7d8 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -5,12 +5,12 @@ use std::sync::{Arc, RwLock}; use std::{thread, time}; use tempfile::tempdir; +use deltachat::chat::*; use deltachat::chatlist::*; use deltachat::config; use deltachat::constants::Event; use deltachat::contact::*; use deltachat::context::*; -use deltachat::dc_chat::*; use deltachat::dc_configure::*; use deltachat::dc_job::{ dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle, diff --git a/src/dc_chat.rs b/src/chat.rs similarity index 100% rename from src/dc_chat.rs rename to src/chat.rs diff --git a/src/chatlist.rs b/src/chatlist.rs index 1d9eb67d1..631177ef1 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -1,7 +1,7 @@ +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::context::*; -use crate::dc_chat::*; use crate::dc_lot::*; use crate::dc_msg::*; use crate::dc_tools::*; diff --git a/src/context.rs b/src/context.rs index 9ca80c361..8f2b8e87b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,9 @@ use std::sync::{Arc, Condvar, Mutex, RwLock}; +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::dc_array::*; -use crate::dc_chat::*; use crate::dc_job::*; use crate::dc_jobthread::*; use crate::dc_loginparam::*; diff --git a/src/dc_imex.rs b/src/dc_imex.rs index 4f8d80c3d..aff4dec8d 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -6,10 +6,10 @@ use mmime::mmapstring::*; use mmime::other::*; use rand::{thread_rng, Rng}; +use crate::chat::*; use crate::config::Config; use crate::constants::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_configure::*; use crate::dc_e2ee::*; use crate::dc_job::*; diff --git a/src/dc_job.rs b/src/dc_job.rs index 8da295ed5..5b7f9bdd3 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -6,9 +6,9 @@ use std::time::Duration; use rand::{thread_rng, Rng}; +use crate::chat::*; use crate::constants::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_configure::*; use crate::dc_imex::*; use crate::dc_jobthread::*; diff --git a/src/dc_location.rs b/src/dc_location.rs index 5c15c1ca5..bb3804b82 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -3,10 +3,10 @@ use std::ffi::CString; use quick_xml; use quick_xml::events::{BytesEnd, BytesStart, BytesText}; +use crate::chat::*; use crate::constants::Event; use crate::constants::*; use crate::context::*; -use crate::dc_chat::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_tools::*; diff --git a/src/dc_lot.rs b/src/dc_lot.rs index c9e7d072c..8b914aa17 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -1,7 +1,7 @@ +use crate::chat::*; use crate::constants::Chattype; use crate::contact::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_msg::*; use crate::dc_tools::*; use crate::stock::StockMessage; diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index d51aede75..bc6f8ab7f 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -10,10 +10,10 @@ use mmime::mailmime_write_mem::*; use mmime::mmapstring::*; use mmime::other::*; +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::context::{dc_get_version_str, Context}; -use crate::dc_chat::*; use crate::dc_e2ee::*; use crate::dc_location::*; use crate::dc_msg::*; diff --git a/src/dc_msg.rs b/src/dc_msg.rs index 6f4c11dad..ec043f6c2 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -1,10 +1,10 @@ use std::ffi::CString; use std::path::Path; +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::context::*; -use crate::dc_chat::*; use crate::dc_job::*; use crate::dc_lot::dc_lot_t; use crate::dc_lot::*; diff --git a/src/dc_qr.rs b/src/dc_qr.rs index a1ac9535f..b5d46e8cc 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -1,9 +1,9 @@ use percent_encoding::percent_decode_str; +use crate::chat::*; use crate::constants::Blocked; use crate::contact::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_lot::*; use crate::dc_strencode::*; use crate::dc_tools::*; diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 1fed9da38..f6a53c2eb 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1,5 +1,6 @@ -use itertools::join; +use std::ptr; +use itertools::join; use mmime::mailimf::*; use mmime::mailimf_types::*; use mmime::mailmime::*; @@ -8,12 +9,11 @@ use mmime::mailmime_types::*; use mmime::mmapstring::*; use mmime::other::*; use sha2::{Digest, Sha256}; -use std::ptr; +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_job::*; use crate::dc_location::*; use crate::dc_mimeparser::*; diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index afe0d328e..5ee3b9da8 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -4,10 +4,10 @@ use mmime::mailimf_types::*; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use crate::aheader::EncryptPreference; +use crate::chat::*; use crate::constants::*; use crate::contact::*; use crate::context::Context; -use crate::dc_chat::*; use crate::dc_configure::*; use crate::dc_e2ee::*; use crate::dc_lot::*; diff --git a/src/lib.rs b/src/lib.rs index b2d7b4888..299e63cd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,6 +23,7 @@ mod log; mod error; mod aheader; +pub mod chat; pub mod chatlist; pub mod config; pub mod constants; @@ -42,7 +43,6 @@ pub mod types; pub mod x; pub mod dc_array; -pub mod dc_chat; pub mod dc_configure; mod dc_dehtml; mod dc_e2ee; diff --git a/src/peerstate.rs b/src/peerstate.rs index 4f6ba5406..a2d2a74ec 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -4,9 +4,9 @@ use std::fmt; use num_traits::FromPrimitive; use crate::aheader::*; +use crate::chat::*; use crate::constants::*; use crate::context::Context; -use crate::dc_chat::*; use crate::key::*; use crate::sql::{self, Sql}; diff --git a/tests/stress.rs b/tests/stress.rs index 90b93aec6..351d09637 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -6,11 +6,11 @@ use std::ffi::CString; use mmime::mailimf_types::*; use tempfile::{tempdir, TempDir}; +use deltachat::chat::*; use deltachat::config; use deltachat::constants::*; use deltachat::contact::*; use deltachat::context::*; -use deltachat::dc_chat::*; use deltachat::dc_configure::*; use deltachat::dc_imex::*; use deltachat::dc_lot::*; From ddfd067e97c2ac4abecbbd19300fd818a5a41380 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 14 Aug 2019 23:36:16 +0200 Subject: [PATCH 3/9] 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)); } } From 001880e1f02e72c3ebc1da419f44e42c66a7d3ba Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 15 Aug 2019 23:36:24 +0200 Subject: [PATCH 4/9] refactor(chat): first round of method renaming and restructuring --- deltachat-ffi/src/lib.rs | 152 ++- examples/repl/cmdline.rs | 151 ++- examples/simple.rs | 6 +- src/chat.rs | 2049 ++++++++++++++++++-------------------- src/chatlist.rs | 2 +- src/contact.rs | 2 +- src/context.rs | 2 +- src/dc_imex.rs | 10 +- src/dc_job.rs | 6 +- src/dc_location.rs | 11 +- src/dc_lot.rs | 2 +- src/dc_mimefactory.rs | 29 +- src/dc_msg.rs | 20 +- src/dc_qr.rs | 12 +- src/dc_receive_imf.rs | 83 +- src/dc_securejoin.rs | 66 +- src/dc_tools.rs | 126 +-- src/peerstate.rs | 2 +- tests/stress.rs | 22 +- 19 files changed, 1335 insertions(+), 1418 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index d3c53f556..8d13539b5 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -322,7 +322,11 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms assert!(!context.is_null()); let context = &*context; - chat::dc_create_chat_by_msg_id(context, msg_id) + log_err_default( + chat::create_by_msg_id(context, msg_id), + context, + "Failed to create chat", + ) } #[no_mangle] @@ -333,7 +337,11 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id( assert!(!context.is_null()); let context = &*context; - chat::dc_create_chat_by_contact_id(context, contact_id) + log_err_default( + chat::create_by_contact_id(context, contact_id), + context, + "Failed to create chat", + ) } #[no_mangle] @@ -344,7 +352,11 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id( assert!(!context.is_null()); let context = &*context; - chat::dc_get_chat_id_by_contact_id(context, contact_id) + log_err_default( + chat::get_by_contact_id(context, contact_id), + context, + "Failed to get chat", + ) } #[no_mangle] @@ -357,7 +369,11 @@ pub unsafe extern "C" fn dc_prepare_msg( assert!(!msg.is_null()); let context = &*context; - chat::dc_prepare_msg(context, chat_id, msg) + log_err_default( + chat::prepare_msg(context, chat_id, msg), + context, + "Failed to prepare message", + ) } #[no_mangle] @@ -370,7 +386,11 @@ pub unsafe extern "C" fn dc_send_msg( assert!(!msg.is_null()); let context = &*context; - chat::dc_send_msg(context, chat_id, msg) + log_err_default( + chat::send_msg(context, chat_id, msg), + context, + "Failed to send message", + ) } #[no_mangle] @@ -384,7 +404,11 @@ pub unsafe extern "C" fn dc_send_text_msg( let context = &*context; let text_to_send = dc_tools::to_string_lossy(text_to_send); - chat::dc_send_text_msg(context, chat_id, text_to_send) + log_err_default( + chat::send_text_msg(context, chat_id, text_to_send), + context, + "Failed to send text message", + ) } #[no_mangle] @@ -396,7 +420,7 @@ pub unsafe extern "C" fn dc_set_draft( assert!(!context.is_null()); let context = &*context; - chat::dc_set_draft(context, chat_id, msg) + chat::set_draft(context, chat_id, msg) } #[no_mangle] @@ -407,7 +431,7 @@ pub unsafe extern "C" fn dc_get_draft<'a>( assert!(!context.is_null()); let context = &*context; - chat::dc_get_draft(context, chat_id) + chat::get_draft(context, chat_id) } #[no_mangle] @@ -420,7 +444,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs( assert!(!context.is_null()); let context = &*context; - chat::dc_get_chat_msgs(context, chat_id, flags, marker1before) + chat::get_chat_msgs(context, chat_id, flags, marker1before) } #[no_mangle] @@ -428,7 +452,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32 assert!(!context.is_null()); let context = &*context; - chat::dc_get_msg_cnt(context, chat_id) + chat::get_msg_cnt(context, chat_id) as libc::c_int } #[no_mangle] @@ -439,7 +463,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt( assert!(!context.is_null()); let context = &*context; - chat::dc_get_fresh_msg_cnt(context, chat_id) + chat::get_fresh_msg_cnt(context, chat_id) as libc::c_int } #[no_mangle] @@ -457,7 +481,11 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id assert!(!context.is_null()); let context = &*context; - chat::dc_marknoticed_chat(context, chat_id); + log_err( + chat::marknoticed_chat(context, chat_id), + context, + "Failed marknoticed chat", + ); } #[no_mangle] @@ -465,7 +493,11 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - chat::dc_marknoticed_all_chats(context); + log_err( + chat::marknoticed_all_chats(context), + context, + "Failed marknoticed all chats", + ); } fn from_prim(s: S) -> Option @@ -493,7 +525,7 @@ pub unsafe extern "C" fn dc_get_chat_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); - chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3) + chat::get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3) } #[no_mangle] @@ -514,7 +546,7 @@ pub unsafe extern "C" fn dc_get_next_media( let or_msg_type3 = from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); - chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3) + chat::get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3) } #[no_mangle] @@ -526,7 +558,19 @@ pub unsafe extern "C" fn dc_archive_chat( assert!(!context.is_null()); let context = &*context; - chat::dc_archive_chat(context, chat_id, archive); + let archive = if archive == 0 { + false + } else if archive == 1 { + true + } else { + return; + }; + + log_err( + chat::archive(context, chat_id, archive), + context, + "Failed archive chat", + ); } #[no_mangle] @@ -535,7 +579,11 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 let context = &*context; // TODO: update to indicate public api success/failure of deletion - chat::dc_delete_chat(context, chat_id); + log_err( + chat::delete(context, chat_id), + context, + "Failed chat delete", + ); } #[no_mangle] @@ -546,7 +594,7 @@ pub unsafe extern "C" fn dc_get_chat_contacts( assert!(!context.is_null()); let context = &*context; - dc_array_t::from(chat::dc_get_chat_contacts(context, chat_id)).into_raw() + dc_array_t::from(chat::get_chat_contacts(context, chat_id)).into_raw() } #[no_mangle] @@ -570,7 +618,7 @@ pub unsafe extern "C" fn dc_get_chat<'a>( assert!(!context.is_null()); let context = &*context; - match chat::dc_get_chat(context, chat_id) { + match chat::Chat::load_from_db(context, chat_id) { Ok(chat) => Box::into_raw(Box::new(chat)), Err(_) => std::ptr::null_mut(), } @@ -586,7 +634,17 @@ pub unsafe extern "C" fn dc_create_group_chat( assert!(!name.is_null()); let context = &*context; - chat::dc_create_group_chat(context, verified, name) + let verified = if let Some(s) = contact::VerifiedStatus::from_i32(verified) { + s + } else { + return 0; + }; + + log_err_default( + chat::create_group_chat(context, verified, name), + context, + "Failed to create group chat", + ) } #[no_mangle] @@ -598,7 +656,7 @@ pub unsafe extern "C" fn dc_is_contact_in_chat( assert!(!context.is_null()); let context = &*context; - chat::dc_is_contact_in_chat(context, chat_id, contact_id) + chat::is_contact_in_chat(context, chat_id, contact_id) } #[no_mangle] @@ -610,7 +668,7 @@ pub unsafe extern "C" fn dc_add_contact_to_chat( assert!(!context.is_null()); let context = &*context; - chat::dc_add_contact_to_chat(context, chat_id, contact_id) + chat::add_contact_to_chat(context, chat_id, contact_id) } #[no_mangle] @@ -622,7 +680,7 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat( assert!(!context.is_null()); let context = &*context; - chat::dc_remove_contact_from_chat(context, chat_id, contact_id) + chat::remove_contact_from_chat(context, chat_id, contact_id) } #[no_mangle] @@ -636,7 +694,7 @@ pub unsafe extern "C" fn dc_set_chat_name( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - chat::dc_set_chat_name(context, chat_id, name) + chat::set_chat_name(context, chat_id, name) } #[no_mangle] @@ -649,7 +707,7 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - chat::dc_set_chat_profile_image(context, chat_id, image) + chat::set_chat_profile_image(context, chat_id, image) } #[no_mangle] @@ -701,7 +759,7 @@ pub unsafe extern "C" fn dc_forward_msgs( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id) + chat::forward_msgs(context, msg_ids, msg_cnt, chat_id) } #[no_mangle] @@ -1275,7 +1333,7 @@ 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) + chat.get_id() } #[no_mangle] @@ -1283,7 +1341,7 @@ 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 + chat.get_type() as libc::c_int } #[no_mangle] @@ -1291,7 +1349,7 @@ pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_ assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_get_name(chat) + chat.get_name() } #[no_mangle] @@ -1299,7 +1357,7 @@ pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_get_subtitle(chat) + chat.get_subtitle() } #[no_mangle] @@ -1307,7 +1365,7 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_get_profile_image(chat) + chat.get_profile_image() } #[no_mangle] @@ -1315,7 +1373,7 @@ 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) + chat.get_color() } #[no_mangle] @@ -1323,7 +1381,7 @@ pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_i assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_get_archived(chat) as libc::c_int + chat.is_archived() as libc::c_int } #[no_mangle] @@ -1331,7 +1389,7 @@ pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_ assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_is_unpromoted(chat) + chat.is_unpromoted() as libc::c_int } #[no_mangle] @@ -1339,7 +1397,7 @@ pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_i assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_is_self_talk(chat) + chat.is_self_talk() as libc::c_int } #[no_mangle] @@ -1347,7 +1405,7 @@ pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_in assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_is_verified(chat) + chat.is_verified() as libc::c_int } #[no_mangle] @@ -1355,7 +1413,7 @@ pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> l assert!(!chat.is_null()); let chat = &*chat; - chat::dc_chat_is_sending_locations(chat) as libc::c_int + chat.is_sending_locations() as libc::c_int } // dc_msg_t @@ -1836,3 +1894,23 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> { Some(dc_tools::as_str(s)) } + +fn log_err(res: Result, context: &context::Context, msg: &str) { + if let Err(err) = res { + error!(context, 0, "{}: {}", msg, err); + } +} + +fn log_err_default( + res: Result, + context: &context::Context, + msg: &str, +) -> T { + match res { + Ok(t) => t, + Err(err) => { + error!(context, 0, "{}: {}", msg, err); + Default::default() + } + } +} diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 066d13ba3..154aba350 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1,7 +1,7 @@ use std::ffi::CString; use std::str::FromStr; -use deltachat::chat::*; +use deltachat::chat::{self, Chat}; use deltachat::chatlist::*; use deltachat::config; use deltachat::constants::*; @@ -151,7 +151,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int } if ok_to_continue { let ok_to_continue2; - suffix = dc_get_filesuffix_lc(real_spec); + suffix = dc_get_filesuffix_lc(as_str(real_spec)); if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0 { if 0 != dc_poke_eml_file(context, real_spec) { @@ -356,7 +356,7 @@ fn chat_prefix(chat: &Chat) -> &'static str { 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).ok() + Chat::load_from_db(context, chat_id).ok() } else { None }; @@ -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 = Chat::load_from_db(context, chatlist.get_chat_id(i))?; + let temp_subtitle = chat.get_subtitle(); + let temp_name = chat.get_name(); info!( context, 0, "{}#{}: {} [{}] [{} fresh]", chat_prefix(&chat), - dc_chat_get_id(&chat) as libc::c_int, + chat.get_id(), as_str(temp_name), as_str(temp_subtitle), - dc_get_fresh_msg_cnt(context, dc_chat_get_id(&chat)) as libc::c_int, + chat::get_fresh_msg_cnt(context, chat.get_id()), ); free(temp_subtitle as *mut libc::c_void); free(temp_name as *mut libc::c_void); let lot = chatlist.get_summary(i, Some(&chat)); - let statestr = if dc_chat_get_archived(&chat) { + let statestr = if chat.is_archived() { " [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 chat.is_sending_locations() { "📍" } else { "" @@ -672,25 +672,25 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if !arg1.is_empty() { let chat_id = arg1.parse()?; println!("Selecting chat #{}", chat_id); - sel_chat = Some(dc_get_chat(context, chat_id)?); + sel_chat = Some(Chat::load_from_db(context, chat_id)?); *context.cmdline_sel_chat_id.write().unwrap() = chat_id; } 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); - let temp_name = dc_chat_get_name(sel_chat); + let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0); + let temp2 = sel_chat.get_subtitle(); + let temp_name = sel_chat.get_name(); info!( context, 0, "{}#{}: {} [{}]{}", chat_prefix(sel_chat), - dc_chat_get_id(sel_chat), + sel_chat.get_id(), as_str(temp_name), as_str(temp2), - if dc_chat_is_sending_locations(sel_chat) { + if sel_chat.is_sending_locations() { "📍" } else { "" @@ -702,65 +702,52 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E log_msglist(context, msglist); dc_array_unref(msglist); } - let draft = dc_get_draft(context, dc_chat_get_id(sel_chat)); + let draft = chat::get_draft(context, sel_chat.get_id()); if !draft.is_null() { log_msg(context, "Draft", draft); dc_msg_unref(draft); } println!( "{} messages.", - dc_get_msg_cnt(context, dc_chat_get_id(sel_chat)) + chat::get_msg_cnt(context, sel_chat.get_id()) ); - dc_marknoticed_chat(context, dc_chat_get_id(sel_chat)); + chat::marknoticed_chat(context, sel_chat.get_id())?; } "createchat" => { ensure!(!arg1.is_empty(), "Argument missing."); let contact_id: libc::c_int = arg1.parse()?; - let chat_id: libc::c_int = - dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int; - if chat_id != 0 { - println!("Single#{} created successfully.", chat_id,); - } else { - bail!("Failed to create chat"); - } + let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?; + + println!("Single#{} created successfully.", chat_id,); } "createchatbymsg" => { ensure!(!arg1.is_empty(), "Argument missing"); - let msg_id_0: libc::c_int = arg1.parse()?; - 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 = dc_get_chat(context, chat_id_0 as uint32_t)?; - println!("{}#{} created successfully.", chat_prefix(&chat), chat_id_0,); - } else { - bail!(""); - } + let msg_id: u32 = arg1.parse()?; + let chat_id = chat::create_by_msg_id(context, msg_id)?; + let chat = Chat::load_from_db(context, chat_id)?; + + println!("{}#{} created successfully.", chat_prefix(&chat), chat_id,); } "creategroup" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int; - if chat_id_1 != 0 { - println!("Group#{} created successfully.", chat_id_1,); - } else { - bail!("Failed to create group"); - } + let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1_c)?; + + println!("Group#{} created successfully.", chat_id); } "createverified" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int; - if chat_id_2 != 0 { - println!("VerifiedGroup#{} created successfully.", chat_id_2,); - } else { - bail!("Failed to create verified group"); - } + let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1_c)?; + + println!("VerifiedGroup#{} created successfully.", chat_id); } "addmember" => { 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( + if 0 != chat::add_contact_to_chat( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), contact_id_0 as uint32_t, ) { println!("Contact added to chat."); @@ -772,9 +759,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 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( + if 0 != chat::remove_contact_from_chat( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), contact_id_1 as uint32_t, ) { println!("Contact added to chat."); @@ -785,7 +772,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "groupname" => { 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.as_ref().unwrap()), arg1_c) { + if 0 != chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1_c) { println!("Chat name set"); } else { bail!("Failed to set chat name"); @@ -795,9 +782,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != dc_set_chat_profile_image( + if 0 != chat::set_chat_profile_image( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), if !arg1.is_empty() { arg1_c } else { @@ -812,18 +799,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "chatinfo" => { ensure!(sel_chat.is_some(), "No chat selected."); - let contacts = - dc_get_chat_contacts(context, dc_chat_get_id(sel_chat.as_ref().unwrap())); + let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id()); 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.as_ref().unwrap()) - ), + dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()), ); } "getlocations" => { @@ -832,7 +815,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let contact_id = arg1.parse().unwrap_or_default(); let locations = dc_get_locations( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), contact_id, 0, 0, @@ -864,10 +847,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty(), "No timeout given."); let seconds = arg1.parse()?; - dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), seconds); + dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds); println!( "Locations will be sent to Chat#{} for {} seconds. Use 'setlocation ' to play around.", - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), seconds ); } @@ -895,23 +878,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let msg = format!("{} {}", arg1, arg2); - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat.as_ref().unwrap()), msg) { - println!("Message sent."); - } else { - bail!("Sending failed."); - } + chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), msg)?; } "sendempty" => { 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."); - } + chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), "".into())?; } "sendimage" | "sendfile" => { ensure!(sel_chat.is_some(), "No chat selected."); @@ -927,14 +898,14 @@ 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.as_ref().unwrap()), msg_0); + chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), msg_0)?; dc_msg_unref(msg_0); } "listmsgs" => { ensure!(!arg1.is_empty(), "Argument missing."); let chat = if let Some(ref sel_chat) = sel_chat { - dc_chat_get_id(sel_chat) + sel_chat.get_id() } else { 0 as libc::c_uint }; @@ -953,13 +924,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 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.as_ref().unwrap()), draft_0); + chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), draft_0); dc_msg_unref(draft_0); println!("Draft saved."); } else { - dc_set_draft( + chat::set_draft( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), 0 as *mut dc_msg_t, ); println!("Draft deleted."); @@ -968,9 +939,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "listmedia" => { ensure!(sel_chat.is_some(), "No chat selected."); - let images = dc_get_chat_media( + let images = chat::get_chat_media( context, - dc_chat_get_id(sel_chat.as_ref().unwrap()), + sel_chat.as_ref().unwrap().get_id(), Viewtype::Image, Viewtype::Gif, Viewtype::Video, @@ -991,12 +962,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "archive" | "unarchive" => { ensure!(!arg1.is_empty(), "Argument missing."); let chat_id = arg1.parse()?; - dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 }); + chat::archive( + context, + chat_id, + if arg0 == "archive" { true } else { false }, + )?; } "delchat" => { ensure!(!arg1.is_empty(), "Argument missing."); let chat_id = arg1.parse()?; - dc_delete_chat(context, chat_id); + chat::delete(context, chat_id)?; } "msginfo" => { ensure!(!arg1.is_empty(), "Argument missing."); @@ -1021,7 +996,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let mut msg_ids = [0; 1]; let chat_id = arg2.parse()?; msg_ids[0] = arg1.parse()?; - dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id); + chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id); } "markseen" => { ensure!(!arg1.is_empty(), "Argument missing."); @@ -1091,8 +1066,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)); + let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?; + res += &format!("{}#{}", chat_prefix(&chat), chat.get_id()); } } diff --git a/examples/simple.rs b/examples/simple.rs index 261e4eb28..2a52d6041 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -5,7 +5,7 @@ use std::sync::{Arc, RwLock}; use std::{thread, time}; use tempfile::tempdir; -use deltachat::chat::*; +use deltachat::chat; use deltachat::chatlist::*; use deltachat::config; use deltachat::constants::Event; @@ -96,8 +96,8 @@ fn main() { println!("sending a message"); let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap(); - let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id); - dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()); + let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap(); + chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap(); println!("fetching chats.."); let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap(); diff --git a/src/chat.rs b/src/chat.rs index 5e66f7016..eefcbe313 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1,4 +1,5 @@ use std::ffi::CString; +use std::path::Path; use crate::chatlist::*; use crate::constants::*; @@ -17,7 +18,7 @@ use crate::x::*; use std::ptr; /// An object representing a single chat in memory. -/// Chat objects are created using eg. dc_get_chat() +/// Chat objects are created using eg. `Chat::load_from_db` /// and are not updated on database changes; /// if you want an update, you have to recreate the object. #[derive(Clone)] @@ -35,662 +36,508 @@ pub struct Chat<'a> { } 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 = 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); - } - } - } - - dc_msg_unref(msg); - - if 0 != send_event { - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); - } - chat_id -} - -pub unsafe fn dc_unblock_chat(context: &Context, chat_id: u32) { - dc_block_chat(context, chat_id, 0i32); -} - -fn dc_block_chat(context: &Context, chat_id: u32, new_blocking: libc::c_int) -> bool { - sql::execute( - context, - &context.sql, - "UPDATE chats SET blocked=? WHERE id=?;", - params![new_blocking, chat_id as i32], - ) - .is_ok() -} - -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 mut c = Chat::new(context); - - c.id = row.get(0)?; - c.typ = row.get(1)?; - c.name = unsafe { row.get::<_, String>(2)?.strdup() }; - c.grpid = unsafe { row.get::<_, String>(3)?.strdup() }; - - c.param = row.get::<_, String>(4)?.parse().unwrap_or_default(); - c.archived = row.get(5)?; - c.blocked = row.get::<_, Option<_>>(6)?.unwrap_or_default(); - c.gossiped_timestamp = row.get(7)?; - c.is_sending_locations = row.get(8)?; - - Ok(c) - }, - ); - - match res { - Err(err @ crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => Err(err), - Err(err) => match err { - _ => { - error!( + pub fn load_from_db(context: &'a 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 mut c = Chat { context, - 0, "chat: failed to load from db {}: {:?}", chat_id, err - ); - Err(err) - } - }, - Ok(mut chat) => { - match chat.id { - 1 => unsafe { - free(chat.name.cast()); - chat.name = chat.context.stock_str(StockMessage::DeadDrop).strdup(); - }, - 6 => unsafe { - 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.cast()); - chat.name = chat.context.stock_str(StockMessage::StarredMsgs).strdup(); - }, - _ => { - unsafe { - 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(); + 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, + }; - if !(*contacts).is_empty() { - if let Ok(contact) = Contact::get_by_id(chat.context, contacts[0]) { - chat_name = contact.get_display_name().to_owned(); - } - } + c.id = row.get(0)?; + c.typ = row.get(1)?; + c.name = unsafe { row.get::<_, String>(2)?.strdup() }; + c.grpid = unsafe { row.get::<_, String>(3)?.strdup() }; - chat.name = (&chat_name).strdup(); - } - } + c.param = row.get::<_, String>(4)?.parse().unwrap_or_default(); + c.archived = row.get(5)?; + c.blocked = row.get::<_, Option<_>>(6)?.unwrap_or_default(); + c.gossiped_timestamp = row.get(7)?; + c.is_sending_locations = row.get(8)?; - if chat.param.exists(Param::Selftalk) { - unsafe { - free(chat.name as *mut libc::c_void); - chat.name = chat.context.stock_str(StockMessage::SelfMsg).strdup(); - } - } - } - } - Ok(chat) - } - } -} - -pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: u32) -> u32 { - let mut chat_id = 0; - let mut chat_blocked = Blocked::Not; - let mut send_event = 0; - dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); - if 0 != chat_id { - if chat_blocked != Blocked::Not { - dc_unblock_chat(context, chat_id); - send_event = 1i32; - } - } else if !Contact::real_exists_by_id(context, contact_id) - && contact_id != DC_CONTACT_ID_SELF as u32 - { - warn!( - context, - 0, "Cannot create chat, contact {} does not exist.", contact_id as libc::c_int, - ); - } else { - dc_create_or_lookup_nchat_by_contact_id( - context, - contact_id, - Blocked::Not, - &mut chat_id, - None, - ); - if 0 != chat_id { - send_event = 1; - } - Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat); - } - if 0 != send_event { - context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); - } - chat_id -} - -pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( - context: &Context, - contact_id: u32, - create_blocked: Blocked, - ret_chat_id: *mut u32, - mut ret_chat_blocked: Option<&mut Blocked>, -) { - let mut chat_id = 0; - let mut chat_blocked = Blocked::Not; - - if !ret_chat_id.is_null() { - *ret_chat_id = 0; - } - - if let Some(b) = ret_chat_blocked.as_mut() { - **b = Blocked::Not; - } - - if !context.sql.is_open() { - return; - } - if contact_id == 0 as libc::c_uint { - return; - } - dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_blocked); - if chat_id != 0 { - if !ret_chat_id.is_null() { - *ret_chat_id = chat_id; - } - - if let Some(b) = ret_chat_blocked.as_mut() { - **b = chat_blocked; - } - return; - } - if let Ok(contact) = Contact::load_from_db(context, contact_id) { - let chat_name = contact.get_display_name(); - - match sql::execute( - context, - &context.sql, - format!( - "INSERT INTO chats (type, name, param, blocked, grpid) VALUES({}, '{}', '{}', {}, '{}')", - 100, - chat_name, - if contact_id == DC_CONTACT_ID_SELF as u32 { "K=1" } else { "" }, - create_blocked as u8, - contact.get_addr(), - ), - params![], - ) { - Ok(_) => { - chat_id = sql::get_rowid( - context, - &context.sql, - "chats", - "grpid", - contact.get_addr(), - ); - - sql::execute( - context, - &context.sql, - format!("INSERT INTO chats_contacts (chat_id, contact_id) VALUES({}, {})", chat_id, contact_id), - params![], - ).ok(); + Ok(c) }, - Err(err) => panic!("{:?}", err), - } - } - - if !ret_chat_id.is_null() { - *ret_chat_id = chat_id - } - if let Some(b) = ret_chat_blocked.as_mut() { - **b = create_blocked; - } -} - -pub fn dc_lookup_real_nchat_by_contact_id( - context: &Context, - contact_id: u32, - ret_chat_id: *mut u32, - ret_chat_blocked: &mut Blocked, -) { - /* checks for "real" chats or self-chat */ - if !ret_chat_id.is_null() { - unsafe { *ret_chat_id = 0 }; - } - if !context.sql.is_open() { - return; - } - - if let Ok((id, blocked)) = context.sql.query_row( - "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", - params![contact_id as i32], - |row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())), - ) { - unsafe { *ret_chat_id = id }; - *ret_chat_blocked = blocked; - } -} - -pub unsafe fn dc_get_chat_id_by_contact_id(context: &Context, contact_id: u32) -> u32 { - let mut chat_id: u32 = 0i32 as u32; - let mut chat_id_blocked = Blocked::Not; - dc_lookup_real_nchat_by_contact_id(context, contact_id, &mut chat_id, &mut chat_id_blocked); - if chat_id_blocked != Blocked::Not { - 0 - } else { - chat_id - } -} - -pub unsafe fn dc_prepare_msg<'a>( - context: &'a Context, - chat_id: u32, - mut msg: *mut dc_msg_t<'a>, -) -> u32 { - if msg.is_null() || chat_id <= 9i32 as libc::c_uint { - return 0i32 as u32; - } - (*msg).state = DC_STATE_OUT_PREPARING; - let msg_id: u32 = prepare_msg_common(context, chat_id, msg); - context.call_cb( - Event::MSGS_CHANGED, - (*msg).chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); - msg_id -} - -pub fn msgtype_has_file(msgtype: Viewtype) -> bool { - match msgtype { - Viewtype::Image => true, - Viewtype::Gif => true, - Viewtype::Audio => true, - Viewtype::Voice => true, - Viewtype::Video => true, - Viewtype::File => true, - _ => false, - } -} - -#[allow(non_snake_case)] -unsafe fn prepare_msg_common<'a>( - context: &'a Context, - chat_id: u32, - mut msg: *mut dc_msg_t<'a>, -) -> u32 { - let mut OK_TO_CONTINUE = true; - (*msg).id = 0i32 as u32; - (*msg).context = context; - if (*msg).type_0 == Viewtype::Text { - /* the caller should check if the message text is empty */ - } else if msgtype_has_file((*msg).type_0) { - let mut pathNfilename = (*msg) - .param - .get(Param::File) - .map(|s| s.strdup()) - .unwrap_or_else(|| std::ptr::null_mut()); - if pathNfilename.is_null() { - error!( - context, - 0, - "Attachment missing for message of type #{}.", - (*msg).type_0, - ); - OK_TO_CONTINUE = false; - } else if (*msg).state == DC_STATE_OUT_PREPARING - && !dc_is_blobdir_path(context, pathNfilename) - { - error!(context, 0, "Files must be created in the blob-directory.",); - OK_TO_CONTINUE = false; - } else if !dc_make_rel_and_copy(context, &mut pathNfilename) { - OK_TO_CONTINUE = false; - } else { - (*msg).param.set(Param::File, as_str(pathNfilename)); - if (*msg).type_0 == Viewtype::File || (*msg).type_0 == Viewtype::Image { - /* Correct the type, take care not to correct already very special formats as GIF or VOICE. - Typical conversions: - - from FILE to AUDIO/VIDEO/IMAGE - - from FILE/IMAGE to GIF */ - - if let Some((type_, mime)) = - dc_msg_guess_msgtype_from_suffix(as_path(pathNfilename)) - { - (*msg).type_0 = type_; - (*msg).param.set(Param::MimeType, mime); - } - } else if !(*msg).param.exists(Param::MimeType) { - if let Some((_, mime)) = dc_msg_guess_msgtype_from_suffix(as_path(pathNfilename)) { - (*msg).param.set(Param::MimeType, mime); - } - } - info!( - context, - 0, - "Attaching \"{}\" for message type #{}.", - as_str(pathNfilename), - (*msg).type_0 - ); - - free(pathNfilename as *mut _); - } - } else { - error!( - context, - 0, - "Cannot send messages of type #{}.", - (*msg).type_0 ); - OK_TO_CONTINUE = false; - } - if OK_TO_CONTINUE { - dc_unarchive_chat(context, 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, - &mut chat, - msg, - dc_create_smeared_timestamp(context), - ); - (*msg).chat_id = chat_id - } - } - (*msg).id -} - -#[allow(non_snake_case)] -unsafe fn prepare_msg_raw( - context: &Context, - chat: &mut Chat, - msg: *mut dc_msg_t, - timestamp: i64, -) -> u32 { - 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 msg_id = 0; - let mut to_id = 0; - let mut location_id = 0; - - 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) - { - log_event!( - context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot send message; self not in group.", - ); - } else { - let from = context.sql.get_config(context, "configured_addr"); - if from.is_none() { - error!(context, 0, "Cannot send message, not configured.",); - } 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 - } else { - ptr::null_mut() - }, - from_c.as_ptr(), - ); - - 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], - 0, - ) { - to_id = id; - } else { + match res { + Err(err @ crate::error::Error::Sql(rusqlite::Error::QueryReturnedNoRows)) => Err(err), + Err(err) => match err { + _ => { error!( context, - 0, "Cannot send message, contact for chat #{} not found.", chat.id, + 0, "chat: failed to load from db {}: {:?}", chat_id, err ); - OK_TO_CONTINUE = false; + Err(err) } + }, + Ok(mut chat) => { + match chat.id { + 1 => unsafe { + free(chat.name.cast()); + chat.name = chat.context.stock_str(StockMessage::DeadDrop).strdup(); + }, + 6 => unsafe { + 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.cast()); + chat.name = chat.context.stock_str(StockMessage::StarredMsgs).strdup(); + }, + _ => { + unsafe { + if chat.typ == Chattype::Single { + free(chat.name.cast()); + let contacts = 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]) + { + chat_name = contact.get_display_name().to_owned(); + } + } + + chat.name = (&chat_name).strdup(); + } + } + + if chat.param.exists(Param::Selftalk) { + unsafe { + free(chat.name as *mut libc::c_void); + chat.name = chat.context.stock_str(StockMessage::SelfMsg).strdup(); + } + } + } + } + Ok(chat) + } + } + } + + pub fn is_self_talk(&self) -> bool { + self.param.exists(Param::Selftalk) + } + + pub fn update_param(&mut self) -> Result<(), Error> { + sql::execute( + self.context, + &self.context.sql, + "UPDATE chats SET param=? WHERE id=?", + params![self.param.to_string(), self.id as i32], + ) + } + + pub unsafe fn get_id(&self) -> u32 { + self.id + } + + pub unsafe fn get_type(&self) -> Chattype { + self.typ + } + + pub unsafe fn get_name(&self) -> *mut libc::c_char { + dc_strdup(self.name) + } + + pub unsafe fn get_subtitle(&self) -> *mut libc::c_char { + /* returns either the address or the number of chat members */ + + let mut ret: *mut libc::c_char = std::ptr::null_mut(); + if self.typ == Chattype::Single && self.param.exists(Param::Selftalk) { + ret = self + .context + .stock_str(StockMessage::SelfTalkSubTitle) + .strdup(); + } else if self.typ == Chattype::Single { + let ret_raw: String = self + .context + .sql + .query_row_col( + self.context, + "SELECT c.addr FROM chats_contacts cc \ + LEFT JOIN contacts c ON c.id=cc.contact_id \ + WHERE cc.chat_id=?;", + params![self.id as i32], + 0, + ) + .unwrap_or_else(|| "Err".into()); + ret = ret_raw.strdup(); + } else if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { + if self.id == 1 { + ret = self.context.stock_str(StockMessage::DeadDrop).strdup(); } 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); - dc_chat_update_param(chat); + let cnt = get_chat_contact_cnt(self.context, self.id); + ret = self + .context + .stock_string_repl_int(StockMessage::Member, cnt) + .strdup(); + } + } + if !ret.is_null() { + ret + } else { + dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char) + } + } + 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(); + + 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(()) + } + + pub unsafe fn get_profile_image(&self) -> *mut libc::c_char { + let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; + + if let Some(image_rel) = self.param.get(Param::ProfileImage) { + if !image_rel.is_empty() { + image_abs = dc_get_abs_path(self.context, image_rel); + } + } else if self.typ == Chattype::Single { + let contacts = get_chat_contacts(self.context, self.id); + if !contacts.is_empty() { + if let Ok(contact) = Contact::get_by_id(self.context, contacts[0]) { + if let Some(img) = contact.get_profile_image() { + image_abs = img.strdup(); } } } - 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![chat.id], - |row| { - let state: String = row.get(1)?; + image_abs + } - 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" - }, - ); + pub fn get_color(&self) -> u32 { + let mut color: u32 = 0i32 as u32; + + if self.typ == Chattype::Single { + let contacts = get_chat_contacts(self.context, self.id); + if !contacts.is_empty() { + if let Ok(contact) = Contact::get_by_id(self.context, contacts[0]) { + color = contact.get_color(); + } + } + } else { + color = unsafe { dc_str_to_color(self.name) } as u32 + } + + color + } + + pub fn is_archived(&self) -> bool { + self.archived + } + + pub fn is_unpromoted(&self) -> bool { + self.param.get_int(Param::Unpromoted).unwrap_or_default() == 1 + } + + pub fn is_verified(&self) -> bool { + (self.typ == Chattype::VerifiedGroup) + } + + pub fn is_sending_locations(&self) -> bool { + self.is_sending_locations + } + + #[allow(non_snake_case)] + unsafe fn prepare_msg_raw( + &mut self, + context: &Context, + msg: *mut dc_msg_t, + timestamp: i64, + ) -> 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 msg_id = 0; + let mut to_id = 0; + let mut location_id = 0; + + if !(self.typ == Chattype::Single + || self.typ == Chattype::Group + || self.typ == Chattype::VerifiedGroup) + { + error!(context, 0, "Cannot send to chat type #{}.", self.typ,); + } else if (self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup) + && 0 == is_contact_in_chat(context, self.id, 1 as u32) + { + log_event!( + context, + Event::ERROR_SELF_NOT_IN_GROUP, + 0, + "Cannot send message; self not in group.", + ); + } else { + let from = context.sql.get_config(context, "configured_addr"); + if from.is_none() { + error!(context, 0, "Cannot send message, not configured.",); + } else { + let from_c = CString::yolo(from.unwrap()); + new_rfc724_mid = dc_create_outgoing_rfc724_mid( + if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { + self.grpid + } else { + ptr::null_mut() + }, + from_c.as_ptr(), + ); + + 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; + } + } 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)?; + + 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; } - } 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); } - 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; + } + } + } + if 0 != do_guarantee_e2ee { + (*msg).param.set_int(Param::GuranteeE2ee, 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 != can_encrypt { - if 0 != all_mutual { - do_guarantee_e2ee = 1; - } else if 0 != last_msg_in_chat_encrypted(context, &context.sql, chat.id) { - do_guarantee_e2ee = 1; - } - } - } - if 0 != do_guarantee_e2ee { - (*msg).param.set_int(Param::GuranteeE2ee, 1); - } - (*msg).param.remove(Param::ErroneousE2ee); - if 0 == dc_chat_is_self_talk(chat) - && 0 != get_parent_mime_headers( - chat, - &mut parent_rfc724_mid, - &mut parent_in_reply_to, - &mut parent_references, - ) - { - 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) - } - } + // add independent location to database - // 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 as i32, - chat.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( + if (*msg).param.exists(Param::SetLatitude) { + if sql::execute( context, &context.sql, - "locations", - "timestamp", - timestamp, - "from_id", - DC_CONTACT_ID_SELF as i32, - ); + "INSERT INTO locations \ + (timestamp,from_id,chat_id, latitude,longitude,independent)\ + VALUES (?,?,?, ?,?,1);", + params![ + timestamp, + DC_CONTACT_ID_SELF as i32, + 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 + // add message to the database - if sql::execute( + 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), - chat.id as i32, + self.id as i32, 1i32, to_id as i32, timestamp, @@ -716,83 +563,298 @@ unsafe fn prepare_msg_raw( context, 0, "Cannot send message, cannot insert to database (chat #{}).", - chat.id, + self.id, ); } + } + } + } + + 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) + } +} + +/// Create a normal chat or a group chat by a messages ID that comes typically +/// from the deaddrop, DC_CHAT_ID_DEADDROP (1). +/// +/// If the given message ID already belongs to a normal chat or to a group chat, +/// the chat ID of this chat is returned and no new chat is created. +/// If a new chat is created, the given message ID is moved to this chat, however, +/// there may be more messages moved to the chat from the deaddrop. To get the +/// chat messages, use dc_get_chat_msgs(). +/// +/// If the user is asked before creation, he should be +/// asked whether he wants to chat with the _contact_ belonging to the message; +/// the group names may be really weird when taken from the subject of implicit +/// groups and this may look confusing. +/// +/// Moreover, this function also scales up the origin of the contact belonging +/// to the message and, depending on the contacts origin, messages from the +/// same group may be shown or not - so, all in all, it is fine to show the +/// contact name only. +pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result { + let mut chat_id = 0; + let mut send_event = false; + let msg = unsafe { dc_msg_new_untyped(context) }; + + if dc_msg_load_from_db(msg, context, msg_id) { + if let Ok(chat) = Chat::load_from_db(context, unsafe { (*msg).chat_id }) { + if chat.id > 9 { + chat_id = chat.id; + if chat.blocked != Blocked::Not { + unblock(context, chat.id); + send_event = true; + } + Contact::scaleup_origin_by_id( + context, + unsafe { (*msg).from_id }, + Origin::CreateChat, + ); } } } - 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); + unsafe { dc_msg_unref(msg) }; - msg_id -} - -// TODO should return bool /rtn -unsafe fn get_parent_mime_headers( - 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 !(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 - .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], - |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() as libc::c_int; - - if 0 == success { - 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], - |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() as libc::c_int; - } + if send_event { + context.call_cb(Event::MSGS_CHANGED, 0, 0); } - success + + ensure!(chat_id > 0, "failed to load create chat"); + + Ok(chat_id) } -pub unsafe fn dc_chat_is_self_talk(chat: &Chat) -> libc::c_int { - chat.param.exists(Param::Selftalk) as libc::c_int +/// Create a normal chat with a single user. To create group chats, +/// see dc_create_group_chat(). +/// +/// If a chat already exists, this ID is returned, otherwise a new chat is created; +/// this new chat may already contain messages, eg. from the deaddrop, to get the +/// chat messages, use dc_get_chat_msgs(). +pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result { + let chat_id = match lookup_by_contact_id(context, contact_id) { + Ok((chat_id, chat_blocked)) => { + if chat_blocked != Blocked::Not { + // unblock chat (typically move it from the deaddrop to view + unblock(context, chat_id); + } + chat_id + } + Err(err) => { + if !Contact::real_exists_by_id(context, contact_id) + && contact_id != DC_CONTACT_ID_SELF as u32 + { + warn!( + context, + 0, "Cannot create chat, contact {} does not exist.", contact_id, + ); + return Err(err); + } else { + let (chat_id, _) = + create_or_lookup_by_contact_id(context, contact_id, Blocked::Not)?; + Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat); + chat_id + } + } + }; + + context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); + + Ok(chat_id) } -/******************************************************************************* - * Sending messages - ******************************************************************************/ -// TODO should return bool /rtn -unsafe fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) -> libc::c_int { +pub fn unblock(context: &Context, chat_id: u32) { + set_blocking(context, chat_id, Blocked::Not); +} + +pub fn set_blocking(context: &Context, chat_id: u32, new_blocking: Blocked) -> bool { + sql::execute( + context, + &context.sql, + "UPDATE chats SET blocked=? WHERE id=?;", + params![new_blocking, chat_id as i32], + ) + .is_ok() +} + +pub fn create_or_lookup_by_contact_id( + context: &Context, + contact_id: u32, + create_blocked: Blocked, +) -> Result<(u32, Blocked), Error> { + ensure!(context.sql.is_open(), "Database not available"); + ensure!(contact_id > 0, "Invalid contact id requested"); + + if let Ok((chat_id, chat_blocked)) = lookup_by_contact_id(context, contact_id) { + // Already exists, no need to create. + return Ok((chat_id, chat_blocked)); + } + + let contact = Contact::load_from_db(context, contact_id)?; + let chat_name = contact.get_display_name(); + + sql::execute( + context, + &context.sql, + format!( + "INSERT INTO chats (type, name, param, blocked, grpid) VALUES({}, '{}', '{}', {}, '{}')", + 100, + chat_name, + if contact_id == DC_CONTACT_ID_SELF as u32 { "K=1" } else { "" }, + create_blocked as u8, + contact.get_addr(), + ), + params![], + )?; + + let chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", contact.get_addr()); + + sql::execute( + context, + &context.sql, + format!( + "INSERT INTO chats_contacts (chat_id, contact_id) VALUES({}, {})", + chat_id, contact_id + ), + params![], + )?; + + Ok((chat_id, create_blocked)) +} + +pub fn lookup_by_contact_id(context: &Context, contact_id: u32) -> Result<(u32, Blocked), Error> { + ensure!(context.sql.is_open(), "Database not available"); + + context.sql.query_row( + "SELECT c.id, c.blocked FROM chats c INNER JOIN chats_contacts j ON c.id=j.chat_id WHERE c.type=100 AND c.id>9 AND j.contact_id=?;", + params![contact_id as i32], + |row| Ok((row.get(0)?, row.get::<_, Option<_>>(1)?.unwrap_or_default())), + ) +} + +pub fn get_by_contact_id(context: &Context, contact_id: u32) -> Result { + let (chat_id, blocked) = lookup_by_contact_id(context, contact_id)?; + ensure_eq!(blocked, Blocked::Not, "Requested contact is blocked"); + + Ok(chat_id) +} + +pub fn prepare_msg<'a>( + context: &'a Context, + chat_id: u32, + mut msg: *mut dc_msg_t<'a>, +) -> Result { + ensure!(!msg.is_null(), "No message provided"); + ensure!( + chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, + "Cannot prepare message for special chat" + ); + + unsafe { (*msg).state = DC_STATE_OUT_PREPARING }; + let msg_id = prepare_msg_common(context, chat_id, msg)?; + context.call_cb( + Event::MSGS_CHANGED, + unsafe { (*msg).chat_id as uintptr_t }, + unsafe { (*msg).id as uintptr_t }, + ); + + Ok(msg_id) +} + +pub fn msgtype_has_file(msgtype: Viewtype) -> bool { + match msgtype { + Viewtype::Image => true, + Viewtype::Gif => true, + Viewtype::Audio => true, + Viewtype::Voice => true, + Viewtype::Video => true, + Viewtype::File => true, + _ => false, + } +} + +fn prepare_msg_common<'a>( + context: &'a Context, + chat_id: u32, + msg: *mut dc_msg_t<'a>, +) -> Result { + ensure!(!msg.is_null(), "No message provided"); + + let msg = unsafe { &mut *msg }; + + msg.id = 0; + msg.context = context; + + if msg.type_0 == Viewtype::Text { + // the caller should check if the message text is empty + } else if msgtype_has_file(msg.type_0) { + let path_filename = msg.param.get(Param::File); + + ensure!( + path_filename.is_some(), + "Attachment missing for message of type #{}.", + msg.type_0 + ); + + let mut path_filename = path_filename.unwrap().to_string(); + + if msg.state == DC_STATE_OUT_PREPARING && !dc_is_blobdir_path(context, &path_filename) { + bail!("Files must be created in the blob-directory."); + } + + ensure!( + dc_make_rel_and_copy(context, &mut path_filename), + "Failed to copy" + ); + + msg.param.set(Param::File, &path_filename); + if msg.type_0 == Viewtype::File || msg.type_0 == Viewtype::Image { + // Correct the type, take care not to correct already very special + // formats as GIF or VOICE. + // + // Typical conversions: + // - from FILE to AUDIO/VIDEO/IMAGE + // - from FILE/IMAGE to GIF */ + if let Some((better_type, better_mime)) = + dc_msg_guess_msgtype_from_suffix(Path::new(&path_filename)) + { + msg.type_0 = better_type; + msg.param.set(Param::MimeType, better_mime); + } + } else if !msg.param.exists(Param::MimeType) { + if let Some((_, mime)) = dc_msg_guess_msgtype_from_suffix(Path::new(&path_filename)) { + msg.param.set(Param::MimeType, mime); + } + } + info!( + context, + 0, "Attaching \"{}\" for message type #{}.", &path_filename, msg.type_0 + ); + } else { + bail!("Cannot send messages of type #{}.", msg.type_0); + } + + unarchive(context, chat_id)?; + + let mut chat = Chat::load_from_db(context, chat_id)?; + if msg.state != DC_STATE_OUT_PREPARING { + msg.state = DC_STATE_OUT_PENDING; + } + + msg.id = unsafe { chat.prepare_msg_raw(context, msg, dc_create_smeared_timestamp(context))? }; + msg.chat_id = chat_id; + + Ok(msg.id) +} + +fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) -> bool { let packed: Option = sql.query_row_col( context, "SELECT param \ @@ -804,33 +866,18 @@ unsafe fn last_msg_in_chat_encrypted(context: &Context, sql: &Sql, chat_id: u32) if let Some(ref packed) = packed { match packed.parse::() { - Ok(param) => param.exists(Param::GuranteeE2ee) as libc::c_int, + Ok(param) => param.exists(Param::GuranteeE2ee), Err(err) => { error!(context, 0, "invalid params stored: '{}', {:?}", packed, err); - 0 + false } } } else { - 0 + false } } -// TODO should return bool /rtn -pub unsafe fn dc_chat_update_param(chat: &mut Chat) -> libc::c_int { - sql::execute( - chat.context, - &chat.context.sql, - "UPDATE chats SET param=? WHERE id=?", - params![chat.param.to_string(), chat.id as i32], - ) - .is_ok() as libc::c_int -} - -pub unsafe fn dc_is_contact_in_chat( - context: &Context, - chat_id: u32, - contact_id: u32, -) -> libc::c_int { +pub fn is_contact_in_chat(context: &Context, chat_id: u32, contact_id: u32) -> libc::c_int { /* this function works for group and for normal chats, however, it is more useful for group chats. DC_CONTACT_ID_SELF may be used to check, if the user itself is in a group chat (DC_CONTACT_ID_SELF is not added to normal chats) */ @@ -844,33 +891,45 @@ pub unsafe fn dc_is_contact_in_chat( } // Should return Result -pub fn dc_unarchive_chat(context: &Context, chat_id: u32) { +pub fn unarchive(context: &Context, chat_id: u32) -> Result<(), Error> { sql::execute( context, &context.sql, "UPDATE chats SET archived=0 WHERE id=?", params![chat_id as i32], ) - .ok(); } -pub unsafe fn dc_send_msg<'a>(context: &'a Context, chat_id: u32, msg: *mut dc_msg_t<'a>) -> u32 { - if msg.is_null() { - return 0; - } +/// Send a message defined by a dc_msg_t object to a chat. +/// +/// Sends the event #DC_EVENT_MSGS_CHANGED on succcess. +/// However, this does not imply, the message really reached the recipient - +/// sending may be delayed eg. due to network problems. However, from your +/// view, you're done with the message. Sooner or later it will find its way. +pub unsafe fn send_msg<'a>( + context: &'a Context, + chat_id: u32, + msg: *mut dc_msg_t<'a>, +) -> Result { + ensure!(!msg.is_null(), "Invalid message"); + if (*msg).state != DC_STATE_OUT_PREPARING { - if 0 == prepare_msg_common(context, chat_id, msg) { - return 0; - } + // automatically prepare normal messages + prepare_msg_common(context, chat_id, msg)?; } else { - if chat_id != 0 && chat_id != (*msg).chat_id { - return 0; - } + // update message state of separately prepared messages + ensure!( + chat_id == 0 || chat_id == (*msg).chat_id, + "Inconsistent chat ID" + ); dc_update_msg_state(context, (*msg).id, DC_STATE_OUT_PENDING); } - if 0 == dc_job_send_msg(context, (*msg).id) { - return 0; - } + + ensure!( + dc_job_send_msg(context, (*msg).id) != 0, + "Failed to initiate send job" + ); + context.call_cb( Event::MSGS_CHANGED, (*msg).chat_id as uintptr_t, @@ -892,7 +951,8 @@ pub unsafe fn dc_send_msg<'a>(context: &'a Context, chat_id: u32, msg: *mut dc_m } else { let copy = dc_get_msg(context, id as u32); if !copy.is_null() { - dc_send_msg(context, 0, copy); + // TODO: handle cleanup and return early instead + send_msg(context, 0, copy).unwrap(); } dc_msg_unref(copy); } @@ -902,67 +962,61 @@ pub unsafe fn dc_send_msg<'a>(context: &'a Context, chat_id: u32, msg: *mut dc_m } } - (*msg).id + Ok((*msg).id) } -pub unsafe fn dc_send_text_msg(context: &Context, chat_id: u32, text_to_send: String) -> u32 { - if chat_id <= 9 { - warn!( - context, - 0, "dc_send_text_msg: bad chat_id = {} <= 9", chat_id - ); - return 0; - } +pub unsafe fn send_text_msg( + context: &Context, + chat_id: u32, + text_to_send: String, +) -> Result { + ensure!( + chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, + "bad chat_id = {} <= 9", + chat_id + ); let mut msg = dc_msg_new(context, Viewtype::Text); (*msg).text = Some(text_to_send); - let ret = dc_send_msg(context, chat_id, msg); - dc_msg_unref(msg); - ret + send_msg(context, chat_id, msg) } -pub unsafe fn dc_set_draft(context: &Context, chat_id: u32, msg: *mut dc_msg_t) { - if chat_id <= 9i32 as libc::c_uint { +pub unsafe fn set_draft(context: &Context, chat_id: u32, msg: *mut dc_msg_t) { + if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { return; } - if 0 != set_draft_raw(context, chat_id, msg) { + if set_draft_raw(context, chat_id, msg) { context.call_cb(Event::MSGS_CHANGED, chat_id as uintptr_t, 0i32 as uintptr_t); }; } -// TODO should return bool /rtn #[allow(non_snake_case)] -unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> libc::c_int { +unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> bool { let mut OK_TO_CONTINUE = true; // similar to as dc_set_draft() but does not emit an event let prev_draft_msg_id: u32; - let mut sth_changed: libc::c_int = 0i32; + let mut sth_changed = false; + prev_draft_msg_id = get_draft_msg_id(context, chat_id); if 0 != prev_draft_msg_id { dc_delete_msg_from_db(context, prev_draft_msg_id); - sth_changed = 1i32 + sth_changed = true; } // save new draft if !msg.is_null() { if (*msg).type_0 == Viewtype::Text { OK_TO_CONTINUE = (*msg).text.as_ref().map_or(false, |s| !s.is_empty()); } else if msgtype_has_file((*msg).type_0) { - let mut pathNfilename = (*msg) - .param - .get(Param::File) - .map(|s| s.strdup()) - .unwrap_or_else(|| std::ptr::null_mut()); - if pathNfilename.is_null() { - OK_TO_CONTINUE = false; - } else if 0 != dc_msg_is_increation(msg) && !dc_is_blobdir_path(context, pathNfilename) - { - OK_TO_CONTINUE = false; - } else if !dc_make_rel_and_copy(context, &mut pathNfilename) { - OK_TO_CONTINUE = false; - } else { - (*msg).param.set(Param::File, as_str(pathNfilename)); + if let Some(path_filename) = (*msg).param.get(Param::File) { + let mut path_filename = path_filename.to_string(); + if 0 != dc_msg_is_increation(msg) && !dc_is_blobdir_path(context, &path_filename) { + OK_TO_CONTINUE = false; + } else if !dc_make_rel_and_copy(context, &mut path_filename) { + OK_TO_CONTINUE = false; + } else { + (*msg).param.set(Param::File, path_filename); + } } - free(pathNfilename as *mut _); } else { OK_TO_CONTINUE = false; } @@ -985,7 +1039,7 @@ unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> ) .is_ok() { - sth_changed = 1; + sth_changed = true; } } } @@ -994,30 +1048,26 @@ unsafe fn set_draft_raw(context: &Context, chat_id: u32, msg: *mut dc_msg_t) -> } fn get_draft_msg_id(context: &Context, chat_id: u32) -> u32 { - let draft_msg_id: i32 = context + context .sql - .query_row_col( + .query_row_col::<_, i32>( context, "SELECT id FROM msgs WHERE chat_id=? AND state=?;", params![chat_id as i32, DC_STATE_OUT_DRAFT], 0, ) - .unwrap_or_default(); - - draft_msg_id as u32 + .unwrap_or_default() as u32 } -pub unsafe fn dc_get_draft(context: &Context, chat_id: u32) -> *mut dc_msg_t { - let draft_msg_id: u32; - let draft_msg: *mut dc_msg_t; - if chat_id <= 9i32 as libc::c_uint { - return 0 as *mut dc_msg_t; - } - draft_msg_id = get_draft_msg_id(context, chat_id); - if draft_msg_id == 0i32 as libc::c_uint { +pub unsafe fn get_draft(context: &Context, chat_id: u32) -> *mut dc_msg_t { + if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { return ptr::null_mut(); } - draft_msg = dc_msg_new_untyped(context); + let draft_msg_id = get_draft_msg_id(context, chat_id); + if draft_msg_id == 0 { + return ptr::null_mut(); + } + let draft_msg = dc_msg_new_untyped(context); if !dc_msg_load_from_db(draft_msg, context, draft_msg_id) { dc_msg_unref(draft_msg); return ptr::null_mut(); @@ -1026,7 +1076,7 @@ pub unsafe fn dc_get_draft(context: &Context, chat_id: u32) -> *mut dc_msg_t { draft_msg } -pub fn dc_get_chat_msgs( +pub fn get_chat_msgs( context: &Context, chat_id: u32, flags: u32, @@ -1106,22 +1156,22 @@ pub fn dc_get_chat_msgs( } } -pub fn dc_get_msg_cnt(context: &Context, chat_id: u32) -> libc::c_int { +pub fn get_msg_cnt(context: &Context, chat_id: u32) -> usize { context .sql - .query_row_col( + .query_row_col::<_, i32>( context, "SELECT COUNT(*) FROM msgs WHERE chat_id=?;", params![chat_id as i32], 0, ) - .unwrap_or_default() + .unwrap_or_default() as usize } -pub fn dc_get_fresh_msg_cnt(context: &Context, chat_id: u32) -> libc::c_int { +pub fn get_fresh_msg_cnt(context: &Context, chat_id: u32) -> usize { context .sql - .query_row_col( + .query_row_col::<_, i32>( context, "SELECT COUNT(*) FROM msgs \ WHERE state=10 \ @@ -1130,66 +1180,53 @@ pub fn dc_get_fresh_msg_cnt(context: &Context, chat_id: u32) -> libc::c_int { params![chat_id as i32], 0, ) - .unwrap_or_default() + .unwrap_or_default() as usize } -pub fn dc_marknoticed_chat(context: &Context, chat_id: u32) -> bool { - if !context - .sql - .exists( - "SELECT id FROM msgs WHERE chat_id=? AND state=?;", - params![chat_id as i32, DC_STATE_IN_FRESH], - ) - .unwrap_or_default() - { - return false; +pub fn marknoticed_chat(context: &Context, chat_id: u32) -> Result<(), Error> { + if !context.sql.exists( + "SELECT id FROM msgs WHERE chat_id=? AND state=?;", + params![chat_id as i32, DC_STATE_IN_FRESH], + )? { + return Ok(()); } - if sql::execute( + + sql::execute( context, &context.sql, "UPDATE msgs \ SET state=13 WHERE chat_id=? AND state=10;", params![chat_id as i32], - ) - .is_err() - { - return false; - } + )?; + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); - true + + Ok(()) } -pub fn dc_marknoticed_all_chats(context: &Context) -> bool { - if !context - .sql - .exists( - "SELECT id FROM msgs \ - WHERE state=10;", - params![], - ) - .unwrap_or_default() - { - return false; +pub fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { + if !context.sql.exists( + "SELECT id FROM msgs \ + WHERE state=10;", + params![], + )? { + return Ok(()); } - if sql::execute( + sql::execute( context, &context.sql, "UPDATE msgs \ SET state=13 WHERE state=10;", params![], - ) - .is_err() - { - return false; - } + )?; context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); - true + Ok(()) } -pub fn dc_get_chat_media( +pub fn get_chat_media( context: &Context, chat_id: u32, msg_type: Viewtype, @@ -1222,7 +1259,7 @@ pub fn dc_get_chat_media( ).unwrap_or_else(|_| std::ptr::null_mut()) } -pub unsafe fn dc_get_next_media( +pub unsafe fn get_next_media( context: &Context, curr_msg_id: u32, dir: libc::c_int, @@ -1237,7 +1274,7 @@ pub unsafe fn dc_get_next_media( let cnt: libc::c_int; if dc_msg_load_from_db(msg, context, curr_msg_id) { - list = dc_get_chat_media( + list = get_chat_media( context, (*msg).chat_id, if msg_type != Viewtype::Unknown { @@ -1277,97 +1314,80 @@ pub unsafe fn dc_get_next_media( ret_msg_id } -pub fn dc_archive_chat(context: &Context, chat_id: u32, archive: libc::c_int) -> bool { - if chat_id <= 9 || archive != 0 && archive != 1 { - return true; - } - if 0 != archive { - if sql::execute( +pub fn archive(context: &Context, chat_id: u32, archive: bool) -> Result<(), Error> { + ensure!( + chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, + "bad chat_id = {} <= 9", + chat_id + ); + + if archive { + sql::execute( context, &context.sql, "UPDATE msgs SET state=? WHERE chat_id=? AND state=?;", params![DC_STATE_IN_NOTICED, chat_id as i32, DC_STATE_IN_FRESH], - ) - .is_err() - { - return false; - } + )?; } - if sql::execute( + + sql::execute( context, &context.sql, "UPDATE chats SET archived=? WHERE id=?;", params![archive, chat_id as i32], - ) - .is_err() - { - return false; - } + )?; + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); - true + Ok(()) } -pub fn dc_delete_chat(context: &Context, chat_id: u32) -> bool { +pub fn delete(context: &Context, chat_id: u32) -> Result<(), Error> { + ensure!( + chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, + "bad chat_id = {} <= 9", + chat_id + ); /* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */ - if chat_id <= 9 { - return false; - } - if dc_chat_load_from_db(context, chat_id).is_err() { - return false; - } - - if sql::execute( + let _chat = Chat::load_from_db(context, chat_id)?; + sql::execute( context, &context.sql, "DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);", params![chat_id as i32], - ) - .is_err() - { - return false; - } - if sql::execute( + )?; + + sql::execute( context, &context.sql, "DELETE FROM msgs WHERE chat_id=?;", params![chat_id as i32], - ) - .is_err() - { - return false; - } - if sql::execute( + )?; + + sql::execute( context, &context.sql, "DELETE FROM chats_contacts WHERE chat_id=?;", params![chat_id as i32], - ) - .is_err() - { - return false; - } - if sql::execute( + )?; + + sql::execute( context, &context.sql, "DELETE FROM chats WHERE id=?;", params![chat_id as i32], - ) - .is_err() - { - return false; - } + )?; context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); dc_job_kill_action(context, 105); unsafe { dc_job_add(context, 105, 0, Params::new(), 10) }; - true + Ok(()) } -pub fn dc_get_chat_contacts(context: &Context, chat_id: u32) -> Vec { +pub fn get_chat_contacts(context: &Context, chat_id: u32) -> Vec { /* Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a groupchat but the chats stays visible, moreover, this makes displaying lists easier) */ @@ -1391,62 +1411,56 @@ pub fn dc_get_chat_contacts(context: &Context, chat_id: u32) -> Vec { .unwrap_or_default() } -pub fn dc_get_chat(context: &Context, chat_id: u32) -> Result { - dc_chat_load_from_db(context, chat_id) -} - -// handle group chats -pub unsafe fn dc_create_group_chat( +pub unsafe fn create_group_chat( context: &Context, - verified: libc::c_int, + verified: VerifiedStatus, chat_name: *const libc::c_char, -) -> u32 { - let mut chat_id = 0; +) -> Result { + ensure!( + !chat_name.is_null() && *chat_name.offset(0) as libc::c_int != 0, + "Invalid chat name" + ); - if chat_name.is_null() || *chat_name.offset(0) as libc::c_int == 0 { - return 0; - } let draft_txt = CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, as_str(chat_name))) .unwrap(); let grpid = dc_create_id(); - if sql::execute( + + sql::execute( context, &context.sql, "INSERT INTO chats (type, name, grpid, param) VALUES(?, ?, ?, \'U=1\');", params![ - if verified != 0 { 130 } else { 120 }, + if verified != VerifiedStatus::Unverified { + Chattype::VerifiedGroup + } else { + Chattype::Group + }, as_str(chat_name), grpid ], - ) - .is_ok() - { - chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid); - if chat_id != 0 { - if 0 != dc_add_to_chat_contacts_table(context, chat_id, 1) { - let draft_msg = dc_msg_new(context, Viewtype::Text); - dc_msg_set_text(draft_msg, draft_txt.as_ptr()); - set_draft_raw(context, chat_id, draft_msg); - dc_msg_unref(draft_msg); - } + )?; + + let chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid); + + if chat_id != 0 { + if 0 != add_to_chat_contacts_table(context, chat_id, 1) { + let draft_msg = dc_msg_new(context, Viewtype::Text); + dc_msg_set_text(draft_msg, draft_txt.as_ptr()); + set_draft_raw(context, chat_id, draft_msg); + dc_msg_unref(draft_msg); } - } - if 0 != chat_id { + context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); } - chat_id + Ok(chat_id) } /* you MUST NOT modify this or the following strings */ // Context functions to work with chats // TODO should return bool /rtn -pub fn dc_add_to_chat_contacts_table( - context: &Context, - chat_id: u32, - contact_id: u32, -) -> libc::c_int { +pub fn add_to_chat_contacts_table(context: &Context, chat_id: u32, contact_id: u32) -> libc::c_int { // add a contact to a chat; the function does not check the type or if any of the record exist or are already // added to the chat! sql::execute( @@ -1458,17 +1472,13 @@ pub fn dc_add_to_chat_contacts_table( .is_ok() as libc::c_int } -pub unsafe fn dc_add_contact_to_chat( - context: &Context, - chat_id: u32, - contact_id: u32, -) -> libc::c_int { - dc_add_contact_to_chat_ex(context, chat_id, contact_id, 0) +pub unsafe fn add_contact_to_chat(context: &Context, chat_id: u32, contact_id: u32) -> libc::c_int { + add_contact_to_chat_ex(context, chat_id, contact_id, 0) } // TODO should return bool /rtn #[allow(non_snake_case)] -pub unsafe fn dc_add_contact_to_chat_ex( +pub unsafe fn add_contact_to_chat_ex( context: &Context, chat_id: u32, contact_id: u32, @@ -1483,11 +1493,11 @@ pub unsafe fn dc_add_contact_to_chat_ex( } let mut msg = dc_msg_new_untyped(context); - dc_reset_gossiped_timestamp(context, chat_id); + reset_gossiped_timestamp(context, chat_id); let contact = contact.unwrap(); /*this also makes sure, not contacts are added to special or normal chats*/ - let chat = dc_chat_load_from_db(context, chat_id); + let chat = Chat::load_from_db(context, chat_id); if !(0 == real_group_exists(context, chat_id) || !Contact::real_exists_by_id(context, contact_id) @@ -1496,7 +1506,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( { let mut chat = chat.unwrap(); - if !(dc_is_contact_in_chat(context, chat_id, 1 as u32) == 1) { + if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1507,7 +1517,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( /* 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); + chat.update_param().unwrap(); } let self_addr = context .sql @@ -1517,7 +1527,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( // 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 != is_contact_in_chat(context, chat_id, contact_id) { if 0 == flags & 0x1 { success = 1; OK_TO_CONTINUE = false; @@ -1534,7 +1544,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( } } if OK_TO_CONTINUE { - if 0 == dc_add_to_chat_contacts_table(context, chat_id, contact_id) { + if 0 == add_to_chat_contacts_table(context, chat_id, contact_id) { OK_TO_CONTINUE = false; } } @@ -1551,7 +1561,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( (*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); + (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); context.call_cb( Event::MSGS_CHANGED, chat_id as uintptr_t, @@ -1586,12 +1596,12 @@ fn real_group_exists(context: &Context, chat_id: u32) -> libc::c_int { .unwrap_or_default() as libc::c_int } -pub fn dc_reset_gossiped_timestamp(context: &Context, chat_id: u32) { - dc_set_gossiped_timestamp(context, chat_id, 0); +pub fn reset_gossiped_timestamp(context: &Context, chat_id: u32) { + set_gossiped_timestamp(context, chat_id, 0); } // Should return Result -pub fn dc_set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) { +pub fn set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) { if 0 != chat_id { info!( context, @@ -1621,7 +1631,7 @@ pub fn dc_set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64 } // TODO should return bool /rtn -pub unsafe fn dc_remove_contact_from_chat( +pub unsafe fn remove_contact_from_chat( context: &Context, chat_id: u32, contact_id: u32, @@ -1636,11 +1646,11 @@ pub unsafe fn dc_remove_contact_from_chat( /* 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); + let chat = 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) { + if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1653,7 +1663,7 @@ pub unsafe fn dc_remove_contact_from_chat( 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); + set_group_explicitly_left(context, chat.grpid); (*msg).text = Some(context.stock_system_msg( StockMessage::MsgGroupLeft, "", @@ -1670,7 +1680,7 @@ pub unsafe fn dc_remove_contact_from_chat( } (*msg).param.set_int(Param::Cmd, 5); (*msg).param.set(Param::Arg, contact.get_addr()); - (*msg).id = dc_send_msg(context, chat_id, msg); + (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); context.call_cb( Event::MSGS_CHANGED, chat_id as uintptr_t, @@ -1698,8 +1708,8 @@ pub unsafe fn dc_remove_contact_from_chat( } // Should return Result -fn dc_set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { - if 0 == dc_is_group_explicitly_left(context, grpid) { +fn set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { + if 0 == is_group_explicitly_left(context, grpid) { sql::execute( context, &context.sql, @@ -1711,7 +1721,7 @@ fn dc_set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { } // TODO should return bool /rtn -pub fn dc_is_group_explicitly_left(context: &Context, grpid: *const libc::c_char) -> libc::c_int { +pub fn is_group_explicitly_left(context: &Context, grpid: *const libc::c_char) -> libc::c_int { context .sql .exists( @@ -1722,7 +1732,7 @@ pub fn dc_is_group_explicitly_left(context: &Context, grpid: *const libc::c_char } // TODO should return bool /rtn -pub unsafe fn dc_set_chat_name( +pub unsafe fn set_chat_name( context: &Context, chat_id: u32, new_name: *const libc::c_char, @@ -1735,13 +1745,13 @@ pub unsafe fn dc_set_chat_name( return 0; } - let chat = dc_chat_load_from_db(context, chat_id); + let chat = 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) { + } else if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1774,7 +1784,7 @@ pub unsafe fn dc_set_chat_name( if !chat.name.is_null() { (*msg).param.set(Param::Arg, as_str(chat.name)); } - (*msg).id = dc_send_msg(context, chat_id, msg); + (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); context.call_cb( Event::MSGS_CHANGED, chat_id as uintptr_t, @@ -1798,7 +1808,7 @@ pub unsafe fn dc_set_chat_name( // TODO should return bool /rtn #[allow(non_snake_case)] -pub unsafe fn dc_set_chat_profile_image( +pub unsafe fn set_chat_profile_image( context: &Context, chat_id: u32, new_image: *const libc::c_char, @@ -1811,12 +1821,12 @@ pub unsafe fn dc_set_chat_profile_image( } 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) { - let chat = dc_chat_load_from_db(context, chat_id); + let mut new_image_rel = None; + let chat = 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) { + if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1826,23 +1836,28 @@ pub unsafe fn dc_set_chat_profile_image( } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ if !new_image.is_null() { - new_image_rel = dc_strdup(new_image); - if !dc_make_rel_and_copy(context, &mut new_image_rel) { + let mut img = to_string(new_image); + if !dc_make_rel_and_copy(context, &mut img) { OK_TO_CONTINUE = false; } + new_image_rel = Some(img); } 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(&mut chat)) { + if let Some(ref new_image_rel) = new_image_rel { + chat.param.set(Param::ProfileImage, new_image_rel); + } + if chat.update_param().is_ok() { 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)); + if let Some(ref new_image_rel) = new_image_rel { + (*msg).param.set(Param::Arg, new_image_rel); + } (*msg).type_0 = Viewtype::Text; (*msg).text = Some(context.stock_system_msg( - if !new_image_rel.is_null() { + if new_image_rel.is_some() { StockMessage::MsgGrpImgChanged } else { StockMessage::MsgGrpImgDeleted @@ -1851,7 +1866,7 @@ pub unsafe fn dc_set_chat_profile_image( "", DC_CONTACT_ID_SELF as u32, )); - (*msg).id = dc_send_msg(context, chat_id, msg); + (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); context.call_cb( Event::MSGS_CHANGED, chat_id as uintptr_t, @@ -1870,12 +1885,11 @@ pub unsafe fn dc_set_chat_profile_image( } dc_msg_unref(msg); - free(new_image_rel as *mut libc::c_void); success } -pub unsafe fn dc_forward_msgs( +pub unsafe fn forward_msgs( context: &Context, msg_ids: *const u32, msg_cnt: libc::c_int, @@ -1889,8 +1903,8 @@ pub unsafe fn dc_forward_msgs( let mut created_db_entries = Vec::new(); let mut curr_timestamp: i64; - dc_unarchive_chat(context, chat_id); - if let Ok(mut chat) = dc_chat_load_from_db(context, chat_id) { + unarchive(context, chat_id).unwrap(); + if let Ok(mut chat) = 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() @@ -1930,7 +1944,9 @@ 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, &mut chat, msg, fresh9); + new_msg_id = chat + .prepare_msg_raw(context, msg, fresh9) + .unwrap_or_default(); let save_param = (*msg).param.clone(); (*msg).param = original_param; (*msg).id = src_msg_id as u32; @@ -1950,7 +1966,9 @@ 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, &mut chat, msg, fresh10); + new_msg_id = chat + .prepare_msg_raw(context, msg, fresh10) + .unwrap_or_default(); dc_job_send_msg(context, new_msg_id); } created_db_entries.push(chat_id); @@ -1968,60 +1986,7 @@ pub unsafe fn dc_forward_msgs( dc_msg_unref(msg); } -pub unsafe fn dc_chat_get_id(chat: &Chat) -> u32 { - chat.id -} - -pub unsafe fn dc_chat_get_type(chat: &Chat) -> Chattype { - chat.typ -} - -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: &Chat) -> *mut libc::c_char { - /* returns either the address or the number of chat members */ - - let mut ret: *mut libc::c_char = std::ptr::null_mut(); - 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 - .context - .sql - .query_row_col( - 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], - 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 { - let cnt = dc_get_chat_contact_cnt(chat.context, chat.id); - ret = chat - .context - .stock_string_repl_int(StockMessage::Member, cnt) - .strdup(); - } - } - if !ret.is_null() { - ret - } else { - dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char) - } -} - -pub fn dc_get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int { +pub fn get_chat_contact_cnt(context: &Context, chat_id: u32) -> libc::c_int { context .sql .query_row_col( @@ -2033,70 +1998,7 @@ 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: &Chat) -> *mut libc::c_char { - let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; - - 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(); - } - } - } - } - - free(image_rel as *mut libc::c_void); - - image_abs -} - -pub unsafe fn dc_chat_get_color(chat: &Chat) -> u32 { - let mut color: u32 = 0i32 as u32; - - 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 - } - - color -} - -// TODO should return bool /rtn -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: &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: &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: &Chat) -> bool { - chat.is_sending_locations -} - -pub fn dc_get_chat_cnt(context: &Context) -> usize { +pub fn get_chat_cnt(context: &Context) -> usize { if context.sql.is_open() { /* no database, no chats - this is no error (needed eg. for information) */ context @@ -2113,7 +2015,7 @@ pub fn dc_get_chat_cnt(context: &Context) -> usize { } } -pub unsafe fn dc_get_chat_id_by_grpid( +pub unsafe fn get_chat_id_by_grpid( context: &Context, grpid: *const libc::c_char, ret_blocked: Option<&mut Blocked>, @@ -2144,7 +2046,7 @@ pub unsafe fn dc_get_chat_id_by_grpid( .unwrap_or_default() } -pub fn dc_add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char) { +pub fn add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char) { if text.is_null() { return; } @@ -2161,7 +2063,7 @@ pub fn dc_add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_c chat_id as i32, 2, 2, - unsafe {dc_create_smeared_timestamp(context)}, + dc_create_smeared_timestamp(context), Viewtype::Text, DC_STATE_IN_NOTICED, as_str(text), @@ -2186,3 +2088,12 @@ pub fn dc_add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_c msg_id as uintptr_t, ); } + +impl<'a> Drop for Chat<'a> { + fn drop(&mut self) { + unsafe { + free(self.name.cast()); + free(self.grpid.cast()); + } + } +} diff --git a/src/chatlist.rs b/src/chatlist.rs index b8e6ac2a9..4fc1c308c 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -265,7 +265,7 @@ impl<'a> Chatlist<'a> { let chat = if let Some(chat) = chat { chat } else { - if let Ok(chat) = dc_get_chat(self.context, self.ids[index].0) { + if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) { chat_loaded = chat; &chat_loaded } else { diff --git a/src/contact.rs b/src/contact.rs index 62024bcd2..d205baace 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -138,7 +138,7 @@ pub enum Modifier { Created, } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)] #[repr(u8)] pub enum VerifiedStatus { /// Contact is not verified. diff --git a/src/context.rs b/src/context.rs index 8f2b8e87b..269046201 100644 --- a/src/context.rs +++ b/src/context.rs @@ -338,7 +338,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { let l = dc_loginparam_read(context, &context.sql, ""); let l2 = dc_loginparam_read(context, &context.sql, "configured_"); let displayname = context.sql.get_config(context, "displayname"); - let chats = dc_get_chat_cnt(context) as usize; + let chats = get_chat_cnt(context) as usize; let real_msgs = dc_get_real_msg_cnt(context) as usize; let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize; let contacts = Contact::get_real_cnt(context) as usize; diff --git a/src/dc_imex.rs b/src/dc_imex.rs index aff4dec8d..f15cf2ec9 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -6,7 +6,7 @@ use mmime::mmapstring::*; use mmime::other::*; use rand::{thread_rng, Rng}; -use crate::chat::*; +use crate::chat; use crate::config::Config; use crate::constants::*; use crate::context::Context; @@ -138,8 +138,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { setup_file_content_c.as_bytes().len(), )) { - let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t); - if !(chat_id == 0i32 as libc::c_uint) { + if let Ok(chat_id) = chat::create_by_contact_id(context, 1) { msg = dc_msg_new_untyped(context); (*msg).type_0 = Viewtype::File; (*msg).param.set(Param::File, as_str(setup_file_name)); @@ -157,8 +156,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { .unwrap() .shall_stop_ongoing { - let msg_id = dc_send_msg(context, chat_id, msg); - if msg_id != 0 { + if let Ok(msg_id) = chat::send_msg(context, chat_id, msg) { dc_msg_unref(msg); msg = ptr::null_mut(); info!(context, 0, "Wait for setup message being sent ...",); @@ -970,7 +968,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> free(suffix as *mut libc::c_void); let name_f = entry.file_name(); let name_c = name_f.to_c_string().unwrap(); - suffix = dc_get_filesuffix_lc(name_c.as_ptr()); + suffix = dc_get_filesuffix_lc(name_f.to_string_lossy()); if suffix.is_null() || strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0 { diff --git a/src/dc_job.rs b/src/dc_job.rs index 2a26142e0..fd17c285e 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -6,7 +6,7 @@ use std::time::Duration; use rand::{thread_rng, Rng}; -use crate::chat::*; +use crate::chat; use crate::constants::*; use crate::context::Context; use crate::dc_configure::*; @@ -1035,7 +1035,7 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in ); } else { // no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed() - if msgtype_has_file((*mimefactory.msg).type_0) { + if chat::msgtype_has_file((*mimefactory.msg).type_0) { if let Some(pathNfilename) = (*mimefactory.msg).param.get(Param::File) { if ((*mimefactory.msg).type_0 == Viewtype::Image || (*mimefactory.msg).type_0 == Viewtype::Gif) @@ -1096,7 +1096,7 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in ); } if 0 != mimefactory.out_gossiped { - dc_set_gossiped_timestamp(context, (*mimefactory.msg).chat_id, time()); + chat::set_gossiped_timestamp(context, (*mimefactory.msg).chat_id, time()); } if 0 != mimefactory.out_last_added_location_id { dc_set_kml_sent_timestamp(context, (*mimefactory.msg).chat_id, time()); diff --git a/src/dc_location.rs b/src/dc_location.rs index bb3804b82..9dd402960 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -3,7 +3,7 @@ use std::ffi::CString; use quick_xml; use quick_xml::events::{BytesEnd, BytesStart, BytesText}; -use crate::chat::*; +use crate::chat; use crate::constants::Event; use crate::constants::*; use crate::context::*; @@ -104,7 +104,7 @@ pub unsafe fn dc_send_locations_to_chat( (*msg).text = Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)); (*msg).param.set_int(Param::Cmd, 8); - dc_send_msg(context, chat_id, msg); + chat::send_msg(context, chat_id, msg).unwrap(); } else if 0 == seconds && is_sending_locations_before { let stock_str = CString::new(context.stock_system_msg( StockMessage::MsgLocationDisabled, @@ -113,7 +113,7 @@ pub unsafe fn dc_send_locations_to_chat( 0, )) .unwrap(); - dc_add_device_msg(context, chat_id, stock_str.as_ptr()); + chat::add_device_msg(context, chat_id, stock_str.as_ptr()); } context.call_cb( Event::CHAT_MODIFIED, @@ -697,7 +697,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu let mut msg = dc_msg_new(context, Viewtype::Text); (*msg).hidden = 1; (*msg).param.set_int(Param::Cmd, 9); - dc_send_msg(context, chat_id as u32, msg); + // TODO: handle cleanup on error + chat::send_msg(context, chat_id as u32, msg).unwrap(); dc_msg_unref(msg); } Ok(()) @@ -736,7 +737,7 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut params![chat_id as i32], ).is_ok() { let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap(); - dc_add_device_msg(context, chat_id, stock_str.as_ptr()); + chat::add_device_msg(context, chat_id, stock_str.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, chat_id as usize, diff --git a/src/dc_lot.rs b/src/dc_lot.rs index b740804a9..134a6bcf6 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -135,7 +135,7 @@ pub unsafe fn dc_lot_fill( (*lot).text1 = context.stock_str(StockMessage::Draft).strdup(); (*lot).text1_meaning = 1i32 } else if (*msg).from_id == 1i32 as libc::c_uint { - if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) { + if 0 != dc_msg_is_info(msg) || chat.is_self_talk() { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else { diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 013eed860..6300e87cb 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -10,7 +10,7 @@ use mmime::mailmime_write_mem::*; use mmime::mmapstring::*; use mmime::other::*; -use crate::chat::*; +use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::{dc_get_version_str, Context}; @@ -112,14 +112,14 @@ pub unsafe fn dc_mimefactory_load_msg( (*factory).msg = dc_msg_new_untyped(context); 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) { + if let Ok(chat) = Chat::load_from_db(context, (*(*factory).msg).chat_id) { (*factory).chat = Some(chat); let chat = (*factory).chat.as_ref().unwrap(); load_from(factory); (*factory).req_mdn = 0; - if 0 != dc_chat_is_self_talk(chat) { + if chat.is_self_talk() { clist_insert_after( (*factory).recipients_names, (*(*factory).recipients_names).last, @@ -830,7 +830,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: free(placeholdertext as *mut libc::c_void); /* add attachment part */ - if msgtype_has_file((*msg).type_0) { + if chat::msgtype_has_file((*msg).type_0) { if !is_file_size_okay(msg) { let error: *mut libc::c_char = dc_mprintf( b"Message exceeds the recommended %i MB.\x00" as *const u8 @@ -1146,22 +1146,20 @@ unsafe fn build_body_file( let mime_fields: *mut mailmime_fields; let mut mime_sub: *mut mailmime = 0 as *mut mailmime; let content: *mut mailmime_content; - let pathNfilename = (*msg) - .param - .get(Param::File) - .map(|s| s.strdup()) - .unwrap_or_else(|| std::ptr::null_mut()); + let path_filename = (*msg).param.get(Param::File); + let mut mimetype = (*msg) .param .get(Param::MimeType) .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); - let suffix = dc_get_filesuffix_lc(pathNfilename); let mut filename_to_send = 0 as *mut libc::c_char; let mut filename_encoded = 0 as *mut libc::c_char; - if !pathNfilename.is_null() { + if let Some(ref path_filename) = path_filename { + let suffix = dc_get_filesuffix_lc(path_filename); + if (*msg).type_0 == Viewtype::Voice { let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0); @@ -1175,7 +1173,7 @@ unsafe fn build_body_file( .to_string(); filename_to_send = res.strdup(); } else if (*msg).type_0 == Viewtype::Audio { - filename_to_send = dc_get_filename(pathNfilename) + filename_to_send = dc_get_filename(path_filename) } else if (*msg).type_0 == Viewtype::Image || (*msg).type_0 == Viewtype::Gif { if base_name.is_null() { base_name = b"image\x00" as *const u8 as *const libc::c_char @@ -1199,7 +1197,7 @@ unsafe fn build_body_file( }, ) } else { - filename_to_send = dc_get_filename(pathNfilename) + filename_to_send = dc_get_filename(path_filename) } if mimetype.is_null() { if suffix.is_null() { @@ -1292,17 +1290,16 @@ unsafe fn build_body_file( ) as *mut libc::c_void, ); mime_sub = mailmime_new_empty(content, mime_fields); - mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, pathNfilename)); + mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, path_filename)); if !ret_file_name_as_sent.is_null() { *ret_file_name_as_sent = dc_strdup(filename_to_send) } } } - free(pathNfilename as *mut libc::c_void); + free(mimetype as *mut libc::c_void); free(filename_to_send as *mut libc::c_void); free(filename_encoded as *mut libc::c_void); - free(suffix as *mut libc::c_void); mime_sub } diff --git a/src/dc_msg.rs b/src/dc_msg.rs index c263ee82d..20713f170 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -1,7 +1,7 @@ use std::ffi::CString; use std::path::Path; -use crate::chat::*; +use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::*; @@ -309,8 +309,7 @@ pub unsafe fn dc_msg_get_file(msg: *const dc_msg_t) -> *mut libc::c_char { if !msg.is_null() { if let Some(file_rel) = (*msg).param.get(Param::File) { - let file_rel_c = CString::yolo(file_rel); - file_abs = dc_get_abs_path((*msg).context, file_rel_c.as_ptr()); + file_abs = dc_get_abs_path((*msg).context, file_rel); } } if !file_abs.is_null() { @@ -681,8 +680,7 @@ pub unsafe fn dc_msg_get_filename(msg: *const dc_msg_t) -> *mut libc::c_char { if !msg.is_null() { if let Some(file) = (*msg).param.get(Param::File) { - let file_c = CString::yolo(file); - ret = dc_get_filename(file_c.as_ptr()); + ret = dc_get_filename(file); } } if !ret.is_null() { @@ -757,7 +755,7 @@ pub unsafe fn dc_msg_get_summary<'a>( let chat = if let Some(chat) = chat { chat } else { - if let Ok(chat) = dc_get_chat((*msg).context, (*msg).chat_id) { + if let Ok(chat) = Chat::load_from_db((*msg).context, (*msg).chat_id) { chat_loaded = chat; &chat_loaded } else { @@ -921,7 +919,7 @@ pub unsafe fn dc_msg_is_increation(msg: *const dc_msg_t) -> libc::c_int { return 0; } - if msgtype_has_file((*msg).type_0) && (*msg).state == DC_STATE_OUT_PREPARING { + if chat::msgtype_has_file((*msg).type_0) && (*msg).state == DC_STATE_OUT_PREPARING { 1 } else { 0 @@ -1252,7 +1250,7 @@ pub unsafe fn dc_mdn_from_ext( (S=Sender, R=Recipient) */ // for rounding, SELF is already included! - let soll_cnt = (dc_get_chat_contact_cnt(context, *ret_chat_id) + 1) / 2; + let soll_cnt = (chat::get_chat_contact_cnt(context, *ret_chat_id) + 1) / 2; if ist_cnt >= soll_cnt { dc_update_msg_state(context, *ret_msg_id, DC_STATE_OUT_MDN_RCVD); read_by_all = 1; @@ -1392,14 +1390,12 @@ mod tests { let res = ctx.set_config(Config::ConfiguredAddr, Some("self@example.com")); assert!(res.is_ok()); - let chat = dc_create_chat_by_contact_id(ctx, contact); - assert!(chat != 0, "failed to create chat"); + let chat = chat::create_by_contact_id(ctx, contact).unwrap(); let msg = dc_msg_new(ctx, Viewtype::Text); assert!(!msg.is_null()); - let msg_id = dc_prepare_msg(ctx, chat, msg); - assert!(msg_id != 0); + let msg_id = chat::prepare_msg(ctx, chat, msg).unwrap(); let msg2 = dc_get_msg(ctx, msg_id); assert!(!msg2.is_null()); diff --git a/src/dc_qr.rs b/src/dc_qr.rs index b5d46e8cc..dbcff7a2e 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -1,6 +1,6 @@ use percent_encoding::percent_decode_str; -use crate::chat::*; +use crate::chat; use crate::constants::Blocked; use crate::contact::*; use crate::context::Context; @@ -231,13 +231,13 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc Contact::add_or_lookup(context, "", addr, Origin::UnhandledQrScan) .map(|(id, _)| id) .unwrap_or_default(); - dc_create_or_lookup_nchat_by_contact_id( + let (id, _) = chat::create_or_lookup_by_contact_id( context, (*qr_parsed).id, Blocked::Deaddrop, - &mut chat_id, - None, - ); + ) + .unwrap_or_default(); + chat_id = id; device_msg = dc_mprintf( b"%s verified.\x00" as *const u8 as *const libc::c_char, peerstate.addr, @@ -288,7 +288,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc (*qr_parsed).text1 = dc_strdup(qr) } if !device_msg.is_null() { - dc_add_device_msg(context, chat_id, device_msg); + chat::add_device_msg(context, chat_id, device_msg); } } } diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index de26cef40..461cf9bf7 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -10,7 +10,7 @@ use mmime::mmapstring::*; use mmime::other::*; use sha2::{Digest, Sha256}; -use crate::chat::*; +use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::Context; @@ -408,15 +408,9 @@ unsafe fn add_parts( state = DC_STATE_IN_SEEN; } } - let mut test_normal_chat_id = 0; - let mut test_normal_chat_id_blocked = Blocked::Not; - dc_lookup_real_nchat_by_contact_id( - context, - *from_id, - &mut test_normal_chat_id, - &mut test_normal_chat_id_blocked, - ); + let (test_normal_chat_id, test_normal_chat_id_blocked) = + chat::lookup_by_contact_id(context, *from_id).unwrap_or_default(); // get the chat_id - a chat_id here is no indicator that the chat is displayed in the normal list, // it might also be blocked and displayed in the deaddrop as a result @@ -444,7 +438,7 @@ unsafe fn add_parts( &mut chat_id_blocked, ); if 0 != *chat_id && Blocked::Not != chat_id_blocked && create_blocked == Blocked::Not { - dc_unblock_chat(context, *chat_id); + chat::unblock(context, *chat_id); chat_id_blocked = Blocked::Not; } } @@ -472,17 +466,15 @@ unsafe fn add_parts( *chat_id = test_normal_chat_id; chat_id_blocked = test_normal_chat_id_blocked; } else if 0 != allow_creation { - dc_create_or_lookup_nchat_by_contact_id( - context, - *from_id, - create_blocked, - chat_id, - Some(&mut chat_id_blocked), - ); + let (id, bl) = + chat::create_or_lookup_by_contact_id(context, *from_id, create_blocked) + .unwrap_or_default(); + *chat_id = id; + chat_id_blocked = bl; } if 0 != *chat_id && Blocked::Not != chat_id_blocked { if Blocked::Not == create_blocked { - dc_unblock_chat(context, *chat_id); + chat::unblock(context, *chat_id); chat_id_blocked = Blocked::Not; } else if 0 != dc_is_reply_to_known_message(context, mime_parser) { // we do not want any chat to be created implicitly. Because of the origin-scale-up, @@ -532,7 +524,7 @@ unsafe fn add_parts( &mut chat_id_blocked, ); if 0 != *chat_id && Blocked::Not != chat_id_blocked { - dc_unblock_chat(context, *chat_id); + chat::unblock(context, *chat_id); chat_id_blocked = Blocked::Not; } } @@ -542,18 +534,17 @@ unsafe fn add_parts( } else { Blocked::Deaddrop }; - dc_create_or_lookup_nchat_by_contact_id( - context, - *to_id, - create_blocked, - chat_id, - Some(&mut chat_id_blocked), - ); + let (id, bl) = + chat::create_or_lookup_by_contact_id(context, *to_id, create_blocked) + .unwrap_or_default(); + *chat_id = id; + chat_id_blocked = bl; + if 0 != *chat_id && Blocked::Not != chat_id_blocked && Blocked::Not == create_blocked { - dc_unblock_chat(context, *chat_id); + chat::unblock(context, *chat_id); chat_id_blocked = Blocked::Not; } } @@ -562,15 +553,13 @@ unsafe fn add_parts( if to_ids.is_empty() && 0 != to_self { // from_id==to_id==DC_CONTACT_ID_SELF - this is a self-sent messages, // maybe an Autocrypt Setup Messag - dc_create_or_lookup_nchat_by_contact_id( - context, - 1, - Blocked::Not, - chat_id, - Some(&mut chat_id_blocked), - ); + let (id, bl) = chat::create_or_lookup_by_contact_id(context, 1, Blocked::Not) + .unwrap_or_default(); + *chat_id = id; + chat_id_blocked = bl; + if 0 != *chat_id && Blocked::Not != chat_id_blocked { - dc_unblock_chat(context, *chat_id); + chat::unblock(context, *chat_id); chat_id_blocked = Blocked::Not; } } @@ -593,7 +582,7 @@ unsafe fn add_parts( ); // unarchive chat - dc_unarchive_chat(context, *chat_id); + chat::unarchive(context, *chat_id).unwrap(); // if the mime-headers should be saved, find out its size // (the mime-header ends with an empty line) @@ -1236,7 +1225,7 @@ unsafe fn create_or_lookup_group( set_better_msg(mime_parser, &better_msg); // check, if we have a chat with this group ID - chat_id = dc_get_chat_id_by_grpid( + chat_id = chat::get_chat_id_by_grpid( context, grpid, Some(&mut chat_id_blocked), @@ -1258,12 +1247,12 @@ unsafe fn create_or_lookup_group( // check if the sender is a member of the existing group - // if not, we'll recreate the group list - if chat_id != 0 && 0 == dc_is_contact_in_chat(context, chat_id, from_id as u32) { + if chat_id != 0 && 0 == chat::is_contact_in_chat(context, chat_id, from_id as u32) { recreate_member_list = 1; } // check if the group does not exist but should be created - group_explicitly_left = dc_is_group_explicitly_left(context, grpid); + group_explicitly_left = chat::is_group_explicitly_left(context, grpid); let self_addr = context .sql @@ -1385,13 +1374,13 @@ unsafe fn create_or_lookup_group( to_string(grpimage) }, ); - if let Ok(mut chat) = dc_chat_load_from_db(context, chat_id) { + if let Ok(mut chat) = 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); + chat.update_param().unwrap(); send_EVENT_CHAT_MODIFIED = 1; } @@ -1419,14 +1408,14 @@ unsafe fn create_or_lookup_group( ) .ok(); if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) { - dc_add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF as u32); + chat::add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF as u32); } if from_id > DC_CHAT_ID_LAST_SPECIAL as u32 { if !Contact::addr_equals_contact(context, &self_addr, from_id as u32) && (skip.is_null() || !Contact::addr_equals_contact(context, to_string(skip), from_id as u32)) { - dc_add_to_chat_contacts_table(context, chat_id, from_id as u32); + chat::add_to_chat_contacts_table(context, chat_id, from_id as u32); } } for &to_id in to_ids.iter() { @@ -1434,11 +1423,11 @@ unsafe fn create_or_lookup_group( && (skip.is_null() || !Contact::addr_equals_contact(context, to_string(skip), to_id)) { - dc_add_to_chat_contacts_table(context, chat_id, to_id); + chat::add_to_chat_contacts_table(context, chat_id, to_id); } } send_EVENT_CHAT_MODIFIED = 1; - dc_reset_gossiped_timestamp(context, chat_id); + chat::reset_gossiped_timestamp(context, chat_id); } if 0 != send_EVENT_CHAT_MODIFIED { @@ -1448,7 +1437,7 @@ unsafe fn create_or_lookup_group( // check the number of receivers - // the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */ if to_ids_cnt == 1 && mime_parser.is_send_by_messenger == 0 { - let is_contact_cnt = dc_get_chat_contact_cnt(context, chat_id); + let is_contact_cnt = chat::get_chat_contact_cnt(context, chat_id); if is_contact_cnt > 3 { // to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt. // So everything up to 3 is no error. @@ -1617,7 +1606,7 @@ unsafe fn create_or_lookup_adhoc_group( chat_id = create_group_record(context, grpid, grpname, create_blocked, 0); chat_id_blocked = create_blocked; for &member_id in &member_ids { - dc_add_to_chat_contacts_table(context, chat_id, member_id); + chat::add_to_chat_contacts_table(context, chat_id, member_id); } context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 88d67f34f..0f66fa171 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -4,7 +4,7 @@ use mmime::mailimf_types::*; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use crate::aheader::EncryptPreference; -use crate::chat::*; +use crate::chat::{self, Chat}; use crate::constants::*; use crate::contact::*; use crate::context::Context; @@ -88,8 +88,8 @@ pub unsafe fn dc_get_securejoin_qr( let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string(); qr = if 0 != group_chat_id { - if let Ok(chat) = dc_get_chat(context, group_chat_id) { - group_name = dc_chat_get_name(&chat); + if let Ok(chat) = Chat::load_from_db(context, group_chat_id) { + group_name = chat.get_name(); group_name_urlencoded = dc_urlencode(group_name); Some(format!( @@ -152,7 +152,8 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 { error!(context, 0, "Unknown QR code.",); } else { - contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id); + contact_chat_id = + chat::create_by_contact_id(context, (*qr_scan).id).unwrap_or_default(); if contact_chat_id == 0i32 as libc::c_uint { error!(context, 0, "Unknown contact.",); } else if !(context @@ -232,7 +233,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> if bob.status == 1 { if 0 != join_vg { ret_chat_id = - dc_get_chat_id_by_grpid(context, (*qr_scan).text2, None, 0 as *mut libc::c_int) + chat::get_chat_id_by_grpid(context, (*qr_scan).text2, None, 0 as *mut libc::c_int) as libc::c_int } else { ret_chat_id = contact_chat_id as libc::c_int @@ -284,12 +285,13 @@ unsafe fn send_handshake_msg( } else { (*msg).param.set_int(Param::GuranteeE2ee, 1); } - dc_send_msg(context, contact_chat_id, msg); + // TODO. handle cleanup on error + chat::send_msg(context, contact_chat_id, msg).unwrap(); dc_msg_unref(msg); } unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t { - let contacts = dc_get_chat_contacts(context, contact_chat_id); + let contacts = chat::get_chat_contacts(context, contact_chat_id); if contacts.len() == 1 { contacts[0] } else { @@ -306,7 +308,7 @@ unsafe fn fingerprint_equals_sender( return 0; } let mut fingerprint_equal: libc::c_int = 0i32; - let contacts = dc_get_chat_contacts(context, contact_chat_id); + let contacts = chat::get_chat_contacts(context, contact_chat_id); if contacts.len() == 1 { if let Ok(contact) = Contact::load_from_db(context, contacts[0]) { @@ -339,8 +341,8 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut scanned_fingerprint_of_alice: *mut libc::c_char = 0 as *mut libc::c_char; let mut auth: *mut libc::c_char = 0 as *mut libc::c_char; let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char; - let mut contact_chat_id: uint32_t = 0i32 as uint32_t; - let mut contact_chat_id_blocked = Blocked::Not; + let contact_chat_id: u32; + let contact_chat_id_blocked: Blocked; let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut ret: libc::c_int = 0i32; @@ -355,15 +357,13 @@ pub unsafe fn dc_handle_securejoin_handshake( ); join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0) as libc::c_int; - dc_create_or_lookup_nchat_by_contact_id( - context, - contact_id, - Blocked::Not, - &mut contact_chat_id, - Some(&mut contact_chat_id_blocked), - ); + let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not) + .unwrap_or_default(); + contact_chat_id = id; + contact_chat_id_blocked = bl; + if Blocked::Not != contact_chat_id_blocked { - dc_unblock_chat(context, contact_chat_id); + chat::unblock(context, contact_chat_id); } ret = 0x2i32; if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32 @@ -574,7 +574,7 @@ pub unsafe fn dc_handle_securejoin_handshake( ); if 0 != join_vg { grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group")); - let group_chat_id: uint32_t = dc_get_chat_id_by_grpid( + let group_chat_id: uint32_t = chat::get_chat_id_by_grpid( context, grpid, None, @@ -584,7 +584,7 @@ pub unsafe fn dc_handle_securejoin_handshake( error!(context, 0, "Chat {} not found.", as_str(grpid),); current_block = 4378276786830486580; } else { - dc_add_contact_to_chat_ex( + chat::add_contact_to_chat_ex( context, group_chat_id, contact_id, @@ -647,7 +647,12 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut vg_expect_encrypted: libc::c_int = 1i32; if 0 != join_vg { let mut is_verified_group: libc::c_int = 0i32; - dc_get_chat_id_by_grpid(context, grpid, None, &mut is_verified_group); + chat::get_chat_id_by_grpid( + context, + grpid, + None, + &mut is_verified_group, + ); if 0 == is_verified_group { vg_expect_encrypted = 0i32 } @@ -803,7 +808,7 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint }; let msg = CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap(); - dc_add_device_msg(context, contact_chat_id, msg.as_ptr()); + chat::add_device_msg(context, contact_chat_id, msg.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, @@ -844,7 +849,7 @@ unsafe fn could_not_establish_secure_connection( }, ); let msg_c = CString::new(msg.as_str()).unwrap(); - dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr()); + chat::add_device_msg(context, contact_chat_id, msg_c.as_ptr()); error!(context, 0, "{} ({})", msg, as_str(details)); } @@ -906,8 +911,6 @@ unsafe fn encrypted_and_signed( } pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) { - let mut contact_chat_id = 0; - // - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal // - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes // together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother @@ -924,13 +927,10 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) ) .unwrap_or_default(); if contact_id > 0 { - dc_create_or_lookup_nchat_by_contact_id( - context, - contact_id as u32, - Blocked::Deaddrop, - &mut contact_chat_id, - None, - ); + let (contact_chat_id, _) = + chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop) + .unwrap_or_default(); + let peeraddr: &str = match peerstate.addr { Some(ref addr) => &addr, None => "", @@ -939,7 +939,7 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr), ) .unwrap(); - dc_add_device_msg(context, contact_chat_id, msg.as_ptr()); + chat::add_device_msg(context, contact_chat_id, msg.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 22ac09582..6bbca2804 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::ffi::{CStr, CString}; +use std::path::Path; use std::str::FromStr; use std::time::SystemTime; use std::{fmt, fs, ptr}; @@ -637,7 +638,7 @@ pub unsafe fn dc_smeared_time(context: &Context) -> i64 { now } -pub unsafe fn dc_create_smeared_timestamp(context: &Context) -> i64 { +pub fn dc_create_smeared_timestamp(context: &Context) -> i64 { let now = time(); let mut ret = now; @@ -652,7 +653,7 @@ pub unsafe fn dc_create_smeared_timestamp(context: &Context) -> i64 { ret } -pub unsafe fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int) -> i64 { +pub fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int) -> i64 { /* get a range to timestamps that can be used uniquely */ let now = time(); let start = now + (if count < 5 { count } else { 5 }) as i64 - count as i64; @@ -848,17 +849,11 @@ unsafe fn dc_validate_filename(filename: *mut libc::c_char) { } } -#[allow(non_snake_case)] -pub unsafe fn dc_get_filename(pathNfilename: *const libc::c_char) -> *mut libc::c_char { - let mut p: *const libc::c_char = strrchr(pathNfilename, '/' as i32); - if p.is_null() { - p = strrchr(pathNfilename, '\\' as i32) - } - if !p.is_null() { - p = p.offset(1isize); - dc_strdup(p) +pub unsafe fn dc_get_filename(path_filename: impl AsRef) -> *mut libc::c_char { + if let Some(p) = Path::new(path_filename.as_ref()).file_name() { + p.to_string_lossy().strdup() } else { - dc_strdup(pathNfilename) + ptr::null_mut() } } @@ -869,12 +864,15 @@ unsafe fn dc_split_filename( ret_basename: *mut *mut libc::c_char, ret_all_suffixes_incl_dot: *mut *mut libc::c_char, ) { + if pathNfilename.is_null() { + return; + } /* splits a filename into basename and all suffixes, eg. "/path/foo.tar.gz" is split into "foo.tar" and ".gz", (we use the _last_ dot which allows the usage inside the filename which are very usual; maybe the detection could be more intelligent, however, for the moment, it is just file) - if there is no suffix, the returned suffix string is empty, eg. "/path/foobar" is split into "foobar" and "" - the case of the returned suffix is preserved; this is to allow reconstruction of (similar) names */ - let basename: *mut libc::c_char = dc_get_filename(pathNfilename); + let basename: *mut libc::c_char = dc_get_filename(as_str(pathNfilename)); let suffix: *mut libc::c_char; let p1: *mut libc::c_char = strrchr(basename, '.' as i32); if !p1.is_null() { @@ -897,16 +895,12 @@ unsafe fn dc_split_filename( // the returned suffix is lower-case #[allow(non_snake_case)] -pub unsafe fn dc_get_filesuffix_lc(pathNfilename: *const libc::c_char) -> *mut libc::c_char { - if !pathNfilename.is_null() { - let mut p: *const libc::c_char = strrchr(pathNfilename, '.' as i32); - if !p.is_null() { - p = p.offset(1isize); - return dc_strlower(p); - } +pub unsafe fn dc_get_filesuffix_lc(path_filename: impl AsRef) -> *mut libc::c_char { + if let Some(p) = Path::new(path_filename.as_ref()).extension() { + p.to_string_lossy().to_lowercase().strdup() + } else { + ptr::null_mut() } - - ptr::null_mut() } /// Returns the `(width, height)` of the given image buffer. @@ -936,34 +930,25 @@ pub fn dc_get_abs_path_safe>( } } -#[allow(non_snake_case)] pub unsafe fn dc_get_abs_path( context: &Context, - pathNfilename: *const libc::c_char, + path_filename: impl AsRef, ) -> *mut libc::c_char { - if pathNfilename.is_null() { - return ptr::null_mut(); - } - - let starts = strncmp( - pathNfilename, - b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, - 8, - ) == 0; + let starts = path_filename.as_ref().starts_with("$BLOBDIR"); if starts && !context.has_blobdir() { return ptr::null_mut(); } - let mut pathNfilename_abs: *mut libc::c_char = dc_strdup(pathNfilename); + let mut path_filename_abs = path_filename.as_ref().strdup(); if starts && context.has_blobdir() { dc_str_replace( - &mut pathNfilename_abs, + &mut path_filename_abs, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, context.get_blobdir(), ); } - pathNfilename_abs + path_filename_abs } pub fn dc_file_exist(context: &Context, path: impl AsRef) -> bool { @@ -1159,56 +1144,49 @@ pub unsafe fn dc_get_fine_pathNfilename( ret } -pub unsafe fn dc_is_blobdir_path(context: &Context, path: *const libc::c_char) -> bool { - strncmp(path, context.get_blobdir(), strlen(context.get_blobdir())) == 0 - || strncmp(path, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, 8) == 0 +pub fn dc_is_blobdir_path(context: &Context, path: impl AsRef) -> bool { + path.as_ref().starts_with(as_str(context.get_blobdir())) + || path.as_ref().starts_with("$BLOBDIR") } -unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char) { - if path.is_null() || (*path).is_null() { - return; +fn dc_make_rel_path(context: &Context, path: &mut String) { + if path.starts_with(as_str(context.get_blobdir())) { + *path = path.replace("$BLOBDIR", as_str(context.get_blobdir())); } - if strncmp(*path, context.get_blobdir(), strlen(context.get_blobdir())) == 0 { - dc_str_replace( - path, - context.get_blobdir(), - b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, - ); - }; } -pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> bool { +pub fn dc_make_rel_and_copy(context: &Context, path: &mut String) -> bool { let mut success = false; - let mut filename: *mut libc::c_char = ptr::null_mut(); - let mut blobdir_path: *mut libc::c_char = ptr::null_mut(); - if !(path.is_null() || (*path).is_null()) { - if dc_is_blobdir_path(context, *path) { - dc_make_rel_path(context, path); - success = true; - } else { - filename = dc_get_filename(*path); - if !(filename.is_null() - || { - blobdir_path = dc_get_fine_pathNfilename( + let mut filename = ptr::null_mut(); + let mut blobdir_path = ptr::null_mut(); + if dc_is_blobdir_path(context, &path) { + dc_make_rel_path(context, path); + success = true; + } else { + filename = unsafe { dc_get_filename(&path) }; + if !(filename.is_null() + || { + blobdir_path = unsafe { + dc_get_fine_pathNfilename( context, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, filename, - ); - blobdir_path.is_null() - } - || !dc_copy_file(context, as_path(*path), as_path(blobdir_path))) - { - free(*path as *mut libc::c_void); - *path = blobdir_path; - blobdir_path = ptr::null_mut(); - dc_make_rel_path(context, path); - success = true; + ) + }; + blobdir_path.is_null() } + || !dc_copy_file(context, &path, as_path(blobdir_path))) + { + *path = to_string(blobdir_path); + blobdir_path = ptr::null_mut(); + dc_make_rel_path(context, path); + success = true; } } - free(blobdir_path as *mut libc::c_void); - free(filename as *mut libc::c_void); - + unsafe { + free(blobdir_path.cast()); + free(filename.cast()); + } success } diff --git a/src/peerstate.rs b/src/peerstate.rs index a2d2a74ec..b203904c7 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -466,7 +466,7 @@ impl<'a> Peerstate<'a> { } if self.to_save == Some(ToSave::All) || create { - dc_reset_gossiped_timestamp(self.context, 0); + reset_gossiped_timestamp(self.context, 0); } success diff --git a/tests/stress.rs b/tests/stress.rs index f1e4d0d90..b3c7c1cf9 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -6,7 +6,7 @@ use std::ffi::CString; use mmime::mailimf_types::*; use tempfile::{tempdir, TempDir}; -use deltachat::chat::*; +use deltachat::chat::{self, Chat}; use deltachat::config; use deltachat::constants::*; use deltachat::contact::*; @@ -66,15 +66,9 @@ unsafe fn stress_functions(context: &Context) { context.get_blobdir(), b"foobar\x00" as *const u8 as *const libc::c_char, ); - assert!(dc_is_blobdir_path(context, abs_path)); - assert!(dc_is_blobdir_path( - context, - b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char, - )); - assert!(!dc_is_blobdir_path( - context, - b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char, - )); + assert!(dc_is_blobdir_path(context, as_str(abs_path))); + assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo",)); + assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo",)); assert!(dc_file_exist(context, as_path(abs_path))); free(abs_path as *mut libc::c_void); assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",)); @@ -765,13 +759,13 @@ fn test_chat() { let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap(); assert_ne!(contact1, 0); - let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1); + let chat_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap(); assert!(chat_id > 9, "chat_id too small {}", chat_id); - let chat = dc_chat_load_from_db(&context.ctx, chat_id).unwrap(); + let chat = Chat::load_from_db(&context.ctx, chat_id).unwrap(); - let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1); + let chat2_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap(); assert_eq!(chat2_id, chat_id); - let chat2 = dc_chat_load_from_db(&context.ctx, chat2_id).unwrap(); + let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap(); assert_eq!(as_str(chat2.name), as_str(chat.name)); } From 6b3245ddfca1f47ccf151e2aa1845873a7d7a94a Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Thu, 15 Aug 2019 23:55:36 +0200 Subject: [PATCH 5/9] refactor(ffi): use nicer exentsion traits for results --- deltachat-ffi/src/lib.rs | 103 +++++++++++++-------------------------- 1 file changed, 33 insertions(+), 70 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 8d13539b5..f2338de65 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -322,11 +322,7 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms assert!(!context.is_null()); let context = &*context; - log_err_default( - chat::create_by_msg_id(context, msg_id), - context, - "Failed to create chat", - ) + chat::create_by_msg_id(context, msg_id).unwrap_or_log_default(context, "Failed to create chat") } #[no_mangle] @@ -337,11 +333,8 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id( assert!(!context.is_null()); let context = &*context; - log_err_default( - chat::create_by_contact_id(context, contact_id), - context, - "Failed to create chat", - ) + chat::create_by_contact_id(context, contact_id) + .unwrap_or_log_default(context, "Failed to create chat") } #[no_mangle] @@ -352,11 +345,8 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id( assert!(!context.is_null()); let context = &*context; - log_err_default( - chat::get_by_contact_id(context, contact_id), - context, - "Failed to get chat", - ) + chat::get_by_contact_id(context, contact_id) + .unwrap_or_log_default(context, "Failed to get chat") } #[no_mangle] @@ -369,11 +359,8 @@ pub unsafe extern "C" fn dc_prepare_msg( assert!(!msg.is_null()); let context = &*context; - log_err_default( - chat::prepare_msg(context, chat_id, msg), - context, - "Failed to prepare message", - ) + chat::prepare_msg(context, chat_id, msg) + .unwrap_or_log_default(context, "Failed to prepare message") } #[no_mangle] @@ -386,11 +373,7 @@ pub unsafe extern "C" fn dc_send_msg( assert!(!msg.is_null()); let context = &*context; - log_err_default( - chat::send_msg(context, chat_id, msg), - context, - "Failed to send message", - ) + chat::send_msg(context, chat_id, msg).unwrap_or_log_default(context, "Failed to send message") } #[no_mangle] @@ -404,11 +387,8 @@ pub unsafe extern "C" fn dc_send_text_msg( let context = &*context; let text_to_send = dc_tools::to_string_lossy(text_to_send); - log_err_default( - chat::send_text_msg(context, chat_id, text_to_send), - context, - "Failed to send text message", - ) + chat::send_text_msg(context, chat_id, text_to_send) + .unwrap_or_log_default(context, "Failed to send text message") } #[no_mangle] @@ -481,11 +461,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id assert!(!context.is_null()); let context = &*context; - log_err( - chat::marknoticed_chat(context, chat_id), - context, - "Failed marknoticed chat", - ); + chat::marknoticed_chat(context, chat_id).log_err(context, "Failed marknoticed chat"); } #[no_mangle] @@ -493,11 +469,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &*context; - log_err( - chat::marknoticed_all_chats(context), - context, - "Failed marknoticed all chats", - ); + chat::marknoticed_all_chats(context).log_err(context, "Failed marknoticed all chats"); } fn from_prim(s: S) -> Option @@ -566,11 +538,7 @@ pub unsafe extern "C" fn dc_archive_chat( return; }; - log_err( - chat::archive(context, chat_id, archive), - context, - "Failed archive chat", - ); + chat::archive(context, chat_id, archive).log_err(context, "Failed archive chat"); } #[no_mangle] @@ -578,12 +546,7 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32 assert!(!context.is_null()); let context = &*context; - // TODO: update to indicate public api success/failure of deletion - log_err( - chat::delete(context, chat_id), - context, - "Failed chat delete", - ); + chat::delete(context, chat_id).log_err(context, "Failed chat delete"); } #[no_mangle] @@ -640,11 +603,8 @@ pub unsafe extern "C" fn dc_create_group_chat( return 0; }; - log_err_default( - chat::create_group_chat(context, verified, name), - context, - "Failed to create group chat", - ) + chat::create_group_chat(context, verified, name) + .unwrap_or_log_default(context, "Failed to create group chat") } #[no_mangle] @@ -1895,22 +1855,25 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> { Some(dc_tools::as_str(s)) } -fn log_err(res: Result, context: &context::Context, msg: &str) { - if let Err(err) = res { - error!(context, 0, "{}: {}", msg, err); - } +pub trait ResultExt { + fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T; + fn log_err(&self, context: &context::Context, message: &str); } -fn log_err_default( - res: Result, - context: &context::Context, - msg: &str, -) -> T { - match res { - Ok(t) => t, - Err(err) => { - error!(context, 0, "{}: {}", msg, err); - Default::default() +impl ResultExt for Result { + fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T { + match self { + Ok(t) => t, + Err(err) => { + error!(context, 0, "{}: {}", message, err); + Default::default() + } + } + } + + fn log_err(&self, context: &context::Context, message: &str) { + if let Err(err) = self { + error!(context, 0, "{}: {}", message, err); } } } From 25e97df641051d0d6f0cfcb2d2f721ead7d1ca6c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 16 Aug 2019 00:24:39 +0200 Subject: [PATCH 6/9] refactor(chat): store rust strings in the chat struct --- deltachat-ffi/src/lib.rs | 6 +- examples/repl/cmdline.rs | 14 +-- src/chat.rs | 193 ++++++++++++++++----------------------- src/contact.rs | 2 +- src/dc_mimefactory.rs | 14 +-- src/dc_receive_imf.rs | 9 +- src/dc_securejoin.rs | 24 ++--- src/dc_tools.rs | 6 +- tests/stress.rs | 2 +- 9 files changed, 113 insertions(+), 157 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index f2338de65..18188c94c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -654,7 +654,7 @@ pub unsafe extern "C" fn dc_set_chat_name( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - chat::set_chat_name(context, chat_id, name) + chat::set_chat_name(context, chat_id, as_str(name)) } #[no_mangle] @@ -1309,7 +1309,7 @@ pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_ assert!(!chat.is_null()); let chat = &*chat; - chat.get_name() + chat.get_name().strdup() } #[no_mangle] @@ -1317,7 +1317,7 @@ pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc assert!(!chat.is_null()); let chat = &*chat; - chat.get_subtitle() + chat.get_subtitle().strdup() } #[no_mangle] diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 154aba350..ed4e2e2b1 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -615,12 +615,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "{}#{}: {} [{}] [{} fresh]", chat_prefix(&chat), chat.get_id(), - as_str(temp_name), - as_str(temp_subtitle), + temp_name, + temp_subtitle, chat::get_fresh_msg_cnt(context, chat.get_id()), ); - free(temp_subtitle as *mut libc::c_void); - free(temp_name as *mut libc::c_void); let lot = chatlist.get_summary(i, Some(&chat)); let statestr = if chat.is_archived() { " [Archived]" @@ -688,16 +686,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "{}#{}: {} [{}]{}", chat_prefix(sel_chat), sel_chat.get_id(), - as_str(temp_name), - as_str(temp2), + temp_name, + temp2, if sel_chat.is_sending_locations() { "📍" } else { "" }, ); - free(temp_name as *mut libc::c_void); - free(temp2 as *mut libc::c_void); if !msglist.is_null() { log_msglist(context, msglist); dc_array_unref(msglist); @@ -772,7 +768,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "groupname" => { ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1_c) { + if 0 != chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1) { println!("Chat name set"); } else { bail!("Failed to set chat name"); diff --git a/src/chat.rs b/src/chat.rs index eefcbe313..5117a8595 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -26,9 +26,9 @@ pub struct Chat<'a> { pub context: &'a Context, pub id: u32, pub typ: Chattype, - pub name: *mut libc::c_char, + pub name: String, archived: bool, - pub grpid: *mut libc::c_char, + pub grpid: String, blocked: Blocked, pub param: Params, pub gossiped_timestamp: i64, @@ -43,30 +43,19 @@ impl<'a> Chat<'a> { FROM chats c WHERE c.id=?;", params![chat_id as i32], |row| { - let mut c = Chat { + let c = 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, + id: row.get(0)?, + typ: row.get(1)?, + name: row.get::<_, String>(2)?, + grpid: row.get::<_, String>(3)?, + param: row.get::<_, String>(4)?.parse().unwrap_or_default(), + archived: row.get(5)?, + blocked: row.get::<_, Option<_>>(6)?.unwrap_or_default(), + gossiped_timestamp: row.get(7)?, + is_sending_locations: row.get(8)?, }; - c.id = row.get(0)?; - c.typ = row.get(1)?; - c.name = unsafe { row.get::<_, String>(2)?.strdup() }; - c.grpid = unsafe { row.get::<_, String>(3)?.strdup() }; - - c.param = row.get::<_, String>(4)?.parse().unwrap_or_default(); - c.archived = row.get(5)?; - c.blocked = row.get::<_, Option<_>>(6)?.unwrap_or_default(); - c.gossiped_timestamp = row.get(7)?; - c.is_sending_locations = row.get(8)?; - Ok(c) }, ); @@ -84,44 +73,33 @@ impl<'a> Chat<'a> { }, Ok(mut chat) => { match chat.id { - 1 => unsafe { - free(chat.name.cast()); - chat.name = chat.context.stock_str(StockMessage::DeadDrop).strdup(); - }, - 6 => unsafe { - free(chat.name.cast()); + 1 => { + chat.name = chat.context.stock_str(StockMessage::DeadDrop).into(); + } + 6 => { 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.cast()); - chat.name = chat.context.stock_str(StockMessage::StarredMsgs).strdup(); - }, + chat.name = format!("{} ({})", tempname, cnt); + } + 5 => { + chat.name = chat.context.stock_str(StockMessage::StarredMsgs).into(); + } _ => { - unsafe { - if chat.typ == Chattype::Single { - free(chat.name.cast()); - let contacts = get_chat_contacts(chat.context, chat.id); - let mut chat_name = "Err [Name not found]".to_owned(); + if chat.typ == Chattype::Single { + let contacts = 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]) - { - chat_name = contact.get_display_name().to_owned(); - } + if !(*contacts).is_empty() { + 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; } if chat.param.exists(Param::Selftalk) { - unsafe { - free(chat.name as *mut libc::c_void); - chat.name = chat.context.stock_str(StockMessage::SelfMsg).strdup(); - } + chat.name = chat.context.stock_str(StockMessage::SelfMsg).into(); } } } @@ -143,29 +121,30 @@ impl<'a> Chat<'a> { ) } - pub unsafe fn get_id(&self) -> u32 { + pub fn get_id(&self) -> u32 { self.id } - pub unsafe fn get_type(&self) -> Chattype { + pub fn get_type(&self) -> Chattype { self.typ } - pub unsafe fn get_name(&self) -> *mut libc::c_char { - dc_strdup(self.name) + pub fn get_name(&self) -> &str { + &self.name } - pub unsafe fn get_subtitle(&self) -> *mut libc::c_char { - /* returns either the address or the number of chat members */ + pub fn get_subtitle(&self) -> String { + // returns either the address or the number of chat members - let mut ret: *mut libc::c_char = std::ptr::null_mut(); if self.typ == Chattype::Single && self.param.exists(Param::Selftalk) { - ret = self + return self .context .stock_str(StockMessage::SelfTalkSubTitle) - .strdup(); - } else if self.typ == Chattype::Single { - let ret_raw: String = self + .into(); + } + + if self.typ == Chattype::Single { + return self .context .sql .query_row_col( @@ -177,24 +156,22 @@ impl<'a> Chat<'a> { 0, ) .unwrap_or_else(|| "Err".into()); - ret = ret_raw.strdup(); - } else if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { + } + + if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { if self.id == 1 { - ret = self.context.stock_str(StockMessage::DeadDrop).strdup(); - } else { - let cnt = get_chat_contact_cnt(self.context, self.id); - ret = self - .context - .stock_string_repl_int(StockMessage::Member, cnt) - .strdup(); + return self.context.stock_str(StockMessage::DeadDrop).into(); } + let cnt = get_chat_contact_cnt(self.context, self.id); + return self + .context + .stock_string_repl_int(StockMessage::Member, cnt) + .into(); } - if !ret.is_null() { - ret - } else { - dc_strdup(b"Err\x00" as *const u8 as *const libc::c_char) - } + + return "Err".into(); } + unsafe fn get_parent_mime_headers( &self, parent_rfc724_mid: *mut *mut libc::c_char, @@ -264,7 +241,7 @@ impl<'a> Chat<'a> { } pub fn get_color(&self) -> u32 { - let mut color: u32 = 0i32 as u32; + let mut color = 0; if self.typ == Chattype::Single { let contacts = get_chat_contacts(self.context, self.id); @@ -274,7 +251,7 @@ impl<'a> Chat<'a> { } } } else { - color = unsafe { dc_str_to_color(self.name) } as u32 + color = dc_str_to_color(&self.name); } color @@ -338,7 +315,7 @@ impl<'a> Chat<'a> { let from_c = CString::yolo(from.unwrap()); new_rfc724_mid = dc_create_outgoing_rfc724_mid( if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { - self.grpid + self.grpid.strdup() } else { ptr::null_mut() }, @@ -1663,7 +1640,7 @@ pub unsafe fn remove_contact_from_chat( 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 { - set_group_explicitly_left(context, chat.grpid); + set_group_explicitly_left(context, chat.grpid).unwrap(); (*msg).text = Some(context.stock_system_msg( StockMessage::MsgGroupLeft, "", @@ -1707,41 +1684,38 @@ pub unsafe fn remove_contact_from_chat( success } -// Should return Result -fn set_group_explicitly_left(context: &Context, grpid: *const libc::c_char) { - if 0 == is_group_explicitly_left(context, grpid) { +fn set_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Result<(), Error> { + if !is_group_explicitly_left(context, grpid.as_ref())? { sql::execute( context, &context.sql, "INSERT INTO leftgrps (grpid) VALUES(?);", - params![as_str(grpid)], - ) - .ok(); + params![grpid.as_ref()], + )?; } + + Ok(()) } // TODO should return bool /rtn -pub fn is_group_explicitly_left(context: &Context, grpid: *const libc::c_char) -> libc::c_int { - context - .sql - .exists( - "SELECT id FROM leftgrps WHERE grpid=?;", - params![as_str(grpid)], - ) - .unwrap_or_default() as libc::c_int +pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Result { + context.sql.exists( + "SELECT id FROM leftgrps WHERE grpid=?;", + params![grpid.as_ref()], + ) } // TODO should return bool /rtn pub unsafe fn set_chat_name( context: &Context, chat_id: u32, - new_name: *const libc::c_char, + new_name: impl AsRef, ) -> 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 mut msg = dc_msg_new_untyped(context); - if new_name.is_null() || *new_name.offset(0isize) as libc::c_int == 0 || chat_id <= 9 { + if new_name.as_ref().is_empty() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { return 0; } @@ -1749,9 +1723,9 @@ pub unsafe fn set_chat_name( 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 !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { + if &chat.name == new_name.as_ref() { + success = 1; + } else if !(is_contact_in_chat(context, chat_id, 1) == 1) { log_event!( context, Event::ERROR_SELF_NOT_IN_GROUP, @@ -1765,7 +1739,7 @@ pub unsafe fn set_chat_name( &context.sql, format!( "UPDATE chats SET name='{}' WHERE id={};", - as_str(new_name), + new_name.as_ref(), chat_id as i32 ), params![], @@ -1776,13 +1750,13 @@ pub unsafe fn set_chat_name( (*msg).type_0 = Viewtype::Text; (*msg).text = Some(context.stock_system_msg( StockMessage::MsgGrpName, - as_str(chat.name), - as_str(new_name), + &chat.name, + new_name.as_ref(), 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)); + if !chat.name.is_empty() { + (*msg).param.set(Param::Arg, &chat.name); } (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); context.call_cb( @@ -2088,12 +2062,3 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char msg_id as uintptr_t, ); } - -impl<'a> Drop for Chat<'a> { - fn drop(&mut self) { - unsafe { - free(self.name.cast()); - free(self.grpid.cast()); - } - } -} diff --git a/src/contact.rs b/src/contact.rs index d205baace..799256d3f 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -791,7 +791,7 @@ impl<'a> Contact<'a> { /// and can be used for an fallback avatar with white initials /// as well as for headlines in bubbles of group chats. pub fn get_color(&self) -> u32 { - dc_str_to_color_safe(&self.addr) + dc_str_to_color(&self.addr) } /// Check if a contact was verified. E.g. by a secure-join QR code scan diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 6300e87cb..fe8dc2b0c 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -548,14 +548,15 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: imf_fields, mailimf_field_new_custom( strdup(b"Chat-Group-ID\x00" as *const u8 as *const libc::c_char), - dc_strdup(chat.grpid), + chat.grpid.strdup(), ), ); + let name = CString::yolo(chat.name.as_bytes()); 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(name.as_ptr()), ), ); if command == 5 { @@ -1090,12 +1091,13 @@ 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 { - ret = dc_mprintf( - b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char, + ret = format!( + "Chat: {}: {}{}", chat.name, - fwd, - raw_subject, + to_string(fwd), + to_string(raw_subject), ) + .strdup() } else { ret = dc_mprintf( b"Chat: %s%s\x00" as *const u8 as *const libc::c_char, diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 461cf9bf7..548dbd924 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1041,7 +1041,7 @@ unsafe fn create_or_lookup_group( ret_chat_id: *mut uint32_t, ret_chat_id_blocked: &mut Blocked, ) { - let group_explicitly_left: libc::c_int; + let group_explicitly_left: bool; let mut chat_id = 0; let mut chat_id_blocked = Blocked::Not; let mut chat_id_verified = 0; @@ -1252,7 +1252,8 @@ unsafe fn create_or_lookup_group( } // check if the group does not exist but should be created - group_explicitly_left = chat::is_group_explicitly_left(context, grpid); + group_explicitly_left = + chat::is_group_explicitly_left(context, as_str(grpid)).unwrap_or_default(); let self_addr = context .sql @@ -1265,7 +1266,7 @@ unsafe fn create_or_lookup_group( // otherwise, a pending "quit" message may pop up && X_MrRemoveFromGrp.is_null() // re-create explicitly left groups only if ourself is re-added - && (0 == group_explicitly_left + && (!group_explicitly_left || !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp))) { let mut create_verified: libc::c_int = 0; @@ -1301,7 +1302,7 @@ unsafe fn create_or_lookup_group( // again, check chat_id if chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { chat_id = 0; - if 0 != group_explicitly_left { + if group_explicitly_left { chat_id = DC_CHAT_ID_TRASH as u32; } else { create_or_lookup_adhoc_group( diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 0f66fa171..512872f2d 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -14,7 +14,6 @@ use crate::dc_lot::*; use crate::dc_mimeparser::*; use crate::dc_msg::*; use crate::dc_qr::*; -use crate::dc_strencode::*; use crate::dc_token::*; use crate::dc_tools::*; use crate::key::*; @@ -36,8 +35,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 group_name = 0 as *mut libc::c_char; - let mut group_name_urlencoded = 0 as *mut libc::c_char; let mut qr: Option = None; dc_ensure_secret_key_exists(context).ok(); @@ -53,12 +50,10 @@ pub unsafe fn dc_get_securejoin_qr( } let self_addr = context.sql.get_config(context, "configured_addr"); - let cleanup = |fingerprint, group_name, group_name_urlencoded| { + let cleanup = |fingerprint| { free(fingerprint as *mut libc::c_void); free(invitenumber as *mut libc::c_void); free(auth as *mut libc::c_void); - free(group_name as *mut libc::c_void); - free(group_name_urlencoded as *mut libc::c_void); if let Some(qr) = qr { qr.strdup() @@ -69,7 +64,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, group_name, group_name_urlencoded); + return cleanup(fingerprint); } let self_addr = self_addr.unwrap(); @@ -81,7 +76,7 @@ pub unsafe fn dc_get_securejoin_qr( fingerprint = get_self_fingerprint(context); if fingerprint.is_null() { - return cleanup(fingerprint, group_name, group_name_urlencoded); + return cleanup(fingerprint); } let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string(); @@ -89,15 +84,16 @@ pub unsafe fn dc_get_securejoin_qr( qr = if 0 != group_chat_id { if let Ok(chat) = Chat::load_from_db(context, group_chat_id) { - group_name = chat.get_name(); - group_name_urlencoded = dc_urlencode(group_name); + let group_name = chat.get_name(); + let group_name_urlencoded = + utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string(); Some(format!( "OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}", as_str(fingerprint), self_addr_urlencoded, - as_str(group_name_urlencoded), - as_str(chat.grpid), + &group_name_urlencoded, + &chat.grpid, as_str(invitenumber), as_str(auth), )) @@ -106,7 +102,7 @@ pub unsafe fn dc_get_securejoin_qr( context, 0, "Cannot get QR-code for chat-id {}", group_chat_id, ); - return cleanup(fingerprint, group_name, group_name_urlencoded); + return cleanup(fingerprint); } } else { Some(format!( @@ -121,7 +117,7 @@ pub unsafe fn dc_get_securejoin_qr( info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap()); - cleanup(fingerprint, group_name, group_name_urlencoded) + cleanup(fingerprint) } fn get_self_fingerprint(context: &Context) -> *mut libc::c_char { diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 6bbca2804..e4f320d1b 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -532,7 +532,7 @@ const COLORS: [u32; 16] = [ 0x39b249, 0xbb243b, 0x964078, 0x66874f, 0x308ab9, 0x127ed0, 0xbe450c, ]; -pub fn dc_str_to_color_safe(s: impl AsRef) -> u32 { +pub fn dc_str_to_color(s: impl AsRef) -> u32 { let str_lower = s.as_ref().to_lowercase(); let mut checksum = 0; let bytes = str_lower.as_bytes(); @@ -545,10 +545,6 @@ pub fn dc_str_to_color_safe(s: impl AsRef) -> u32 { COLORS[color_index] } -pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int { - dc_str_to_color_safe(as_str(str)) as libc::c_int -} - /* clist tools */ /* calls free() for each item content */ pub unsafe fn clist_free_content(haystack: *const clist) { diff --git a/tests/stress.rs b/tests/stress.rs index b3c7c1cf9..3ab927d14 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -767,7 +767,7 @@ fn test_chat() { assert_eq!(chat2_id, chat_id); let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap(); - assert_eq!(as_str(chat2.name), as_str(chat.name)); + assert_eq!(chat2.name, chat.name); } } From 9b1a74cc221d5c8e860f66d29a92d0bf118267c0 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 16 Aug 2019 00:48:06 +0200 Subject: [PATCH 7/9] refactor(chat): remove C strings from the public interface --- deltachat-ffi/src/lib.rs | 10 +++-- examples/repl/cmdline.rs | 15 ++----- src/chat.rs | 40 +++++++---------- src/dc_location.rs | 17 +++----- src/dc_qr.rs | 12 ++--- src/dc_receive_imf.rs | 94 +++++++++++++++++++--------------------- src/dc_securejoin.rs | 61 +++++++++++++------------- 7 files changed, 109 insertions(+), 140 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 18188c94c..1fd78b42c 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -13,6 +13,7 @@ extern crate num_traits; use num_traits::{FromPrimitive, ToPrimitive}; use std::convert::TryInto; +use std::ptr; use std::str::FromStr; use deltachat::contact::Contact; @@ -603,7 +604,7 @@ pub unsafe extern "C" fn dc_create_group_chat( return 0; }; - chat::create_group_chat(context, verified, name) + chat::create_group_chat(context, verified, as_str(name)) .unwrap_or_log_default(context, "Failed to create group chat") } @@ -667,7 +668,7 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; - chat::set_chat_profile_image(context, chat_id, image) + chat::set_chat_profile_image(context, chat_id, as_str(image)) } #[no_mangle] @@ -1325,7 +1326,10 @@ pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut assert!(!chat.is_null()); let chat = &*chat; - chat.get_profile_image() + match chat.get_profile_image() { + Some(i) => i.strdup(), + None => ptr::null_mut(), + } } #[no_mangle] diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index ed4e2e2b1..2a7d9ab01 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -726,13 +726,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "creategroup" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1_c)?; + let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1)?; println!("Group#{} created successfully.", chat_id); } "createverified" => { ensure!(!arg1.is_empty(), "Argument missing."); - let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1_c)?; + let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1)?; println!("VerifiedGroup#{} created successfully.", chat_id); } @@ -778,15 +778,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != chat::set_chat_profile_image( - context, - sel_chat.as_ref().unwrap().get_id(), - if !arg1.is_empty() { - arg1_c - } else { - std::ptr::null_mut() - }, - ) { + if 0 != chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1) + { println!("Chat image set"); } else { bail!("Failed to set chat image"); diff --git a/src/chat.rs b/src/chat.rs index 5117a8595..211ee18f8 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -219,25 +219,21 @@ impl<'a> Chat<'a> { Ok(()) } - pub unsafe fn get_profile_image(&self) -> *mut libc::c_char { - let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; - + pub unsafe fn get_profile_image(&self) -> Option { if let Some(image_rel) = self.param.get(Param::ProfileImage) { if !image_rel.is_empty() { - image_abs = dc_get_abs_path(self.context, image_rel); + return Some(to_string(dc_get_abs_path(self.context, image_rel))); } } else if self.typ == Chattype::Single { let contacts = get_chat_contacts(self.context, self.id); if !contacts.is_empty() { if let Ok(contact) = Contact::get_by_id(self.context, contacts[0]) { - if let Some(img) = contact.get_profile_image() { - image_abs = img.strdup(); - } + return contact.get_profile_image(); } } } - image_abs + None } pub fn get_color(&self) -> u32 { @@ -1391,15 +1387,12 @@ pub fn get_chat_contacts(context: &Context, chat_id: u32) -> Vec { pub unsafe fn create_group_chat( context: &Context, verified: VerifiedStatus, - chat_name: *const libc::c_char, + chat_name: impl AsRef, ) -> Result { - ensure!( - !chat_name.is_null() && *chat_name.offset(0) as libc::c_int != 0, - "Invalid chat name" - ); + ensure!(!chat_name.as_ref().is_empty(), "Invalid chat name"); let draft_txt = - CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, as_str(chat_name))) + CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, &chat_name)) .unwrap(); let grpid = dc_create_id(); @@ -1413,7 +1406,7 @@ pub unsafe fn create_group_chat( } else { Chattype::Group }, - as_str(chat_name), + chat_name.as_ref(), grpid ], )?; @@ -1785,7 +1778,7 @@ pub unsafe fn set_chat_name( pub unsafe fn set_chat_profile_image( context: &Context, chat_id: u32, - new_image: *const libc::c_char, + new_image: impl AsRef, ) -> libc::c_int { let mut OK_TO_CONTINUE = true; let mut success: libc::c_int = 0i32; @@ -1809,8 +1802,8 @@ pub unsafe fn set_chat_profile_image( ); } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if !new_image.is_null() { - let mut img = to_string(new_image); + if !new_image.as_ref().is_empty() { + let mut img = new_image.as_ref().to_string(); if !dc_make_rel_and_copy(context, &mut img) { OK_TO_CONTINUE = false; } @@ -1991,7 +1984,7 @@ pub fn get_chat_cnt(context: &Context) -> usize { pub unsafe fn get_chat_id_by_grpid( context: &Context, - grpid: *const libc::c_char, + grpid: impl AsRef, ret_blocked: Option<&mut Blocked>, ret_verified: *mut libc::c_int, ) -> u32 { @@ -2003,7 +1996,7 @@ pub unsafe fn get_chat_id_by_grpid( .sql .query_row( "SELECT id, blocked, type FROM chats WHERE grpid=?;", - params![as_str(grpid)], + params![grpid.as_ref()], |row| { let chat_id = row.get(0)?; @@ -2020,10 +2013,7 @@ pub unsafe fn get_chat_id_by_grpid( .unwrap_or_default() } -pub fn add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char) { - if text.is_null() { - return; - } +pub fn add_device_msg(context: &Context, chat_id: u32, text: impl AsRef) { let rfc724_mid = unsafe { dc_create_outgoing_rfc724_mid( ptr::null(), @@ -2040,7 +2030,7 @@ pub fn add_device_msg(context: &Context, chat_id: u32, text: *const libc::c_char dc_create_smeared_timestamp(context), Viewtype::Text, DC_STATE_IN_NOTICED, - as_str(text), + text.as_ref(), as_str(rfc724_mid), ] ).is_err() { diff --git a/src/dc_location.rs b/src/dc_location.rs index 9dd402960..86a23908e 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -1,5 +1,3 @@ -use std::ffi::CString; - use quick_xml; use quick_xml::events::{BytesEnd, BytesStart, BytesText}; @@ -106,14 +104,9 @@ pub unsafe fn dc_send_locations_to_chat( (*msg).param.set_int(Param::Cmd, 8); chat::send_msg(context, chat_id, msg).unwrap(); } else if 0 == seconds && is_sending_locations_before { - let stock_str = CString::new(context.stock_system_msg( - StockMessage::MsgLocationDisabled, - "", - "", - 0, - )) - .unwrap(); - chat::add_device_msg(context, chat_id, stock_str.as_ptr()); + let stock_str = + context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); + chat::add_device_msg(context, chat_id, stock_str); } context.call_cb( Event::CHAT_MODIFIED, @@ -736,8 +729,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut "UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?", params![chat_id as i32], ).is_ok() { - let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap(); - chat::add_device_msg(context, chat_id, stock_str.as_ptr()); + let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0); + chat::add_device_msg(context, chat_id, stock_str); context.call_cb( Event::CHAT_MODIFIED, chat_id as usize, diff --git a/src/dc_qr.rs b/src/dc_qr.rs index dbcff7a2e..82951c021 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -35,7 +35,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc let mut auth: *mut libc::c_char = 0 as *mut libc::c_char; let mut qr_parsed: *mut dc_lot_t = dc_lot_new(); let mut chat_id: uint32_t = 0i32 as uint32_t; - let mut device_msg: *mut libc::c_char = 0 as *mut libc::c_char; + let mut device_msg = "".to_string(); let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char; (*qr_parsed).state = 0i32; @@ -238,13 +238,10 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc ) .unwrap_or_default(); chat_id = id; - device_msg = dc_mprintf( - b"%s verified.\x00" as *const u8 as *const libc::c_char, - peerstate.addr, - ) + device_msg = format!("{} verified.", peerstate.addr.unwrap_or_default()); } else { (*qr_parsed).text1 = dc_format_fingerprint_c(fingerprint); - (*qr_parsed).state = 230i32 + (*qr_parsed).state = 230i32; } } else { if !grpid.is_null() && !grpname.is_null() { @@ -287,7 +284,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc (*qr_parsed).state = 330i32; (*qr_parsed).text1 = dc_strdup(qr) } - if !device_msg.is_null() { + if !device_msg.is_empty() { chat::add_device_msg(context, chat_id, device_msg); } } @@ -298,7 +295,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc free(name as *mut libc::c_void); free(invitenumber as *mut libc::c_void); free(auth as *mut libc::c_void); - free(device_msg as *mut libc::c_void); free(grpname as *mut libc::c_void); free(grpid as *mut libc::c_void); diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 548dbd924..bce036626 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -1045,7 +1045,7 @@ unsafe fn create_or_lookup_group( let mut chat_id = 0; let mut chat_id_blocked = Blocked::Not; let mut chat_id_verified = 0; - let mut grpid = std::ptr::null_mut(); + let mut grpid = "".to_string(); let mut grpname = std::ptr::null_mut(); let to_ids_cnt = to_ids.len(); let mut recreate_member_list = 0; @@ -1059,14 +1059,12 @@ unsafe fn create_or_lookup_group( let mut better_msg: String = From::from(""); let mut failure_reason = std::ptr::null_mut(); - let cleanup = |grpid: *mut libc::c_char, - grpname: *mut libc::c_char, + let cleanup = |grpname: *mut libc::c_char, failure_reason: *mut libc::c_char, ret_chat_id: *mut uint32_t, ret_chat_id_blocked: &mut Blocked, chat_id: u32, chat_id_blocked: Blocked| { - free(grpid.cast()); free(grpname.cast()); free(failure_reason.cast()); @@ -1089,40 +1087,44 @@ unsafe fn create_or_lookup_group( // search the grpid in the header let optional_field = dc_mimeparser_lookup_optional_field(mime_parser, "Chat-Group-ID"); if !optional_field.is_null() { - grpid = dc_strdup((*optional_field).fld_value) + grpid = to_string((*optional_field).fld_value) } - if grpid.is_null() { + if grpid.is_empty() { if let Some(field) = lookup_field(mime_parser, "Message-ID", MAILIMF_FIELD_MESSAGE_ID) { let fld_message_id = (*field).fld_data.fld_message_id; if !fld_message_id.is_null() { if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value)) { - grpid = extracted_grpid.strdup(); + grpid = extracted_grpid.to_string(); } else { - grpid = std::ptr::null_mut(); + grpid = "".to_string(); } } } - if grpid.is_null() { + if grpid.is_empty() { if let Some(field) = lookup_field(mime_parser, "In-Reply-To", MAILIMF_FIELD_IN_REPLY_TO) { let fld_in_reply_to = (*field).fld_data.fld_in_reply_to; if !fld_in_reply_to.is_null() { - grpid = dc_extract_grpid_from_rfc724_mid_list((*fld_in_reply_to).mid_list) + grpid = to_string(dc_extract_grpid_from_rfc724_mid_list( + (*fld_in_reply_to).mid_list, + )); } } - if grpid.is_null() { + if grpid.is_empty() { if let Some(field) = lookup_field(mime_parser, "References", MAILIMF_FIELD_REFERENCES) { let fld_references = (*field).fld_data.fld_references; if !fld_references.is_null() { - grpid = dc_extract_grpid_from_rfc724_mid_list((*fld_references).mid_list) + grpid = to_string(dc_extract_grpid_from_rfc724_mid_list( + (*fld_references).mid_list, + )); } } - if grpid.is_null() { + if grpid.is_empty() { create_or_lookup_adhoc_group( context, mime_parser, @@ -1134,7 +1136,6 @@ unsafe fn create_or_lookup_group( &mut chat_id_blocked, ); cleanup( - grpid, grpname, failure_reason, ret_chat_id, @@ -1227,7 +1228,7 @@ unsafe fn create_or_lookup_group( // check, if we have a chat with this group ID chat_id = chat::get_chat_id_by_grpid( context, - grpid, + &grpid, Some(&mut chat_id_blocked), &mut chat_id_verified, ); @@ -1252,8 +1253,7 @@ unsafe fn create_or_lookup_group( } // check if the group does not exist but should be created - group_explicitly_left = - chat::is_group_explicitly_left(context, as_str(grpid)).unwrap_or_default(); + group_explicitly_left = chat::is_group_explicitly_left(context, &grpid).unwrap_or_default(); let self_addr = context .sql @@ -1261,7 +1261,7 @@ unsafe fn create_or_lookup_group( .unwrap_or_default(); if chat_id == 0 && 0 == dc_mimeparser_is_mailinglist_message(mime_parser) - && !grpid.is_null() + && !grpid.is_empty() && !grpname.is_null() // otherwise, a pending "quit" message may pop up && X_MrRemoveFromGrp.is_null() @@ -1269,9 +1269,9 @@ unsafe fn create_or_lookup_group( && (!group_explicitly_left || !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp))) { - let mut create_verified: libc::c_int = 0; + let mut create_verified = VerifiedStatus::Unverified; if !dc_mimeparser_lookup_field(mime_parser, "Chat-Verified").is_null() { - create_verified = 1; + create_verified = VerifiedStatus::Verified; if 0 == check_verified_properties( context, mime_parser, @@ -1284,7 +1284,6 @@ unsafe fn create_or_lookup_group( } if 0 == allow_creation { cleanup( - grpid, grpname, failure_reason, ret_chat_id, @@ -1294,7 +1293,7 @@ unsafe fn create_or_lookup_group( ); return; } - chat_id = create_group_record(context, grpid, grpname, create_blocked, create_verified); + chat_id = create_group_record(context, &grpid, grpname, create_blocked, create_verified); chat_id_blocked = create_blocked; recreate_member_list = 1; } @@ -1317,7 +1316,6 @@ unsafe fn create_or_lookup_group( ); } cleanup( - grpid, grpname, failure_reason, ret_chat_id, @@ -1457,7 +1455,6 @@ unsafe fn create_or_lookup_group( } cleanup( - grpid, grpname, failure_reason, ret_chat_id, @@ -1482,16 +1479,13 @@ unsafe fn create_or_lookup_adhoc_group( // group matching the to-list or if we can create one let mut chat_id = 0; let mut chat_id_blocked = Blocked::Not; - let mut grpid = 0 as *mut libc::c_char; let mut grpname = 0 as *mut libc::c_char; - let cleanup = |grpid: *mut libc::c_char, - grpname: *mut libc::c_char, + let cleanup = |grpname: *mut libc::c_char, ret_chat_id: *mut uint32_t, ret_chat_id_blocked: &mut Blocked, chat_id: u32, chat_id_blocked: Blocked| { - free(grpid as *mut libc::c_void); free(grpname as *mut libc::c_void); if !ret_chat_id.is_null() { @@ -1504,7 +1498,6 @@ unsafe fn create_or_lookup_adhoc_group( if to_ids.is_empty() || 0 != dc_mimeparser_is_mailinglist_message(mime_parser) { // too few contacts or a mailinglist cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1524,7 +1517,6 @@ unsafe fn create_or_lookup_adhoc_group( if member_ids.len() < 3 { // too few contacts given cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1554,7 +1546,6 @@ unsafe fn create_or_lookup_adhoc_group( chat_id_blocked = id_blocked; /* success, chat found */ cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1567,7 +1558,6 @@ unsafe fn create_or_lookup_adhoc_group( if 0 == allow_creation { cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1581,10 +1571,9 @@ unsafe fn create_or_lookup_adhoc_group( // create a new ad-hoc group // - there is no need to check if this group exists; otherwise we would have caught it above - grpid = create_adhoc_grp_id(context, &member_ids); - if grpid.is_null() { + let grpid = create_adhoc_grp_id(context, &member_ids); + if grpid.is_empty() { cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1604,7 +1593,13 @@ unsafe fn create_or_lookup_adhoc_group( } // create group record - chat_id = create_group_record(context, grpid, grpname, create_blocked, 0); + chat_id = create_group_record( + context, + &grpid, + grpname, + create_blocked, + VerifiedStatus::Unverified, + ); chat_id_blocked = create_blocked; for &member_id in &member_ids { chat::add_to_chat_contacts_table(context, chat_id, member_id); @@ -1613,7 +1608,6 @@ unsafe fn create_or_lookup_adhoc_group( context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); cleanup( - grpid, grpname, ret_chat_id, ret_chat_id_blocked, @@ -1624,19 +1618,23 @@ unsafe fn create_or_lookup_adhoc_group( fn create_group_record( context: &Context, - grpid: *const libc::c_char, + grpid: impl AsRef, grpname: *const libc::c_char, create_blocked: Blocked, - create_verified: libc::c_int, + create_verified: VerifiedStatus, ) -> u32 { if sql::execute( context, &context.sql, "INSERT INTO chats (type, name, grpid, blocked) VALUES(?, ?, ?, ?);", params![ - if 0 != create_verified { 130 } else { 120 }, + if VerifiedStatus::Unverified != create_verified { + Chattype::VerifiedGroup + } else { + Chattype::Group + }, as_str(grpname), - as_str(grpid), + grpid.as_ref(), create_blocked, ], ) @@ -1645,10 +1643,10 @@ fn create_group_record( return 0; } - sql::get_rowid(context, &context.sql, "chats", "grpid", as_str(grpid)) + sql::get_rowid(context, &context.sql, "chats", "grpid", grpid.as_ref()) } -unsafe fn create_adhoc_grp_id(context: &Context, member_ids: &Vec) -> *mut libc::c_char { +fn create_adhoc_grp_id(context: &Context, member_ids: &Vec) -> String { /* algorithm: - sort normalized, lowercased, e-mail addresses alphabetically - put all e-mail addresses into a single string, separate the address by a single comma @@ -1684,14 +1682,13 @@ unsafe fn create_adhoc_grp_id(context: &Context, member_ids: &Vec) -> *mut ) .unwrap_or_else(|_| member_cs); - hex_hash(&members) as *mut _ + hex_hash(&members) } -fn hex_hash(s: impl AsRef) -> *const libc::c_char { +fn hex_hash(s: impl AsRef) -> String { let bytes = s.as_ref().as_bytes(); let result = Sha256::digest(bytes); - let result_hex = hex::encode(&result[..8]); - unsafe { result_hex.strdup() as *const _ } + hex::encode(&result[..8]) } #[allow(non_snake_case)] @@ -2168,8 +2165,7 @@ mod tests { fn test_hex_hash() { let data = "hello world"; - let res_c = hex_hash(data); - let res = to_string(res_c); + let res = hex_hash(data); assert_eq!(res, "b94d27b9934d3e08"); } } diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 512872f2d..11a162e2b 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -1,5 +1,3 @@ -use std::ffi::CString; - use mmime::mailimf_types::*; use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; @@ -187,9 +185,9 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> (*qr_scan).auth, own_fingerprint, if 0 != join_vg { - (*qr_scan).text2 + as_str((*qr_scan).text2) } else { - 0 as *mut libc::c_char + "" }, ); free(own_fingerprint as *mut libc::c_void); @@ -205,7 +203,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> }, (*qr_scan).invitenumber, 0 as *const libc::c_char, - 0 as *const libc::c_char, + "", ); } @@ -228,9 +226,12 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) -> bob.expects = 0; if bob.status == 1 { if 0 != join_vg { - ret_chat_id = - chat::get_chat_id_by_grpid(context, (*qr_scan).text2, None, 0 as *mut libc::c_int) - as libc::c_int + ret_chat_id = chat::get_chat_id_by_grpid( + context, + to_string((*qr_scan).text2), + None, + 0 as *mut libc::c_int, + ) as libc::c_int } else { ret_chat_id = contact_chat_id as libc::c_int } @@ -250,7 +251,7 @@ unsafe fn send_handshake_msg( step: *const libc::c_char, param2: *const libc::c_char, fingerprint: *const libc::c_char, - grpid: *const libc::c_char, + grpid: impl AsRef, ) { let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); (*msg).type_0 = Viewtype::Text; @@ -268,8 +269,8 @@ unsafe fn send_handshake_msg( if !fingerprint.is_null() { (*msg).param.set(Param::Arg3, as_str(fingerprint)); } - if !grpid.is_null() { - (*msg).param.set(Param::Arg4, as_str(grpid)); + if !grpid.as_ref().is_empty() { + (*msg).param.set(Param::Arg4, grpid.as_ref()); } if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32 || strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32 @@ -339,7 +340,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char; let contact_chat_id: u32; let contact_chat_id_blocked: Blocked; - let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; + let mut grpid = "".to_string(); let mut ret: libc::c_int = 0i32; if !(contact_id <= 9i32 as libc::c_uint) { @@ -399,7 +400,7 @@ pub unsafe fn dc_handle_securejoin_handshake( }, 0 as *const libc::c_char, 0 as *const libc::c_char, - 0 as *const libc::c_char, + "", ); current_block = 10256747982273457880; } @@ -429,7 +430,7 @@ pub unsafe fn dc_handle_securejoin_handshake( scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint); auth = dc_strdup((*scan).auth); if 0 != join_vg { - grpid = dc_strdup((*scan).text2) + grpid = to_string((*scan).text2); } } if !encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) { @@ -569,15 +570,15 @@ pub unsafe fn dc_handle_securejoin_handshake( 600i32 as uintptr_t, ); if 0 != join_vg { - grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group")); + grpid = to_string(lookup_field(mimeparser, "Secure-Join-Group")); let group_chat_id: uint32_t = chat::get_chat_id_by_grpid( context, - grpid, + &grpid, None, 0 as *mut libc::c_int, ); if group_chat_id == 0i32 as libc::c_uint { - error!(context, 0, "Chat {} not found.", as_str(grpid),); + error!(context, 0, "Chat {} not found.", &grpid); current_block = 4378276786830486580; } else { chat::add_contact_to_chat_ex( @@ -595,7 +596,7 @@ pub unsafe fn dc_handle_securejoin_handshake( b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char, 0 as *const libc::c_char, 0 as *const libc::c_char, - 0 as *const libc::c_char, + "", ); context.call_cb( Event::SECUREJOIN_INVITER_PROGRESS, @@ -637,7 +638,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let scan = context.bob.clone().read().unwrap().qr_scan; scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint); if 0 != join_vg { - grpid = dc_strdup((*scan).text2) + grpid = to_string((*scan).text2); } } let mut vg_expect_encrypted: libc::c_int = 1i32; @@ -725,7 +726,7 @@ pub unsafe fn dc_handle_securejoin_handshake( as *const libc::c_char, 0 as *const libc::c_char, 0 as *const libc::c_char, - 0 as *const libc::c_char, + "", ); } end_bobs_joining(context, 1i32); @@ -784,7 +785,6 @@ pub unsafe fn dc_handle_securejoin_handshake( free(scanned_fingerprint_of_alice as *mut libc::c_void); free(auth as *mut libc::c_void); free(own_fingerprint as *mut libc::c_void); - free(grpid as *mut libc::c_void); ret } @@ -802,9 +802,8 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint } else { "?" }; - let msg = - CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap(); - chat::add_device_msg(context, contact_chat_id, msg.as_ptr()); + let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr); + chat::add_device_msg(context, contact_chat_id, msg); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, @@ -844,9 +843,9 @@ unsafe fn could_not_establish_secure_connection( "?" }, ); - let msg_c = CString::new(msg.as_str()).unwrap(); - chat::add_device_msg(context, contact_chat_id, msg_c.as_ptr()); - error!(context, 0, "{} ({})", msg, as_str(details)); + + chat::add_device_msg(context, contact_chat_id, &msg); + error!(context, 0, "{} ({})", &msg, as_str(details)); } unsafe fn mark_peer_as_verified( @@ -931,11 +930,9 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) Some(ref addr) => &addr, None => "", }; - let msg = CString::new( - context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr), - ) - .unwrap(); - chat::add_device_msg(context, contact_chat_id, msg.as_ptr()); + let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr); + + chat::add_device_msg(context, contact_chat_id, msg); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, From 13d8306a7b763bd0c79a9feeb6fc19e15cfdab0b Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 16 Aug 2019 01:03:58 +0200 Subject: [PATCH 8/9] refactor(chat): some minor cleanup and api improvements --- deltachat-ffi/src/lib.rs | 6 ++ examples/repl/cmdline.rs | 27 ++---- src/chat.rs | 192 ++++++++++++++++++++------------------- 3 files changed, 115 insertions(+), 110 deletions(-) diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 1fd78b42c..843d0cdba 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -642,6 +642,8 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat( let context = &*context; chat::remove_contact_from_chat(context, chat_id, contact_id) + .map(|_| 1) + .unwrap_or_log_default(context, "Failed to remove contact") } #[no_mangle] @@ -656,6 +658,8 @@ pub unsafe extern "C" fn dc_set_chat_name( let context = &*context; chat::set_chat_name(context, chat_id, as_str(name)) + .map(|_| 1) + .unwrap_or_log_default(context, "Failed to set chat name") } #[no_mangle] @@ -669,6 +673,8 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( let context = &*context; chat::set_chat_profile_image(context, chat_id, as_str(image)) + .map(|_| 1) + .unwrap_or_log_default(context, "Failed to set profile image") } #[no_mangle] diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 2a7d9ab01..0e9843616 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -755,35 +755,28 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 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 != chat::remove_contact_from_chat( + chat::remove_contact_from_chat( context, sel_chat.as_ref().unwrap().get_id(), contact_id_1 as uint32_t, - ) { - println!("Contact added to chat."); - } else { - bail!("Cannot remove member from chat."); - } + )?; + + println!("Contact added to chat."); } "groupname" => { ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1) { - println!("Chat name set"); - } else { - bail!("Failed to set chat name"); - } + chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1)?; + + println!("Chat name set"); } "groupimage" => { ensure!(sel_chat.is_some(), "No chat selected."); ensure!(!arg1.is_empty(), "Argument missing."); - if 0 != chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1) - { - println!("Chat image set"); - } else { - bail!("Failed to set chat image"); - } + chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1)?; + + println!("Chat image set"); } "chatinfo" => { ensure!(sel_chat.is_some(), "No chat selected."); diff --git a/src/chat.rs b/src/chat.rs index 211ee18f8..bdf3da164 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -579,7 +579,7 @@ pub fn create_by_msg_id(context: &Context, msg_id: u32) -> Result { if dc_msg_load_from_db(msg, context, msg_id) { if let Ok(chat) = Chat::load_from_db(context, unsafe { (*msg).chat_id }) { - if chat.id > 9 { + if chat.id > DC_CHAT_ID_LAST_SPECIAL as u32 { chat_id = chat.id; if chat.blocked != Blocked::Not { unblock(context, chat.id); @@ -1458,7 +1458,7 @@ pub unsafe fn add_contact_to_chat_ex( let mut success: libc::c_int = 0; let contact = Contact::get_by_id(context, contact_id); - if contact.is_err() || chat_id <= 9 { + if contact.is_err() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { return 0; } let mut msg = dc_msg_new_untyped(context); @@ -1469,7 +1469,7 @@ pub unsafe fn add_contact_to_chat_ex( /*this also makes sure, not contacts are added to special or normal chats*/ let chat = Chat::load_from_db(context, chat_id); - if !(0 == real_group_exists(context, chat_id) + if !(!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()) @@ -1550,11 +1550,10 @@ pub unsafe fn add_contact_to_chat_ex( success } -// TODO should return bool /rtn -fn real_group_exists(context: &Context, chat_id: u32) -> libc::c_int { +fn real_group_exists(context: &Context, chat_id: u32) -> bool { // check if a group or a verified group exists under the given ID - if !context.sql.is_open() || chat_id <= 9 { - return 02; + if !context.sql.is_open() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { + return false; } context @@ -1563,7 +1562,7 @@ fn real_group_exists(context: &Context, chat_id: u32) -> libc::c_int { "SELECT id FROM chats WHERE id=? AND (type=120 OR type=130);", params![chat_id as i32], ) - .unwrap_or_default() as libc::c_int + .unwrap_or_default() } pub fn reset_gossiped_timestamp(context: &Context, chat_id: u32) { @@ -1600,25 +1599,29 @@ pub fn set_gossiped_timestamp(context: &Context, chat_id: u32, timestamp: i64) { } } -// TODO should return bool /rtn pub unsafe fn remove_contact_from_chat( context: &Context, chat_id: u32, contact_id: u32, -) -> libc::c_int { - let mut success = 0; - - if chat_id <= 9 || contact_id <= 9 && contact_id != DC_CONTACT_ID_SELF as u32 { - return 0; - } +) -> Result<(), Error> { + ensure!( + chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, + "bad chat_id = {} <= 9", + chat_id + ); + ensure!( + contact_id != DC_CONTACT_ID_SELF as u32, + "Cannot remove self" + ); let mut msg = dc_msg_new_untyped(context); + let mut success = false; /* 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 = Chat::load_from_db(context, chat_id); - if !(0 == real_group_exists(context, chat_id) || chat.is_err()) { + if !(!real_group_exists(context, chat_id) || chat.is_err()) { let chat = chat.unwrap(); if !(is_contact_in_chat(context, chat_id, 1 as u32) == 1) { log_event!( @@ -1667,14 +1670,18 @@ pub unsafe fn remove_contact_from_chat( .is_ok() { context.call_cb(Event::CHAT_MODIFIED, chat_id as uintptr_t, 0 as uintptr_t); - success = 1; + success = true; } } } dc_msg_unref(msg); - success + if !success { + bail!("Failed to remove contact"); + } + + Ok(()) } fn set_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Result<(), Error> { @@ -1690,7 +1697,6 @@ fn set_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Resul Ok(()) } -// TODO should return bool /rtn pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Result { context.sql.exists( "SELECT id FROM leftgrps WHERE grpid=?;", @@ -1698,26 +1704,23 @@ pub fn is_group_explicitly_left(context: &Context, grpid: impl AsRef) -> Re ) } -// TODO should return bool /rtn pub unsafe fn set_chat_name( context: &Context, chat_id: u32, new_name: impl AsRef, -) -> libc::c_int { +) -> Result<(), Error> { /* 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 mut success = false; + + ensure!(!new_name.as_ref().is_empty(), "Invalid name"); + ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, "Invalid chat ID"); + + let chat = Chat::load_from_db(context, chat_id)?; let mut msg = dc_msg_new_untyped(context); - if new_name.as_ref().is_empty() || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { - return 0; - } - - let chat = Chat::load_from_db(context, chat_id); - - if !(0i32 == real_group_exists(context, chat_id) || chat.is_err()) { - let chat = chat.unwrap(); + if real_group_exists(context, chat_id) { if &chat.name == new_name.as_ref() { - success = 1; + success = true; } else if !(is_contact_in_chat(context, chat_id, 1) == 1) { log_event!( context, @@ -1763,97 +1766,100 @@ pub unsafe fn set_chat_name( chat_id as uintptr_t, 0i32 as uintptr_t, ); - success = 1i32; + success = true; } } } dc_msg_unref(msg); - success + if !success { + bail!("Failed to set name"); + } + + Ok(()) } -// TODO should return bool /rtn #[allow(non_snake_case)] pub unsafe fn set_chat_profile_image( context: &Context, chat_id: u32, new_image: impl AsRef, -) -> libc::c_int { +) -> Result<(), Error> { + ensure!(chat_id > DC_CHAT_ID_LAST_SPECIAL as u32, "Invalid chat ID"); + let mut OK_TO_CONTINUE = true; - let mut success: libc::c_int = 0i32; - - if chat_id <= 9 { - return 0; - } + let mut success = false; + let mut chat = Chat::load_from_db(context, chat_id)?; let mut msg = dc_msg_new_untyped(context); - if !(chat_id <= 9i32 as libc::c_uint) { - let mut new_image_rel = None; - let chat = Chat::load_from_db(context, chat_id); - if !(0i32 == real_group_exists(context, chat_id) || chat.is_err()) { - let mut chat = chat.unwrap(); - if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { - log_event!( - context, - Event::ERROR_SELF_NOT_IN_GROUP, - 0, - "Cannot set chat profile image; self not in group.", - ); - } else { - /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if !new_image.as_ref().is_empty() { - let mut img = new_image.as_ref().to_string(); - if !dc_make_rel_and_copy(context, &mut img) { - OK_TO_CONTINUE = false; - } - new_image_rel = Some(img); - } else { + let mut new_image_rel = None; + + if real_group_exists(context, chat_id) { + if !(is_contact_in_chat(context, chat_id, 1i32 as u32) == 1i32) { + log_event!( + context, + Event::ERROR_SELF_NOT_IN_GROUP, + 0, + "Cannot set chat profile image; self not in group.", + ); + } else { + /* we should respect this - whatever we send to the group, it gets discarded anyway! */ + if !new_image.as_ref().is_empty() { + let mut img = new_image.as_ref().to_string(); + if !dc_make_rel_and_copy(context, &mut img) { OK_TO_CONTINUE = false; } + new_image_rel = Some(img); + } else { + OK_TO_CONTINUE = false; } - if OK_TO_CONTINUE { - if let Some(ref new_image_rel) = new_image_rel { - chat.param.set(Param::ProfileImage, new_image_rel); - } - if chat.update_param().is_ok() { - if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).param.set_int(Param::Cmd, 3); - if let Some(ref new_image_rel) = new_image_rel { - (*msg).param.set(Param::Arg, new_image_rel); - } - (*msg).type_0 = Viewtype::Text; - (*msg).text = Some(context.stock_system_msg( - if new_image_rel.is_some() { - StockMessage::MsgGrpImgChanged - } else { - StockMessage::MsgGrpImgDeleted - }, - "", - "", - DC_CONTACT_ID_SELF as u32, - )); - (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); - context.call_cb( - Event::MSGS_CHANGED, - chat_id as uintptr_t, - (*msg).id as uintptr_t, - ); + } + if OK_TO_CONTINUE { + if let Some(ref new_image_rel) = new_image_rel { + chat.param.set(Param::ProfileImage, new_image_rel); + } + if chat.update_param().is_ok() { + if chat.param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { + (*msg).param.set_int(Param::Cmd, 3); + if let Some(ref new_image_rel) = new_image_rel { + (*msg).param.set(Param::Arg, new_image_rel); } + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( + if new_image_rel.is_some() { + StockMessage::MsgGrpImgChanged + } else { + StockMessage::MsgGrpImgDeleted + }, + "", + "", + DC_CONTACT_ID_SELF as u32, + )); + (*msg).id = send_msg(context, chat_id, msg).unwrap_or_default(); 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 = true; } } } dc_msg_unref(msg); - success + if !success { + bail!("Failed to set profile image"); + } + + Ok(()) } pub unsafe fn forward_msgs( @@ -1862,7 +1868,7 @@ pub unsafe fn forward_msgs( msg_cnt: libc::c_int, chat_id: u32, ) { - if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= 9 { + if msg_ids.is_null() || msg_cnt <= 0 || chat_id <= DC_CHAT_ID_LAST_SPECIAL as u32 { return; } From 4645a4300edcf778a98a313dccb3727151f2dd53 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 16 Aug 2019 11:25:02 +0200 Subject: [PATCH 9/9] refactor: remove unused methods --- src/dc_strencode.rs | 50 +++++---------------------------------------- src/dc_tools.rs | 5 ----- 2 files changed, 5 insertions(+), 50 deletions(-) diff --git a/src/dc_strencode.rs b/src/dc_strencode.rs index 4aff165d3..41b8bc553 100644 --- a/src/dc_strencode.rs +++ b/src/dc_strencode.rs @@ -18,48 +18,6 @@ fn isalnum(c: libc::c_int) -> libc::c_int { } } -pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char { - let mut pstr: *const libc::c_char = to_encode; - if to_encode.is_null() { - return dc_strdup(b"\x00" as *const u8 as *const libc::c_char); - } - let buf: *mut libc::c_char = - malloc(strlen(to_encode).wrapping_mul(3).wrapping_add(1)) as *mut libc::c_char; - let mut pbuf: *mut libc::c_char = buf; - assert!(!buf.is_null()); - - while 0 != *pstr { - if 0 != isalnum(*pstr as libc::c_int) - || *pstr as libc::c_int == '-' as i32 - || *pstr as libc::c_int == '_' as i32 - || *pstr as libc::c_int == '.' as i32 - || *pstr as libc::c_int == '~' as i32 - { - let fresh0 = pbuf; - pbuf = pbuf.offset(1); - *fresh0 = *pstr - } else if *pstr as libc::c_int == ' ' as i32 { - let fresh1 = pbuf; - pbuf = pbuf.offset(1); - *fresh1 = '+' as i32 as libc::c_char - } else { - let fresh2 = pbuf; - pbuf = pbuf.offset(1); - *fresh2 = '%' as i32 as libc::c_char; - let fresh3 = pbuf; - pbuf = pbuf.offset(1); - *fresh3 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char); - let fresh4 = pbuf; - pbuf = pbuf.offset(1); - *fresh4 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char) - } - pstr = pstr.offset(1isize) - } - *pbuf = '\u{0}' as i32 as libc::c_char; - - buf -} - /* ****************************************************************************** * URL encoding and decoding, RFC 3986 ******************************************************************************/ @@ -713,6 +671,7 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) { #[cfg(test)] mod tests { use super::*; + use percent_encoding::{percent_encode, NON_ALPHANUMERIC}; use std::ffi::CStr; #[test] @@ -892,14 +851,15 @@ mod tests { #[test] fn test_dc_urlencode_urldecode() { unsafe { - let buf1 = - dc_urlencode(b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char); + let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC) + .to_string() + .strdup(); assert_eq!( CStr::from_ptr(buf1 as *const libc::c_char) .to_str() .unwrap(), - "Bj%C3%B6rn+Petersen" + "Bj%C3%B6rn%20Petersen" ); let buf2 = dc_urldecode(buf1); diff --git a/src/dc_tools.rs b/src/dc_tools.rs index e4f320d1b..87d8ec9bb 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -167,11 +167,6 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) { dc_rtrim(buf); } -/* the result must be free()'d */ -unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char { - to_string(in_0).to_lowercase().strdup() -} - pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) { let raw = CString::yolo(to_string(in_0).to_lowercase()); assert_eq!(strlen(in_0), strlen(raw.as_ptr()));