diff --git a/src/config.rs b/src/config.rs index 3c55c1fb4..ff7aa0905 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,9 +4,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString}; use crate::constants::DC_VERSION_STR; use crate::context::Context; use crate::dc_job::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::error::Error; +use crate::stock::StockMessage; use crate::x::*; /// The available configuration keys. @@ -94,12 +94,7 @@ impl Context { // Default values match key { - Config::Selfstatus => { - let s = unsafe { dc_stock_str(self, 13) }; - let res = to_string(s); - unsafe { free(s as *mut _) }; - Some(res) - } + Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()), _ => key.get_str("default").map(|s| s.to_string()), } } @@ -129,15 +124,14 @@ impl Context { ret } Config::Selfstatus => { - let def = unsafe { dc_stock_str(self, 13) }; - let val = if value.is_none() || value.unwrap() == as_str(def) { + let def = self.stock_str(StockMessage::StatusLine); + let val = if value.is_none() || value.unwrap() == def { None } else { value }; let ret = self.sql.set_config(self, key, val); - unsafe { free(def as *mut libc::c_void) }; ret } _ => self.sql.set_config(self, key, value), diff --git a/src/dc_chat.rs b/src/dc_chat.rs index 0b4307484..8f7ac8ddf 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use crate::constants::*; use crate::context::Context; use crate::dc_array::*; @@ -6,9 +8,9 @@ use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::sql::{self, Sql}; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -167,27 +169,24 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { match c.id { 1 => unsafe { free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 8); + (*chat).name = to_cstring((*chat).context.stock_str(StockMessage::DeadDrop)); }, 6 => unsafe { free((*chat).name as *mut libc::c_void); - let tempname: *mut libc::c_char = dc_stock_str((*chat).context, 40); - (*chat).name = dc_mprintf( - b"%s (%i)\x00" as *const u8 as *const libc::c_char, - tempname, - dc_get_archived_cnt((*chat).context), - ); - free(tempname as *mut libc::c_void); + let tempname = (*chat).context.stock_str(StockMessage::ArchivedChats); + let cnt = dc_get_archived_cnt((*chat).context); + (*chat).name = to_cstring(format!("{} ({})", tempname, cnt)); }, 5 => unsafe { free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 41); + (*chat).name = to_cstring((*chat).context.stock_str(StockMessage::StarredMsgs)); }, _ => { if 0 != unsafe { dc_param_exists((*chat).param, DC_PARAM_SELFTALK as i32) } { unsafe { free((*chat).name as *mut libc::c_void); - (*chat).name = dc_stock_str((*chat).context, 2); + (*chat).name = + to_cstring((*chat).context.stock_str(StockMessage::SelfMsg)); } } } @@ -1498,10 +1497,10 @@ pub unsafe fn dc_create_group_chat( if chat_name.is_null() || *chat_name.offset(0) as libc::c_int == 0 { return 0; } - - let draft_txt = dc_stock_str_repl_string(context, 14, chat_name); + let draft_txt = + CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, as_str(chat_name))) + .unwrap(); let grpid = as_str(dc_create_id()); - if sql::execute( context, &context.sql, @@ -1518,15 +1517,12 @@ pub unsafe fn dc_create_group_chat( if chat_id != 0 { if 0 != dc_add_to_chat_contacts_table(context, chat_id, 1) { let draft_msg = dc_msg_new(context, 10); - dc_msg_set_text(draft_msg, draft_txt); + dc_msg_set_text(draft_msg, draft_txt.as_ptr()); set_draft_raw(context, chat_id, draft_msg); dc_msg_unref(draft_msg); } } } - - free(draft_txt as *mut libc::c_void); - if 0 != chat_id { context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); } @@ -1633,13 +1629,12 @@ pub unsafe fn dc_add_contact_to_chat_ex( if OK_TO_CONTINUE { if dc_param_get_int((*chat).param, DC_PARAM_UNPROMOTED as i32, 0) == 0 { (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = dc_stock_system_msg( - context, - 17, - (*contact).addr, - 0 as *const libc::c_char, - 1 as uint32_t, - ); + (*msg).text = to_cstring(context.stock_system_msg( + StockMessage::MsgAddMember, + as_str((*contact).addr), + "", + DC_CONTACT_ID_SELF as uint32_t, + )); dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 4); dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, (*contact).addr); dc_param_set_int((*msg).param, DC_PARAM_CMD_ARG2 as i32, flags); @@ -1745,21 +1740,19 @@ pub unsafe fn dc_remove_contact_from_chat( (*msg).type_0 = DC_MSG_TEXT; if (*contact).id == 1 as libc::c_uint { dc_set_group_explicitly_left(context, (*chat).grpid); - (*msg).text = dc_stock_system_msg( - context, - 19, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 1 as uint32_t, - ) + (*msg).text = to_cstring(context.stock_system_msg( + StockMessage::MsgGroupLeft, + "", + "", + DC_CONTACT_ID_SELF as u32, + )); } else { - (*msg).text = dc_stock_system_msg( - context, - 18, - (*contact).addr, - 0 as *const libc::c_char, - 1 as uint32_t, - ) + (*msg).text = to_cstring(context.stock_system_msg( + StockMessage::MsgDelMember, + as_str((*contact).addr), + "", + DC_CONTACT_ID_SELF as u32, + )); } dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 5); dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, (*contact).addr); @@ -1858,14 +1851,13 @@ pub unsafe fn dc_set_chat_name( { if dc_param_get_int((*chat).param, DC_PARAM_UNPROMOTED as i32, 0i32) == 0i32 { (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = dc_stock_system_msg( - context, - 15i32, - (*chat).name, - new_name, - 1i32 as uint32_t, - ); - dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 2i32); + (*msg).text = to_cstring(context.stock_system_msg( + StockMessage::MsgGrpName, + as_str((*chat).name), + as_str(new_name), + DC_CONTACT_ID_SELF as u32, + )); + dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 2); dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, (*chat).name); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( @@ -1927,17 +1919,16 @@ pub unsafe fn dc_set_chat_profile_image( dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 3i32); dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, new_image_rel); (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = dc_stock_system_msg( - context, + (*msg).text = to_cstring(context.stock_system_msg( if !new_image_rel.is_null() { - 16i32 + StockMessage::MsgGrpImgChanged } else { - 33i32 + StockMessage::MsgGrpImgDeleted }, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 1i32 as uint32_t, - ); + "", + "", + DC_CONTACT_ID_SELF as uint32_t, + )); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( Event::MSGS_CHANGED, @@ -2116,7 +2107,7 @@ 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 && 0 != dc_param_exists((*chat).param, DC_PARAM_SELFTALK as i32) { - ret = dc_stock_str((*chat).context, 50) + ret = to_cstring((*chat).context.stock_str(StockMessage::SelfTalkSubTitle)); } else if (*chat).type_0 == 100 { let ret_raw: String = (*chat) .context @@ -2133,10 +2124,14 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { ret = to_cstring(ret_raw); } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { if (*chat).id == 1 { - ret = dc_stock_str((*chat).context, 8) + ret = to_cstring((*chat).context.stock_str(StockMessage::DeadDrop)); } else { let cnt = dc_get_chat_contact_cnt((*chat).context, (*chat).id); - ret = dc_stock_str_repl_int((*chat).context, 4, cnt) + ret = to_cstring( + (*chat) + .context + .stock_string_repl_int(StockMessage::Member, cnt), + ); } } return if !ret.is_null() { diff --git a/src/dc_chatlist.rs b/src/dc_chatlist.rs index 555416e3d..15df6aad3 100644 --- a/src/dc_chatlist.rs +++ b/src/dc_chatlist.rs @@ -4,8 +4,8 @@ use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_lot::*; use crate::dc_msg::*; -use crate::dc_stock::*; use crate::dc_tools::*; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -375,7 +375,8 @@ pub unsafe fn dc_chatlist_get_summary<'a>( if (*chat).id == 6i32 as libc::c_uint { (*ret).text2 = dc_strdup(0 as *const libc::c_char) } else if lastmsg.is_null() || (*lastmsg).from_id == 0i32 as libc::c_uint { - (*ret).text2 = dc_stock_str((*chatlist).context, 1i32) + (*ret).text2 = + to_cstring((*chatlist).context.stock_str(StockMessage::NoMessages)); } else { dc_lot_fill(ret, lastmsg, chat, lastcontact, (*chatlist).context); } diff --git a/src/dc_contact.rs b/src/dc_contact.rs index 9fc72ba80..4a3a62afd 100644 --- a/src/dc_contact.rs +++ b/src/dc_contact.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use crate::aheader::EncryptPreference; use crate::config; use crate::constants::*; @@ -5,11 +7,11 @@ use crate::context::Context; use crate::dc_array::*; use crate::dc_e2ee::*; use crate::dc_loginparam::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::key::*; use crate::peerstate::*; use crate::sql::{self, Sql}; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -276,7 +278,7 @@ pub unsafe fn dc_contact_load_from_db( if contact_id == 1 as libc::c_uint { (*contact).id = contact_id; - (*contact).name = dc_stock_str((*contact).context, 2); + (*contact).name = to_cstring((*contact).context.stock_str(StockMessage::SelfMsg)); (*contact).addr = to_cstring( (*contact) .context @@ -575,16 +577,15 @@ pub fn dc_get_contacts( .get_config(context, "displayname") .unwrap_or_default(); - let self_name2 = unsafe { dc_stock_str(context, 2) }; + let self_name2 = CString::new(context.stock_str(StockMessage::SelfMsg).as_ref()).unwrap(); if query.is_null() || self_addr.contains(as_str(query)) || self_name.contains(as_str(query)) - || 0 != unsafe { dc_str_contains(self_name2, query) } + || 0 != unsafe { dc_str_contains(self_name2.as_ptr(), query) } { add_self = true; } - unsafe { free(self_name2 as *mut _) }; } else { add_self = true; @@ -650,7 +651,6 @@ pub unsafe fn dc_get_contact_encrinfo( let mut fingerprint_self = 0 as *mut libc::c_char; let mut fingerprint_other_verified = 0 as *mut libc::c_char; let mut fingerprint_other_unverified = 0 as *mut libc::c_char; - let mut p: *mut libc::c_char; if !(!dc_contact_load_from_db(contact, &context.sql, contact_id)) { let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); @@ -660,23 +660,18 @@ pub unsafe fn dc_get_contact_encrinfo( if peerstate.is_some() && peerstate.as_ref().and_then(|p| p.peek_key(0)).is_some() { let peerstate = peerstate.as_ref().unwrap(); - p = dc_stock_str( - context, - if peerstate.prefer_encrypt == EncryptPreference::Mutual { - 34i32 - } else { - 25i32 - }, - ); - ret += as_str(p); - free(p as *mut libc::c_void); + let p = context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual { + StockMessage::E2ePreferred + } else { + StockMessage::E2eAvailable + }); + ret += &p; if self_key.is_none() { dc_ensure_secret_key_exists(context); self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); } - p = dc_stock_str(context, 30i32); - ret += &format!(" {}:", as_str(p)); - free(p as *mut libc::c_void); + let p = context.stock_str(StockMessage::FingerPrints); + ret += &format!(" {}:", p); fingerprint_self = self_key .map(|k| k.formatted_fingerprint_c()) @@ -717,13 +712,9 @@ pub unsafe fn dc_get_contact_encrinfo( ); } } else if 0 == loginparam.server_flags & 0x400 && 0 == loginparam.server_flags & 0x40000 { - p = dc_stock_str(context, 27); - ret += as_str(p); - free(p as *mut libc::c_void); + ret += &context.stock_str(StockMessage::EncrTransp); } else { - p = dc_stock_str(context, 28); - ret += as_str(p); - free(p as *mut libc::c_void); + ret += &context.stock_str(StockMessage::EncrNone); } } diff --git a/src/dc_imex.rs b/src/dc_imex.rs index 408806f40..b2bf84452 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -14,11 +14,11 @@ use crate::dc_e2ee::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::key::*; use crate::pgp::*; use crate::sql::{self, Sql}; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -260,25 +260,19 @@ pub unsafe extern "C" fn dc_render_setup_file( replacement, ); free(replacement as *mut libc::c_void); - let setup_message_title: *mut libc::c_char = dc_stock_str(context, 42i32); - let mut setup_message_body: *mut libc::c_char = dc_stock_str(context, 43i32); - dc_str_replace( - &mut setup_message_body, - b"\r\x00" as *const u8 as *const libc::c_char, - 0 as *const libc::c_char, - ); - dc_str_replace( - &mut setup_message_body, - b"\n\x00" as *const u8 as *const libc::c_char, - b"
\x00" as *const u8 as *const libc::c_char, - ); + let setup_message_title = + CString::new(context.stock_str(StockMessage::AcSetupMsgSubject).as_ref()) + .unwrap(); + let setup_message_body = context.stock_str(StockMessage::AcSetupMsgBody); + let msg_body_head: &str = setup_message_body.split('\r').next().unwrap(); + let msg_body_html = CString::new(msg_body_head.replace("\n", "
")).unwrap(); ret_setupfilecontent = dc_mprintf(b"\r\n\r\n\r\n%s\r\n\r\n\r\n

