Make stock strings rusty

This converts the stock strings API to be more safe-rust style.  The
API is kept roughly the same for now but moved to methods on the
context.
This commit is contained in:
Floris Bruynooghe
2019-07-13 14:34:50 +02:00
committed by Floris Bruynooghe
parent 44b8629811
commit 4902310138
16 changed files with 711 additions and 592 deletions

View File

@@ -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),

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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"<br>\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", "<br>")).unwrap();
ret_setupfilecontent =
dc_mprintf(b"<!DOCTYPE html>\r\n<html>\r\n<head>\r\n<title>%s</title>\r\n</head>\r\n<body>\r\n<h1>%s</h1>\r\n<p>%s</p>\r\n<pre>\r\n%s\r\n</pre>\r\n</body>\r\n</html>\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);
}
}

View File

@@ -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);
}

View File

@@ -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() {

View File

@@ -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
{

View File

@@ -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,

View File

@@ -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)

View File

@@ -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<T: AsRef<str>>(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);
}
};
}

View File

@@ -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,

View File

@@ -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
}

View File

@@ -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;

423
src/stock.rs Normal file
View File

@@ -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<str> {
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<str>) -> 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<str>,
insert2: impl AsRef<str>,
) -> 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<str>,
param2: impl AsRef<str>,
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."
)
}
}

50
src/test_utils.rs Normal file
View File

@@ -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<dc_callback_t>) -> 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)
}