%s

\r\n

%s

\r\n
\r\n%s\r\n
\r\n\r\n\r\n\x00" - as *const u8 as *const libc::c_char, - setup_message_title, setup_message_title, - setup_message_body, encr_string); - free(setup_message_title as *mut libc::c_void); - free(setup_message_body as *mut libc::c_void); + as *const u8 as *const libc::c_char, + setup_message_title.as_ptr(), + setup_message_title.as_ptr(), + msg_body_html.as_ptr(), + encr_string); free(encr_string as *mut libc::c_void); } } diff --git a/src/dc_location.rs b/src/dc_location.rs index 542180d1f..2a89d84ab 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use crate::constants::Event; use crate::context::*; use crate::dc_array::*; @@ -6,9 +8,9 @@ use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_saxparser::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::sql; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -71,7 +73,6 @@ pub unsafe fn dc_send_locations_to_chat( ) { let now = time(); let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t; - let mut stock_str: *mut libc::c_char = 0 as *mut libc::c_char; let is_sending_locations_before: bool; if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) { is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id); @@ -96,24 +97,23 @@ pub unsafe fn dc_send_locations_to_chat( { if 0 != seconds && !is_sending_locations_before { msg = dc_msg_new(context, 10i32); - (*msg).text = dc_stock_system_msg( - context, - 64, - 0 as *const libc::c_char, - 0 as *const libc::c_char, + (*msg).text = to_cstring(context.stock_system_msg( + StockMessage::MsgLocationEnabled, + "", + "", 0, - ); + )); dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 8); dc_send_msg(context, chat_id, msg); } else if 0 == seconds && is_sending_locations_before { - stock_str = dc_stock_system_msg( - context, - 65i32, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0i32 as uint32_t, - ); - dc_add_device_msg(context, chat_id, stock_str); + let stock_str = CString::new(context.stock_system_msg( + StockMessage::MsgLocationDisabled, + "", + "", + 0, + )) + .unwrap(); + dc_add_device_msg(context, chat_id, stock_str.as_ptr()); } context.call_cb( Event::CHAT_MODIFIED, @@ -132,7 +132,6 @@ pub unsafe fn dc_send_locations_to_chat( } } } - free(stock_str as *mut libc::c_void); dc_msg_unref(msg); } @@ -736,7 +735,6 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut // if so, a device-message is added if not yet done. let chat_id = (*job).foreign_id; - let mut stock_str = 0 as *mut libc::c_char; if let Ok((send_begin, send_until)) = context.sql.query_row( "SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?", @@ -753,14 +751,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() { - stock_str = dc_stock_system_msg( - context, - 65, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - 0, - ); - dc_add_device_msg(context, chat_id, stock_str); + let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap(); + dc_add_device_msg(context, chat_id, stock_str.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, chat_id as usize, @@ -770,5 +762,4 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut } } } - free(stock_str as *mut libc::c_void); } diff --git a/src/dc_lot.rs b/src/dc_lot.rs index cb1287e6f..69c517a1d 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -2,8 +2,8 @@ use crate::context::Context; use crate::dc_chat::*; use crate::dc_contact::*; use crate::dc_msg::*; -use crate::dc_stock::*; use crate::dc_tools::*; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -134,14 +134,14 @@ pub unsafe fn dc_lot_fill( return; } if (*msg).state == 19i32 { - (*lot).text1 = dc_stock_str(context, 3i32); + (*lot).text1 = to_cstring(context.stock_str(StockMessage::Draft)); (*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) { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else { - (*lot).text1 = dc_stock_str(context, 2i32); + (*lot).text1 = to_cstring(context.stock_str(StockMessage::SelfMsg)); (*lot).text1_meaning = 3i32 } } else if chat.is_null() { diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 3c3ab5a1a..7fc45bb1f 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use chrono::TimeZone; use mmime::mailimf_types::*; use mmime::mailimf_types_helper::*; @@ -16,9 +18,9 @@ use crate::dc_e2ee::*; use crate::dc_location::*; use crate::dc_msg::*; use crate::dc_param::*; -use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_tools::*; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -282,7 +284,7 @@ unsafe fn load_from(mut factory: *mut dc_mimefactory_t) { .unwrap_or_default(), ); if (*factory).selfstatus.is_null() { - (*factory).selfstatus = dc_stock_str((*factory).context, 13) + (*factory).selfstatus = to_cstring((*factory).context.stock_str(StockMessage::StatusLine)); }; } @@ -677,7 +679,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: strdup(b"v1\x00" as *const u8 as *const libc::c_char), ), ); - placeholdertext = dc_stock_str((*factory).context, 43) + placeholdertext = + to_cstring((*factory).context.stock_str(StockMessage::AcSetupMsgBody)); } if command == 7 { let step: *mut libc::c_char = dc_param_get( @@ -992,14 +995,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: let p1: *mut libc::c_char; let p2: *mut libc::c_char; if 0 != dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0) { - p1 = dc_stock_str((*factory).context, 24) + p1 = to_cstring((*factory).context.stock_str(StockMessage::EncryptedMsg)); } else { p1 = dc_msg_get_summarytext((*factory).msg, 32) } - p2 = dc_stock_str_repl_string((*factory).context, 32, p1); + p2 = to_cstring( + (*factory) + .context + .stock_string_repl_str(StockMessage::ReadRcptMailBody, as_str(p1)), + ); message_text = dc_mprintf(b"%s\r\n\x00" as *const u8 as *const libc::c_char, p2); - free(p2 as *mut libc::c_void); free(p1 as *mut libc::c_void); + free(p2 as *mut libc::c_void); let human_mime_part: *mut mailmime = build_body_text(message_text); mailmime_add_part(multipart, human_mime_part); message_text2 = @@ -1031,10 +1038,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: if (*factory).loaded as libc::c_uint == DC_MF_MDN_LOADED as libc::c_int as libc::c_uint { - let e: *mut libc::c_char = dc_stock_str((*factory).context, 31); - subject_str = - dc_mprintf(b"Chat: %s\x00" as *const u8 as *const libc::c_char, e); - free(e as *mut libc::c_void); + let e = CString::new( + (*factory) + .context + .stock_str(StockMessage::ReadRcpt) + .as_ref(), + ) + .unwrap(); + subject_str = dc_mprintf( + b"Chat: %s\x00" as *const u8 as *const libc::c_char, + e.as_ptr(), + ); } else { subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email) } @@ -1118,7 +1132,7 @@ unsafe fn get_subject( b"\x00" as *const u8 as *const libc::c_char }; if dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0) == 6 { - ret = dc_stock_str(context, 42) + ret = to_cstring(context.stock_str(StockMessage::AcSetupMsgSubject)) } 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 { diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index b7a1ea1cb..6a7cd3040 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -1,5 +1,5 @@ use std::collections::{HashMap, HashSet}; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use charset::Charset; use mmime::mailimf::*; @@ -17,9 +17,9 @@ use crate::dc_e2ee::*; use crate::dc_location::*; use crate::dc_param::*; use crate::dc_simplify::*; -use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_tools::*; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -667,11 +667,18 @@ unsafe fn dc_mimeparser_parse_mime_recursive( 40 => { let mut part: *mut dc_mimepart_t = dc_mimepart_new(); (*part).type_0 = 10i32; - let msg_body: *mut libc::c_char = dc_stock_str((*mimeparser).context, 29i32); - (*part).msg = - dc_mprintf(b"[%s]\x00" as *const u8 as *const libc::c_char, msg_body); + let msg_body = CString::new( + (*mimeparser) + .context + .stock_str(StockMessage::CantDecryptMsgBody) + .as_ref(), + ) + .unwrap(); + (*part).msg = dc_mprintf( + b"[%s]\x00" as *const u8 as *const libc::c_char, + msg_body.as_ptr(), + ); (*part).msg_raw = dc_strdup((*part).msg); - free(msg_body as *mut libc::c_void); carray_add( (*mimeparser).parts, part as *mut libc::c_void, diff --git a/src/dc_msg.rs b/src/dc_msg.rs index 79e01046f..2282ff7a4 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use crate::constants::*; use crate::context::*; use crate::dc_chat::*; @@ -6,10 +8,10 @@ use crate::dc_job::*; use crate::dc_lot::dc_lot_t; use crate::dc_lot::*; use crate::dc_param::*; -use crate::dc_stock::*; use crate::dc_tools::*; use crate::pgp::*; use crate::sql; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -840,17 +842,16 @@ pub unsafe fn dc_msg_get_summarytext_by_raw( let mut ret; let mut prefix: *mut libc::c_char = 0 as *mut libc::c_char; let mut pathNfilename: *mut libc::c_char = 0 as *mut libc::c_char; - let mut label: *mut libc::c_char = 0 as *mut libc::c_char; let mut value: *mut libc::c_char = 0 as *mut libc::c_char; let mut append_text: libc::c_int = 1i32; match type_0 { - 20 => prefix = dc_stock_str(context, 9i32), - 21 => prefix = dc_stock_str(context, 23i32), - 50 => prefix = dc_stock_str(context, 10i32), - 41 => prefix = dc_stock_str(context, 7i32), + 20 => prefix = to_cstring(context.stock_str(StockMessage::Image)), + 21 => prefix = to_cstring(context.stock_str(StockMessage::Gif)), + 50 => prefix = to_cstring(context.stock_str(StockMessage::Video)), + 41 => prefix = to_cstring(context.stock_str(StockMessage::VoiceMessage)), 40 | 60 => { - if dc_param_get_int(param, DC_PARAM_CMD as i32, 0) == 6i32 { - prefix = dc_stock_str(context, 42i32); + if dc_param_get_int(param, DC_PARAM_CMD as i32, 0) == 6 { + prefix = to_cstring(context.stock_str(StockMessage::AcSetupMsgSubject)); append_text = 0i32 } else { pathNfilename = dc_param_get( @@ -859,24 +860,26 @@ pub unsafe fn dc_msg_get_summarytext_by_raw( b"ErrFilename\x00" as *const u8 as *const libc::c_char, ); value = dc_get_filename(pathNfilename); - label = dc_stock_str( - context, - if type_0 == DC_MSG_AUDIO as libc::c_int { - 11i32 - } else { - 12i32 - }, - ); + let label = CString::new( + context + .stock_str(if type_0 == DC_MSG_AUDIO { + StockMessage::Audio + } else { + StockMessage::File + }) + .as_ref(), + ) + .unwrap(); prefix = dc_mprintf( b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char, - label, + label.as_ptr(), value, ) } } _ => { if dc_param_get_int(param, DC_PARAM_CMD as i32, 0) == 9i32 { - prefix = dc_stock_str(context, 66i32); + prefix = to_cstring(context.stock_str(StockMessage::Location)); append_text = 0i32 } } @@ -901,7 +904,6 @@ pub unsafe fn dc_msg_get_summarytext_by_raw( } free(prefix as *mut libc::c_void); free(pathNfilename as *mut libc::c_void); - free(label as *mut libc::c_void); free(value as *mut libc::c_void); if ret.is_null() { ret = dc_strdup(0 as *const libc::c_char) diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index d3775c957..3a346a8de 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -19,11 +19,11 @@ use crate::dc_move::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_securejoin::*; -use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_tools::*; use crate::peerstate::*; use crate::sql; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -944,18 +944,13 @@ unsafe fn create_or_lookup_group( let mut X_MrAddToGrp: *mut libc::c_char = 0 as *mut libc::c_char; let mut X_MrGrpNameChanged: libc::c_int = 0; let mut X_MrGrpImageChanged: *const libc::c_char = 0 as *const libc::c_char; - let mut better_msg: *mut libc::c_char = 0 as *mut libc::c_char; + let mut better_msg: String = From::from(""); let mut failure_reason: *mut libc::c_char = 0 as *mut libc::c_char; - if mime_parser.is_system_message == 8 { - better_msg = dc_stock_system_msg( - context, - 64, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - from_id as uint32_t, - ) + if mime_parser.is_system_message == 8i32 { + better_msg = + context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32) } - set_better_msg(mime_parser, &mut better_msg); + set_better_msg(mime_parser, &better_msg); /* search the grpid in the header */ let mut field: *mut mailimf_field; let mut optional_field: *mut mailimf_optional_field; @@ -1034,12 +1029,15 @@ unsafe fn create_or_lookup_group( let left_group: libc::c_int = (dc_lookup_contact_id_by_addr(context, X_MrRemoveFromGrp) == from_id as libc::c_uint) as libc::c_int; - better_msg = dc_stock_system_msg( - context, - if 0 != left_group { 19 } else { 18 }, - X_MrRemoveFromGrp, - 0 as *const libc::c_char, - from_id as uint32_t, + better_msg = context.stock_system_msg( + if 0 != left_group { + StockMessage::MsgGroupLeft + } else { + StockMessage::MsgDelMember + }, + as_str(X_MrRemoveFromGrp), + "", + from_id as u32, ) } else { optional_field = dc_mimeparser_lookup_optional_field( @@ -1056,12 +1054,11 @@ unsafe fn create_or_lookup_group( if !optional_field.is_null() { X_MrGrpImageChanged = (*optional_field).fld_value } - better_msg = dc_stock_system_msg( - context, - 17, - X_MrAddToGrp, - 0 as *const libc::c_char, - from_id as uint32_t, + better_msg = context.stock_system_msg( + StockMessage::MsgAddMember, + as_str(X_MrAddToGrp), + "", + from_id as u32, ) } else { optional_field = dc_mimeparser_lookup_optional_field( @@ -1069,14 +1066,13 @@ unsafe fn create_or_lookup_group( b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char, ); if !optional_field.is_null() { - X_MrGrpNameChanged = 1; - mime_parser.is_system_message = 2; - better_msg = dc_stock_system_msg( - context, - 15, - (*optional_field).fld_value, - grpname, - from_id as uint32_t, + X_MrGrpNameChanged = 1i32; + mime_parser.is_system_message = 2i32; + better_msg = context.stock_system_msg( + StockMessage::MsgGrpName, + as_str((*optional_field).fld_value), + as_str(grpname), + from_id as u32, ) } else { optional_field = dc_mimeparser_lookup_optional_field( @@ -1086,26 +1082,25 @@ unsafe fn create_or_lookup_group( if !optional_field.is_null() { X_MrGrpImageChanged = (*optional_field).fld_value; mime_parser.is_system_message = 3; - better_msg = dc_stock_system_msg( - context, + better_msg = context.stock_system_msg( if strcmp( X_MrGrpImageChanged, b"0\x00" as *const u8 as *const libc::c_char, ) == 0 { - 33 + StockMessage::MsgGrpImgDeleted } else { - 16 + StockMessage::MsgGrpImgChanged }, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - from_id as uint32_t, + "", + "", + from_id as u32, ) } } } } - set_better_msg(mime_parser, &mut better_msg); + set_better_msg(mime_parser, &better_msg); chat_id = dc_get_chat_id_by_grpid( context, grpid, @@ -1345,8 +1340,6 @@ unsafe fn create_or_lookup_group( } free(grpid as *mut libc::c_void); free(grpname as *mut libc::c_void); - - free(better_msg as *mut libc::c_void); free(failure_reason as *mut libc::c_void); if !ret_chat_id.is_null() { *ret_chat_id = chat_id @@ -1432,11 +1425,10 @@ unsafe fn create_or_lookup_adhoc_group( { grpname = dc_strdup(mime_parser.subject) } else { - grpname = dc_stock_str_repl_int( - context, - 4, + grpname = to_cstring(context.stock_string_repl_int( + StockMessage::Member, dc_array_get_cnt(member_ids) as libc::c_int, - ) + )); } chat_id = create_group_record(context, grpid, grpname, create_blocked, 0); @@ -1732,14 +1724,14 @@ unsafe fn check_verified_properties( 1 } -unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) { - if !(*better_msg).is_null() && carray_count((*mime_parser).parts) > 0 as libc::c_uint { +unsafe fn set_better_msg>(mime_parser: &dc_mimeparser_t, better_msg: T) { + let msg = better_msg.as_ref(); + if !(msg.len() > 0) && carray_count((*mime_parser).parts) > 0 { let mut part: *mut dc_mimepart_t = carray_get(mime_parser.parts, 0 as libc::c_uint) as *mut dc_mimepart_t; if (*part).type_0 == 10 { free((*part).msg as *mut libc::c_void); - (*part).msg = *better_msg; - *better_msg = 0 as *mut libc::c_char + (*part).msg = to_cstring(msg); } }; } diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 41c90238b..bac1148c2 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -1,3 +1,5 @@ +use std::ffi::CString; + use mmime::mailimf_types::*; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; @@ -14,12 +16,12 @@ use crate::dc_mimeparser::*; use crate::dc_msg::*; use crate::dc_param::*; use crate::dc_qr::*; -use crate::dc_stock::*; use crate::dc_strencode::*; use crate::dc_token::*; use crate::dc_tools::*; use crate::key::*; use crate::peerstate::*; +use crate::stock::StockMessage; use crate::types::*; use crate::x::*; @@ -797,22 +799,21 @@ unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) { unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) { let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id); let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); - let msg: *mut libc::c_char = dc_stock_str_repl_string( - context, - 35i32, + let msg = CString::new(context.stock_string_repl_str( + StockMessage::ContactVerified, if !contact.is_null() { - (*contact).addr + as_str((*contact).addr) } else { - b"?\x00" as *const u8 as *const libc::c_char + "?" }, - ); - dc_add_device_msg(context, contact_chat_id, msg); + )) + .unwrap(); + dc_add_device_msg(context, contact_chat_id, msg.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, 0i32 as uintptr_t, ); - free(msg as *mut libc::c_void); dc_contact_unref(contact); } @@ -840,18 +841,17 @@ unsafe fn could_not_establish_secure_connection( ) { let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id); let contact = dc_get_contact(context, contact_id); - let msg: *mut libc::c_char = dc_stock_str_repl_string( - context, - 36i32, + let msg = context.stock_string_repl_str( + StockMessage::ContactNotVerified, if !contact.is_null() { - (*contact).addr + as_str((*contact).addr) } else { - b"?\x00" as *const u8 as *const libc::c_char + "?" }, ); - dc_add_device_msg(context, contact_chat_id, msg); - error!(context, 0, "{} ({})", as_str(msg), to_string(details),); - free(msg as *mut libc::c_void); + let msg_c = CString::new(msg.as_str()).unwrap(); + dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr()); + error!(context, 0, "{} ({})", msg, as_str(details)); dc_contact_unref(contact); } @@ -939,15 +939,15 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) &mut contact_chat_id, 0 as *mut libc::c_int, ); - let c_addr_ptr = if let Some(ref addr) = peerstate.addr { - to_cstring(addr) - } else { - std::ptr::null_mut() + let peeraddr: &str = match peerstate.addr { + Some(ref addr) => &addr, + None => "", }; - let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr); - dc_add_device_msg(context, contact_chat_id, msg); - free(msg as *mut libc::c_void); - free(c_addr_ptr as *mut _); + let msg = CString::new( + context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr), + ) + .unwrap(); + dc_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_stock.rs b/src/dc_stock.rs deleted file mode 100644 index 7e870939f..000000000 --- a/src/dc_stock.rs +++ /dev/null @@ -1,338 +0,0 @@ -use crate::constants::Event; -use crate::context::Context; -use crate::dc_contact::*; -use crate::dc_tools::*; -use crate::types::*; -use crate::x::*; - -/* Return the string with the given ID by calling DC_EVENT_GET_STRING. -The result must be free()'d! */ -pub unsafe fn dc_stock_str(context: &Context, id: libc::c_int) -> *mut libc::c_char { - return get_string(context, id, 0i32); -} - -unsafe fn get_string(context: &Context, id: libc::c_int, qty: libc::c_int) -> *mut libc::c_char { - let mut ret: *mut libc::c_char; - - ret = - context.call_cb(Event::GET_STRING, id as uintptr_t, qty as uintptr_t) as *mut libc::c_char; - - if ret.is_null() { - ret = default_string(id) - } - - ret -} - -/* Add translated strings that are used by the messager backend. -As the logging functions may use these strings, do not log any -errors from here. */ -unsafe fn default_string(id: libc::c_int) -> *mut libc::c_char { - // TODO match on enum values /rtn - match id { - 1 => { - return dc_strdup(b"No messages.\x00" as *const u8 as - *const libc::c_char) - } - 2 => { - return dc_strdup(b"Me\x00" as *const u8 as *const libc::c_char) - } - 3 => { - return dc_strdup(b"Draft\x00" as *const u8 as *const libc::c_char) - } - 4 => { - return dc_strdup(b"%1$s member(s)\x00" as *const u8 as - *const libc::c_char) - } - 6 => { - return dc_strdup(b"%1$s contact(s)\x00" as *const u8 as - *const libc::c_char) - } - 7 => { - return dc_strdup(b"Voice message\x00" as *const u8 as - *const libc::c_char) - } - 8 => { - return dc_strdup(b"Contact requests\x00" as *const u8 as - *const libc::c_char) - } - 9 => { - return dc_strdup(b"Image\x00" as *const u8 as *const libc::c_char) - } - 23 => { - return dc_strdup(b"GIF\x00" as *const u8 as *const libc::c_char) - } - 10 => { - return dc_strdup(b"Video\x00" as *const u8 as *const libc::c_char) - } - 11 => { - return dc_strdup(b"Audio\x00" as *const u8 as *const libc::c_char) - } - 12 => { - return dc_strdup(b"File\x00" as *const u8 as *const libc::c_char) - } - 66 => { - return dc_strdup(b"Location\x00" as *const u8 as - *const libc::c_char) - } - 24 => { - return dc_strdup(b"Encrypted message\x00" as *const u8 as - *const libc::c_char) - } - 13 => { - return dc_strdup(b"Sent with my Delta Chat Messenger: https://delta.chat\x00" - as *const u8 as *const libc::c_char) - } - 14 => { - return dc_strdup(b"Hello, I\'ve just created the group \"%1$s\" for us.\x00" - as *const u8 as *const libc::c_char) - } - 15 => { - return dc_strdup(b"Group name changed from \"%1$s\" to \"%2$s\".\x00" - as *const u8 as *const libc::c_char) - } - 16 => { - return dc_strdup(b"Group image changed.\x00" as *const u8 as - *const libc::c_char) - } - 17 => { - return dc_strdup(b"Member %1$s added.\x00" as *const u8 as - *const libc::c_char) - } - 18 => { - return dc_strdup(b"Member %1$s removed.\x00" as *const u8 as - *const libc::c_char) - } - 19 => { - return dc_strdup(b"Group left.\x00" as *const u8 as - *const libc::c_char) - } - 64 => { - return dc_strdup(b"Location streaming enabled.\x00" as *const u8 - as *const libc::c_char) - } - 65 => { - return dc_strdup(b"Location streaming disabled.\x00" as *const u8 - as *const libc::c_char) - } - 62 => { - return dc_strdup(b"%1$s by %2$s.\x00" as *const u8 as - *const libc::c_char) - } - 63 => { - return dc_strdup(b"%1$s by me.\x00" as *const u8 as - *const libc::c_char) - } - 25 => { - return dc_strdup(b"End-to-end encryption available.\x00" as - *const u8 as *const libc::c_char) - } - 27 => { - return dc_strdup(b"Transport-encryption.\x00" as *const u8 as - *const libc::c_char) - } - 28 => { - return dc_strdup(b"No encryption.\x00" as *const u8 as - *const libc::c_char) - } - 30 => { - return dc_strdup(b"Fingerprints\x00" as *const u8 as - *const libc::c_char) - } - 31 => { - return dc_strdup(b"Return receipt\x00" as *const u8 as - *const libc::c_char) - } - 32 => { - return dc_strdup(b"This is a return receipt for the message \"%1$s\".\x00" - as *const u8 as *const libc::c_char) - } - 33 => { - return dc_strdup(b"Group image deleted.\x00" as *const u8 as - *const libc::c_char) - } - 34 => { - return dc_strdup(b"End-to-end encryption preferred.\x00" as - *const u8 as *const libc::c_char) - } - 35 => { - return dc_strdup(b"%1$s verified.\x00" as *const u8 as - *const libc::c_char) - } - 36 => { - return dc_strdup(b"Cannot verifiy %1$s\x00" as *const u8 as - *const libc::c_char) - } - 37 => { - return dc_strdup(b"Changed setup for %1$s\x00" as *const u8 as - *const libc::c_char) - } - 40 => { - return dc_strdup(b"Archived chats\x00" as *const u8 as - *const libc::c_char) - } - 41 => { - return dc_strdup(b"Starred messages\x00" as *const u8 as - *const libc::c_char) - } - 42 => { - return dc_strdup(b"Autocrypt Setup Message\x00" as *const u8 as - *const libc::c_char) - } - 43 => { - return dc_strdup(b"This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device.\x00" - as *const u8 as *const libc::c_char) - } - 50 => { - return dc_strdup(b"Messages I sent to myself\x00" as *const u8 as - *const libc::c_char) - } - 29 => { - return dc_strdup(b"This message was encrypted for another setup.\x00" - as *const u8 as *const libc::c_char) - } - 60 => { - return dc_strdup(b"Cannot login as %1$s.\x00" as *const u8 as - *const libc::c_char) - } - 61 => { - return dc_strdup(b"Response from %1$s: %2$s\x00" as *const u8 as - *const libc::c_char) - } - _ => { } - } - - dc_strdup(b"ErrStr\x00" as *const u8 as *const libc::c_char) -} - -/* Replaces the first `%1$s` in the given String-ID by the given value. -The result must be free()'d! */ -pub unsafe fn dc_stock_str_repl_string( - context: &Context, - id: libc::c_int, - to_insert: *const libc::c_char, -) -> *mut libc::c_char { - let mut ret: *mut libc::c_char = get_string(context, id, 0i32); - dc_str_replace( - &mut ret, - b"%1$s\x00" as *const u8 as *const libc::c_char, - to_insert, - ); - dc_str_replace( - &mut ret, - b"%1$d\x00" as *const u8 as *const libc::c_char, - to_insert, - ); - - ret -} - -pub unsafe fn dc_stock_str_repl_int( - context: &Context, - id: libc::c_int, - to_insert_int: libc::c_int, -) -> *mut libc::c_char { - let mut ret: *mut libc::c_char = get_string(context, id, to_insert_int); - let to_insert_str: *mut libc::c_char = dc_mprintf( - b"%i\x00" as *const u8 as *const libc::c_char, - to_insert_int as libc::c_int, - ); - dc_str_replace( - &mut ret, - b"%1$s\x00" as *const u8 as *const libc::c_char, - to_insert_str, - ); - dc_str_replace( - &mut ret, - b"%1$d\x00" as *const u8 as *const libc::c_char, - to_insert_str, - ); - free(to_insert_str as *mut libc::c_void); - - ret -} - -/* Replaces the first `%1$s` and `%2$s` in the given String-ID by the two given strings. -The result must be free()'d! */ -pub unsafe fn dc_stock_str_repl_string2( - context: &Context, - id: libc::c_int, - to_insert: *const libc::c_char, - to_insert2: *const libc::c_char, -) -> *mut libc::c_char { - let mut ret: *mut libc::c_char = get_string(context, id, 0i32); - dc_str_replace( - &mut ret, - b"%1$s\x00" as *const u8 as *const libc::c_char, - to_insert, - ); - dc_str_replace( - &mut ret, - b"%1$d\x00" as *const u8 as *const libc::c_char, - to_insert, - ); - dc_str_replace( - &mut ret, - b"%2$s\x00" as *const u8 as *const libc::c_char, - to_insert2, - ); - dc_str_replace( - &mut ret, - b"%2$d\x00" as *const u8 as *const libc::c_char, - to_insert2, - ); - - ret -} - -/* Misc. */ -pub unsafe fn dc_stock_system_msg( - context: &Context, - str_id: libc::c_int, - mut param1: *const libc::c_char, - param2: *const libc::c_char, - from_id: uint32_t, -) -> *mut libc::c_char { - let ret: *mut libc::c_char; - let mut mod_contact: *mut dc_contact_t = 0 as *mut dc_contact_t; - let mut mod_displayname: *mut libc::c_char = 0 as *mut libc::c_char; - let mut from_contact: *mut dc_contact_t = 0 as *mut dc_contact_t; - let mut from_displayname: *mut libc::c_char = 0 as *mut libc::c_char; - if str_id == 17i32 || str_id == 18i32 { - let mod_contact_id: uint32_t = dc_lookup_contact_id_by_addr(context, param1); - if mod_contact_id != 0i32 as libc::c_uint { - mod_contact = dc_get_contact(context, mod_contact_id); - mod_displayname = dc_contact_get_name_n_addr(mod_contact); - param1 = mod_displayname - } - } - let action: *mut libc::c_char = dc_stock_str_repl_string2(context, str_id, param1, param2); - if 0 != from_id { - if 0 != strlen(action) - && *action.offset(strlen(action).wrapping_sub(1) as isize) as libc::c_int == '.' as i32 - { - *action.offset(strlen(action).wrapping_sub(1) as isize) = 0i32 as libc::c_char - } - from_contact = dc_get_contact(context, from_id); - from_displayname = dc_contact_get_display_name(from_contact); - ret = dc_stock_str_repl_string2( - context, - if from_id == 1i32 as libc::c_uint { - 63i32 - } else { - 62i32 - }, - action, - from_displayname, - ) - } else { - ret = dc_strdup(action) - } - free(action as *mut libc::c_void); - free(from_displayname as *mut libc::c_void); - free(mod_displayname as *mut libc::c_void); - dc_contact_unref(from_contact); - dc_contact_unref(mod_contact); - - ret -} diff --git a/src/lib.rs b/src/lib.rs index 13ec3304b..82e9173d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,6 +34,7 @@ pub mod peerstate; pub mod pgp; pub mod smtp; pub mod sql; +pub mod stock; pub mod types; pub mod x; @@ -60,9 +61,11 @@ pub mod dc_receive_imf; pub mod dc_saxparser; pub mod dc_securejoin; pub mod dc_simplify; -pub mod dc_stock; pub mod dc_strencode; pub mod dc_token; pub mod dc_tools; pub use self::constants::*; + +#[cfg(test)] +pub mod test_utils; diff --git a/src/stock.rs b/src/stock.rs new file mode 100644 index 000000000..3dcd66dfb --- /dev/null +++ b/src/stock.rs @@ -0,0 +1,423 @@ +use std::borrow::Cow; +use std::ffi::CString; + +use strum::EnumProperty; +use strum_macros::EnumProperty; + +use crate::constants::Event; +use crate::context::Context; +use crate::dc_contact::*; +use crate::dc_tools::*; +use libc::free; + +/// Stock strings +/// +/// These identify the string to return in [Context.stock_str]. The +/// numbers must stay in sync with `deltachat.h` `DC_STR_*` constants. +/// +/// See the `stock_*` methods on [Context] to use these. +/// +/// [Context]: crate::context::Context +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, EnumProperty)] +#[repr(u32)] +pub enum StockMessage { + #[strum(props(fallback = "No messages."))] + NoMessages = 1, + #[strum(props(fallback = "Me"))] + SelfMsg = 2, + #[strum(props(fallback = "Draft"))] + Draft = 3, + #[strum(props(fallback = "%1$s member(s)"))] + Member = 4, + #[strum(props(fallback = "%1$s contact(s)"))] + Contact = 6, + #[strum(props(fallback = "Voice message"))] + VoiceMessage = 7, + #[strum(props(fallback = "Contact requests"))] + DeadDrop = 8, + #[strum(props(fallback = "Image"))] + Image = 9, + #[strum(props(fallback = "Video"))] + Video = 10, + #[strum(props(fallback = "Audio"))] + Audio = 11, + #[strum(props(fallback = "File"))] + File = 12, + #[strum(props(fallback = "Sent with my Delta Chat Messenger: https://delta.chat"))] + StatusLine = 13, + #[strum(props(fallback = "Hello, I\'ve just created the group \"%1$s\" for us."))] + NewGroupDraft = 14, + #[strum(props(fallback = "Group name changed from \"%1$s\" to \"%2$s\"."))] + MsgGrpName = 15, + #[strum(props(fallback = "Group image changed."))] + MsgGrpImgChanged = 16, + #[strum(props(fallback = "Member %1$s added."))] + MsgAddMember = 17, + #[strum(props(fallback = "Member %1$s removed."))] + MsgDelMember = 18, + #[strum(props(fallback = "Group left."))] + MsgGroupLeft = 19, + #[strum(props(fallback = "GIF"))] + Gif = 23, + #[strum(props(fallback = "Encrypted message"))] + EncryptedMsg = 24, + #[strum(props(fallback = "End-to-end encryption available."))] + E2eAvailable = 25, + #[strum(props(fallback = "Transport-encryption."))] + EncrTransp = 27, + #[strum(props(fallback = "No encryption."))] + EncrNone = 28, + #[strum(props(fallback = "This message was encrypted for another setup."))] + CantDecryptMsgBody = 29, + #[strum(props(fallback = "Fingerprints"))] + FingerPrints = 30, + #[strum(props(fallback = "Return receipt"))] + ReadRcpt = 31, + #[strum(props(fallback = "This is a return receipt for the message \"%1$s\"."))] + ReadRcptMailBody = 32, + #[strum(props(fallback = "Group image deleted."))] + MsgGrpImgDeleted = 33, + #[strum(props(fallback = "End-to-end encryption preferred."))] + E2ePreferred = 34, + #[strum(props(fallback = "%1$s verified."))] + ContactVerified = 35, + #[strum(props(fallback = "Cannot verify %1$s"))] + ContactNotVerified = 36, + #[strum(props(fallback = "Changed setup for %1$s"))] + ContactSetupChanged = 37, + #[strum(props(fallback = "Archived chats"))] + ArchivedChats = 40, + #[strum(props(fallback = "Starred messages"))] + StarredMsgs = 41, + #[strum(props(fallback = "Autocrypt Setup Message"))] + AcSetupMsgSubject = 42, + #[strum(props( + fallback = "This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device." + ))] + AcSetupMsgBody = 43, + #[strum(props(fallback = "Messages I sent to myself"))] + SelfTalkSubTitle = 50, + #[strum(props(fallback = "Cannot login as %1$s."))] + CannotLogin = 60, + #[strum(props(fallback = "Response from %1$s: %2$s"))] + ServerResponse = 61, + #[strum(props(fallback = "%1$s by %2$s."))] + MsgActionByUser = 62, + #[strum(props(fallback = "%1$s by me."))] + MsgActionByMe = 63, + #[strum(props(fallback = "Location streaming enabled."))] + MsgLocationEnabled = 64, + #[strum(props(fallback = "Location streaming disabled."))] + MsgLocationDisabled = 65, + #[strum(props(fallback = "Location"))] + Location = 66, +} + +impl StockMessage { + /// Default untranslated strings for stock messages. + /// + /// These could be used in logging calls, so no logging here. + fn fallback(&self) -> &'static str { + self.get_str("fallback").unwrap() + } +} + +impl Context { + /// Return the stock string for the [StockMessage]. + /// + /// If the context callback responds with a string to use, e.g. a + /// translation, then this string will be returned. Otherwise a + /// default (English) string is returned. + pub fn stock_str(&self, id: StockMessage) -> Cow { + let ptr = self.call_cb(Event::GET_STRING, id as usize, 0) as *mut libc::c_char; + if ptr.is_null() { + Cow::Borrowed(id.fallback()) + } else { + let ret = to_string(ptr); + unsafe { free(ptr as *mut libc::c_void) }; + Cow::Owned(ret) + } + } + + /// Return stock string, replacing placeholders with provided string. + /// + /// This replaces both the *first* `%1$s` **and** `%1$d` + /// placeholders with the provided string. + pub fn stock_string_repl_str(&self, id: StockMessage, insert: impl AsRef) -> String { + self.stock_str(id) + .replacen("%1$s", insert.as_ref(), 1) + .replacen("%1$d", insert.as_ref(), 1) + } + + /// Return stock string, replacing placeholders with provided int. + /// + /// Like [Context::stock_string_repl_str] but substitute the placeholders + /// with an integer. + pub fn stock_string_repl_int(&self, id: StockMessage, insert: i32) -> String { + self.stock_string_repl_str(id, format!("{}", insert).as_str()) + } + + /// Return stock string, replacing 2 placeholders with provided string. + /// + /// This replaces both the *first* `%1$s` **and** `%1$d` + /// placeholders with the string in `insert` and does the same for + /// `%2$s` and `%2$d` for `insert2`. + fn stock_string_repl_str2( + &self, + id: StockMessage, + insert: impl AsRef, + insert2: impl AsRef, + ) -> String { + self.stock_str(id) + .replacen("%1$s", insert.as_ref(), 1) + .replacen("%1$d", insert.as_ref(), 1) + .replacen("%2$s", insert2.as_ref(), 1) + .replacen("%2$d", insert2.as_ref(), 1) + } + + /// Return some kind of stock message + /// + /// If the `id` is [StockMessage::MsgAddMember] or + /// [StockMessage::MsgDelMember] then `param1` is considered to be the + /// contact address and will be replaced by that contact's display + /// name. + /// + /// If `from_id` is not `0`, any trailing dot is removed from the + /// first stock string created so far. If the `from_id` contact is + /// the user itself, i.e. `DC_CONTACT_ID_SELF` the string is used + /// itself as param to the [StockMessage::MsgActionByMe] stock string + /// resulting in a string like "Member Alice added by me." (for + /// [StockMessage::MsgAddMember] as `id`). If the `from_id` contact + /// is any other user than the contact's display name is looked up and + /// used as the second parameter to [StockMessage::MsgActionByUser] with + /// again the original stock string being used as the first parameter, + /// resulting in a string like "Member Alice added by Bob.". + pub fn stock_system_msg( + &self, + id: StockMessage, + param1: impl AsRef, + param2: impl AsRef, + from_id: u32, + ) -> String { + let insert1 = if id == StockMessage::MsgAddMember || id == StockMessage::MsgDelMember { + unsafe { + let param1_c = CString::new(param1.as_ref()).unwrap(); + let contact_id = dc_lookup_contact_id_by_addr(self, param1_c.as_ptr()); + if contact_id != 0 { + let contact = dc_get_contact(self, contact_id); + let displayname = dc_contact_get_name_n_addr(contact); + let ret = to_string(displayname); + free(contact as *mut libc::c_void); + free(displayname as *mut libc::c_void); + ret + } else { + param1.as_ref().to_string() + } + } + } else { + param1.as_ref().to_string() + }; + let action = self.stock_string_repl_str2(id, insert1, param2.as_ref().to_string()); + let action1 = action.trim_end_matches('.'); + match from_id { + 0 => action, + 1 => self.stock_string_repl_str(StockMessage::MsgActionByMe, action1), // DC_CONTACT_ID_SELF + _ => unsafe { + let contact = dc_get_contact(self, from_id); + let displayname = dc_contact_get_display_name(contact); + let ret = self.stock_string_repl_str2( + StockMessage::MsgActionByUser, + action1, + as_str(displayname), + ); + free(contact as *mut libc::c_void); + free(displayname as *mut libc::c_void); + ret + }, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::test_utils::*; + + use std::ffi::CString; + + use crate::constants::DC_CONTACT_ID_SELF; + use crate::context::dc_context_new; + use crate::types::uintptr_t; + + use num_traits::ToPrimitive; + + #[test] + fn test_enum_mapping() { + assert_eq!(StockMessage::NoMessages.to_usize().unwrap(), 1); + assert_eq!(StockMessage::SelfMsg.to_usize().unwrap(), 2); + } + + #[test] + fn test_fallback() { + assert_eq!(StockMessage::NoMessages.fallback(), "No messages."); + } + + #[test] + fn test_stock_str() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages."); + } + + unsafe extern "C" fn test_stock_str_no_fallback_cb( + _ctx: &Context, + evt: Event, + d1: uintptr_t, + _d2: uintptr_t, + ) -> uintptr_t { + if evt == Event::GET_STRING && d1 == StockMessage::NoMessages.to_usize().unwrap() { + let tmp = CString::new("Hello there").unwrap(); + dc_strdup(tmp.as_ptr()) as usize + } else { + 0 + } + } + + #[test] + fn test_stock_str_no_fallback() { + let t = test_context(Some(test_stock_str_no_fallback_cb)); + assert_eq!(t.ctx.stock_str(StockMessage::NoMessages), "Hello there"); + } + + #[test] + fn test_stock_string_repl_str() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + // uses %1$s substitution + assert_eq!( + ctx.stock_string_repl_str(StockMessage::Member, "42"), + "42 member(s)" + ); + // We have no string using %1$d to test... + } + + #[test] + fn test_stock_string_repl_int() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + assert_eq!( + ctx.stock_string_repl_int(StockMessage::Member, 42), + "42 member(s)" + ); + } + + #[test] + fn test_stock_string_repl_str2() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + assert_eq!( + ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"), + "Response from foo: bar" + ); + } + + #[test] + fn test_stock_system_msg_simple() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + assert_eq!( + ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0), + "Location streaming enabled." + ) + } + + #[test] + fn test_stock_system_msg_add_member_by_me() { + let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + assert_eq!( + ctx.stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + DC_CONTACT_ID_SELF as u32 + ), + "Member alice@example.com added by me." + ) + } + + #[test] + fn test_stock_system_msg_add_member_by_me_with_displayname() { + let t = dummy_context(); + unsafe { + let name = CString::new("Alice").unwrap(); + let addr = CString::new("alice@example.com").unwrap(); + assert!(dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()) > 0); + } + assert_eq!( + t.ctx.stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + DC_CONTACT_ID_SELF as u32 + ), + "Member Alice (alice@example.com) added by me." + ) + } + + #[test] + fn test_stock_system_msg_add_member_by_other_with_displayname() { + let t = dummy_context(); + let contact_id = unsafe { + let name = CString::new("Alice").unwrap(); + let addr = CString::new("alice@example.com").unwrap(); + assert!( + dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()) > 0, + "Failed to create contact Alice" + ); + let name = CString::new("Bob").unwrap(); + let addr = CString::new("bob@example.com").unwrap(); + let id = dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()); + assert!(id > 0, "Failed to create contact Bob"); + id + }; + assert_eq!( + t.ctx.stock_system_msg( + StockMessage::MsgAddMember, + "alice@example.com", + "", + contact_id, + ), + "Member Alice (alice@example.com) added by Bob." + ) + } + + #[test] + fn test_stock_system_msg_grp_name() { + let t = dummy_context(); + assert_eq!( + t.ctx.stock_system_msg( + StockMessage::MsgGrpName, + "Some chat", + "Other chat", + DC_CONTACT_ID_SELF as u32 + ), + "Group name changed from \"Some chat\" to \"Other chat\" by me." + ) + } + + #[test] + fn test_stock_system_msg_grp_name_other() { + let t = dummy_context(); + let contact_id = unsafe { + let name = CString::new("Alice").unwrap(); + let addr = CString::new("alice@example.com").unwrap(); + let id = dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()); + assert!(id > 0, "Failed to create contact Alice"); + id + }; + assert_eq!( + t.ctx.stock_system_msg( + StockMessage::MsgGrpName, + "Some chat", + "Other chat", + contact_id + ), + "Group name changed from \"Some chat\" to \"Other chat\" by Alice." + ) + } +} diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 000000000..1109bd4ec --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,50 @@ +//! Utilities to help writing tests. +//! +//! This module is only compiled for test runs. + +use tempfile::{tempdir, TempDir}; + +use crate::context::{dc_context_new, dc_open, Context}; +use crate::types::dc_callback_t; + +use crate::dc_tools::OsStrExt; + +/// A Context and temporary directory. +/// +/// The temporary directory can be used to store the SQLite database, +/// see e.g. [test_context] which does this. +pub struct TestContext { + pub ctx: Context, + pub dir: TempDir, +} + +/// Create a new, opened [TestContext] using given callback. +/// +/// The [Context] will be opened with the SQLite database named +/// "db.sqlite" in the [TestContext.dir] directory. +/// +/// [Context]: crate::context::Context +pub fn test_context(cb: Option) -> TestContext { + unsafe { + let mut ctx = dc_context_new(cb, std::ptr::null_mut(), std::ptr::null_mut()); + let dir = tempdir().unwrap(); + let dbfile = dir.path().join("db.sqlite"); + let dbfile_c = dbfile.to_c_string().unwrap(); + assert_eq!( + dc_open(&mut ctx, dbfile_c.as_ptr(), std::ptr::null()), + 1, + "Failed to open {}", + dbfile.display(), + ); + TestContext { ctx: ctx, dir: dir } + } +} + +/// Return a dummy [TestContext]. +/// +/// The context will be opened and use the SQLite database as +/// specified in [test_context] but there is no callback hooked up, +/// i.e. [Context::call_cb] will always return `0`. +pub fn dummy_context() -> TestContext { + test_context(None) +}