Compare commits

..

1 Commits

Author SHA1 Message Date
holger krekel
b83316bd4d try write a test for unshown messages 2019-07-22 15:52:27 +02:00
66 changed files with 6012 additions and 5807 deletions

View File

@@ -169,7 +169,7 @@ workflows:
- build_test_docs_wheel
- upload_docs_wheels:
requires:
- build_test_docs_wheel
- build_test_docs_wheel
- cargo_fetch
- rustfmt:
requires:

333
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@ imap = "1.0.1"
mmime = "0.1.0"
base64 = "0.10"
charset = "0.1"
percent-encoding = "2.0"
percent-encoding = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
chrono = "0.4.6"
@@ -35,15 +35,14 @@ failure_derive = "0.1.5"
rustyline = "4.1.0"
lazy_static = "1.3.0"
regex = "1.1.6"
rusqlite = { version = "0.20", features = ["bundled"] }
rusqlite = { version = "0.19", features = ["bundled"] }
addr = "0.2.0"
r2d2_sqlite = "0.12.0"
r2d2_sqlite = "0.11.0"
r2d2 = "0.8.5"
strum = "0.15.0"
strum_macros = "0.15.0"
thread-local-object = "0.1.0"
backtrace = "0.3.33"
byteorder = "1.3.1"
[dev-dependencies]
tempfile = "3.0"
@@ -55,6 +54,10 @@ members = [
"deltachat-ffi"
]
[patch.crates-io]
rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] }
[[example]]
name = "simple"
path = "examples/simple.rs"

View File

@@ -63,11 +63,6 @@ Single#10: yourfriends@email.org [yourfriends@email.org]
Message sent.
```
If `yourfriend@email.org` uses DeltaChat, but does not receive message just
sent, it is advisable to check `Spam` folder. It is known that at least
`gmx.com` treat such test messages as spam, unless told otherwise with web
interface.
List messages when inside a chat:
```

View File

@@ -4,7 +4,6 @@ set -ex
export RUST_TEST_THREADS=1
export RUST_BACKTRACE=1
export RUSTFLAGS='--deny warnings'
export OPT="--target=$TARGET"
export OPT_RELEASE="--release ${OPT}"
export OPT_FFI_RELEASE="--manifest-path=deltachat-ffi/Cargo.toml --release"

View File

@@ -18,7 +18,6 @@ crate-type = ["cdylib", "staticlib"]
deltachat = { path = "../", default-features = false }
libc = "0.2"
human-panic = "1.0.1"
num-traits = "0.2.6"
[features]
default = ["vendored", "nightly", "ringbuf"]

View File

@@ -9,13 +9,9 @@
#[macro_use]
extern crate human_panic;
extern crate num_traits;
use num_traits::{FromPrimitive, ToPrimitive};
use std::ptr;
use std::str::FromStr;
use deltachat::dc_tools::StrExt;
use deltachat::*;
// TODO: constants
@@ -34,13 +30,7 @@ pub unsafe extern "C" fn dc_context_new(
) -> *mut dc_context_t {
setup_panic!();
let os_name = if os_name.is_null() {
None
} else {
Some(dc_tools::to_string_lossy(os_name))
};
let ctx = context::dc_context_new(cb, userdata, os_name);
Box::into_raw(Box::new(ctx))
}
@@ -48,8 +38,7 @@ pub unsafe extern "C" fn dc_context_new(
pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) {
assert!(!context.is_null());
let context = &mut *context;
context::dc_context_unref(context);
Box::from_raw(context);
context::dc_context_unref(context)
}
#[no_mangle]
@@ -67,16 +56,9 @@ pub unsafe extern "C" fn dc_open(
blobdir: *mut libc::c_char,
) -> libc::c_int {
assert!(!context.is_null());
assert!(!dbfile.is_null());
let context = &mut *context;
let dbfile_str = dc_tools::as_str(dbfile);
let blobdir_str = if blobdir.is_null() {
None
} else {
Some(dc_tools::as_str(blobdir))
};
context::dc_open(context, dbfile_str, blobdir_str) as libc::c_int
context::dc_open(context, dbfile, blobdir)
}
#[no_mangle]
@@ -127,7 +109,10 @@ pub unsafe extern "C" fn dc_get_config(
let context = &*context;
match config::Config::from_str(dc_tools::as_str(key)) {
Ok(key) => context.get_config(key).unwrap_or_default().strdup(),
Ok(key) => {
let value = context.get_config(key).unwrap_or_default();
dc_tools::to_cstring(value)
}
Err(_) => std::ptr::null_mut(),
}
}
@@ -152,7 +137,7 @@ pub unsafe extern "C" fn dc_get_oauth2_url(
let addr = dc_tools::to_string(addr);
let redirect = dc_tools::to_string(redirect);
match oauth2::dc_get_oauth2_url(context, addr, redirect) {
Some(res) => res.strdup(),
Some(res) => dc_tools::to_cstring(res),
None => std::ptr::null_mut(),
}
}
@@ -296,20 +281,11 @@ pub unsafe extern "C" fn dc_get_chatlist<'a>(
flags: libc::c_int,
query_str: *mut libc::c_char,
query_id: u32,
) -> *mut dc_chatlist_t<'a> {
) -> *mut dc_chatlist::dc_chatlist_t<'a> {
assert!(!context.is_null());
let context = &*context;
let qs = if query_str.is_null() {
None
} else {
Some(dc_tools::as_str(query_str))
};
let qi = if query_id == 0 { None } else { Some(query_id) };
match chatlist::Chatlist::try_load(context, flags as usize, qs, qi) {
Ok(list) => Box::into_raw(Box::new(list)),
Err(_) => std::ptr::null_mut(),
}
dc_chatlist::dc_get_chatlist(context, flags, query_str, query_id)
}
#[no_mangle]
@@ -459,14 +435,6 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) {
dc_chat::dc_marknoticed_all_chats(context);
}
fn from_prim<S, T>(s: S) -> Option<T>
where
T: FromPrimitive,
S: Into<i64>,
{
FromPrimitive::from_i64(s.into())
}
#[no_mangle]
pub unsafe extern "C" fn dc_get_chat_media(
context: *mut dc_context_t,
@@ -478,15 +446,7 @@ pub unsafe extern "C" fn dc_get_chat_media(
assert!(!context.is_null());
let context = &*context;
if let (Some(msg_type), Some(or_msg_type2), Some(or_msg_type3)) = (
from_prim(msg_type),
from_prim(or_msg_type2),
from_prim(or_msg_type3),
) {
dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3)
} else {
ptr::null_mut()
}
dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3)
}
#[no_mangle]
@@ -501,15 +461,7 @@ pub unsafe extern "C" fn dc_get_next_media(
assert!(!context.is_null());
let context = &*context;
if let (Some(msg_type), Some(or_msg_type2), Some(or_msg_type3)) = (
from_prim(msg_type),
from_prim(or_msg_type2),
from_prim(or_msg_type3),
) {
dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
} else {
0
}
dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
}
#[no_mangle]
@@ -1123,65 +1075,51 @@ pub unsafe fn dc_array_is_independent(
// dc_chatlist_t
#[no_mangle]
pub type dc_chatlist_t<'a> = chatlist::Chatlist<'a>;
pub type dc_chatlist_t<'a> = dc_chatlist::dc_chatlist_t<'a>;
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_unref(chatlist: *mut dc_chatlist_t) {
assert!(!chatlist.is_null());
Box::from_raw(chatlist);
pub unsafe extern "C" fn dc_chatlist_unref(chatlist: *mut dc_chatlist::dc_chatlist_t) {
dc_chatlist::dc_chatlist_unref(chatlist)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_get_cnt(chatlist: *mut dc_chatlist_t) -> libc::size_t {
assert!(!chatlist.is_null());
let list = &*chatlist;
list.len() as libc::size_t
pub unsafe extern "C" fn dc_chatlist_get_cnt(
chatlist: *mut dc_chatlist::dc_chatlist_t,
) -> libc::size_t {
dc_chatlist::dc_chatlist_get_cnt(chatlist)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_get_chat_id(
chatlist: *mut dc_chatlist_t,
chatlist: *mut dc_chatlist::dc_chatlist_t,
index: libc::size_t,
) -> u32 {
assert!(!chatlist.is_null());
let list = &*chatlist;
list.get_chat_id(index as usize)
dc_chatlist::dc_chatlist_get_chat_id(chatlist, index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_get_msg_id(
chatlist: *mut dc_chatlist_t,
chatlist: *mut dc_chatlist::dc_chatlist_t,
index: libc::size_t,
) -> u32 {
assert!(!chatlist.is_null());
let list = &*chatlist;
list.get_msg_id(index as usize)
dc_chatlist::dc_chatlist_get_msg_id(chatlist, index)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_get_summary<'a>(
chatlist: *mut dc_chatlist_t<'a>,
chatlist: *mut dc_chatlist::dc_chatlist_t<'a>,
index: libc::size_t,
chat: *mut dc_chat_t<'a>,
) -> *mut dc_lot::dc_lot_t {
assert!(!chatlist.is_null());
let list = &*chatlist;
list.get_summary(index as usize, chat)
dc_chatlist::dc_chatlist_get_summary(chatlist, index, chat)
}
#[no_mangle]
pub unsafe extern "C" fn dc_chatlist_get_context(
chatlist: *mut dc_chatlist_t,
chatlist: *mut dc_chatlist::dc_chatlist_t,
) -> *const dc_context_t {
assert!(!chatlist.is_null());
let list = &*chatlist;
list.get_context() as *const _
(*chatlist).context as *const _
}
// dc_chat_t
@@ -1261,11 +1199,8 @@ pub unsafe extern "C" fn dc_msg_new<'a>(
) -> *mut dc_msg::dc_msg_t<'a> {
assert!(!context.is_null());
let context = &*context;
if let Some(viewtype) = from_prim(viewtype) {
dc_msg::dc_msg_new(context, viewtype)
} else {
ptr::null_mut()
}
dc_msg::dc_msg_new(context, viewtype)
}
#[no_mangle]
@@ -1296,8 +1231,6 @@ pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg::dc_msg_t) -> u32 {
#[no_mangle]
pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
dc_msg::dc_msg_get_viewtype(msg)
.to_i64()
.expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int
}
#[no_mangle]
@@ -1366,9 +1299,9 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> l
}
#[no_mangle]
pub unsafe extern "C" fn dc_msg_get_summary<'a>(
msg: *mut dc_msg::dc_msg_t<'a>,
chat: *mut dc_chat_t<'a>,
pub unsafe extern "C" fn dc_msg_get_summary(
msg: *mut dc_msg::dc_msg_t,
chat: *mut dc_chat_t,
) -> *mut dc_lot::dc_lot_t {
dc_msg::dc_msg_get_summary(msg, chat)
}
@@ -1398,7 +1331,7 @@ pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg::dc_msg_t) -> libc::c_i
#[no_mangle]
pub unsafe extern "C" fn dc_msg_is_starred(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
dc_msg::dc_msg_is_starred(msg).into()
dc_msg::dc_msg_is_starred(msg)
}
#[no_mangle]

View File

@@ -1,12 +1,11 @@
use std::ffi::CString;
use std::str::FromStr;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::constants::*;
use deltachat::context::*;
use deltachat::dc_array::*;
use deltachat::dc_chat::*;
use deltachat::dc_chatlist::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_imex::*;
@@ -147,7 +146,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
} else {
current_block = 7149356873433890176;
}
real_spec = rs.unwrap_or_default().strdup();
real_spec = to_cstring(rs.unwrap_or_default());
}
match current_block {
8522321847195001863 => {}
@@ -184,10 +183,11 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
if name.ends_with(".eml") {
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
info!(context, 0, "Import: {}", path_plus_name);
let path_plus_name_c = CString::yolo(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
let path_plus_name_c = to_cstring(path_plus_name);
if 0 != dc_poke_eml_file(context, path_plus_name_c) {
read_cnt += 1
}
free(path_plus_name_c as *mut _);
}
}
current_block = 1622411330066726685;
@@ -222,13 +222,13 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
let contact_name: *mut libc::c_char = dc_contact_get_name(contact);
let contact_id: libc::c_int = dc_contact_get_id(contact) as libc::c_int;
let statestr = match dc_msg_get_state(msg) {
DC_STATE_OUT_PENDING => " o",
DC_STATE_OUT_DELIVERED => "",
DC_STATE_OUT_MDN_RCVD => " √√",
DC_STATE_OUT_FAILED => " !!",
20 => " o",
26 => "",
28 => " √√",
24 => " !!",
_ => "",
};
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let temp2: *mut libc::c_char = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
let msgtext: *mut libc::c_char = dc_msg_get_text(msg);
info!(
context,
@@ -245,12 +245,16 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
as_str(contact_name),
contact_id,
as_str(msgtext),
if dc_msg_is_starred(msg) { "" } else { "" },
if 0 != dc_msg_is_starred(msg) {
""
} else {
""
},
if dc_msg_get_from_id(msg) == 1 as libc::c_uint {
""
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
} else if dc_msg_get_state(msg) == 16 {
"[SEEN]"
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
} else if dc_msg_get_state(msg) == 13 {
"[NOTICED]"
} else {
"[FRESH]"
@@ -261,9 +265,10 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
""
},
statestr,
&temp2,
as_str(temp2),
);
free(msgtext as *mut libc::c_void);
free(temp2 as *mut libc::c_void);
free(contact_name as *mut libc::c_void);
dc_contact_unref(contact);
}
@@ -385,13 +390,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1.strdup() as *const _
to_cstring(arg1) as *const _
};
let arg2 = args.next().unwrap_or_default();
let arg2_c = if arg2.is_empty() {
std::ptr::null()
} else {
arg2.strdup() as *const _
to_cstring(arg2) as *const _
};
match arg0 {
@@ -445,7 +450,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dellocations\n\
getlocations [<contact-id>]\n\
send <text>\n\
send-garbage\n\
sendimage <file> [<text>]\n\
sendfile <file>\n\
draft [<text>]\n\
@@ -497,7 +501,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"open" => {
ensure!(!arg1.is_empty(), "Argument <file> missing");
dc_close(context);
ensure!(dc_open(context, arg1, None), "Open failed");
ensure!(
0 != dc_open(context, arg1_c, 0 as *const libc::c_char),
"Open failed"
);
}
"close" => {
dc_close(context);
@@ -516,7 +523,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"get-setupcodebegin" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let msg_id: u32 = arg1.parse()?;
let msg_id: u32 = arg1.parse().unwrap();
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
if dc_msg_is_setupmessage(msg) {
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
@@ -536,7 +543,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Arguments <msg-id> <setup-code> expected"
);
if 0 == dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c) {
bail!("Continue key transfer failed");
}
}
@@ -560,26 +567,39 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_imex(context, 2, context.get_blobdir(), 0 as *const libc::c_char);
}
"export-setup" => {
let setup_code = dc_create_setup_code(context);
let setup_code: *mut libc::c_char = dc_create_setup_code(context);
let file_name: *mut libc::c_char = dc_mprintf(
b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
context.get_blobdir(),
);
let file_content = dc_render_setup_file(context, &setup_code)?;
std::fs::write(as_str(file_name), file_content)?;
println!(
"Setup message written to: {}\nSetup code: {}",
as_str(file_name),
&setup_code,
);
let file_content: *mut libc::c_char;
file_content = dc_render_setup_file(context, setup_code);
if !file_content.is_null()
&& 0 != dc_write_file(
context,
file_name,
file_content as *const libc::c_void,
strlen(file_content),
)
{
println!(
"Setup message written to: {}\nSetup code: {}",
as_str(file_name),
as_str(setup_code),
)
} else {
bail!("");
}
free(file_content as *mut libc::c_void);
free(file_name as *mut libc::c_void);
free(setup_code as *mut libc::c_void);
}
"poke" => {
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
}
"reset" => {
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
let bits: i32 = arg1.parse()?;
let bits: i32 = arg1.parse().unwrap();
ensure!(bits < 16, "<bits> must be lower than 16.");
ensure!(0 != dc_reset_tables(context, bits), "Reset failed");
}
@@ -609,10 +629,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"listchats" | "listarchived" | "chats" => {
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
let chatlist = Chatlist::try_load(context, listflags, Some(arg1), None)?;
let chatlist = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t);
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
let mut i: usize;
let cnt = chatlist.len();
let mut i: libc::c_int;
let cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
if cnt > 0 {
info!(
context, 0,
@@ -621,8 +642,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
i = cnt - 1;
while i > 0 {
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
while i >= 0 {
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
let temp_subtitle = dc_chat_get_subtitle(chat);
let temp_name = dc_chat_get_name(chat);
info!(
@@ -637,7 +658,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
free(temp_subtitle as *mut libc::c_void);
free(temp_name as *mut libc::c_void);
let lot = chatlist.get_summary(i, chat);
let lot = dc_chatlist_get_summary(chatlist, i as size_t, chat);
let statestr = if 0 != dc_chat_get_archived(chat) {
" [Archived]"
} else {
@@ -660,7 +681,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if !text1.is_null() { ": " } else { "" },
to_string(text2),
statestr,
&timestr,
as_str(timestr),
if 0 != dc_chat_is_sending_locations(chat) {
"📍"
} else {
@@ -669,6 +690,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
free(text1 as *mut libc::c_void);
free(text2 as *mut libc::c_void);
free(timestr as *mut libc::c_void);
dc_lot_unref(lot);
dc_chat_unref(chat);
info!(
@@ -683,6 +705,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
info!(context, 0, "Location streaming enabled.");
}
println!("{} chats", cnt);
dc_chatlist_unref(chatlist);
}
"chat" => {
if sel_chat.is_null() && arg1.is_empty() {
@@ -692,7 +715,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
dc_chat_unref(sel_chat);
}
if !arg1.is_empty() {
let chat_id = arg1.parse()?;
let chat_id = arg1.parse().unwrap();
println!("Selecting chat #{}", chat_id);
sel_chat = dc_get_chat(context, chat_id);
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
@@ -736,7 +759,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createchat" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id: libc::c_int = arg1.parse()?;
let contact_id: libc::c_int = arg1.parse().unwrap();
let chat_id: libc::c_int =
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
if chat_id != 0 {
@@ -747,7 +770,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"createchatbymsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
let msg_id_0: libc::c_int = arg1.parse()?;
let msg_id_0: libc::c_int = arg1.parse().unwrap();
let chat_id_0: libc::c_int =
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
if chat_id_0 != 0 {
@@ -784,7 +807,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_0: libc::c_int = arg1.parse()?;
let contact_id_0: libc::c_int = arg1.parse().unwrap();
if 0 != dc_add_contact_to_chat(
context,
dc_chat_get_id(sel_chat),
@@ -798,7 +821,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"removemember" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id_1: libc::c_int = arg1.parse()?;
let contact_id_1: libc::c_int = arg1.parse().unwrap();
if 0 != dc_remove_contact_from_chat(
context,
dc_chat_get_id(sel_chat),
@@ -863,7 +886,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
0,
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
dc_array_get_id(loc, j as size_t),
&timestr_0,
as_str(timestr_0),
dc_array_get_latitude(loc, j as size_t),
dc_array_get_longitude(loc, j as size_t),
dc_array_get_accuracy(loc, j as size_t),
@@ -876,6 +899,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"-"
},
);
free(timestr_0 as *mut libc::c_void);
free(marker as *mut libc::c_void);
j += 1
}
@@ -888,7 +912,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No timeout given.");
let seconds = arg1.parse()?;
let seconds = arg1.parse().unwrap();
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
}
@@ -897,8 +921,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
!arg1.is_empty() && !arg2.is_empty(),
"Latitude or longitude not given."
);
let latitude = arg1.parse()?;
let longitude = arg2.parse()?;
let latitude = arg1.parse().unwrap();
let longitude = arg2.parse().unwrap();
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
if 0 != continue_streaming {
@@ -914,23 +938,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty(), "No message text given.");
let msg = CString::yolo(format!("{} {}", arg1, arg2));
let msg = to_cstring(format!("{} {}", arg1, arg2));
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) {
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
println!("Message sent.");
free(msg as *mut _);
} else {
free(msg as *mut _);
bail!("Sending failed.");
}
}
"send-garbage" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
let msg = b"\xff\x00"; // NUL-terminated C-string, that is malformed utf-8
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr().cast()) {
println!("Malformed utf-8 succesfully send. Not nice.");
} else {
bail!("Garbage sending failed, as expected.");
}
}
"sendempty" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
if 0 != dc_send_text_msg(
@@ -947,14 +964,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
let msg_0 = dc_msg_new(
context,
if arg0 == "sendimage" {
Viewtype::Image
} else {
Viewtype::File
},
);
let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 });
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
dc_msg_set_text(msg_0, arg2_c);
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
@@ -981,7 +991,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
ensure!(!sel_chat.is_null(), "No chat selected.");
if !arg1.is_empty() {
let draft_0 = dc_msg_new(context, Viewtype::Text);
let draft_0 = dc_msg_new(context, 10);
dc_msg_set_text(draft_0, arg1_c);
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
dc_msg_unref(draft_0);
@@ -994,13 +1004,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"listmedia" => {
ensure!(!sel_chat.is_null(), "No chat selected.");
let images = dc_get_chat_media(
context,
dc_chat_get_id(sel_chat),
Viewtype::Image,
Viewtype::Gif,
Viewtype::Video,
);
let images = dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20, 21, 50);
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
println!("{} images or videos: ", icnt);
for i in 0..icnt {
@@ -1016,17 +1020,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"archive" | "unarchive" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
let chat_id = arg1.parse().unwrap();
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
}
"delchat" => {
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
let chat_id = arg1.parse()?;
let chat_id = arg1.parse().unwrap();
dc_delete_chat(context, chat_id);
}
"msginfo" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let id = arg1.parse()?;
let id = arg1.parse().unwrap();
let res = dc_get_msg_info(context, id);
println!("{}", as_str(res));
}
@@ -1045,20 +1049,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
);
let mut msg_ids = [0; 1];
let chat_id = arg2.parse()?;
msg_ids[0] = arg1.parse()?;
let chat_id = arg2.parse().unwrap();
msg_ids[0] = arg1.parse().unwrap();
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
}
"markseen" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
msg_ids[0] = arg1.parse().unwrap();
dc_markseen_msgs(context, msg_ids.as_mut_ptr(), 1);
}
"star" | "unstar" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut msg_ids = [0; 1];
msg_ids[0] = arg1.parse()?;
msg_ids[0] = arg1.parse().unwrap();
dc_star_msgs(
context,
msg_ids.as_mut_ptr(),
@@ -1069,7 +1073,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"delmsg" => {
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
let mut ids = [0; 1];
ids[0] = arg1.parse()?;
ids[0] = arg1.parse().unwrap();
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
}
"listcontacts" | "contacts" | "listverified" => {
@@ -1110,7 +1114,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
"contactinfo" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
let contact_id = arg1.parse()?;
let contact_id = arg1.parse().unwrap();
let contact = dc_get_contact(context, contact_id);
let name_n_addr = dc_contact_get_name_n_addr(contact);
@@ -1122,8 +1126,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
res += as_str(encrinfo);
free(encrinfo as *mut libc::c_void);
let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?;
let chatlist_cnt = chatlist.len();
let chatlist = dc_get_chatlist(context, 0, 0 as *const libc::c_char, contact_id);
let chatlist_cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
if chatlist_cnt > 0 {
res += &format!(
"\n\n{} chats shared with Contact#{}: ",
@@ -1133,17 +1137,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
if 0 != i {
res += ", ";
}
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
dc_chat_unref(chat);
}
}
dc_chatlist_unref(chatlist);
println!("{}", res);
}
"delcontact" => {
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
if !dc_delete_contact(context, arg1.parse()?) {
if !dc_delete_contact(context, arg1.parse().unwrap()) {
bail!("Failed to delete contact");
}
}
@@ -1161,8 +1165,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
}
"event" => {
ensure!(!arg1.is_empty(), "Argument <id> missing.");
let event = arg1.parse()?;
let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?;
let event = Event::from_u32(arg1.parse().unwrap()).unwrap();
let r = context.call_cb(event, 0 as uintptr_t, 0 as uintptr_t);
println!(
"Sending event {:?}({}), received value {}.",
@@ -1189,7 +1192,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
bail!("Command failed.");
}
}
"" => (),
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
}

View File

@@ -3,7 +3,6 @@
//!
//! Usage: cargo run --example repl --release -- <databasefile>
//! All further options can be set using the set-command (type ? for help).
#![feature(ptr_cast)]
#[macro_use]
extern crate deltachat;
@@ -291,7 +290,7 @@ const DB_COMMANDS: [&'static str; 11] = [
"housekeeping",
];
const CHAT_COMMANDS: [&'static str; 25] = [
const CHAT_COMMANDS: [&'static str; 24] = [
"listchats",
"listarchived",
"chat",
@@ -309,7 +308,6 @@ const CHAT_COMMANDS: [&'static str; 25] = [
"dellocations",
"getlocations",
"send",
"send-garbage",
"sendimage",
"sendfile",
"draft",
@@ -393,13 +391,18 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
let mut context = dc_context_new(
Some(receive_event),
0 as *mut libc::c_void,
Some("CLI".into()),
b"CLI\x00" as *const u8 as *const libc::c_char,
);
unsafe { dc_cmdline_skip_auth() };
if args.len() == 2 {
if unsafe { !dc_open(&mut context, &args[1], None) } {
if 0 == unsafe {
let a = to_cstring(&args[1]);
let res = dc_open(&mut context, a, 0 as *const _);
free(a as *mut _);
res
} {
println!("Error: Cannot open {}.", args[0],);
}
} else if args.len() != 1 {
@@ -481,7 +484,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
let arg1_c = if arg1.is_empty() {
std::ptr::null()
} else {
arg1.strdup()
to_cstring(arg1)
};
match arg0 {

View File

@@ -5,11 +5,11 @@ use std::sync::{Arc, RwLock};
use std::{thread, time};
use tempfile::tempdir;
use deltachat::chatlist::*;
use deltachat::config;
use deltachat::constants::Event;
use deltachat::context::*;
use deltachat::dc_chat::*;
use deltachat::dc_chatlist::*;
use deltachat::dc_configure::*;
use deltachat::dc_contact::*;
use deltachat::dc_job::{
@@ -41,7 +41,7 @@ extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> us
fn main() {
unsafe {
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let running = Arc::new(RwLock::new(true));
let info = dc_get_info(&ctx);
let info_s = CStr::from_ptr(info);
@@ -76,11 +76,11 @@ fn main() {
});
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
println!("opening database {:?}", dbfile);
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1);
println!("configuring");
let args = std::env::args().collect::<Vec<String>>();
@@ -101,10 +101,10 @@ fn main() {
dc_send_text_msg(&ctx, chat_id, msg_text.as_ptr());
println!("fetching chats..");
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
let chats = dc_get_chatlist(&ctx, 0, std::ptr::null(), 0);
for i in 0..chats.len() {
let summary = chats.get_summary(0, std::ptr::null_mut());
for i in 0..dc_chatlist_get_cnt(chats) {
let summary = dc_chatlist_get_summary(chats, 0, std::ptr::null_mut());
let text1 = dc_lot_get_text1(summary);
let text2 = dc_lot_get_text2(summary);
@@ -121,6 +121,7 @@ fn main() {
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
dc_lot_unref(summary);
}
dc_chatlist_unref(chats);
thread::sleep(duration);

View File

@@ -11,6 +11,8 @@ high level API reference
- :class:`deltachat.chatting.Contact`
- :class:`deltachat.chatting.Chat`
- :class:`deltachat.message.Message`
- :class:`deltachat.message.MessageType`
- :class:`deltachat.message.MessageState`
Account
-------

View File

@@ -288,6 +288,10 @@ intersphinx_mapping = {'http://docs.python.org/': None}
autodoc_member_order = "bysource"
# always document __init__ functions
def skip(app, what, name, obj, skip, options):
import attr
if name == "__init__":
if not hasattr(obj.im_class, "__attrs_attrs__"):
return False
return skip
def setup(app):

View File

@@ -17,7 +17,7 @@ def main():
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
long_description=long_description,
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
install_requires=['cffi>=1.0.0', 'six'],
install_requires=['cffi>=1.0.0', 'attrs', 'six'],
packages=setuptools.find_packages('src'),
package_dir={'': 'src'},
cffi_modules=['src/deltachat/_build.py:ffibuilder'],

View File

@@ -7,9 +7,9 @@ import re
import time
from array import array
try:
from queue import Queue, Empty
from queue import Queue
except ImportError:
from Queue import Queue, Empty
from Queue import Queue
import deltachat
from . import const
@@ -142,6 +142,15 @@ class Account(object):
self.check_is_configured()
return Contact(self._dc_context, const.DC_CONTACT_ID_SELF)
def create_message(self, view_type):
""" create a new non persistent message.
:param view_type: a string specifying "text", "video",
"image", "audio" or "file".
:returns: :class:`deltachat.message.Message` instance.
"""
return Message.new(self._dc_context, view_type)
def create_contact(self, email, name=None):
""" create a (new) Contact. If there already is a Contact
with that e-mail address, it is unblocked and its name is
@@ -157,17 +166,6 @@ class Account(object):
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
return Contact(self._dc_context, contact_id)
def delete_contact(self, contact):
""" delete a Contact.
:param contact: contact object obtained
:returns: True if deletion succeeded (contact was deleted)
"""
contact_id = contact.id
assert contact._dc_context == self._dc_context
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL
return bool(lib.dc_delete_contact(self._dc_context, contact_id))
def get_contacts(self, query=None, with_self=False, only_verified=False):
""" get a (filtered) list of contacts.
@@ -203,7 +201,7 @@ class Account(object):
assert isinstance(contact, int)
contact_id = contact
chat_id = lib.dc_create_chat_by_contact_id(self._dc_context, contact_id)
return Chat(self, chat_id)
return Chat(self._dc_context, chat_id)
def create_chat_by_message(self, message):
""" create or get an existing chat object for the
@@ -220,7 +218,7 @@ class Account(object):
assert isinstance(message, int)
msg_id = message
chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)
return Chat(self, chat_id)
return Chat(self._dc_context, chat_id)
def create_group_chat(self, name, verified=False):
""" create a new group chat object.
@@ -232,7 +230,7 @@ class Account(object):
"""
bytes_name = name.encode("utf8")
chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name)
return Chat(self, chat_id)
return Chat(self._dc_context, chat_id)
def get_chats(self):
""" return list of chats.
@@ -248,15 +246,15 @@ class Account(object):
chatlist = []
for i in range(0, lib.dc_chatlist_get_cnt(dc_chatlist)):
chat_id = lib.dc_chatlist_get_chat_id(dc_chatlist, i)
chatlist.append(Chat(self, chat_id))
chatlist.append(Chat(self._dc_context, chat_id))
return chatlist
def get_deaddrop_chat(self):
return Chat(self, const.DC_CHAT_ID_DEADDROP)
return Chat(self._dc_context, const.DC_CHAT_ID_DEADDROP)
def get_message_by_id(self, msg_id):
""" return Message instance. """
return Message.from_db(self, msg_id)
return Message.from_db(self._dc_context, msg_id)
def mark_seen_messages(self, messages):
""" mark the given set of messages as seen.
@@ -439,17 +437,6 @@ class EventLogger:
raise ValueError("{}({!r},{!r})".format(*ev))
return ev
def ensure_event_not_queued(self, event_name_regex):
__tracebackhide__ = True
rex = re.compile("(?:{}).*".format(event_name_regex))
while 1:
try:
ev = self._event_queue.get(False)
except Empty:
break
else:
assert not rex.match(ev[0]), "event found {}".format(ev)
def get_matching(self, event_name_regex, check_error=True):
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
rex = re.compile("(?:{}).*".format(event_name_regex))

View File

@@ -1,30 +1,24 @@
""" chatting related objects: Contact, Chat, Message. """
import mimetypes
import os
from . import props
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
from .capi import lib, ffi
from . import const
import attr
from attr import validators as v
from .message import Message
@attr.s
class Contact(object):
""" Delta-Chat Contact.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, dc_context, id):
self._dc_context = dc_context
self.id = id
def __eq__(self, other):
return self._dc_context == other._dc_context and self.id == other.id
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Contact id={} addr={} dc_context={}>".format(self.id, self.addr, self._dc_context)
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int))
@property
def _dc_contact(self):
@@ -52,26 +46,14 @@ class Contact(object):
return lib.dc_contact_is_verified(self._dc_contact)
@attr.s
class Chat(object):
""" Chat object which manages members and through which you can send and retrieve messages.
You obtain instances of it through :class:`deltachat.account.Account`.
"""
def __init__(self, account, id):
self.account = account
self._dc_context = account._dc_context
self.id = id
def __eq__(self, other):
return self.id == getattr(other, "id", None) and \
self._dc_context == getattr(other, "_dc_context", None)
def __ne__(self, other):
return not (self == other)
def __repr__(self):
return "<Chat id={} name={} dc_context={}>".format(self.id, self.get_name(), self._dc_context)
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
id = attr.ib(validator=v.instance_of(int))
@property
def _dc_chat(self):
@@ -144,7 +126,7 @@ class Chat(object):
msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
if msg_id == 0:
raise ValueError("message could not be send, does chat exist?")
return Message.from_db(self.account, msg_id)
return Message.from_db(self._dc_context, msg_id)
def send_file(self, path, mime_type="application/octet-stream"):
""" send a file and return the resulting Message instance.
@@ -154,9 +136,14 @@ class Chat(object):
:raises ValueError: if message can not be send/chat does not exist.
:returns: the resulting :class:`deltachat.message.Message` instance
"""
msg = self.prepare_message_file(path=path, mime_type=mime_type)
self.send_prepared(msg)
return msg
path = as_dc_charpointer(path)
mtype = as_dc_charpointer(mime_type)
msg = Message.new(self._dc_context, "file")
msg.set_file(path, mtype)
msg_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be send, does chat exist?")
return Message.from_db(self._dc_context, msg_id)
def send_image(self, path):
""" send an image message and return the resulting Message instance.
@@ -165,25 +152,14 @@ class Chat(object):
:raises ValueError: if message can not be send/chat does not exist.
:returns: the resulting :class:`deltachat.message.Message` instance
"""
mime_type = mimetypes.guess_type(path)[0]
msg = self.prepare_message_file(path=path, mime_type=mime_type, view_type="image")
self.send_prepared(msg)
return msg
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
msg = Message.new(self._dc_context, "image")
msg.set_file(path)
msg_id = lib.dc_send_msg(self._dc_context, self.id, msg._dc_msg)
return Message.from_db(self._dc_context, msg_id)
def prepare_message(self, msg):
""" create a new prepared message.
:param msg: the message to be prepared.
:returns: :class:`deltachat.message.Message` instance.
"""
msg_id = lib.dc_prepare_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be prepared")
# invalidate passed in message which is not safe to use anymore
msg._dc_msg = msg.id = None
return Message.from_db(self.account, msg_id)
def prepare_message_file(self, path, mime_type=None, view_type="file"):
def prepare_file(self, path, mime_type=None, view_type="file"):
""" prepare a message for sending and return the resulting Message instance.
To actually send the message, call :meth:`send_prepared`.
@@ -191,13 +167,18 @@ class Chat(object):
:param path: path to the file.
:param mime_type: the mime-type of this file, defaults to auto-detection.
:param view_type: "text", "image", "gif", "audio", "video", "file"
:param view_type: passed to :meth:`MessageType.new`.
:raises ValueError: if message can not be prepared/chat does not exist.
:returns: the resulting :class:`Message` instance
"""
msg = Message.new_empty(self.account, view_type)
msg.set_file(path, mime_type)
return self.prepare_message(msg)
path = as_dc_charpointer(path)
mtype = as_dc_charpointer(mime_type)
msg = Message.new(self._dc_context, view_type)
msg.set_file(path, mtype)
msg_id = lib.dc_prepare_msg(self._dc_context, self.id, msg._dc_msg)
if msg_id == 0:
raise ValueError("message could not be prepared, does chat exist?")
return Message.from_db(self._dc_context, msg_id)
def send_prepared(self, message):
""" send a previously prepared message.
@@ -205,42 +186,12 @@ class Chat(object):
:param message: a :class:`Message` instance previously returned by
:meth:`prepare_file`.
:raises ValueError: if message can not be sent.
:returns: a :class:`deltachat.message.Message` instance as sent out.
:returns: a :class:`deltachat.message.Message` instance with updated state
"""
assert message.id != 0 and message.is_out_preparing()
# get a fresh copy of dc_msg, the core needs it
msg = Message.from_db(self.account, message.id)
# pass 0 as chat-id because core-docs say it's ok when out-preparing
sent_id = lib.dc_send_msg(self._dc_context, 0, msg._dc_msg)
if sent_id == 0:
msg_id = lib.dc_send_msg(self._dc_context, 0, message._dc_msg)
if msg_id == 0:
raise ValueError("message could not be sent")
assert sent_id == msg.id
# modify message in place to avoid bad state for the caller
msg._dc_msg = Message.from_db(self.account, sent_id)._dc_msg
def set_draft(self, message):
""" set message as draft.
:param message: a :class:`Message` instance
:returns: None
"""
if message is None:
lib.dc_set_draft(self._dc_context, self.id, ffi.NULL)
else:
lib.dc_set_draft(self._dc_context, self.id, message._dc_msg)
def get_draft(self):
""" get draft message for this chat.
:param message: a :class:`Message` instance
:returns: Message object or None (if no draft available)
"""
x = lib.dc_get_draft(self._dc_context, self.id)
if x == ffi.NULL:
return None
dc_msg = ffi.gc(x, lib.dc_msg_unref)
return Message(self.account, dc_msg)
return Message.from_db(self._dc_context, msg_id)
def get_messages(self):
""" return list of messages in this chat.
@@ -251,7 +202,7 @@ class Chat(object):
lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0),
lib.dc_array_unref
)
return list(iter_array(dc_array, lambda x: Message.from_db(self.account, x)))
return list(iter_array(dc_array, lambda x: Message.from_db(self._dc_context, x)))
def count_fresh_messages(self):
""" return number of fresh messages in this chat.

View File

@@ -1,55 +1,59 @@
""" chatting related objects: Contact, Chat, Message. """
import os
import shutil
from . import props
from .cutil import from_dc_charpointer, as_dc_charpointer
from .capi import lib, ffi
from . import const
from datetime import datetime
import attr
from attr import validators as v
@attr.s
class Message(object):
""" Message object.
You obtain instances of it through :class:`deltachat.account.Account` or
:class:`deltachat.chatting.Chat`.
"""
def __init__(self, account, dc_msg):
self.account = account
self._dc_context = account._dc_context
assert isinstance(self._dc_context, ffi.CData)
assert isinstance(dc_msg, ffi.CData)
assert dc_msg != ffi.NULL
self._dc_msg = dc_msg
self.id = lib.dc_msg_get_id(dc_msg)
assert self.id is not None and self.id >= 0, repr(self.id)
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
try:
id = attr.ib(validator=v.instance_of((int, long)))
except NameError: # py35
id = attr.ib(validator=v.instance_of(int))
def __eq__(self, other):
return self.account == other.account and self.id == other.id
def __repr__(self):
return "<Message id={} dc_context={}>".format(self.id, self._dc_context)
@classmethod
def from_db(cls, account, id):
assert id > 0
return cls(account, ffi.gc(
lib.dc_get_msg(account._dc_context, id),
@property
def _dc_msg(self):
if self.id > 0:
return ffi.gc(
lib.dc_get_msg(self._dc_context, self.id),
lib.dc_msg_unref
))
)
return self._dc_msg_volatile
@classmethod
def new_empty(cls, account, view_type):
""" create a non-persistent message.
def from_db(cls, _dc_context, id):
assert id > 0
return cls(_dc_context, id)
:param: view_type is "text", "audio", "video", "file"
"""
view_type_code = get_viewtype_code_from_name(view_type)
return Message(account, ffi.gc(
lib.dc_msg_new(account._dc_context, view_type_code),
@classmethod
def new(cls, dc_context, view_type):
""" create a non-persistent method. """
msg = cls(dc_context, 0)
view_type_code = MessageType.get_typecode(view_type)
msg._dc_msg_volatile = ffi.gc(
lib.dc_msg_new(dc_context, view_type_code),
lib.dc_msg_unref
))
)
return msg
def get_state(self):
""" get the message in/out state.
:returns: :class:`deltachat.message.MessageState`
"""
return MessageState(self)
@props.with_doc
def text(self):
@@ -58,9 +62,7 @@ class Message(object):
def set_text(self, text):
"""set text of this message. """
assert self.id > 0, "message not prepared"
assert self.is_out_preparing()
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
return lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
@props.with_doc
def filename(self):
@@ -68,23 +70,9 @@ class Message(object):
return from_dc_charpointer(lib.dc_msg_get_file(self._dc_msg))
def set_file(self, path, mime_type=None):
"""set file for this message from path and mime_type. """
mtype = ffi.NULL if mime_type is None else as_dc_charpointer(mime_type)
if not os.path.exists(path):
raise ValueError("path does not exist: {!r}".format(path))
blobdir = self.account.get_blobdir()
if not path.startswith(blobdir):
for i in range(50):
ext = "" if i == 0 else "-" + str(i)
dest = os.path.join(blobdir, os.path.basename(path) + ext)
if os.path.exists(dest):
continue
shutil.copyfile(path, dest)
break
else:
raise ValueError("could not create blobdir-path for {}".format(path))
path = dest
assert path.startswith(blobdir), path
"""set file for this message. """
mtype = ffi.NULL if mime_type is None else mime_type
assert os.path.exists(path)
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
@props.with_doc
@@ -97,17 +85,18 @@ class Message(object):
"""mime type of the file (if it exists)"""
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
@props.with_doc
def view_type(self):
"""the view type of this message.
:returns: a :class:`deltachat.message.MessageType` instance.
"""
return MessageType(lib.dc_msg_get_viewtype(self._dc_msg))
def is_setup_message(self):
""" return True if this message is a setup message. """
return lib.dc_msg_is_setupmessage(self._dc_msg)
def get_message_info(self):
""" Return informational text for a single message.
The text is multiline and may contain eg. the raw text of the message.
"""
return from_dc_charpointer(lib.dc_get_msg_info(self._dc_context, self.id))
def continue_key_transfer(self, setup_code):
""" extract key and use it as primary key for this account. """
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
@@ -155,7 +144,7 @@ class Message(object):
"""
from .chatting import Chat
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
return Chat(self.account, chat_id)
return Chat(self._dc_context, chat_id)
def get_sender_contact(self):
"""return the contact of who wrote the message.
@@ -166,20 +155,66 @@ class Message(object):
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
return Contact(self._dc_context, contact_id)
#
# Message State query methods
#
@attr.s
class MessageType(object):
""" DeltaChat message type, with is_* methods. """
_type = attr.ib(validator=v.instance_of(int))
_mapping = {
const.DC_MSG_TEXT: 'text',
const.DC_MSG_IMAGE: 'image',
const.DC_MSG_GIF: 'gif',
const.DC_MSG_AUDIO: 'audio',
const.DC_MSG_VIDEO: 'video',
const.DC_MSG_FILE: 'file'
}
@classmethod
def get_typecode(cls, view_type):
for code, value in cls._mapping.items():
if value == view_type:
return code
raise ValueError("message typecode not found for {!r}".format(view_type))
@props.with_doc
def name(self):
""" human readable type name. """
return self._mapping.get(self._type, "")
def is_text(self):
""" return True if it's a text message. """
return self._type == const.DC_MSG_TEXT
def is_image(self):
""" return True if it's an image message. """
return self._type == const.DC_MSG_IMAGE
def is_gif(self):
""" return True if it's a gif message. """
return self._type == const.DC_MSG_GIF
def is_audio(self):
""" return True if it's an audio message. """
return self._type == const.DC_MSG_AUDIO
def is_video(self):
""" return True if it's a video message. """
return self._type == const.DC_MSG_VIDEO
def is_file(self):
""" return True if it's a file message. """
return self._type == const.DC_MSG_FILE
@attr.s
class MessageState(object):
""" Current Message In/Out state, updated on each call of is_* methods.
"""
message = attr.ib(validator=v.instance_of(Message))
@property
def _msgstate(self):
if self.id == 0:
dc_msg = self.message._dc_msg
else:
# load message from db to get a fresh/current state
dc_msg = ffi.gc(
lib.dc_get_msg(self._dc_context, self.id),
lib.dc_msg_unref
)
return lib.dc_msg_get_state(dc_msg)
return lib.dc_msg_get_state(self.message._dc_msg)
def is_in_fresh(self):
""" return True if Message is incoming fresh message (un-noticed).
@@ -233,56 +268,3 @@ class Message(object):
state, you'll receive the event DC_EVENT_MSG_READ.
"""
return self._msgstate == const.DC_STATE_OUT_MDN_RCVD
#
# Message type query methods
#
@property
def _view_type(self):
assert self.id > 0
return lib.dc_msg_get_viewtype(self._dc_msg)
def is_text(self):
""" return True if it's a text message. """
return self._view_type == const.DC_MSG_TEXT
def is_image(self):
""" return True if it's an image message. """
return self._view_type == const.DC_MSG_IMAGE
def is_gif(self):
""" return True if it's a gif message. """
return self._view_type == const.DC_MSG_GIF
def is_audio(self):
""" return True if it's an audio message. """
return self._view_type == const.DC_MSG_AUDIO
def is_video(self):
""" return True if it's a video message. """
return self._view_type == const.DC_MSG_VIDEO
def is_file(self):
""" return True if it's a file message. """
return self._view_type == const.DC_MSG_FILE
# some code for handling DC_MSG_* view types
_view_type_mapping = {
const.DC_MSG_TEXT: 'text',
const.DC_MSG_IMAGE: 'image',
const.DC_MSG_GIF: 'gif',
const.DC_MSG_AUDIO: 'audio',
const.DC_MSG_VIDEO: 'video',
const.DC_MSG_FILE: 'file'
}
def get_viewtype_code_from_name(view_type_name):
for code, value in _view_type_mapping.items():
if value == view_type_name:
return code
raise ValueError("message typecode not found for {!r}, "
"available {!r}".format(view_type_name, list(_view_type_mapping.values())))

View File

@@ -2,12 +2,11 @@ from __future__ import print_function
import pytest
import os
from deltachat import const, Account
from deltachat.message import Message
from datetime import datetime, timedelta
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
class TestOfflineAccountBasic:
class TestOfflineAccount:
def test_wrong_db(self, tmpdir):
p = tmpdir.join("hello.db")
p.write("123")
@@ -58,22 +57,16 @@ class TestOfflineAccountBasic:
with pytest.raises(KeyError):
ac1.get_config("123123")
class TestOfflineContact:
def test_contact_attr(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@hello.com", name="some1")
contact2 = ac1.create_contact(email="some1@hello.com", name="some1")
str(contact1)
repr(contact1)
assert contact1 == contact2
assert contact1.id
assert contact1.addr == "some1@hello.com"
assert contact1.display_name == "some1"
assert not contact1.is_blocked()
assert not contact1.is_verified()
def test_get_contacts_and_delete(self, acfactory):
def test_get_contacts(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@hello.com", name="some1")
contacts = ac1.get_contacts()
@@ -86,48 +79,26 @@ class TestOfflineContact:
contacts = ac1.get_contacts(with_self=True)
assert len(contacts) == 2
assert ac1.delete_contact(contact1)
assert contact1 not in ac1.get_contacts()
def test_get_contacts_and_delete_fails(self, acfactory):
def test_chat(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("one messae")
assert not ac1.delete_contact(contact1)
class TestOfflineChat:
@pytest.fixture
def ac1(self, acfactory):
return acfactory.get_configured_offline_account()
@pytest.fixture
def chat1(self, ac1):
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL, chat.id
return chat
def test_display(self, chat1):
str(chat1)
repr(chat1)
def test_chat_idempotent(self, chat1, ac1):
contact1 = chat1.get_contacts()[0]
chat2 = ac1.create_chat_by_contact(contact1.id)
assert chat2.id == chat1.id
assert chat2.get_name() == chat1.get_name()
assert chat1 == chat2
assert not (chat1 != chat2)
assert chat2.id == chat.id
assert chat2.get_name() == chat.get_name()
assert chat == chat2
assert not (chat != chat2)
for ichat in ac1.get_chats():
if ichat.id == chat1.id:
if ichat.id == chat.id:
break
else:
pytest.fail("could not find chat")
def test_group_chat_creation(self, ac1):
def test_group_chat_creation(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
contact2 = ac1.create_contact("some2@hello.com", name="some2")
chat = ac1.create_group_chat(name="title1")
@@ -140,73 +111,64 @@ class TestOfflineChat:
chat.set_name("title2")
assert chat.get_name() == "title2"
def test_delete_and_send_fails(self, ac1, chat1):
chat1.delete()
def test_delete_and_send_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.delete()
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
with pytest.raises(ValueError):
chat1.send_text("msg1")
chat.send_text("msg1")
def test_prepare_message_and_send(self, ac1, chat1):
msg = chat1.prepare_message(Message.new_empty(chat1.account, "text"))
msg.set_text("hello world")
assert msg.text == "hello world"
assert msg.id > 0
chat1.send_prepared(msg)
assert "Sent" in msg.get_message_info()
str(msg)
repr(msg)
assert msg == ac1.get_message_by_id(msg.id)
def test_create_message(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
message = ac1.create_message("text")
assert message.id == 0
assert message._dc_msg is message._dc_msg
message.set_text("hello")
assert message.text == "hello"
assert message.id == 0
def test_prepare_file(self, ac1, chat1):
blobdir = ac1.get_blobdir()
p = os.path.join(blobdir, "somedata.txt")
with open(p, "w") as f:
f.write("some data")
message = chat1.prepare_message_file(p)
assert message.id > 0
message.set_text("hello world")
assert message.is_out_preparing()
assert message.text == "hello world"
chat1.send_prepared(message)
assert "Sent" in message.get_message_info()
def test_message_eq_contains(self, chat1):
msg = chat1.send_text("msg1")
assert msg in chat1.get_messages()
assert not (msg not in chat1.get_messages())
str(msg)
repr(msg)
def test_message_send_text(self, chat1):
msg = chat1.send_text("msg1")
def test_message(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("msg1")
assert msg
assert msg.is_text()
assert not msg.is_audio()
assert not msg.is_video()
assert not msg.is_gif()
assert not msg.is_file()
assert not msg.is_image()
assert msg.view_type.is_text()
assert msg.view_type.name == "text"
assert not msg.view_type.is_audio()
assert not msg.view_type.is_video()
assert not msg.view_type.is_gif()
assert not msg.view_type.is_file()
assert not msg.view_type.is_image()
msg_state = msg.get_state()
assert not msg_state.is_in_fresh()
assert not msg_state.is_in_noticed()
assert not msg_state.is_in_seen()
assert msg_state.is_out_pending()
assert not msg_state.is_out_failed()
assert not msg_state.is_out_delivered()
assert not msg_state.is_out_mdn_received()
assert not msg.is_in_fresh()
assert not msg.is_in_noticed()
assert not msg.is_in_seen()
assert msg.is_out_pending()
assert not msg.is_out_failed()
assert not msg.is_out_delivered()
assert not msg.is_out_mdn_received()
def test_create_chat_by_mssage_id(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
msg = chat.send_text("msg1")
assert chat == ac1.create_chat_by_message(msg)
assert chat == ac1.create_chat_by_message(msg.id)
def test_create_chat_by_message_id(self, ac1, chat1):
msg = chat1.send_text("msg1")
assert chat1 == ac1.create_chat_by_message(msg)
assert chat1 == ac1.create_chat_by_message(msg.id)
def test_message_image(self, chat1, data, lp):
def test_message_image(self, acfactory, data, lp):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
with pytest.raises(ValueError):
chat1.send_image(path="notexists")
chat.send_image(path="notexists")
fn = data.get_path("d.png")
lp.sec("sending image")
msg = chat1.send_image(fn)
assert msg.is_image()
msg = chat.send_image(fn)
assert msg.view_type.name == "image"
assert msg
assert msg.id > 0
assert os.path.exists(msg.filename)
@@ -217,19 +179,20 @@ class TestOfflineChat:
("text/plain", "text/plain"),
("image/png", "image/png"),
])
def test_message_file(self, ac1, chat1, data, lp, typein, typeout):
def test_message_file(self, acfactory, data, lp, typein, typeout):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
lp.sec("sending file")
fn = data.get_path("r.txt")
msg = chat1.send_file(fn, typein)
msg = chat.send_file(fn, typein)
assert msg
assert msg.id > 0
assert msg.is_file()
assert msg.view_type.name == "file"
assert msg.view_type.is_file()
assert os.path.exists(msg.filename)
assert msg.filename.endswith(msg.basename)
assert msg.filemime == typeout
msg2 = chat1.send_file(fn, typein)
assert msg2 != msg
assert msg2.filename != msg.filename
def test_create_chat_mismatch(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
@@ -242,9 +205,12 @@ class TestOfflineChat:
with pytest.raises(ValueError):
ac2.create_chat_by_message(msg)
def test_chat_message_distinctions(self, ac1, chat1):
def test_chat_message_distinctions(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact("some1@hello.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
past1s = datetime.utcnow() - timedelta(seconds=1)
msg = chat1.send_text("msg1")
msg = chat.send_text("msg1")
ts = msg.time_sent
assert msg.time_received is None
assert ts.strftime("Y")
@@ -252,7 +218,8 @@ class TestOfflineChat:
contact = msg.get_sender_contact()
assert contact == ac1.get_self_contact()
def test_basic_configure_ok_addr_setting_forbidden(self, ac1):
def test_basic_configure_ok_addr_setting_forbidden(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
assert ac1.get_config("mail_pw")
assert ac1.is_configured()
with pytest.raises(ValueError):
@@ -291,21 +258,11 @@ class TestOfflineChat:
assert messages[0].text == "msg1"
assert os.path.exists(messages[1].filename)
def test_ac_setup_message_fails(self, ac1):
def test_ac_setup_message_fails(self, acfactory):
ac1 = acfactory.get_configured_offline_account()
with pytest.raises(RuntimeError):
ac1.initiate_key_transfer()
def test_set_get_draft(self, chat1):
msg = Message.new_empty(chat1.account, "text")
msg1 = chat1.prepare_message(msg)
msg1.set_text("hello")
chat1.set_draft(msg1)
msg1.set_text("obsolete")
msg2 = chat1.get_draft()
assert msg2.text == "hello"
chat1.set_draft(None)
assert chat1.get_draft() is None
class TestOnlineAccount:
def test_one_account_init(self, acfactory):
@@ -337,13 +294,48 @@ class TestOnlineAccount:
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
msg_out = chat.send_text("message1")
txt = "message1"
msg_out = chat.send_text(txt)
# wait for other account to receive
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.text == "message1"
assert msg_in.text == txt
def test_two_accounts_send_receive_encrypted(self, acfactory, lp):
lp.sec("starting accounts, waiting for configuration")
ac1 = acfactory.get_online_configuring_account()
ac2 = acfactory.get_online_configuring_account()
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_successful_IMAP_SMTP_connection(ac1)
wait_configuration_progress(ac1, 1000)
wait_successful_IMAP_SMTP_connection(ac2)
wait_configuration_progress(ac2, 1000)
lp.sec("sending message from ac1 to ac2")
msg_out = chat.send_text("hello")
lp.sec("wait for second account to receive")
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
lp.sec("chat back with text message, encrypted from ac2 to ac1")
chat_back = ac2.create_chat_by_message(msg_out.id)
txt = "https://testflight.apple.com/join/uEMc1NxS"
msg_back = chat_back.send_text(txt)
lp.sec("wait for first account to receive")
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
if ev[2] != msg_back.id:
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_back.id
chat_back = ac1.create_chat_by_message(msg_back.id)
msg_in = chat_back.get_messages()[-1]
assert msg_in.text == text
def test_forward_messages(self, acfactory):
ac1 = acfactory.get_online_configuring_account()
@@ -394,7 +386,7 @@ class TestOnlineAccount:
evt_name, data1, data2 = ev
assert data1 == chat.id
assert data2 == msg_out.id
assert msg_out.is_out_delivered()
assert msg_out.get_state().is_out_delivered()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
@@ -423,7 +415,8 @@ class TestOnlineAccount:
lp.step("1")
ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
lp.step("2")
assert msg_out.is_out_mdn_received()
# ac1._evlogger.get_info_matching("Message marked as seen")
assert msg_out.get_state().is_out_mdn_received()
def test_saved_mime_on_received_message(self, acfactory, lp):
lp.sec("starting accounts, waiting for configuration")
@@ -463,13 +456,13 @@ class TestOnlineAccount:
evt_name, data1, data2 = ev
assert data1 == chat.id
assert data2 == msg_out.id
assert msg_out.is_out_delivered()
assert msg_out.get_state().is_out_delivered()
lp.sec("wait for ac2 to receive message")
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev[2] == msg_out.id
msg_in = ac2.get_message_by_id(msg_out.id)
assert msg_in.is_image()
assert msg_in.view_type.is_image()
assert os.path.exists(msg_in.filename)
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
@@ -505,7 +498,7 @@ class TestOnlineAccount:
wait_configuration_progress(ac1, 1000)
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
setup_code = ac1.initiate_key_transfer()
ac2._evlogger.set_timeout(30)
ac2._evlogger.set_timeout(10)
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
msg = ac2.get_message_by_id(ev[2])
assert msg.is_setup_message()

View File

@@ -1,4 +1,6 @@
from __future__ import print_function
import os
import shutil
from filecmp import cmp
from deltachat import const
from conftest import wait_configuration_progress, wait_msgs_changed
@@ -11,15 +13,18 @@ class TestInCreation:
wait_configuration_progress(ac1, 1000)
wait_configuration_progress(ac2, 1000)
blobdir = ac1.get_blobdir()
c2 = ac1.create_contact(email=ac2.get_config("addr"))
chat = ac1.create_chat_by_contact(c2)
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
wait_msgs_changed(ac1, 0, 0) # why no chat id?
lp.sec("create a message with a file in creation")
path = data.get_path("d.png")
prepared_original = chat.prepare_message_file(path)
assert prepared_original.is_out_preparing()
path = os.path.join(blobdir, "d.png")
open(path, 'a').close()
prepared_original = chat.prepare_file(path)
assert prepared_original.get_state().is_out_preparing()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
lp.sec("forward the message while still in creation")
@@ -32,36 +37,35 @@ class TestInCreation:
forwarded_id = wait_msgs_changed(ac1, chat2.id)
if forwarded_id == 0:
forwarded_id = wait_msgs_changed(ac1, chat2.id)
assert forwarded_id
forwarded_msg = ac1.get_message_by_id(forwarded_id)
assert forwarded_msg.is_out_preparing()
assert forwarded_msg.get_state().is_out_preparing()
lp.sec("finish creating the file and send it")
assert prepared_original.is_out_preparing()
chat.send_prepared(prepared_original)
assert prepared_original.is_out_pending() or prepared_original.is_out_delivered()
wait_msgs_changed(ac1, chat.id, prepared_original.id)
shutil.copy(data.get_path("d.png"), path)
sent_original = chat.send_prepared(prepared_original)
assert sent_original.id == prepared_original.id
state = sent_original.get_state()
assert state.is_out_pending() or state.is_out_delivered()
wait_msgs_changed(ac1, chat.id, sent_original.id)
lp.sec("expect the forwarded message to be sent now too")
wait_msgs_changed(ac1, chat2.id, forwarded_id)
fwd_msg = ac1.get_message_by_id(forwarded_id)
assert fwd_msg.is_out_pending() or fwd_msg.is_out_delivered()
state = ac1.get_message_by_id(forwarded_id).get_state()
assert state.is_out_pending() or state.is_out_delivered()
lp.sec("wait for the messages to be delivered to SMTP")
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
assert ev[1] == chat.id
assert ev[2] == prepared_original.id
assert ev[2] == sent_original.id
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
assert ev[1] == chat2.id
assert ev[2] == forwarded_id
lp.sec("wait1 for original or forwarded messages to arrive")
lp.sec("wait for both messages to arrive")
ev1 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev1[1] >= const.DC_CHAT_ID_LAST_SPECIAL
received_original = ac2.get_message_by_id(ev1[2])
assert cmp(received_original.filename, path, False)
lp.sec("wait2 for original or forwarded messages to arrive")
ev2 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
assert ev2[1] >= const.DC_CHAT_ID_LAST_SPECIAL
assert ev2[1] != ev1[1]

View File

@@ -64,14 +64,3 @@ def test_sig():
assert sig(const.DC_EVENT_SMTP_CONNECTED) == 2
assert sig(const.DC_EVENT_IMAP_CONNECTED) == 2
assert sig(const.DC_EVENT_SMTP_MESSAGE_SENT) == 2
def test_markseen_invalid_message_ids(acfactory):
ac1 = acfactory.get_configured_offline_account()
contact1 = ac1.create_contact(email="some1@example.com", name="some1")
chat = ac1.create_chat_by_contact(contact1)
chat.send_text("one messae")
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
msg_ids = [9]
lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids))
ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR")

View File

@@ -8,7 +8,7 @@ envlist =
[testenv]
commands =
pytest -v -rsXx {posargs:tests}
pytest -rsXx {posargs:tests}
python tests/package_wheels.py {toxworkdir}/wheelhouse
passenv =
TRAVIS

View File

@@ -1,336 +0,0 @@
use crate::constants::*;
use crate::context::*;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_lot::*;
use crate::dc_msg::*;
use crate::dc_tools::*;
use crate::error::Result;
use crate::stock::StockMessage;
/// An object representing a single chatlist in memory.
///
/// Chatlist objects contain chat IDs and, if possible, message IDs belonging to them.
/// The chatlist object is not updated; if you want an update, you have to recreate the object.
///
/// For a **typical chat overview**, the idea is to get the list of all chats via dc_get_chatlist()
/// without any listflags (see below) and to implement a "virtual list" or so
/// (the count of chats is known by chatlist.len()).
///
/// Only for the items that are in view (the list may have several hundreds chats),
/// the UI should call chatlist.get_summary() then.
/// chatlist.get_summary() provides all elements needed for painting the item.
///
/// On a click of such an item, the UI should change to the chat view
/// and get all messages from this view via dc_get_chat_msgs().
/// Again, a "virtual list" is created (the count of messages is known)
/// and for each messages that is scrolled into view, dc_get_msg() is called then.
///
/// Why no listflags?
/// Without listflags, dc_get_chatlist() adds the deaddrop and the archive "link" automatically as needed.
/// The UI can just render these items differently then. Although the deaddrop link is currently always the
/// first entry and only present on new messages, there is the rough idea that it can be optionally always
/// present and sorted into the list by date. Rendering the deaddrop in the described way
/// would not add extra work in the UI then.
pub struct Chatlist<'a> {
context: &'a Context,
/// Stores pairs of `chat_id, message_id`
ids: Vec<(u32, u32)>,
}
impl<'a> Chatlist<'a> {
pub fn get_context(&self) -> &Context {
self.context
}
/// Get a list of chats.
/// The list can be filtered by query parameters.
///
/// The list is already sorted and starts with the most recent chat in use.
/// The sorting takes care of invalid sending dates, drafts and chats without messages.
/// Clients should not try to re-sort the list as this would be an expensive action
/// and would result in inconsistencies between clients.
///
/// To get information about each entry, use eg. chatlist.get_summary().
///
/// By default, the function adds some special entries to the list.
/// These special entries can be identified by the ID returned by chatlist.get_chat_id():
/// - DC_CHAT_ID_DEADDROP (1) - this special chat is present if there are
/// messages from addresses that have no relationship to the configured account.
/// The last of these messages is represented by DC_CHAT_ID_DEADDROP and you can retrieve details
/// about it with chatlist.get_msg_id(). Typically, the UI asks the user "Do you want to chat with NAME?"
/// and offers the options "Yes" (call dc_create_chat_by_msg_id()), "Never" (call dc_block_contact())
/// or "Not now".
/// The UI can also offer a "Close" button that calls dc_marknoticed_contact() then.
/// - DC_CHAT_ID_ARCHIVED_LINK (6) - this special chat is present if the user has
/// archived _any_ chat using dc_archive_chat(). The UI should show a link as
/// "Show archived chats", if the user clicks this item, the UI should show a
/// list of all archived chats that can be created by this function hen using
/// the DC_GCL_ARCHIVED_ONLY flag.
/// - DC_CHAT_ID_ALLDONE_HINT (7) - this special chat is present
/// if DC_GCL_ADD_ALLDONE_HINT is added to listflags
/// and if there are only archived chats.
///
/// The `listflags` is a combination of flags:
/// - if the flag DC_GCL_ARCHIVED_ONLY is set, only archived chats are returned.
/// if DC_GCL_ARCHIVED_ONLY is not set, only unarchived chats are returned and
/// the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are _any_ archived
/// chats
/// - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added
/// to the list (may be used eg. for selecting chats on forwarding, the flag is
/// not needed when DC_GCL_ARCHIVED_ONLY is already set)
/// - if the flag DC_GCL_ADD_ALLDONE_HINT is set, DC_CHAT_ID_ALLDONE_HINT
/// is added as needed.
/// `query`: An optional query for filtering the list. Only chats matching this query
/// are returned.
/// `query_contact_id`: An optional contact ID for filtering the list. Only chats including this contact ID
/// are returned.
pub fn try_load(
context: &'a Context,
listflags: usize,
query: Option<&str>,
query_contact_id: Option<u32>,
) -> Result<Self> {
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id as u32, msg_id as u32))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
rows.collect::<std::result::Result<Vec<_>, _>>()
.map_err(Into::into)
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let mut ids = if let Some(query_contact_id) = query_contact_id {
// show chats shared with a given contact
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)?
} else if 0 != listflags & DC_GCL_ARCHIVED_ONLY {
// show archived chats
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)?
} else if let Some(query) = query {
let query = query.trim().to_string();
ensure!(!query.is_empty(), "missing query");
let str_like_cmd = format!("%{}%", query);
context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![str_like_cmd],
process_row,
process_rows,
)?
} else {
// show normal chatlist
let mut ids = context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)?;
if 0 == listflags & DC_GCL_NO_SPECIALS {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg(context);
if last_deaddrop_fresh_msg_id > 0 {
ids.push((1, last_deaddrop_fresh_msg_id));
}
add_archived_link_item = 1;
}
ids
};
if 0 != add_archived_link_item && dc_get_archived_cnt(context) > 0 {
if ids.is_empty() && 0 != listflags & DC_GCL_ADD_ALLDONE_HINT {
ids.push((DC_CHAT_ID_ALLDONE_HINT as u32, 0));
}
ids.push((DC_CHAT_ID_ARCHIVED_LINK as u32, 0));
}
Ok(Chatlist { context, ids })
}
/// Find out the number of chats.
pub fn len(&self) -> usize {
self.ids.len()
}
pub fn is_empty(&self) -> bool {
self.ids.is_empty()
}
/// Get a single chat ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_chat().
pub fn get_chat_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].0
}
/// Get a single message ID of a chatlist.
///
/// To get the message object from the message ID, use dc_get_msg().
pub fn get_msg_id(&self, index: usize) -> u32 {
if index >= self.ids.len() {
return 0;
}
self.ids[index].1
}
/// Get a summary for a chatlist index.
///
/// The summary is returned by a dc_lot_t object with the following fields:
///
/// - dc_lot_t::text1: contains the username or the strings "Me", "Draft" and so on.
/// The string may be colored by having a look at text1_meaning.
/// If there is no such name or it should not be displayed, the element is NULL.
/// - dc_lot_t::text1_meaning: one of DC_TEXT1_USERNAME, DC_TEXT1_SELF or DC_TEXT1_DRAFT.
/// Typically used to show dc_lot_t::text1 with different colors. 0 if not applicable.
/// - dc_lot_t::text2: contains an excerpt of the message text or strings as
/// "No messages". May be NULL of there is no such text (eg. for the archive link)
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
// 0 if not applicable.
pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t {
// The summary is created by the chat, not by the last message.
// This is because we may want to display drafts here or stuff as
// "is typing".
// Also, sth. as "No messages" would not work if the summary comes from a message.
let mut ret = dc_lot_new();
if index >= self.ids.len() {
(*ret).text2 = "ErrBadChatlistIndex".strdup();
return ret;
}
let lastmsg_id = self.ids[index].1;
let mut lastcontact = 0 as *mut dc_contact_t;
if chat.is_null() {
chat = dc_chat_new(self.context);
let chat_to_delete = chat;
if !dc_chat_load_from_db(chat, self.ids[index].0) {
(*ret).text2 = "ErrCannotReadChat".strdup();
dc_chat_unref(chat_to_delete);
return ret;
}
}
let lastmsg = if 0 != lastmsg_id {
let lastmsg = dc_msg_new_untyped(self.context);
dc_msg_load_from_db(lastmsg, self.context, lastmsg_id);
if (*lastmsg).from_id != 1 as libc::c_uint
&& ((*chat).type_0 == DC_CHAT_TYPE_GROUP
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP)
{
lastcontact = dc_contact_new(self.context);
dc_contact_load_from_db(lastcontact, &self.context.sql, (*lastmsg).from_id);
}
lastmsg
} else {
std::ptr::null_mut()
};
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_SELF as u32 {
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
} else {
dc_lot_fill(ret, lastmsg, chat, lastcontact, self.context);
}
dc_msg_unref(lastmsg);
dc_contact_unref(lastcontact);
ret
}
}
pub fn dc_get_archived_cnt(context: &Context) -> u32 {
context
.sql
.query_row_col(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// We have an index over the state-column, this should be sufficient as there are typically
// only few fresh messages.
context
.sql
.query_row_col(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
}

View File

@@ -1,14 +1,12 @@
use std::ffi::CString;
use strum::{EnumProperty, IntoEnumIterator};
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.
@@ -74,8 +72,10 @@ impl Context {
let rel_path = self.sql.get_config(self, key);
rel_path.map(|p| {
let v = unsafe {
let n = CString::yolo(p);
dc_get_abs_path(self, n.as_ptr())
let n = to_cstring(p);
let res = dc_get_abs_path(self, n);
free(n as *mut libc::c_void);
res
};
let r = to_string(v);
unsafe { free(v as *mut _) };
@@ -94,7 +94,12 @@ impl Context {
// Default values
match key {
Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()),
Config::Selfstatus => {
let s = unsafe { dc_stock_str(self, 13) };
let res = to_string(s);
unsafe { free(s as *mut _) };
Some(res)
}
_ => key.get_str("default").map(|s| s.to_string()),
}
}
@@ -124,14 +129,15 @@ impl Context {
ret
}
Config::Selfstatus => {
let def = self.stock_str(StockMessage::StatusLine);
let val = if value.is_none() || value.unwrap() == def {
let def = unsafe { dc_stock_str(self, 13) };
let val = if value.is_none() || value.unwrap() == as_str(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,8 +1,4 @@
//! Constants
#![allow(non_camel_case_types)]
use num_traits::{FromPrimitive, ToPrimitive};
use rusqlite as sql;
use rusqlite::types::*;
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
@@ -63,26 +59,26 @@ pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
/// larger chat IDs are "real" chats, their messages are "real" messages.
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
pub const DC_CHAT_TYPE_UNDEFINED: i32 = 0;
pub const DC_CHAT_TYPE_SINGLE: i32 = 100;
pub const DC_CHAT_TYPE_GROUP: i32 = 120;
pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130;
pub const DC_CHAT_TYPE_UNDEFINED: usize = 0;
pub const DC_CHAT_TYPE_SINGLE: usize = 100;
pub const DC_CHAT_TYPE_GROUP: usize = 120;
pub const DC_CHAT_TYPE_VERIFIED_GROUP: usize = 130;
pub const DC_MSG_ID_MARKER1: usize = 1;
pub const DC_MSG_ID_DAYMARKER: usize = 9;
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
pub const DC_STATE_UNDEFINED: i32 = 0;
pub const DC_STATE_IN_FRESH: i32 = 10;
pub const DC_STATE_IN_NOTICED: i32 = 13;
pub const DC_STATE_IN_SEEN: i32 = 16;
pub const DC_STATE_OUT_PREPARING: i32 = 18;
pub const DC_STATE_OUT_DRAFT: i32 = 19;
pub const DC_STATE_OUT_PENDING: i32 = 20;
pub const DC_STATE_OUT_FAILED: i32 = 24;
pub const DC_STATE_UNDEFINED: usize = 0;
pub const DC_STATE_IN_FRESH: usize = 10;
pub const DC_STATE_IN_NOTICED: usize = 13;
pub const DC_STATE_IN_SEEN: usize = 16;
pub const DC_STATE_OUT_PREPARING: usize = 18;
pub const DC_STATE_OUT_DRAFT: usize = 19;
pub const DC_STATE_OUT_PENDING: usize = 20;
pub const DC_STATE_OUT_FAILED: usize = 24;
/// to check if a mail was sent, use dc_msg_is_sent()
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
pub const DC_STATE_OUT_DELIVERED: usize = 26;
pub const DC_STATE_OUT_MDN_RCVD: usize = 28;
/// approx. max. length returned by dc_msg_get_text()
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
@@ -99,6 +95,46 @@ pub const DC_TEXT1_SELF: usize = 3;
pub const DC_CREATE_MVBOX: usize = 1;
/// Text message.
/// The text of the message is set using dc_msg_set_text()
/// and retrieved with dc_msg_get_text().
pub const DC_MSG_TEXT: usize = 10;
/// Image message.
/// If the image is an animated GIF, the type DC_MSG_GIF should be used.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension
/// and retrieved via dc_msg_set_file(), dc_msg_set_dimension().
pub const DC_MSG_IMAGE: usize = 20;
/// Animated GIF message.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension()
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
pub const DC_MSG_GIF: usize = 21;
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
pub const DC_MSG_AUDIO: usize = 40;
/// A voice message that was directly recorded by the user.
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
pub const DC_MSG_VOICE: usize = 41;
/// Video messages.
/// File, width, height and durarion
/// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration()
/// and retrieved via
/// dc_msg_get_file(), dc_msg_get_width(),
/// dc_msg_get_height(), dc_msg_get_duration().
pub const DC_MSG_VIDEO: usize = 50;
/// Message containing any file, eg. a PDF.
/// The file is set via dc_msg_set_file()
/// and retrieved via dc_msg_get_file().
pub const DC_MSG_FILE: usize = 60;
// Flags for configuring IMAP and SMTP servers.
// These flags are optional
// and may be set together with the username, password etc.
@@ -146,78 +182,6 @@ pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
#[repr(i32)]
pub enum Viewtype {
Unknown = 0,
/// Text message.
/// The text of the message is set using dc_msg_set_text()
/// and retrieved with dc_msg_get_text().
Text = 10,
/// Image message.
/// If the image is an animated GIF, the type DC_MSG_GIF should be used.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension
/// and retrieved via dc_msg_set_file(), dc_msg_set_dimension().
Image = 20,
/// Animated GIF message.
/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension()
/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height().
Gif = 21,
/// Message containing an Audio file.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration().
Audio = 40,
/// A voice message that was directly recorded by the user.
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
Voice = 41,
/// Video messages.
/// File, width, height and durarion
/// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration()
/// and retrieved via
/// dc_msg_get_file(), dc_msg_get_width(),
/// dc_msg_get_height(), dc_msg_get_duration().
Video = 50,
/// Message containing any file, eg. a PDF.
/// The file is set via dc_msg_set_file()
/// and retrieved via dc_msg_get_file().
File = 60,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn derive_display_works_as_expected() {
assert_eq!(format!("{}", Viewtype::Audio), "Audio");
}
}
impl ToSql for Viewtype {
fn to_sql(&self) -> sql::Result<ToSqlOutput> {
let num: i64 = self
.to_i64()
.expect("impossible: Viewtype -> i64 conversion failed");
Ok(ToSqlOutput::Owned(Value::Integer(num)))
}
}
impl FromSql for Viewtype {
fn column_result(col: ValueRef) -> FromSqlResult<Self> {
let inner = FromSql::column_result(col)?;
FromPrimitive::from_i64(inner).ok_or(FromSqlError::InvalidType)
}
}
// These constants are used as events
// reported to the callback given to dc_context_new().
// If you do not want to handle an event, it is always safe to return 0,

View File

@@ -14,7 +14,6 @@ use crate::dc_receive_imf::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::key::*;
use crate::param::Params;
use crate::smtp::*;
use crate::sql::Sql;
use crate::types::*;
@@ -27,15 +26,15 @@ pub struct Context {
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
pub sql: Sql,
pub inbox: Arc<RwLock<Imap>>,
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
pub probe_imap_network: Arc<RwLock<bool>>,
pub perform_inbox_jobs_needed: Arc<RwLock<i32>>,
pub probe_imap_network: Arc<RwLock<i32>>,
pub sentbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub mvbox_thread: Arc<RwLock<dc_jobthread_t>>,
pub smtp: Arc<Mutex<Smtp>>,
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
pub oauth2_critical: Arc<Mutex<()>>,
pub cb: Option<dc_callback_t>,
pub os_name: Option<String>,
pub os_name: *mut libc::c_char,
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
pub bob: Arc<RwLock<BobStatus>>,
pub last_smeared_timestamp: Arc<RwLock<i64>>,
@@ -106,17 +105,33 @@ impl Default for BobStatus {
#[derive(Default, Debug)]
pub struct SmtpState {
pub idle: bool,
pub suspended: bool,
pub doing_jobs: bool,
pub suspended: i32,
pub doing_jobs: i32,
pub perform_jobs_needed: i32,
pub probe_network: bool,
pub probe_network: i32,
}
// location handling
#[derive(Copy, Clone)]
#[repr(C)]
pub struct _dc_location {
pub location_id: uint32_t,
pub latitude: libc::c_double,
pub longitude: libc::c_double,
pub accuracy: libc::c_double,
pub timestamp: i64,
pub contact_id: uint32_t,
pub msg_id: uint32_t,
pub chat_id: uint32_t,
pub marker: *mut libc::c_char,
pub independent: uint32_t,
}
// create/open/config/information
pub fn dc_context_new(
cb: Option<dc_callback_t>,
userdata: *mut libc::c_void,
os_name: Option<String>,
os_name: *const libc::c_char,
) -> Context {
Context {
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
@@ -131,7 +146,7 @@ pub fn dc_context_new(
})),
userdata,
cb,
os_name: os_name,
os_name: unsafe { dc_strdup_keep_null(os_name) },
running_state: Arc::new(RwLock::new(Default::default())),
sql: Sql::new(),
smtp: Arc::new(Mutex::new(Smtp::new())),
@@ -160,18 +175,8 @@ pub fn dc_context_new(
cb_receive_imf,
),
))),
probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_crashes_on_context_deref() {
let mut ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
unsafe { dc_context_unref(&mut ctx) };
probe_imap_network: Arc::new(RwLock::new(0)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(0)),
}
}
@@ -236,15 +241,26 @@ unsafe fn cb_precheck_imf(
}
dc_do_heuristics_moves(context, server_folder, msg_id);
if 0 != mark_seen {
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
dc_job_add(
context,
130i32,
msg_id as libc::c_int,
0 as *const libc::c_char,
0i32,
);
}
}
free(old_server_folder as *mut libc::c_void);
return rfc724_mid_exists;
}
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
context.sql.set_config(context, key, value).ok();
unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) {
let v = if value.is_null() {
None
} else {
Some(as_str(value))
};
context.sql.set_config(context, as_str(key), v).ok();
}
/* *
@@ -254,14 +270,24 @@ fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
*
* @private @memberof Context
*/
fn cb_get_config(context: &Context, key: &str) -> Option<String> {
context.sql.get_config(context, key)
unsafe fn cb_get_config(
context: &Context,
key: *const libc::c_char,
def: *const libc::c_char,
) -> *mut libc::c_char {
let res = context
.sql
.get_config(context, as_str(key))
.unwrap_or_else(|| to_string(def));
to_cstring(res)
}
pub unsafe fn dc_context_unref(context: &mut Context) {
if 0 != dc_is_open(context) {
dc_close(context);
}
free(context.os_name as *mut libc::c_void);
}
pub unsafe fn dc_close(context: &Context) {
@@ -305,26 +331,32 @@ pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
context.userdata as *mut _
}
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
let mut success = false;
pub unsafe fn dc_open(
context: &Context,
dbfile: *const libc::c_char,
blobdir: *const libc::c_char,
) -> libc::c_int {
let mut success = 0;
if 0 != dc_is_open(context) {
return false;
return 0;
}
*context.dbfile.write().unwrap() = dbfile.strdup();
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup();
*context.blobdir.write().unwrap() = dir;
} else {
let dir = (dbfile.to_string() + "-blobs").strdup();
dc_create_folder(context, dir);
*context.blobdir.write().unwrap() = dir;
if !dbfile.is_null() {
*context.dbfile.write().unwrap() = dc_strdup(dbfile);
if !blobdir.is_null() && 0 != *blobdir.offset(0isize) as libc::c_int {
let dir = dc_strdup(blobdir);
dc_ensure_no_slash(dir);
*context.blobdir.write().unwrap() = dir;
} else {
let dir = dc_mprintf(b"%s-blobs\x00" as *const u8 as *const libc::c_char, dbfile);
dc_create_folder(context, dir);
*context.blobdir.write().unwrap() = dir;
}
// Create/open sqlite database, this may already use the blobdir
if context.sql.open(context, as_path(dbfile), 0) {
success = 1i32
}
}
// Create/open sqlite database, this may already use the blobdir
let dbfile_path = std::path::Path::new(dbfile);
if context.sql.open(context, dbfile_path, 0) {
success = true
}
if !success {
if 0 == success {
dc_close(context);
}
success
@@ -483,7 +515,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
fingerprint_str,
);
res.strdup()
to_cstring(res)
}
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
@@ -505,19 +537,18 @@ pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|row| row.get(0),
|rows| {
let mut ret = dc_array_t::new(128);
let ret = unsafe { dc_array_new(128 as size_t) };
for row in rows {
let id = row?;
ret.add_id(id);
unsafe { dc_array_add_id(ret, id) };
}
Ok(ret.into_raw())
Ok(ret)
},
)
.unwrap()
}
#[allow(non_snake_case)]
pub fn dc_search_msgs(
context: &Context,
chat_id: uint32_t,
@@ -545,7 +576,7 @@ pub fn dc_search_msgs(
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
};
let mut ret = dc_array_t::new(100);
let ret = unsafe { dc_array_new(100 as size_t) };
let success = context
.sql
@@ -555,7 +586,7 @@ pub fn dc_search_msgs(
|row| row.get::<_, i32>(0),
|rows| {
for id in rows {
ret.add_id(id? as u32);
unsafe { dc_array_add_id(ret, id? as u32) };
}
Ok(())
},
@@ -563,9 +594,12 @@ pub fn dc_search_msgs(
.is_ok();
if success {
return ret.into_raw();
return ret;
}
if !ret.is_null() {
unsafe { dc_array_unref(ret) };
}
std::ptr::null_mut()
}

View File

@@ -1,175 +1,78 @@
use crate::dc_location::dc_location;
use crate::context::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
const DC_ARRAY_MAGIC: uint32_t = 0x000a11aa;
/* * the structure behind dc_array_t */
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub enum dc_array_t {
Locations(Vec<dc_location>),
Uint(Vec<uintptr_t>),
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_array_t {
pub magic: uint32_t,
pub allocated: size_t,
pub count: size_t,
pub type_0: libc::c_int,
pub array: *mut uintptr_t,
}
impl dc_array_t {
pub fn new(capacity: usize) -> Self {
dc_array_t::Uint(Vec::with_capacity(capacity))
}
/// Constructs a new, empty `dc_array_t` holding locations with specified `capacity`.
pub fn new_locations(capacity: usize) -> Self {
dc_array_t::Locations(Vec::with_capacity(capacity))
}
pub fn into_raw(self) -> *mut Self {
Box::into_raw(Box::new(self))
}
pub fn add_uint(&mut self, item: uintptr_t) {
if let Self::Uint(array) = self {
array.push(item);
} else {
panic!("Attempt to add uint to array of other type");
}
}
pub fn add_id(&mut self, item: uint32_t) {
self.add_uint(item as uintptr_t);
}
pub fn add_location(&mut self, location: dc_location) {
if let Self::Locations(array) = self {
array.push(location)
} else {
panic!("Attempt to add a location to array of other type");
}
}
pub fn get_uint(&self, index: usize) -> uintptr_t {
if let Self::Uint(array) = self {
array[index]
} else {
panic!("Attempt to get uint from array of other type");
}
}
pub fn get_id(&self, index: usize) -> uint32_t {
match self {
Self::Locations(array) => array[index].location_id,
Self::Uint(array) => array[index] as uint32_t,
}
}
pub fn get_ptr(&self, index: size_t) -> *mut libc::c_void {
if let Self::Uint(array) = self {
array[index] as *mut libc::c_void
} else {
panic!("Not an array of pointers");
}
}
pub fn get_location(&self, index: usize) -> &dc_location {
if let Self::Locations(array) = self {
&array[index]
} else {
panic!("Not an array of locations")
}
}
pub fn get_latitude(&self, index: usize) -> libc::c_double {
self.get_location(index).latitude
}
pub fn get_longitude(&self, index: size_t) -> libc::c_double {
self.get_location(index).longitude
}
pub fn get_accuracy(&self, index: size_t) -> libc::c_double {
self.get_location(index).accuracy
}
pub fn get_timestamp(&self, index: size_t) -> i64 {
self.get_location(index).timestamp
}
pub fn get_chat_id(&self, index: size_t) -> uint32_t {
self.get_location(index).chat_id
}
pub fn get_contact_id(&self, index: size_t) -> uint32_t {
self.get_location(index).contact_id
}
pub fn get_msg_id(&self, index: size_t) -> uint32_t {
self.get_location(index).msg_id
}
pub fn is_empty(&self) -> bool {
match self {
Self::Locations(array) => array.is_empty(),
Self::Uint(array) => array.is_empty(),
}
}
/// Returns the number of elements in the array.
pub fn len(&self) -> usize {
match self {
Self::Locations(array) => array.len(),
Self::Uint(array) => array.len(),
}
}
pub fn clear(&mut self) {
match self {
Self::Locations(array) => array.clear(),
Self::Uint(array) => array.clear(),
}
}
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
if let Self::Uint(array) = self {
for (i, &u) in array.iter().enumerate() {
if u == needle {
return Some(i);
}
}
None
} else {
panic!("Attempt to search for id in array of other type");
}
}
pub fn sort_ids(&mut self) {
if let dc_array_t::Uint(v) = self {
v.sort();
} else {
panic!("Attempt to sort array of something other than uints");
}
}
}
impl From<Vec<dc_location>> for dc_array_t {
fn from(array: Vec<dc_location>) -> Self {
dc_array_t::Locations(array)
}
}
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
if array.is_null() {
/**
* @class dc_array_t
*
* An object containing a simple array.
* This object is used in several places where functions need to return an array.
* The items of the array are typically IDs.
* To free an array object, use dc_array_unref().
*/
pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
Box::from_raw(array);
if (*array).type_0 == 1i32 {
dc_array_free_ptr(array);
}
free((*array).array as *mut libc::c_void);
(*array).magic = 0i32 as uint32_t;
free(array as *mut libc::c_void);
}
pub unsafe fn dc_array_add_uint(array: *mut dc_array_t, item: uintptr_t) {
if !array.is_null() {
(*array).add_uint(item);
pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
let mut i: size_t = 0i32 as size_t;
while i < (*array).count {
if (*array).type_0 == 1i32 {
free(
(*(*(*array).array.offset(i as isize) as *mut _dc_location)).marker
as *mut libc::c_void,
);
}
free(*(*array).array.offset(i as isize) as *mut libc::c_void);
*(*array).array.offset(i as isize) = 0i32 as uintptr_t;
i = i.wrapping_add(1)
}
}
pub unsafe fn dc_array_add_uint(mut array: *mut dc_array_t, item: uintptr_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
if (*array).count == (*array).allocated {
let newsize = (*array).allocated.wrapping_mul(2).wrapping_add(10);
(*array).array = realloc(
(*array).array as *mut libc::c_void,
(newsize).wrapping_mul(::std::mem::size_of::<uintptr_t>()),
) as *mut uintptr_t;
assert!(!(*array).array.is_null());
(*array).allocated = newsize as size_t
}
*(*array).array.offset((*array).count as isize) = item;
(*array).count = (*array).count.wrapping_add(1);
}
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_t) {
if !array.is_null() {
(*array).add_id(item);
}
dc_array_add_uint(array, item as uintptr_t);
}
pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
@@ -177,107 +80,130 @@ pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void)
}
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
if array.is_null() {
0
} else {
(*array).len()
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0i32 as size_t;
}
(*array).count
}
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_uint(index)
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uintptr_t;
}
*(*array).array.offset(index as isize)
}
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_id(index)
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0i32 as uint32_t;
}
if (*array).type_0 == 1i32 {
return (*(*(*array).array.offset(index as isize) as *mut _dc_location)).location_id;
}
*(*array).array.offset(index as isize) as uint32_t
}
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
if array.is_null() || index >= (*array).len() {
std::ptr::null_mut()
} else {
(*array).get_ptr(index)
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
return 0 as *mut libc::c_void;
}
*(*array).array.offset(index as isize) as *mut libc::c_void
}
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_latitude(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).latitude
}
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_longitude(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).longitude
}
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
if array.is_null() || index >= (*array).len() {
0.0
} else {
(*array).get_accuracy(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as libc::c_double;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).accuracy
}
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_timestamp(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).timestamp
}
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_chat_id(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).chat_id
}
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_contact_id(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).contact_id
}
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
if array.is_null() || index >= (*array).len() {
0
} else {
(*array).get_msg_id(index)
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0i32 as uint32_t;
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).msg_id
}
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
if array.is_null() || index >= (*array).len() {
return std::ptr::null_mut();
}
if let dc_array_t::Locations(v) = &*array {
if let Some(s) = &v[index].marker {
s.strdup()
} else {
std::ptr::null_mut()
}
} else {
std::ptr::null_mut()
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0 as *mut libc::c_char;
}
dc_strdup_keep_null((*(*(*array).array.offset(index as isize) as *mut _dc_location)).marker)
}
/**
@@ -291,15 +217,16 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
* 1=Location was reported independently.
*/
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
if array.is_null() || index >= (*array).len() {
if array.is_null()
|| (*array).magic != DC_ARRAY_MAGIC
|| index >= (*array).count
|| (*array).type_0 != 1i32
|| *(*array).array.offset(index as isize) == 0
{
return 0;
}
if let dc_array_t::Locations(v) = &*array {
v[index].independent as libc::c_int
} else {
panic!("Attempt to get location independent field from array of something other than locations");
}
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).independent as libc::c_int
}
pub unsafe fn dc_array_search_id(
@@ -307,84 +234,176 @@ pub unsafe fn dc_array_search_id(
needle: uint32_t,
ret_index: *mut size_t,
) -> bool {
if array.is_null() {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return false;
}
if let Some(i) = (*array).search_id(needle as uintptr_t) {
if !ret_index.is_null() {
*ret_index = i
let data: *mut uintptr_t = (*array).array;
let mut i: size_t = 0;
let cnt: size_t = (*array).count;
while i < cnt {
if *data.offset(i as isize) == needle as size_t {
if !ret_index.is_null() {
*ret_index = i
}
return true;
}
true
} else {
false
i = i.wrapping_add(1)
}
false
}
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
if array.is_null() {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0 as *const uintptr_t;
}
if let dc_array_t::Uint(v) = &*array {
v.as_ptr()
} else {
panic!("Attempt to convert array of something other than uints to raw");
(*array).array
}
pub unsafe fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_new_typed(0, initsize)
}
pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut dc_array_t {
let mut array: *mut dc_array_t;
array = calloc(1, ::std::mem::size_of::<dc_array_t>()) as *mut dc_array_t;
assert!(!array.is_null());
(*array).magic = DC_ARRAY_MAGIC;
(*array).count = 0i32 as size_t;
(*array).allocated = if initsize < 1 { 1 } else { initsize };
(*array).type_0 = type_0;
(*array).array = malloc(
(*array)
.allocated
.wrapping_mul(::std::mem::size_of::<uintptr_t>()),
) as *mut uintptr_t;
if (*array).array.is_null() {
exit(48i32);
}
array
}
pub fn dc_array_new(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new(initsize).into_raw()
}
pub fn dc_array_new_locations(initsize: size_t) -> *mut dc_array_t {
dc_array_t::new_locations(initsize).into_raw()
}
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
if array.is_null() {
pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return;
}
(*array).clear()
(*array).count = 0i32 as size_t;
}
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
if array.is_null() {
std::ptr::null_mut()
} else {
(*array).clone().into_raw()
let mut ret: *mut dc_array_t;
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
return 0 as *mut dc_array_t;
}
ret = dc_array_new((*array).allocated);
(*ret).count = (*array).count;
memcpy(
(*ret).array as *mut libc::c_void,
(*array).array as *const libc::c_void,
(*array)
.count
.wrapping_mul(::std::mem::size_of::<uintptr_t>()),
);
ret
}
pub unsafe fn dc_array_sort_ids(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
}
qsort(
(*array).array as *mut libc::c_void,
(*array).count,
::std::mem::size_of::<uintptr_t>(),
Some(cmp_intptr_t),
);
}
unsafe extern "C" fn cmp_intptr_t(p1: *const libc::c_void, p2: *const libc::c_void) -> libc::c_int {
let v1: uintptr_t = *(p1 as *mut uintptr_t);
let v2: uintptr_t = *(p2 as *mut uintptr_t);
return if v1 < v2 {
-1i32
} else if v1 > v2 {
1i32
} else {
0i32
};
}
pub unsafe fn dc_array_sort_strings(array: *mut dc_array_t) {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
return;
}
qsort(
(*array).array as *mut libc::c_void,
(*array).count,
::std::mem::size_of::<*mut libc::c_char>(),
Some(cmp_strings_t),
);
}
unsafe extern "C" fn cmp_strings_t(
p1: *const libc::c_void,
p2: *const libc::c_void,
) -> libc::c_int {
let v1: *const libc::c_char = *(p1 as *mut *const libc::c_char);
let v2: *const libc::c_char = *(p2 as *mut *const libc::c_char);
strcmp(v1, v2)
}
pub unsafe fn dc_array_get_string(
array: *const dc_array_t,
sep: *const libc::c_char,
) -> *mut libc::c_char {
if array.is_null() || sep.is_null() {
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || sep.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if let dc_array_t::Uint(v) = &*array {
let cnt = v.len();
let sep = as_str(sep);
let cnt = (*array).count as usize;
let slice = std::slice::from_raw_parts((*array).array, cnt);
let sep = as_str(sep);
let res = v
.iter()
.enumerate()
.fold(String::with_capacity(2 * cnt), |res, (i, n)| {
if i == 0 {
res + &n.to_string()
} else {
res + sep + &n.to_string()
}
});
res.strdup()
} else {
panic!("Attempt to get string from array of other type");
let res = slice
.iter()
.enumerate()
.fold(String::with_capacity(2 * cnt), |mut res, (i, n)| {
if i == 0 {
res += &n.to_string();
} else {
res += sep;
res += &n.to_string();
}
res
});
to_cstring(res)
}
/// return comma-separated value-string from integer array
pub unsafe fn dc_arr_to_string(arr: *const uint32_t, cnt: libc::c_int) -> *mut libc::c_char {
if arr.is_null() || cnt == 0 {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
let slice = std::slice::from_raw_parts(arr, cnt as usize);
let res = slice.iter().enumerate().fold(
String::with_capacity(2 * cnt as usize),
|mut res, (i, n)| {
if i == 0 {
res += &n.to_string();
} else {
res += ",";
res += &n.to_string();
}
res
},
);
to_cstring(res)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::x::*;
use std::ffi::CStr;
#[test]
@@ -420,7 +439,7 @@ mod tests {
dc_array_add_id(arr, 0 as uint32_t);
dc_array_add_id(arr, 5000 as uint32_t);
(*arr).sort_ids();
dc_array_sort_ids(arr);
assert_eq!(dc_array_get_id(arr, 0 as size_t), 0);
assert_eq!(dc_array_get_id(arr, 1 as size_t), 7);
@@ -434,6 +453,38 @@ mod tests {
);
free(str as *mut libc::c_void);
dc_array_empty(arr);
dc_array_add_ptr(
arr,
b"XX\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"item1\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"bbb\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_add_ptr(
arr,
b"aaa\x00" as *const u8 as *const libc::c_char as *mut libc::c_void,
);
dc_array_sort_strings(arr);
let str = dc_array_get_ptr(arr, 0 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "XX");
let str = dc_array_get_ptr(arr, 1 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "aaa");
let str = dc_array_get_ptr(arr, 2 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "bbb");
let str = dc_array_get_ptr(arr, 3 as size_t) as *mut libc::c_char;
assert_eq!(CStr::from_ptr(str).to_str().unwrap(), "item1");
dc_array_unref(arr);
}
}

File diff suppressed because it is too large Load Diff

389
src/dc_chatlist.rs Normal file
View File

@@ -0,0 +1,389 @@
use crate::context::*;
use crate::dc_array::*;
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::types::*;
use crate::x::*;
/* * the structure behind dc_chatlist_t */
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_chatlist_t<'a> {
pub magic: uint32_t,
pub context: &'a Context,
pub cnt: size_t,
pub chatNlastmsg_ids: *mut dc_array_t,
}
// handle chatlists
pub unsafe fn dc_get_chatlist<'a>(
context: &'a Context,
listflags: libc::c_int,
query_str: *const libc::c_char,
query_id: uint32_t,
) -> *mut dc_chatlist_t<'a> {
let obj = dc_chatlist_new(context);
if 0 != dc_chatlist_load_from_db(obj, listflags, query_str, query_id) {
return obj;
}
dc_chatlist_unref(obj);
return 0 as *mut dc_chatlist_t;
}
/**
* @class dc_chatlist_t
*
* An object representing a single chatlist in memory.
* Chatlist objects contain chat IDs
* and, if possible, message IDs belonging to them.
* The chatlist object is not updated;
* if you want an update, you have to recreate the object.
*
* For a **typical chat overview**,
* the idea is to get the list of all chats via dc_get_chatlist()
* without any listflags (see below)
* and to implement a "virtual list" or so
* (the count of chats is known by dc_chatlist_get_cnt()).
*
* Only for the items that are in view
* (the list may have several hundreds chats),
* the UI should call dc_chatlist_get_summary() then.
* dc_chatlist_get_summary() provides all elements needed for painting the item.
*
* On a click of such an item,
* the UI should change to the chat view
* and get all messages from this view via dc_get_chat_msgs().
* Again, a "virtual list" is created
* (the count of messages is known)
* and for each messages that is scrolled into view, dc_get_msg() is called then.
*
* Why no listflags?
* Without listflags, dc_get_chatlist() adds the deaddrop
* and the archive "link" automatically as needed.
* The UI can just render these items differently then.
* Although the deaddrop link is currently always the first entry
* and only present on new messages,
* there is the rough idea that it can be optionally always present
* and sorted into the list by date.
* Rendering the deaddrop in the described way
* would not add extra work in the UI then.
*/
pub unsafe fn dc_chatlist_new(context: &Context) -> *mut dc_chatlist_t {
let mut chatlist: *mut dc_chatlist_t;
chatlist = calloc(1, ::std::mem::size_of::<dc_chatlist_t>()) as *mut dc_chatlist_t;
assert!(!chatlist.is_null());
(*chatlist).magic = 0xc4a71157u32;
(*chatlist).context = context;
(*chatlist).chatNlastmsg_ids = dc_array_new(128i32 as size_t);
assert!(!(*chatlist).chatNlastmsg_ids.is_null());
chatlist
}
pub unsafe fn dc_chatlist_unref(mut chatlist: *mut dc_chatlist_t) {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return;
}
dc_chatlist_empty(chatlist);
dc_array_unref((*chatlist).chatNlastmsg_ids);
(*chatlist).magic = 0i32 as uint32_t;
free(chatlist as *mut libc::c_void);
}
pub unsafe fn dc_chatlist_empty(mut chatlist: *mut dc_chatlist_t) {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return;
}
(*chatlist).cnt = 0i32 as size_t;
dc_array_empty((*chatlist).chatNlastmsg_ids);
}
/**
* Load a chatlist from the database to the chatlist object.
*
* @private @memberof dc_chatlist_t
*/
// TODO should return bool /rtn
unsafe fn dc_chatlist_load_from_db(
mut chatlist: *mut dc_chatlist_t,
listflags: libc::c_int,
query__: *const libc::c_char,
query_contact_id: u32,
) -> libc::c_int {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return 0;
}
dc_chatlist_empty(chatlist);
let mut add_archived_link_item = 0;
// select with left join and minimum:
// - the inner select must use `hidden` and _not_ `m.hidden`
// which would refer the outer select and take a lot of time
// - `GROUP BY` is needed several messages may have the same timestamp
// - the list starts with the newest chats
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let process_row = |row: &rusqlite::Row| {
let chat_id: i32 = row.get(0)?;
// TODO: verify that it is okay for this to be Null
let msg_id: i32 = row.get(1).unwrap_or_default();
Ok((chat_id, msg_id))
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
for row in rows {
let (id1, id2) = row?;
dc_array_add_id((*chatlist).chatNlastmsg_ids, id1 as u32);
dc_array_add_id((*chatlist).chatNlastmsg_ids, id2 as u32);
}
Ok(())
};
// nb: the query currently shows messages from blocked contacts in groups.
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
// (otherwise it would be hard to follow conversations, wa and tg do the same)
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
// shown at all permanent in the chatlist.
let success = if query_contact_id != 0 {
// show chats shared with a given contact
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![query_contact_id as i32],
process_row,
process_rows,
)
} else if 0 != listflags & 0x1 {
// show archived chats
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else if query__.is_null() {
// show normal chatlist
if 0 == listflags & 0x2 {
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg((*chatlist).context);
if last_deaddrop_fresh_msg_id > 0 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1);
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
}
add_archived_link_item = 1;
}
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c \
LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.archived=0 \
GROUP BY c.id \
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![],
process_row,
process_rows,
)
} else {
let query = to_string(query__).trim().to_string();
if query.is_empty() {
return 1;
} else {
let strLikeCmd = format!("%{}%", query);
(*chatlist).context.sql.query_map(
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
ON c.id=m.chat_id \
AND m.timestamp=( SELECT MAX(timestamp) \
FROM msgs WHERE chat_id=c.id \
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
AND c.blocked=0 AND c.name LIKE ? \
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
params![strLikeCmd],
process_row,
process_rows,
)
}
};
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0 {
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 && 0 != listflags & 0x4 {
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6);
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
}
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids) / 2;
match success {
Ok(_) => 1,
Err(err) => {
error!(
(*chatlist).context,
0, "chatlist: failed to load from database: {:?}", err
);
0
}
}
}
// Context functions to work with chatlist
pub fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
context
.sql
.query_row_col(
context,
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
params![],
0,
)
.unwrap_or_default()
}
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
// we have an index over the state-column, this should be sufficient as there are typically only few fresh messages
context
.sql
.query_row_col(
context,
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
WHERE m.state=10 \
AND m.hidden=0 \
AND c.blocked=2 \
ORDER BY m.timestamp DESC, m.id DESC;",
params![],
0,
)
.unwrap_or_default()
}
pub unsafe fn dc_chatlist_get_cnt(chatlist: *const dc_chatlist_t) -> size_t {
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
return 0i32 as size_t;
}
(*chatlist).cnt
}
pub unsafe fn dc_chatlist_get_chat_id(chatlist: *const dc_chatlist_t, index: size_t) -> uint32_t {
if chatlist.is_null()
|| (*chatlist).magic != 0xc4a71157u32
|| (*chatlist).chatNlastmsg_ids.is_null()
|| index >= (*chatlist).cnt
{
return 0i32 as uint32_t;
}
dc_array_get_id((*chatlist).chatNlastmsg_ids, index.wrapping_mul(2))
}
pub unsafe fn dc_chatlist_get_msg_id(chatlist: *const dc_chatlist_t, index: size_t) -> uint32_t {
if chatlist.is_null()
|| (*chatlist).magic != 0xc4a71157u32
|| (*chatlist).chatNlastmsg_ids.is_null()
|| index >= (*chatlist).cnt
{
return 0i32 as uint32_t;
}
dc_array_get_id(
(*chatlist).chatNlastmsg_ids,
index.wrapping_mul(2).wrapping_add(1),
)
}
pub unsafe fn dc_chatlist_get_summary<'a>(
chatlist: *const dc_chatlist_t<'a>,
index: size_t,
mut chat: *mut Chat<'a>,
) -> *mut dc_lot_t {
let current_block: u64;
/* The summary is created by the chat, not by the last message.
This is because we may want to display drafts here or stuff as
"is typing".
Also, sth. as "No messages" would not work if the summary comes from a
message. */
/* the function never returns NULL */
let mut ret: *mut dc_lot_t = dc_lot_new();
let lastmsg_id: uint32_t;
let mut lastmsg: *mut dc_msg_t = 0 as *mut dc_msg_t;
let mut lastcontact: *mut dc_contact_t = 0 as *mut dc_contact_t;
let mut chat_to_delete: *mut Chat = 0 as *mut Chat;
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 || index >= (*chatlist).cnt {
(*ret).text2 = dc_strdup(b"ErrBadChatlistIndex\x00" as *const u8 as *const libc::c_char)
} else {
lastmsg_id = dc_array_get_id(
(*chatlist).chatNlastmsg_ids,
index.wrapping_mul(2).wrapping_add(1),
);
if chat.is_null() {
chat = dc_chat_new((*chatlist).context);
chat_to_delete = chat;
if !dc_chat_load_from_db(
chat,
dc_array_get_id((*chatlist).chatNlastmsg_ids, index.wrapping_mul(2)),
) {
(*ret).text2 =
dc_strdup(b"ErrCannotReadChat\x00" as *const u8 as *const libc::c_char);
current_block = 3777403817673069519;
} else {
current_block = 7651349459974463963;
}
} else {
current_block = 7651349459974463963;
}
match current_block {
3777403817673069519 => {}
_ => {
if 0 != lastmsg_id {
lastmsg = dc_msg_new_untyped((*chatlist).context);
dc_msg_load_from_db(lastmsg, (*chatlist).context, lastmsg_id);
if (*lastmsg).from_id != 1i32 as libc::c_uint
&& ((*chat).type_0 == 120i32 || (*chat).type_0 == 130i32)
{
lastcontact = dc_contact_new((*chatlist).context);
dc_contact_load_from_db(
lastcontact,
&(*chatlist).context.sql,
(*lastmsg).from_id,
);
}
}
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)
} else {
dc_lot_fill(ret, lastmsg, chat, lastcontact, (*chatlist).context);
}
}
}
}
dc_msg_unref(lastmsg);
dc_contact_unref(lastcontact);
dc_chat_unref(chat_to_delete);
ret
}

View File

@@ -1,4 +1,4 @@
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use crate::constants::Event;
use crate::context::Context;
@@ -9,7 +9,6 @@ use crate::dc_saxparser::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::oauth2::*;
use crate::param::Params;
use crate::types::*;
use crate::x::*;
@@ -61,10 +60,9 @@ pub unsafe fn dc_configure(context: &Context) {
);
return;
}
dc_job_kill_action(context, 900);
dc_job_add(context, 900, 0, Params::new(), 0);
dc_job_kill_action(context, 900i32);
dc_job_add(context, 900i32, 0i32, 0 as *const libc::c_char, 0i32);
}
pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
let s_a = context.running_state.clone();
let s = s_a.read().unwrap();
@@ -101,7 +99,6 @@ pub fn dc_stop_ongoing_process(context: &Context) {
}
// the other dc_job_do_DC_JOB_*() functions are declared static in the c-file
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_job_t) {
let flags: libc::c_int;
let mut current_block: u64;
@@ -210,7 +207,8 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
let parsed = parsed.unwrap();
let param_domain = parsed.host();
let param_addr_urlencoded =
utf8_percent_encode(&param.addr, NON_ALPHANUMERIC).to_string();
utf8_percent_encode(&param.addr, DEFAULT_ENCODE_SET)
.to_string();
if !s.shall_stop_ongoing {
context.call_cb(
@@ -572,13 +570,13 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
{
param.send_pw = param.mail_pw.clone()
}
if !dc_exactly_one_bit_set(
if 0 == dc_exactly_one_bit_set(
param.server_flags & (0x2 | 0x4),
) {
param.server_flags &= !(0x2 | 0x4);
param.server_flags |= 0x4
}
if !dc_exactly_one_bit_set(
if 0 == dc_exactly_one_bit_set(
param.server_flags & (0x100 | 0x200 | 0x400),
) {
param.server_flags &= !(0x100 | 0x200 | 0x400);
@@ -588,7 +586,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
0x200
}
}
if !dc_exactly_one_bit_set(
if 0 == dc_exactly_one_bit_set(
param.server_flags & (0x10000 | 0x20000 | 0x40000),
) {
param.server_flags &=
@@ -1102,14 +1100,14 @@ unsafe fn moz_autoconfigure(
tag_config: 0,
};
let url_c = url.strdup();
let url_c = to_cstring(url);
let xml_raw = read_autoconf_file(context, url_c);
free(url_c as *mut libc::c_void);
if xml_raw.is_null() {
return None;
}
moz_ac.in_emaillocalpart = param_in.addr.strdup();
moz_ac.in_emaillocalpart = to_cstring(&param_in.addr);
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
if p.is_null() {
@@ -1166,7 +1164,7 @@ unsafe fn moz_autoconfigure_text_cb(
let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t;
let mut val: *mut libc::c_char = dc_strdup(text);
dc_trim(val);
let addr = (*moz_ac).in_0.addr.strdup();
let addr = to_cstring(&(*moz_ac).in_0.addr);
dc_str_replace(
&mut val,
b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char,
@@ -1306,7 +1304,7 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc:
.send()
.and_then(|mut res| res.text())
{
Ok(res) => unsafe { res.strdup() },
Ok(res) => unsafe { to_cstring(res) },
Err(_err) => {
info!(context, 0, "Can\'t read file.",);
@@ -1322,7 +1320,7 @@ unsafe fn outlk_autodiscover(
) -> Option<dc_loginparam_t> {
let current_block: u64;
let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char;
let mut url = url__.strdup();
let mut url = to_cstring(url__);
let mut outlk_ad = outlk_autodiscover_t {
in_0: param_in,
out: dc_loginparam_new(),

View File

@@ -1,17 +1,15 @@
use std::ffi::CString;
use crate::aheader::EncryptPreference;
use crate::config;
use crate::constants::*;
use crate::constants::Event;
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::*;
@@ -34,8 +32,8 @@ pub fn dc_marknoticed_contact(context: &Context, contact_id: u32) {
if sql::execute(
context,
&context.sql,
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
params![DC_STATE_IN_NOTICED, contact_id as i32, DC_STATE_IN_FRESH],
"UPDATE msgs SET state=13 WHERE from_id=? AND state=10;",
params![contact_id as i32],
)
.is_ok()
{
@@ -278,13 +276,14 @@ pub unsafe fn dc_contact_load_from_db(
if contact_id == 1 as libc::c_uint {
(*contact).id = contact_id;
(*contact).name = (*contact).context.stock_str(StockMessage::SelfMsg).strdup();
(*contact).addr = (*contact)
.context
.sql
.get_config((*contact).context, "configured_addr")
.unwrap_or_default()
.strdup();
(*contact).name = dc_stock_str((*contact).context, 2);
(*contact).addr = to_cstring(
(*contact)
.context
.sql
.get_config((*contact).context, "configured_addr")
.unwrap_or_default(),
);
true
} else {
sql.query_row(
@@ -292,11 +291,11 @@ pub unsafe fn dc_contact_load_from_db(
params![contact_id as i32],
|row| {
(*contact).id = contact_id;
(*contact).name = row.get::<_, String>(0)?.strdup();
(*contact).addr = row.get::<_, String>(1)?.strdup();
(*contact).name = to_cstring(row.get::<_, String>(0)?);
(*contact).addr = to_cstring(row.get::<_, String>(1)?);
(*contact).origin = row.get(2)?;
(*contact).blocked = row.get::<_, Option<i32>>(3)?.unwrap_or_default();
(*contact).authname = row.get::<_, String>(4)?.strdup();
(*contact).authname = to_cstring(row.get::<_, String>(4)?);
Ok(())
}
).is_ok()
@@ -453,31 +452,39 @@ pub fn dc_add_or_lookup_contact(
row_id
}
#[allow(non_snake_case)]
pub unsafe fn dc_add_address_book(context: &Context, adr_book: *const libc::c_char) -> libc::c_int {
let mut lines: *mut carray = 0 as *mut carray;
let mut i: size_t;
let iCnt: size_t;
let mut sth_modified: libc::c_int = 0i32;
let mut modify_cnt: libc::c_int = 0i32;
if !(adr_book.is_null()) {
let lines = dc_split_into_lines(adr_book);
for chunk in lines.chunks(2) {
let name: *mut libc::c_char = chunk[0];
let addr: *mut libc::c_char = chunk[1];
dc_normalize_name(name);
dc_add_or_lookup_contact(context, name, addr, 0x80000i32, &mut sth_modified);
if 0 != sth_modified {
modify_cnt += 1
lines = dc_split_into_lines(adr_book);
if !lines.is_null() {
iCnt = carray_count(lines) as size_t;
i = 0i32 as size_t;
while i.wrapping_add(1) < iCnt {
let name: *mut libc::c_char =
carray_get(lines, i as libc::c_uint) as *mut libc::c_char;
let addr: *mut libc::c_char =
carray_get(lines, i.wrapping_add(1) as libc::c_uint) as *mut libc::c_char;
dc_normalize_name(name);
dc_add_or_lookup_contact(context, name, addr, 0x80000i32, &mut sth_modified);
if 0 != sth_modified {
modify_cnt += 1
}
i = (i as libc::c_ulong).wrapping_add(2i32 as libc::c_ulong) as size_t as size_t
}
if 0 != modify_cnt {
context.call_cb(
Event::CONTACTS_CHANGED,
0i32 as uintptr_t,
0i32 as uintptr_t,
);
}
}
if 0 != modify_cnt {
context.call_cb(
Event::CONTACTS_CHANGED,
0i32 as uintptr_t,
0i32 as uintptr_t,
);
}
dc_free_splitted_lines(lines);
}
dc_free_splitted_lines(lines);
modify_cnt
}
@@ -517,7 +524,6 @@ pub unsafe fn dc_normalize_name(full_name: *mut libc::c_char) {
};
}
#[allow(non_snake_case)]
pub fn dc_get_contacts(
context: &Context,
listflags: u32,
@@ -529,7 +535,7 @@ pub fn dc_get_contacts(
.unwrap_or_default();
let mut add_self = false;
let mut ret = dc_array_t::new(100);
let ret = unsafe { dc_array_new(100) };
if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || !query.is_null() {
let s3strLikeCmd = format!("%{}%", if !query.is_null() { as_str(query) } else { "" });
@@ -557,7 +563,7 @@ pub fn dc_get_contacts(
|row| row.get::<_, i32>(0),
|ids| {
for id in ids {
ret.add_id(id? as u32);
unsafe { dc_array_add_id(ret, id? as u32) };
}
Ok(())
},
@@ -569,15 +575,16 @@ pub fn dc_get_contacts(
.get_config(context, "displayname")
.unwrap_or_default();
let self_name2 = CString::new(context.stock_str(StockMessage::SelfMsg).as_ref()).unwrap();
let self_name2 = unsafe { dc_stock_str(context, 2) };
if query.is_null()
|| self_addr.contains(as_str(query))
|| self_name.contains(as_str(query))
|| 0 != unsafe { dc_str_contains(self_name2.as_ptr(), query) }
|| 0 != unsafe { dc_str_contains(self_name2, query) }
{
add_self = true;
}
unsafe { free(self_name2 as *mut _) };
} else {
add_self = true;
@@ -587,7 +594,7 @@ pub fn dc_get_contacts(
|row| row.get::<_, i32>(0),
|ids| {
for id in ids {
ret.add_id(id? as u32);
unsafe { dc_array_add_id(ret, id? as u32) };
}
Ok(())
}
@@ -595,10 +602,10 @@ pub fn dc_get_contacts(
}
if 0 != listflags & 0x2 && add_self {
ret.add_id(1);
unsafe { dc_array_add_id(ret, 1) };
}
ret.into_raw()
ret
}
pub fn dc_get_blocked_cnt(context: &Context) -> libc::c_int {
@@ -621,13 +628,13 @@ pub fn dc_get_blocked_contacts(context: &Context) -> *mut dc_array_t {
params![9],
|row| row.get::<_, i32>(0),
|ids| {
let mut ret = dc_array_t::new(100);
let ret = unsafe { dc_array_new(100) };
for id in ids {
ret.add_id(id? as u32);
unsafe { dc_array_add_id(ret, id? as u32) };
}
Ok(ret.into_raw())
Ok(ret)
},
)
.unwrap_or_else(|_| std::ptr::null_mut())
@@ -643,6 +650,7 @@ 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));
@@ -652,18 +660,23 @@ 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();
let p = context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual {
StockMessage::E2ePreferred
} else {
StockMessage::E2eAvailable
});
ret += &p;
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);
if self_key.is_none() {
dc_ensure_secret_key_exists(context);
self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
}
let p = context.stock_str(StockMessage::FingerPrints);
ret += &format!(" {}:", p);
p = dc_stock_str(context, 30i32);
ret += &format!(" {}:", as_str(p));
free(p as *mut libc::c_void);
fingerprint_self = self_key
.map(|k| k.formatted_fingerprint_c())
@@ -704,9 +717,13 @@ pub unsafe fn dc_get_contact_encrinfo(
);
}
} else if 0 == loginparam.server_flags & 0x400 && 0 == loginparam.server_flags & 0x40000 {
ret += &context.stock_str(StockMessage::EncrTransp);
p = dc_stock_str(context, 27);
ret += as_str(p);
free(p as *mut libc::c_void);
} else {
ret += &context.stock_str(StockMessage::EncrNone);
p = dc_stock_str(context, 28);
ret += as_str(p);
free(p as *mut libc::c_void);
}
}
@@ -716,7 +733,7 @@ pub unsafe fn dc_get_contact_encrinfo(
free(fingerprint_other_verified as *mut libc::c_void);
free(fingerprint_other_unverified as *mut libc::c_void);
ret.strdup()
to_cstring(ret)
}
unsafe fn cat_fingerprint(
@@ -779,7 +796,7 @@ pub fn dc_delete_contact(context: &Context, contact_id: u32) -> bool {
0
};
if count_msgs == 0 {
if count_msgs > 0 {
if sql::execute(
context,
&context.sql,
@@ -791,14 +808,9 @@ pub fn dc_delete_contact(context: &Context, contact_id: u32) -> bool {
context.call_cb(Event::CONTACTS_CHANGED, 0, 0);
true
} else {
error!(context, 0, "delete_contact {} failed", contact_id);
false
}
} else {
info!(
context,
0, "could not delete contact {}, there are {} messages with it", contact_id, count_msgs
);
false
}
}
@@ -891,7 +903,7 @@ pub fn dc_contact_get_profile_image(contact: *const dc_contact_t) -> *mut libc::
if unsafe { (*contact).id } == 1 {
let context = unsafe { (*contact) }.context;
if let Some(avatar) = context.get_config(config::Config::Selfavatar) {
image_abs = unsafe { avatar.strdup() };
image_abs = unsafe { to_cstring(avatar) };
}
}
// TODO: else get image_abs from contact param

View File

@@ -53,7 +53,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
dc_saxparser_parse(&mut saxparser, buf_terminated);
free(dehtml.last_href as *mut libc::c_void);
dehtml.strbuilder.strdup()
to_cstring(dehtml.strbuilder)
}
unsafe fn dehtml_text_cb(

View File

@@ -34,7 +34,6 @@ use crate::x::*;
// to get the netto sizes, we subtract 1 mb header-overhead and the base64-overhead.
// some defaults
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct dc_e2ee_helper_t {
pub encryption_successfull: libc::c_int,
pub cdata_to_free: *mut libc::c_void,
@@ -55,7 +54,6 @@ impl Default for dc_e2ee_helper_t {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_e2ee_encrypt(
context: &Context,
recipients_addr: *const clist,
@@ -177,12 +175,16 @@ pub unsafe fn dc_e2ee_encrypt(
let p = peerstates[i as usize]
.render_gossip_header(min_verified as usize);
if let Some(header) = p {
if p.is_some() {
let header = to_cstring(p.unwrap());
mailimf_fields_add(
imffields_encrypted,
mailimf_field_new_custom(
"Autocrypt-Gossip".strdup(),
header.strdup(),
strdup(
b"Autocrypt-Gossip\x00" as *const u8
as *const libc::c_char,
),
header,
),
);
}
@@ -292,7 +294,7 @@ pub unsafe fn dc_e2ee_encrypt(
sign_key.as_ref(),
) {
let ctext_bytes = ctext_v.len();
let ctext = ctext_v.strdup();
let ctext = to_cstring(ctext_v);
(*helper).cdata_to_free = ctext as *mut _;
/* create MIME-structure that will contain the encrypted text */
@@ -316,11 +318,11 @@ pub unsafe fn dc_e2ee_encrypt(
as *mut libc::c_char,
) as *mut libc::c_void,
);
static mut VERSION_CONTENT: [libc::c_char; 13] =
static mut version_content: [libc::c_char; 13] =
[86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0];
let version_mime: *mut mailmime = new_data_part(
VERSION_CONTENT.as_mut_ptr() as *mut libc::c_void,
strlen(VERSION_CONTENT.as_mut_ptr()),
version_content.as_mut_ptr() as *mut libc::c_void,
strlen(version_content.as_mut_ptr()),
b"application/pgp-encrypted\x00" as *const u8
as *const libc::c_char
as *mut libc::c_char,
@@ -350,11 +352,13 @@ pub unsafe fn dc_e2ee_encrypt(
14181132614457621749 => {}
_ => {
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
let rendered = to_cstring(aheader.to_string());
mailimf_fields_add(
imffields_unprotected,
mailimf_field_new_custom(
"Autocrypt".strdup(),
aheader.to_string().strdup(),
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
rendered,
),
);
}
@@ -487,7 +491,7 @@ unsafe fn load_or_generate_self_public_key(
_random_data_mime: *mut mailmime,
) -> Option<Key> {
/* avoid double creation (we unlock the database during creation) */
static mut S_IN_KEY_CREATION: libc::c_int = 0;
static mut s_in_key_creation: libc::c_int = 0;
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
if key.is_some() {
@@ -495,11 +499,11 @@ unsafe fn load_or_generate_self_public_key(
}
/* create the keypair - this may take a moment, however, as this is in a thread, this is no big deal */
if 0 != S_IN_KEY_CREATION {
if 0 != s_in_key_creation {
return None;
}
let key_creation_here = 1;
S_IN_KEY_CREATION = 1;
s_in_key_creation = 1;
let start = clock();
info!(
@@ -533,7 +537,7 @@ unsafe fn load_or_generate_self_public_key(
}
if 0 != key_creation_here {
S_IN_KEY_CREATION = 0;
s_in_key_creation = 0;
}
key

View File

@@ -1,11 +1,11 @@
use std::ffi::CString;
use failure::format_err;
use mmime::mailmime_content::*;
use mmime::mmapstring::*;
use mmime::other::*;
use rand::{thread_rng, Rng};
use crate::config::Config;
use crate::constants::*;
use crate::context::Context;
use crate::dc_chat::*;
@@ -13,13 +13,12 @@ use crate::dc_configure::*;
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::error::*;
use crate::key::*;
use crate::param::*;
use crate::pgp::*;
use crate::sql::{self, Sql};
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -34,17 +33,13 @@ pub unsafe fn dc_imex(
param1: *const libc::c_char,
param2: *const libc::c_char,
) {
let mut param = Params::new();
param.set_int(Param::Cmd, what as i32);
if !param1.is_null() {
param.set(Param::Arg, as_str(param1));
}
if !param2.is_null() {
param.set(Param::Arg2, as_str(param2));
}
dc_job_kill_action(context, 910);
dc_job_add(context, 910, 0, param, 0);
let param: *mut dc_param_t = dc_param_new();
dc_param_set_int(param, DC_PARAM_CMD as i32, what);
dc_param_set(param, DC_PARAM_CMD_ARG as i32, param1);
dc_param_set(param, DC_PARAM_CMD_ARG2 as i32, param2);
dc_job_kill_action(context, 910i32);
dc_job_add(context, 910i32, 0i32, (*param).packed, 0i32);
dc_param_unref(param);
}
/// Returns the filename of the backup if found, nullptr otherwise.
@@ -100,85 +95,102 @@ pub unsafe fn dc_imex_has_backup(
}
pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
let current_block: u64;
let mut success: libc::c_int = 0i32;
let mut setup_code: *mut libc::c_char;
let mut setup_file_content: *mut libc::c_char = 0 as *mut libc::c_char;
let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char;
let chat_id: uint32_t;
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
if dc_alloc_ongoing(context) == 0 {
return std::ptr::null_mut();
let msg_id: uint32_t;
if 0 == dc_alloc_ongoing(context) {
return 0 as *mut libc::c_char;
}
let setup_code = dc_create_setup_code(context);
/* this may require a keypair to be created. this may take a second ... */
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
if let Ok(setup_file_content) = dc_render_setup_file(context, &setup_code) {
let setup_file_content_c = CString::yolo(setup_file_content.as_str());
/* encrypting may also take a while ... */
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
setup_file_name = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
);
if !(setup_file_name.is_null()
|| 0 == dc_write_file(
context,
setup_file_name,
setup_file_content_c.as_ptr() as *const libc::c_void,
setup_file_content_c.as_bytes().len(),
))
setup_code = dc_create_setup_code(context);
if !setup_code.is_null() {
/* this may require a keypair to be created. this may take a second ... */
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
setup_file_content = dc_render_setup_file(context, setup_code);
if !setup_file_content.is_null() {
/* encrypting may also take a while ... */
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
if !(chat_id == 0i32 as libc::c_uint) {
msg = dc_msg_new_untyped(context);
(*msg).type_0 = Viewtype::File;
(*msg).param.set(Param::File, as_str(setup_file_name));
(*msg)
.param
.set(Param::MimeType, "application/autocrypt-setup");
(*msg).param.set_int(Param::Cmd, 6);
(*msg).param.set_int(Param::ForcePlaintext, 2);
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
let msg_id = dc_send_msg(context, chat_id, msg);
if msg_id != 0 {
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t;
info!(context, 0, "Wait for setup message being sent ...",);
loop {
if context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
break;
}
std::thread::sleep(std::time::Duration::from_secs(1));
msg = dc_get_msg(context, msg_id);
if 0 != dc_msg_is_sent(msg) {
info!(context, 0, "... setup message sent.",);
break;
}
setup_file_name = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
b"autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char,
);
if !(setup_file_name.is_null()
|| 0 == dc_write_file(
context,
setup_file_name,
setup_file_content as *const libc::c_void,
strlen(setup_file_content),
))
{
chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
if !(chat_id == 0i32 as libc::c_uint) {
msg = dc_msg_new_untyped(context);
(*msg).type_0 = 60i32;
dc_param_set((*msg).param, DC_PARAM_FILE as i32, setup_file_name);
dc_param_set(
(*msg).param,
DC_PARAM_MIMETYPE as i32,
b"application/autocrypt-setup\x00" as *const u8
as *const libc::c_char,
);
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 6);
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 2);
if !context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
msg_id = dc_send_msg(context, chat_id, msg);
if !(msg_id == 0i32 as libc::c_uint) {
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t
msg = 0 as *mut dc_msg_t;
info!(context, 0, "Wait for setup message being sent ...",);
loop {
if context
.running_state
.clone()
.read()
.unwrap()
.shall_stop_ongoing
{
current_block = 6116957410927263949;
break;
}
std::thread::sleep(std::time::Duration::from_secs(1));
msg = dc_get_msg(context, msg_id);
if 0 != dc_msg_is_sent(msg) {
current_block = 6450636197030046351;
break;
}
dc_msg_unref(msg);
msg = 0 as *mut dc_msg_t
}
match current_block {
6116957410927263949 => {}
_ => {
info!(context, 0, "... setup message sent.",);
success = 1;
}
}
}
}
}
@@ -187,80 +199,97 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
}
}
}
if 0 == success {
free(setup_code as *mut libc::c_void);
setup_code = 0 as *mut libc::c_char
}
free(setup_file_name as *mut libc::c_void);
free(setup_file_content as *mut libc::c_void);
dc_msg_unref(msg);
dc_free_ongoing(context);
setup_code.strdup()
setup_code
}
pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<String> {
ensure!(
passphrase.len() >= 2,
"Passphrase must be at least 2 chars long."
);
unsafe {
ensure!(
!(dc_ensure_secret_key_exists(context) == 0),
"No secret key available."
);
pub unsafe extern "C" fn dc_render_setup_file(
context: &Context,
passphrase: *const libc::c_char,
) -> *mut libc::c_char {
let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
let mut passphrase_begin: [libc::c_char; 8] = [0; 8];
let mut ret_setupfilecontent: *mut libc::c_char = 0 as *mut libc::c_char;
if !(passphrase.is_null() || strlen(passphrase) < 2) {
strncpy(passphrase_begin.as_mut_ptr(), passphrase, 2);
passphrase_begin[2usize] = 0i32 as libc::c_char;
/* create the payload */
if !(0 == dc_ensure_secret_key_exists(context)) {
let self_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default();
let curr_private_key = Key::from_self_private(context, self_addr, &context.sql);
let e2ee_enabled = context
.sql
.get_config_int(context, "e2ee_enabled")
.unwrap_or_else(|| 1);
let headers = if 0 != e2ee_enabled {
Some(("Autocrypt-Prefer-Encrypt", "mutual"))
} else {
None
};
if let Some(payload_key_asc) = curr_private_key.map(|k| k.to_asc_c(headers)) {
if let Some(encr) = dc_pgp_symm_encrypt(
passphrase,
payload_key_asc as *const libc::c_void,
strlen(payload_key_asc),
) {
let encr_string_c = CString::new(encr).unwrap();
let mut encr_string = strdup(encr_string_c.as_ptr());
free(payload_key_asc as *mut libc::c_void);
let replacement: *mut libc::c_char =
dc_mprintf(b"-----BEGIN PGP MESSAGE-----\r\nPassphrase-Format: numeric9x4\r\nPassphrase-Begin: %s\x00"
as *const u8 as *const libc::c_char,
passphrase_begin.as_mut_ptr());
dc_str_replace(
&mut encr_string,
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
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,
);
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);
free(encr_string as *mut libc::c_void);
}
}
}
}
let self_addr = context
.get_config(Config::ConfiguredAddr)
.ok_or(format_err!("Failed to get self address."))?;
let private_key = Key::from_self_private(context, self_addr, &context.sql)
.ok_or(format_err!("Failed to get private key."))?;
let ac_headers = match context
.sql
.get_config_int(context, Config::E2eeEnabled)
.unwrap_or(1)
{
0 => None,
_ => Some(("Autocrypt-Prefer-Encrypt", "mutual")),
};
let private_key_asc = private_key.to_asc(ac_headers);
let encr = {
let private_key_asc_c = CString::yolo(private_key_asc);
let passphrase_c = CString::yolo(passphrase);
dc_pgp_symm_encrypt(
passphrase_c.as_ptr(),
private_key_asc_c.as_ptr() as *const libc::c_void,
private_key_asc_c.as_bytes().len(),
)
.ok_or(format_err!("Failed to encrypt private key."))?
};
let replacement = format!(
concat!(
"-----BEGIN PGP MESSAGE-----\r\n",
"Passphrase-Format: numeric9x4\r\n",
"Passphrase-Begin: {}"
),
&passphrase[..2]
);
let pgp_msg = encr.replace("-----BEGIN PGP MESSAGE-----", &replacement);
sqlite3_finalize(stmt);
let msg_subj = context.stock_str(StockMessage::AcSetupMsgSubject);
let msg_body = context.stock_str(StockMessage::AcSetupMsgBody);
let msg_body_html = msg_body.replace("\r", "").replace("\n", "<br>");
Ok(format!(
concat!(
"<!DOCTYPE html>\r\n",
"<html>\r\n",
" <head>\r\n",
" <title>{}</title>\r\n",
" </head>\r\n",
" <body>\r\n",
" <h1>{}</h1>\r\n",
" <p>{}</p>\r\n",
" <pre>\r\n{}\r\n</pre>\r\n",
" </body>\r\n",
"</html>\r\n"
),
msg_subj, msg_subj, msg_body_html, pgp_msg
))
ret_setupfilecontent
}
pub fn dc_create_setup_code(_context: &Context) -> String {
pub unsafe fn dc_create_setup_code(_context: &Context) -> *mut libc::c_char {
let mut random_val: uint16_t;
let mut rng = thread_rng();
let mut ret = String::new();
@@ -280,7 +309,7 @@ pub fn dc_create_setup_code(_context: &Context) -> String {
);
}
ret
to_cstring(ret)
}
// TODO should return bool /rtn
@@ -437,18 +466,20 @@ pub unsafe fn dc_decrypt_setup_file(
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
fc_buf = dc_strdup(filecontent);
if dc_split_armored_data(
fc_buf,
&mut fc_headerline,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
&mut fc_base64,
) && !fc_headerline.is_null()
&& strcmp(
if !(0
== dc_split_armored_data(
fc_buf,
&mut fc_headerline,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
&mut fc_base64,
)
|| fc_headerline.is_null()
|| strcmp(
fc_headerline,
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
) == 0
&& !fc_base64.is_null()
) != 0i32
|| fc_base64.is_null())
{
/* convert base64 to binary */
/*must be freed using mmap_string_unref()*/
@@ -466,8 +497,7 @@ pub unsafe fn dc_decrypt_setup_file(
if let Some(plain) =
dc_pgp_symm_decrypt(passphrase, binary as *const libc::c_void, binary_bytes)
{
let payload_c = CString::new(plain).unwrap();
payload = strdup(payload_c.as_ptr());
payload = strdup(CString::new(plain).unwrap().as_ptr());
}
}
}
@@ -509,31 +539,38 @@ pub unsafe fn dc_normalize_setup_code(
p1 = p1.offset(1);
}
out.strdup()
to_cstring(out)
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) {
let mut current_block: u64;
let mut success: libc::c_int = 0;
let mut ongoing_allocated_here: libc::c_int = 0;
let mut success: libc::c_int = 0i32;
let mut ongoing_allocated_here: libc::c_int = 0i32;
let what: libc::c_int;
let mut param1: *mut libc::c_char = 0 as *mut libc::c_char;
let mut param2: *mut libc::c_char = 0 as *mut libc::c_char;
if !(0 == dc_alloc_ongoing(context)) {
ongoing_allocated_here = 1;
what = (*job).param.get_int(Param::Cmd).unwrap_or_default();
let param1 = CString::yolo((*job).param.get(Param::Arg).unwrap_or_default());
let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default());
if strlen(param1.as_ptr()) == 0 {
ongoing_allocated_here = 1i32;
what = dc_param_get_int((*job).param, DC_PARAM_CMD as i32, 0);
param1 = dc_param_get(
(*job).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
param2 = dc_param_get(
(*job).param,
DC_PARAM_CMD_ARG2 as i32,
0 as *const libc::c_char,
);
if param1.is_null() {
error!(context, 0, "No Import/export dir/file given.",);
} else {
info!(context, 0, "Import/export process started.",);
context.call_cb(Event::IMEX_PROGRESS, 10 as uintptr_t, 0 as uintptr_t);
context.call_cb(Event::IMEX_PROGRESS, 10i32 as uintptr_t, 0i32 as uintptr_t);
if !context.sql.is_open() {
error!(context, 0, "Import/export: Database not opened.",);
} else {
if what == 1 || what == 11 {
if what == 1i32 || what == 11i32 {
/* before we export anything, make sure the private key exists */
if 0 == dc_ensure_secret_key_exists(context) {
error!(
@@ -543,7 +580,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
);
current_block = 3568988166330621280;
} else {
dc_create_folder(context, param1.as_ptr());
dc_create_folder(context, param1);
current_block = 4495394744059808450;
}
} else {
@@ -556,28 +593,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
current_block = 10991094515395304355;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
if 0 == import_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
if 0 == import_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
if 0 == export_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
if 0 == export_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
@@ -588,7 +625,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
success = 1i32
}
}
}
@@ -596,28 +633,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
current_block = 11250025114629486028;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
if 0 == import_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
if 0 == import_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
if 0 == export_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
if 0 == export_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
@@ -628,7 +665,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
success = 1i32
}
}
}
@@ -636,28 +673,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
current_block = 12669919903773909120;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
if 0 == import_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
if 0 == import_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
if 0 == export_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
if 0 == export_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
@@ -668,7 +705,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
success = 1i32
}
}
}
@@ -676,28 +713,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
current_block = 2973387206439775448;
match current_block {
2973387206439775448 => {
if 0 == import_backup(context, param1.as_ptr()) {
if 0 == import_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
11250025114629486028 => {
if 0 == import_self_keys(context, param1.as_ptr()) {
if 0 == import_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
12669919903773909120 => {
if 0 == export_backup(context, param1.as_ptr()) {
if 0 == export_backup(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
}
}
_ => {
if 0 == export_self_keys(context, param1.as_ptr()) {
if 0 == export_self_keys(context, param1) {
current_block = 3568988166330621280;
} else {
current_block = 1118134448028020070;
@@ -708,7 +745,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
3568988166330621280 => {}
_ => {
info!(context, 0, "Import/export completed.",);
success = 1
success = 1i32
}
}
}
@@ -719,13 +756,15 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
}
}
free(param1 as *mut libc::c_void);
free(param2 as *mut libc::c_void);
if 0 != ongoing_allocated_here {
dc_free_ongoing(context);
}
context.call_cb(
Event::IMEX_PROGRESS,
(if 0 != success { 1000 } else { 0 }) as uintptr_t,
0 as uintptr_t,
(if 0 != success { 1000i32 } else { 0i32 }) as uintptr_t,
0i32 as uintptr_t,
);
}
@@ -734,7 +773,6 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
******************************************************************************/
// TODO should return bool /rtn
#[allow(non_snake_case)]
unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char) -> libc::c_int {
info!(
context,
@@ -832,7 +870,7 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
}
if !loop_success {
return Err(format_err!("fail"));
return Err(format_err!("fail").into());
}
Ok(())
},
@@ -853,7 +891,6 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
/* the FILE_PROGRESS macro calls the callback with the permille of files processed.
The macro avoids weird values of 0% or 100% while still working. */
// TODO should return bool /rtn
#[allow(non_snake_case)]
unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_int {
let mut current_block: u64;
let mut success: libc::c_int = 0;
@@ -865,8 +902,9 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
let res = chrono::NaiveDateTime::from_timestamp(now as i64, 0)
.format("delta-chat-%Y-%m-%d.bak")
.to_string();
let buffer = CString::yolo(res);
let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer.as_ptr());
let buffer = to_cstring(res);
let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer);
free(buffer as *mut _);
if dest_pathNfilename.is_null() {
error!(context, 0, "Cannot get backup file name.",);
@@ -1065,6 +1103,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
let mut imported_cnt: libc::c_int = 0;
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char;
let mut name_c: *mut libc::c_char = 0 as *mut libc::c_char;
let mut set_default: libc::c_int;
let mut buf: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf_bytes: size_t = 0 as size_t;
@@ -1092,8 +1131,9 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
let entry = entry.unwrap();
free(suffix as *mut libc::c_void);
let name_f = entry.file_name();
let name_c = name_f.to_c_string().unwrap();
suffix = dc_get_filesuffix_lc(name_c.as_ptr());
free(name_c as *mut libc::c_void);
name_c = to_cstring(name_f.to_string_lossy());
suffix = dc_get_filesuffix_lc(name_c);
if suffix.is_null()
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
{
@@ -1103,7 +1143,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
path_plus_name = dc_mprintf(
b"%s/%s\x00" as *const u8 as *const libc::c_char,
dir_name,
name_c.as_ptr(),
name_c,
);
info!(context, 0, "Checking: {}", as_str(path_plus_name));
free(buf as *mut libc::c_void);
@@ -1120,7 +1160,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
private_key = buf;
free(buf2 as *mut libc::c_void);
buf2 = dc_strdup(buf);
if dc_split_armored_data(
if 0 != dc_split_armored_data(
buf2,
&mut buf2_headerline,
0 as *mut *const libc::c_char,
@@ -1141,12 +1181,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
}
}
set_default = 1;
if !strstr(
name_c.as_ptr(),
b"legacy\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
if !strstr(name_c, b"legacy\x00" as *const u8 as *const libc::c_char).is_null() {
info!(
context,
0,
@@ -1171,6 +1206,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
}
}
free(name_c as *mut libc::c_void);
free(suffix as *mut libc::c_void);
free(path_plus_name as *mut libc::c_void);
free(buf as *mut libc::c_void);
@@ -1280,201 +1316,3 @@ unsafe fn export_key_to_asc_file(
success
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CStr;
use num_traits::ToPrimitive;
use crate::config::Config;
use crate::key;
use crate::test_utils::*;
unsafe extern "C" fn logging_cb(
_ctx: &Context,
evt: Event,
_d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
match evt {
Event::INFO => println!("I: {}", to_str(d2)),
Event::WARNING => println!("W: {}", to_str(d2)),
Event::ERROR => println!("E: {}", to_str(d2)),
_ => (),
}
0
}
/// Create Alice with a pre-generated keypair.
fn create_alice_keypair(ctx: &Context) {
ctx.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
.unwrap();
// The keypair was created using:
// let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com")
// .unwrap();
// println!("{}", public.to_base64(64));
// println!("{}", private.to_base64(64));
let public = key::Key::from_base64(
concat!(
"xsBNBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAHNEzxhbGljZUBleGFtcGxl",
"LmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iai",
"x4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9",
"OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkK",
"A8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea",
"6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6",
"GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUK",
"u5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxD",
"Fc7ATQRdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG",
"9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av",
"62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/R",
"noW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q",
"4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAm",
"jxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABwsB2BBgBCAAgBQJdPOn4",
"AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoW",
"qEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwX",
"FNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9m",
"MjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6RDXIeYJf",
"qrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMlammDliPw",
"sK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKObzPqgJCGw",
"jTglkixw+aSTXw=="
),
KeyType::Public,
)
.unwrap();
let private = key::Key::from_base64(
concat!(
"xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtq",
"m1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353",
"r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68",
"JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6F",
"FrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHb",
"Iu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3V",
"WushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0S",
"ut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQ",
"sWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEm",
"dr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8k",
"QrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJW",
"yyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj",
"5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3",
"jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeG",
"Kyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl08",
"6fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQ",
"k6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Ee",
"h+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BM",
"zcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwb",
"YklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP",
"12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmh",
"o0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugz",
"OmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnF",
"n7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6",
"uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEe",
"LrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCIC",
"N1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86K",
"C2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIdd",
"KsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T",
"/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZL",
"j3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtp",
"Do5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9u",
"RMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe",
"/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH",
"95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9",
"QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ",
"8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//",
"wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg",
"9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwK",
"Gjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPB",
"f4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAg",
"BQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/",
"dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJ",
"ywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJ",
"uiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6",
"RDXIeYJfqrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMl",
"ammDliPwsK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKOb",
"zPqgJCGwjTglkixw+aSTXw=="
),
KeyType::Private,
)
.unwrap();
let saved = key::dc_key_save_self_keypair(
&ctx,
&public,
&private,
"alice@example.org",
1,
&ctx.sql,
);
assert_eq!(saved, true, "Failed to save Alice's key");
}
#[test]
fn test_render_setup_file() {
let t = test_context(Some(logging_cb));
create_alice_keypair(&t.ctx); // Trick things to think we're configured.
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
println!("{}", &msg);
// Check some substrings, indicating things got substituted.
// In particular note the mixing of `\r\n` and `\n` depending
// on who generated the stings.
assert!(msg.contains("<title>Autocrypt Setup Message</title"));
assert!(msg.contains("<h1>Autocrypt Setup Message</h1>"));
assert!(msg.contains("<p>This is the Autocrypt Setup Message used to"));
assert!(msg.contains("-----BEGIN PGP MESSAGE-----\r\n"));
assert!(msg.contains("Passphrase-Format: numeric9x4\r\n"));
assert!(msg.contains("Passphrase-Begin: he\n"));
assert!(msg.contains("==\n"));
assert!(msg.contains("-----END PGP MESSAGE-----\n"));
}
unsafe extern "C" fn ac_setup_msg_cb(
ctx: &Context,
evt: Event,
d1: uintptr_t,
d2: uintptr_t,
) -> uintptr_t {
if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() {
"hello\r\nthere".strdup() as usize
} else {
logging_cb(ctx, evt, d1, d2)
}
}
#[test]
fn test_render_setup_file_newline_replace() {
let t = test_context(Some(ac_setup_msg_cb));
create_alice_keypair(&t.ctx);
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
println!("{}", &msg);
assert!(msg.contains("<p>hello<br>there</p>"));
}
#[test]
fn test_create_setup_code() {
let t = dummy_context();
let setupcode = dc_create_setup_code(&t.ctx);
assert_eq!(setupcode.len(), 44);
assert_eq!(setupcode.chars().nth(4).unwrap(), '-');
assert_eq!(setupcode.chars().nth(9).unwrap(), '-');
assert_eq!(setupcode.chars().nth(14).unwrap(), '-');
assert_eq!(setupcode.chars().nth(19).unwrap(), '-');
assert_eq!(setupcode.chars().nth(24).unwrap(), '-');
assert_eq!(setupcode.chars().nth(29).unwrap(), '-');
assert_eq!(setupcode.chars().nth(34).unwrap(), '-');
assert_eq!(setupcode.chars().nth(39).unwrap(), '-');
}
}

View File

@@ -5,7 +5,7 @@ use std::time::Duration;
use rand::{thread_rng, Rng};
use crate::constants::*;
use crate::constants::Event;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_configure::*;
@@ -15,17 +15,14 @@ use crate::dc_location::*;
use crate::dc_loginparam::*;
use crate::dc_mimefactory::*;
use crate::dc_msg::*;
use crate::dc_param::*;
use crate::dc_tools::*;
use crate::imap::*;
use crate::keyhistory::*;
use crate::param::*;
use crate::sql;
use crate::types::*;
use crate::x::*;
const DC_IMAP_THREAD: libc::c_int = 100;
const DC_SMTP_THREAD: libc::c_int = 5000;
// thread IDs
// jobs in the INBOX-thread, range from DC_IMAP_THREAD..DC_IMAP_THREAD+999
// low priority ...
@@ -36,7 +33,7 @@ const DC_SMTP_THREAD: libc::c_int = 5000;
// timeouts until actions are aborted.
// this may also affects IDLE to return, so a re-connect may take this time.
// mailcore2 uses 30 seconds, k-9 uses 10 seconds
#[derive(Clone)]
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_job_t {
pub job_id: uint32_t,
@@ -45,7 +42,7 @@ pub struct dc_job_t {
pub desired_timestamp: i64,
pub added_timestamp: i64,
pub tries: libc::c_int,
pub param: Params,
pub param: *mut dc_param_t,
pub try_again: libc::c_int,
pub pending_error: *mut libc::c_char,
}
@@ -54,15 +51,15 @@ pub unsafe fn dc_perform_imap_jobs(context: &Context) {
info!(context, 0, "dc_perform_imap_jobs starting.",);
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
*context.probe_imap_network.write().unwrap() = false;
*context.perform_inbox_jobs_needed.write().unwrap() = false;
*context.probe_imap_network.write().unwrap() = 0;
*context.perform_inbox_jobs_needed.write().unwrap() = 0;
dc_job_perform(context, DC_IMAP_THREAD, probe_imap_network);
dc_job_perform(context, 100, probe_imap_network);
info!(context, 0, "dc_perform_imap_jobs ended.",);
}
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: bool) {
let query = if !probe_network {
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) {
let query = if probe_network == 0 {
// processing for first-try and after backoff-timeouts:
// process jobs in the order they were added.
"SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries \
@@ -77,7 +74,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
let params_no_probe = params![thread as i64, time()];
let params_probe = params![thread as i64];
let params: &[&dyn rusqlite::ToSql] = if !probe_network {
let params: &[&dyn rusqlite::ToSql] = if probe_network == 0 {
params_no_probe
} else {
params_probe
@@ -94,11 +91,15 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
desired_timestamp: row.get(5)?,
added_timestamp: row.get(4)?,
tries: row.get(6)?,
param: row.get::<_, String>(3)?.parse().unwrap_or_default(),
param: dc_param_new(),
try_again: 0,
pending_error: 0 as *mut libc::c_char,
};
let packed: String = row.get(3)?;
let packed_c = to_cstring(packed);
dc_param_set_packed(job.param, packed_c);
free(packed_c as *mut _);
Ok(job)
},
|jobs| {
@@ -119,11 +120,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{}, action {} started...",
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
if thread == 100 { "INBOX" } else { "SMTP" },
job.job_id,
job.action,
);
@@ -136,7 +133,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
dc_job_kill_action(context, job.action);
dc_jobthread_suspend(context, &context.sentbox_thread.clone().read().unwrap(), 1);
dc_jobthread_suspend(context, &context.mvbox_thread.clone().read().unwrap(), 1);
dc_suspend_smtp_thread(context, true);
dc_suspend_smtp_thread(context, 1);
}
let mut tries = 0;
@@ -174,7 +171,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
&mut context.mvbox_thread.clone().read().unwrap(),
0,
);
dc_suspend_smtp_thread(context, false);
dc_suspend_smtp_thread(context, 0);
break;
} else if job.try_again == 2 {
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
@@ -182,11 +179,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{} not yet ready and will be delayed.",
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
if thread == 100 { "INBOX" } else { "SMTP" },
job.job_id
);
} else if job.try_again == -1 || job.try_again == 3 {
@@ -200,17 +193,13 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
context,
0,
"{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).",
if thread == DC_IMAP_THREAD {
"INBOX"
} else {
"SMTP"
},
if thread == 100 { "INBOX" } else { "SMTP" },
job.job_id as libc::c_int,
tries,
time_offset,
job.added_timestamp + time_offset - time()
);
if thread == DC_SMTP_THREAD && tries < 17 - 1 {
if thread == 5000 && tries < 17 - 1 {
context
.smtp_state
.clone()
@@ -225,7 +214,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
}
dc_job_delete(context, &mut job);
}
if !probe_network {
if 0 == probe_network {
continue;
}
// on dc_maybe_network() we stop trying here;
@@ -236,6 +225,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
} else {
dc_job_delete(context, &mut job);
}
dc_param_unref(job.param);
free(job.pending_error as *mut libc::c_void);
}
}
@@ -250,8 +240,7 @@ fn dc_job_delete(context: &Context, job: &dc_job_t) -> bool {
/* ******************************************************************************
* Tools
******************************************************************************/
#[allow(non_snake_case)]
fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
unsafe fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
// results in ~3 weeks for the last backoff timespan
let mut N = 2_i32.pow((c_tries - 1) as u32);
N = N * 60;
@@ -272,31 +261,30 @@ fn dc_job_update(context: &Context, job: &dc_job_t) -> bool {
params![
job.desired_timestamp,
job.tries as i64,
job.param.to_string(),
as_str(unsafe { (*job.param).packed }),
job.job_id as i32,
],
)
.is_ok()
}
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: bool) {
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: libc::c_int) {
context.smtp_state.0.lock().unwrap().suspended = suspend;
if suspend {
if 0 != suspend {
loop {
if !context.smtp_state.0.lock().unwrap().doing_jobs {
if context.smtp_state.0.lock().unwrap().doing_jobs == 0 {
return;
}
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
}
}
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
let mut buf_bytes: size_t = 0i32 as size_t;
let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char;
/* connect to SMTP server, if not yet done */
if !context.smtp.lock().unwrap().is_connected() {
@@ -314,15 +302,20 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
}
match current_block {
13109137661213826276 => {
filename = job.param.get(Param::File).unwrap_or_default().strdup();
if strlen(filename) == 0 {
filename = dc_param_get(job.param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
if filename.is_null() {
warn!(context, 0, "Missing file name for job {}", job.job_id,);
} else if !(0 == dc_read_file(context, filename, &mut buf, &mut buf_bytes)) {
let recipients = job.param.get(Param::Recipients);
if recipients.is_none() {
recipients = dc_param_get(
job.param,
DC_PARAM_RECIPIENTS as i32,
0 as *const libc::c_char,
);
if recipients.is_null() {
warn!(context, 0, "Missing recipients for job {}", job.job_id,);
} else {
let recipients_list = recipients
let recipients_list = std::ffi::CStr::from_ptr(recipients)
.to_str()
.unwrap()
.split("\x1e")
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
@@ -372,11 +365,7 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
} else {
dc_delete_file(context, filename);
if 0 != job.foreign_id {
dc_update_msg_state(
context,
job.foreign_id,
DC_STATE_OUT_DELIVERED,
);
dc_update_msg_state(context, job.foreign_id, 26i32);
let chat_id: i32 = context
.sql
.query_row_col(
@@ -400,6 +389,7 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
}
_ => {}
}
free(recipients as *mut libc::c_void);
free(buf);
free(filename as *mut libc::c_void);
}
@@ -415,7 +405,6 @@ pub unsafe fn dc_job_try_again_later(
job.pending_error = dc_strdup_keep_null(pending_error);
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MOVE_MSG(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let msg = dc_msg_new_untyped(context);
@@ -512,22 +501,21 @@ fn connect_to_inbox(context: &Context, inbox: &Imap) -> libc::c_int {
ret_connected
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let current_block: u64;
let folder = job
.param
.get(Param::ServerFolder)
.unwrap_or_default()
.to_string();
let uid = job.param.get_int(Param::ServerUid).unwrap_or_default() as u32;
let mut dest_uid = 0;
let folder: *mut libc::c_char = dc_param_get(
job.param,
DC_PARAM_SERVER_FOLDER as i32,
0 as *const libc::c_char,
);
let uid: uint32_t = dc_param_get_int(job.param, DC_PARAM_SERVER_UID as i32, 0) as uint32_t;
let mut dest_uid: uint32_t = 0i32 as uint32_t;
let inbox = context.inbox.read().unwrap();
if !inbox.is_connected() {
connect_to_inbox(context, &inbox);
if !inbox.is_connected() {
dc_job_try_again_later(job, 3, 0 as *const libc::c_char);
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
current_block = 2670689566614003383;
} else {
current_block = 11006700562992250127;
@@ -537,10 +525,11 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_
}
match current_block {
11006700562992250127 => {
if inbox.set_seen(context, &folder, uid) == 0 {
let folder = CStr::from_ptr(folder).to_str().unwrap();
if inbox.set_seen(context, folder, uid) as libc::c_uint == 0i32 as libc::c_uint {
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
if 0 != job.param.get_int(Param::AlsoMove).unwrap_or_default() {
if 0 != dc_param_get_int(job.param, DC_PARAM_ALSO_MOVE as i32, 0i32) {
if context
.sql
.get_config_int(context, "folders_configured")
@@ -561,9 +550,9 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_
}
_ => {}
}
free(folder as *mut libc::c_void);
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let msg: *mut dc_msg_t = dc_msg_new_untyped(context);
@@ -593,8 +582,12 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
_ => {
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
&& 0 != context
if 0 != dc_param_get_int(
(*msg).param,
DC_PARAM_WANTS_MDN as i32,
0i32,
) && 0
!= context
.sql
.get_config_int(context, "mdns_enabled")
.unwrap_or_else(|| 1)
@@ -647,7 +640,7 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MSG_ON_IMAP(context: &Context, job: &mut dc_
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
}
_ => {
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
if 0 != dc_param_get_int((*msg).param, DC_PARAM_WANTS_MDN as i32, 0)
&& 0 != context
.sql
.get_config_int(context, "mdns_enabled")
@@ -744,7 +737,6 @@ unsafe fn dc_send_mdn(context: &Context, msg_id: uint32_t) {
* @param mimefactory An instance of dc_mimefactory_t with a loaded and rendered message or MDN
* @return 1=success, 0=error
*/
#[allow(non_snake_case)]
unsafe fn dc_add_smtp_job(
context: &Context,
action: libc::c_int,
@@ -753,7 +745,7 @@ unsafe fn dc_add_smtp_job(
let pathNfilename: *mut libc::c_char;
let mut success: libc::c_int = 0i32;
let mut recipients: *mut libc::c_char = 0 as *mut libc::c_char;
let mut param = Params::new();
let param: *mut dc_param_t = dc_param_new();
pathNfilename = dc_get_fine_pathNfilename(
context,
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
@@ -786,8 +778,8 @@ unsafe fn dc_add_smtp_job(
(*mimefactory).recipients_addr,
b"\x1e\x00" as *const u8 as *const libc::c_char,
);
param.set(Param::File, as_str(pathNfilename));
param.set(Param::File, as_str(recipients));
dc_param_set(param, DC_PARAM_FILE as i32, pathNfilename);
dc_param_set(param, DC_PARAM_RECIPIENTS as i32, recipients);
dc_job_add(
context,
action,
@@ -796,30 +788,30 @@ unsafe fn dc_add_smtp_job(
{
(*(*mimefactory).msg).id
} else {
0
0i32 as libc::c_uint
}) as libc::c_int,
param,
0,
(*param).packed,
0i32,
);
success = 1i32
}
dc_param_unref(param);
free(recipients as *mut libc::c_void);
free(pathNfilename as *mut libc::c_void);
return success;
}
pub unsafe fn dc_job_add(
context: &Context,
action: libc::c_int,
foreign_id: libc::c_int,
param: Params,
param: *const libc::c_char,
delay_seconds: libc::c_int,
) {
let timestamp = time();
let thread = if action >= DC_IMAP_THREAD && action < DC_IMAP_THREAD + 1000 {
DC_IMAP_THREAD
} else if action >= DC_SMTP_THREAD && action < DC_SMTP_THREAD + 1000 {
DC_SMTP_THREAD
let thread = if action >= 100 && action < 100 + 1000 {
100
} else if action >= 5000 && action < 5000 + 1000 {
5000
} else {
return;
};
@@ -833,12 +825,16 @@ pub unsafe fn dc_job_add(
thread,
action,
foreign_id,
param.to_string(),
if !param.is_null() {
as_str(param)
} else {
""
},
(timestamp + delay_seconds as i64)
]
).ok();
if thread == DC_IMAP_THREAD {
if thread == 100 {
dc_interrupt_imap_idle(context);
} else {
dc_interrupt_smtp_idle(context);
@@ -859,11 +855,10 @@ pub unsafe fn dc_interrupt_smtp_idle(context: &Context) {
pub unsafe fn dc_interrupt_imap_idle(context: &Context) {
info!(context, 0, "Interrupting IMAP-IDLE...",);
*context.perform_inbox_jobs_needed.write().unwrap() = true;
*context.perform_inbox_jobs_needed.write().unwrap() = 1;
context.inbox.read().unwrap().interrupt_idle();
}
#[allow(non_snake_case)]
unsafe fn dc_job_do_DC_JOB_DELETE_MSG_ON_IMAP(context: &Context, job: &mut dc_job_t) {
let mut current_block: u64;
let mut delete_from_server: libc::c_int = 1i32;
@@ -967,7 +962,7 @@ pub fn dc_perform_imap_idle(context: &Context) {
connect_to_inbox(context, &inbox);
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
if 0 != *context.perform_inbox_jobs_needed.clone().read().unwrap() {
info!(
context,
0, "INBOX-IDLE will not be started because of waiting jobs."
@@ -1042,26 +1037,26 @@ pub unsafe fn dc_perform_smtp_jobs(context: &Context) {
let mut state = lock.lock().unwrap();
let probe_smtp_network = state.probe_network;
state.probe_network = false;
state.probe_network = 0;
state.perform_jobs_needed = 0;
if state.suspended {
if 0 != state.suspended {
info!(context, 0, "SMTP-jobs suspended.",);
return;
}
state.doing_jobs = true;
state.doing_jobs = 1;
probe_smtp_network
};
info!(context, 0, "SMTP-jobs started...",);
dc_job_perform(context, DC_SMTP_THREAD, probe_smtp_network);
dc_job_perform(context, 5000, probe_smtp_network);
info!(context, 0, "SMTP-jobs ended.");
{
let &(ref lock, _) = &*context.smtp_state.clone();
let mut state = lock.lock().unwrap();
state.doing_jobs = false;
state.doing_jobs = 0;
}
}
@@ -1077,7 +1072,7 @@ pub unsafe fn dc_perform_smtp_idle(context: &Context) {
0, "SMTP-idle will not be started because of waiting jobs.",
);
} else {
let dur = get_next_wakeup_time(context, DC_SMTP_THREAD);
let dur = get_next_wakeup_time(context, 5000);
loop {
let res = cvar.wait_timeout(state, dur).unwrap();
@@ -1123,9 +1118,9 @@ pub unsafe fn dc_maybe_network(context: &Context) {
{
let &(ref lock, _) = &*context.smtp_state.clone();
let mut state = lock.lock().unwrap();
state.probe_network = true;
state.probe_network = 1;
*context.probe_imap_network.write().unwrap() = true;
*context.probe_imap_network.write().unwrap() = 1;
}
dc_interrupt_smtp_idle(context);
@@ -1142,7 +1137,6 @@ pub fn dc_job_action_exists(context: &Context, action: libc::c_int) -> bool {
}
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
#[allow(non_snake_case)]
pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_int {
let mut success = 0;
let mut mimefactory = dc_mimefactory_t {
@@ -1176,23 +1170,28 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
);
} else {
// no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed()
if msgtype_has_file((*mimefactory.msg).type_0) {
let pathNfilename = (*mimefactory.msg)
.param
.get(Param::File)
.unwrap_or_default()
.strdup();
if strlen(pathNfilename) > 0 {
if ((*mimefactory.msg).type_0 == Viewtype::Image
|| (*mimefactory.msg).type_0 == Viewtype::Gif)
&& !(*mimefactory.msg).param.exists(Param::Width)
if (*mimefactory.msg).type_0 == 20i32
|| (*mimefactory.msg).type_0 == 21i32
|| (*mimefactory.msg).type_0 == 40i32
|| (*mimefactory.msg).type_0 == 41i32
|| (*mimefactory.msg).type_0 == 50i32
|| (*mimefactory.msg).type_0 == 60i32
{
let pathNfilename = dc_param_get(
(*mimefactory.msg).param,
DC_PARAM_FILE as i32,
0 as *const libc::c_char,
);
if !pathNfilename.is_null() {
if ((*mimefactory.msg).type_0 == 20i32 || (*mimefactory.msg).type_0 == 21i32)
&& 0 == dc_param_exists((*mimefactory.msg).param, DC_PARAM_WIDTH as i32)
{
let mut buf = 0 as *mut libc::c_uchar;
let mut buf: *mut libc::c_uchar = 0 as *mut libc::c_uchar;
let mut buf_bytes: size_t = 0;
let mut w = 0;
let mut h = 0;
(*mimefactory.msg).param.set_int(Param::Width, 0);
(*mimefactory.msg).param.set_int(Param::Height, 0);
let mut w: uint32_t = 0;
let mut h: uint32_t = 0;
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_WIDTH as i32, 0);
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_HEIGHT as i32, 0);
if 0 != dc_read_file(
context,
pathNfilename,
@@ -1205,8 +1204,16 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
&mut w,
&mut h,
) {
(*mimefactory.msg).param.set_int(Param::Width, w as i32);
(*mimefactory.msg).param.set_int(Param::Height, h as i32);
dc_param_set_int(
(*mimefactory.msg).param,
DC_PARAM_WIDTH as i32,
w as int32_t,
);
dc_param_set_int(
(*mimefactory.msg).param,
DC_PARAM_HEIGHT as i32,
h as int32_t,
);
}
}
free(buf as *mut libc::c_void);
@@ -1218,19 +1225,15 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
/* create message */
if 0 == dc_mimefactory_render(&mut mimefactory) {
dc_set_msg_failed(context, msg_id, mimefactory.error);
} else if 0
!= (*mimefactory.msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
} else if 0 != dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
&& 0 == mimefactory.out_encrypted
{
warn!(
context,
0,
"e2e encryption unavailable {} - {:?}",
"e2e encryption unavailable {} - {}",
msg_id,
(*mimefactory.msg).param.get_int(Param::GuranteeE2ee),
dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0),
);
dc_set_msg_failed(
context,
@@ -1268,13 +1271,10 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
}
}
if 0 != mimefactory.out_encrypted
&& (*mimefactory.msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
&& dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
== 0
{
(*mimefactory.msg).param.set_int(Param::GuranteeE2ee, 1);
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
dc_msg_save_param_to_disk(mimefactory.msg);
}
dc_add_to_keyhistory(

View File

@@ -1,24 +1,21 @@
use std::ffi::CString;
use crate::constants::Event;
use crate::constants::*;
use crate::context::*;
use crate::dc_array::*;
use crate::dc_chat::*;
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::param::*;
use crate::sql;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
// location handling
#[derive(Clone, Default)]
#[allow(non_camel_case_types)]
pub struct dc_location {
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_location_t {
pub location_id: uint32_t,
pub latitude: libc::c_double,
pub longitude: libc::c_double,
@@ -27,45 +24,16 @@ pub struct dc_location {
pub contact_id: uint32_t,
pub msg_id: uint32_t,
pub chat_id: uint32_t,
pub marker: Option<String>,
pub independent: uint32_t,
pub marker: *mut libc::c_char,
}
impl dc_location {
pub fn new() -> Self {
dc_location {
location_id: 0,
latitude: 0.0,
longitude: 0.0,
accuracy: 0.0,
timestamp: 0,
contact_id: 0,
msg_id: 0,
chat_id: 0,
marker: None,
independent: 0,
}
}
}
#[derive(Clone)]
#[allow(non_camel_case_types)]
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_kml_t {
pub addr: *mut libc::c_char,
pub locations: Option<Vec<dc_location>>,
pub locations: *mut dc_array_t,
pub tag: libc::c_int,
pub curr: dc_location,
}
impl dc_kml_t {
pub fn new() -> Self {
dc_kml_t {
addr: std::ptr::null_mut(),
locations: None,
tag: 0,
curr: dc_location::new(),
}
}
pub curr: dc_location_t,
}
// location streaming
@@ -76,6 +44,7 @@ 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);
@@ -99,21 +68,25 @@ pub unsafe fn dc_send_locations_to_chat(
.is_ok()
{
if 0 != seconds && !is_sending_locations_before {
msg = dc_msg_new(context, Viewtype::Text);
(*msg).text = context
.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)
.strdup();
(*msg).param.set_int(Param::Cmd, 8);
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,
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 {
let stock_str = CString::new(context.stock_system_msg(
StockMessage::MsgLocationDisabled,
"",
"",
0,
))
.unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
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);
}
context.call_cb(
Event::CHAT_MODIFIED,
@@ -126,22 +99,22 @@ pub unsafe fn dc_send_locations_to_chat(
context,
5007i32,
chat_id as libc::c_int,
Params::new(),
0 as *const libc::c_char,
seconds + 1i32,
);
}
}
}
free(stock_str as *mut libc::c_void);
dc_msg_unref(msg);
}
/*******************************************************************************
* job to send locations out to all chats that want them
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: libc::c_int) {
if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) {
dc_job_add(context, 5005, 0, Params::new(), 60);
dc_job_add(context, 5005, 0, 0 as *const libc::c_char, 60);
};
}
@@ -224,43 +197,54 @@ pub fn dc_get_locations(
timestamp_from,
timestamp_to,
],
|row| {
let msg_id = row.get(6)?;
let txt: String = row.get(9)?;
let marker = if msg_id != 0 && is_marker(&txt) {
Some(txt)
} else {
None
};
|row| unsafe {
let mut loc: *mut _dc_location =
calloc(1, ::std::mem::size_of::<_dc_location>()) as *mut _dc_location;
assert!(!loc.is_null(), "allocation failed");
let loc = dc_location {
location_id: row.get(0)?,
latitude: row.get(1)?,
longitude: row.get(2)?,
accuracy: row.get(3)?,
timestamp: row.get(4)?,
independent: row.get(5)?,
msg_id: msg_id,
contact_id: row.get(7)?,
chat_id: row.get(8)?,
marker: marker,
};
(*loc).location_id = row.get(0)?;
(*loc).latitude = row.get(1)?;
(*loc).longitude = row.get(2)?;
(*loc).accuracy = row.get(3)?;
(*loc).timestamp = row.get(4)?;
(*loc).independent = row.get(5)?;
(*loc).msg_id = row.get(6)?;
(*loc).contact_id = row.get(7)?;
(*loc).chat_id = row.get(8)?;
if 0 != (*loc).msg_id {
let txt: String = row.get(9)?;
let txt_c = to_cstring(txt);
if 0 != is_marker(txt_c) {
(*loc).marker = txt_c;
} else {
free(txt_c as *mut _);
}
}
Ok(loc)
},
|locations| {
let mut ret = Vec::new();
let ret = unsafe { dc_array_new_typed(1, 500) };
for location in locations {
ret.push(location?);
unsafe { dc_array_add_ptr(ret, location? as *mut libc::c_void) };
}
Ok(dc_array_t::from(ret).into_raw())
Ok(ret)
},
)
.unwrap_or_else(|_| std::ptr::null_mut())
}
fn is_marker(txt: &str) -> bool {
txt.len() == 1 && txt.chars().next().unwrap() != ' '
// TODO should be bool /rtn
unsafe fn is_marker(txt: *const libc::c_char) -> libc::c_int {
if !txt.is_null() {
let len: libc::c_int = dc_utf8_strlen(txt) as libc::c_int;
if len == 1 && *txt.offset(0isize) as libc::c_int != ' ' as i32 {
return 1;
}
}
0
}
pub fn dc_delete_all_locations(context: &Context) -> bool {
@@ -348,7 +332,7 @@ pub fn dc_get_location_kml(
}
if 0 != success {
unsafe { ret.strdup() }
unsafe { to_cstring(ret) }
} else {
std::ptr::null_mut()
}
@@ -362,7 +346,7 @@ unsafe fn get_kml_timestamp(utc: i64) -> *mut libc::c_char {
let res = chrono::NaiveDateTime::from_timestamp(utc, 0)
.format("%Y-%m-%dT%H:%M:%SZ")
.to_string();
res.strdup()
to_cstring(res)
}
pub unsafe fn dc_get_message_kml(
@@ -420,14 +404,13 @@ pub unsafe fn dc_save_locations(
context: &Context,
chat_id: u32,
contact_id: u32,
locations_opt: &Option<Vec<dc_location>>,
locations: *const dc_array_t,
independent: libc::c_int,
) -> u32 {
if chat_id <= 9 || locations_opt.is_none() {
if chat_id <= 9 || locations.is_null() {
return 0;
}
let locations = locations_opt.as_ref().unwrap();
context
.sql
.prepare2(
@@ -439,29 +422,31 @@ pub unsafe fn dc_save_locations(
let mut newest_timestamp = 0;
let mut newest_location_id = 0;
for location in locations {
for i in 0..dc_array_get_cnt(locations) {
let location = dc_array_get_ptr(locations, i as size_t) as *mut dc_location_t;
let exists =
stmt_test.exists(params![location.timestamp, contact_id as i32])?;
stmt_test.exists(params![(*location).timestamp, contact_id as i32])?;
if 0 != independent || !exists {
stmt_insert.execute(params![
location.timestamp,
(*location).timestamp,
contact_id as i32,
chat_id as i32,
location.latitude,
location.longitude,
location.accuracy,
(*location).latitude,
(*location).longitude,
(*location).accuracy,
independent,
])?;
if location.timestamp > newest_timestamp {
newest_timestamp = location.timestamp;
if (*location).timestamp > newest_timestamp {
newest_timestamp = (*location).timestamp;
newest_location_id = sql::get_rowid2_with_conn(
context,
conn,
"locations",
"timestamp",
location.timestamp,
(*location).timestamp,
"from_id",
contact_id as i32,
);
@@ -478,8 +463,8 @@ pub unsafe fn dc_kml_parse(
context: &Context,
content: *const libc::c_char,
content_bytes: size_t,
) -> dc_kml_t {
let mut kml = dc_kml_t::new();
) -> *mut dc_kml_t {
let mut kml: *mut dc_kml_t = calloc(1, ::std::mem::size_of::<dc_kml_t>()) as *mut dc_kml_t;
let mut content_nullterminated: *mut libc::c_char = 0 as *mut libc::c_char;
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
starttag_cb: None,
@@ -496,11 +481,8 @@ pub unsafe fn dc_kml_parse(
} else {
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
if !content_nullterminated.is_null() {
kml.locations = Some(Vec::with_capacity(100));
dc_saxparser_init(
&mut saxparser,
&mut kml as *mut dc_kml_t as *mut libc::c_void,
);
(*kml).locations = dc_array_new_typed(1, 100 as size_t);
dc_saxparser_init(&mut saxparser, kml as *mut libc::c_void);
dc_saxparser_set_tag_handler(
&mut saxparser,
Some(kml_starttag_cb),
@@ -581,8 +563,10 @@ unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
&& 0. != (*kml).curr.latitude
&& 0. != (*kml).curr.longitude
{
let location = (*kml).curr.clone();
((*kml).locations.as_mut().unwrap()).push(location);
let location: *mut dc_location_t =
calloc(1, ::std::mem::size_of::<dc_location_t>()) as *mut dc_location_t;
*location = (*kml).curr;
dc_array_add_ptr((*kml).locations, location as *mut libc::c_void);
}
(*kml).tag = 0
};
@@ -633,11 +617,15 @@ unsafe fn kml_starttag_cb(
};
}
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
pub unsafe fn dc_kml_unref(kml: *mut dc_kml_t) {
if kml.is_null() {
return;
}
dc_array_unref((*kml).locations);
free((*kml).addr as *mut libc::c_void);
free(kml as *mut libc::c_void);
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mut dc_job_t) {
let now = time();
let mut continue_streaming: libc::c_int = 1;
@@ -700,9 +688,9 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
// the easiest way to determine this, is to check for an empty message queue.
// (might not be 100%, however, as positions are sent combined later
// and dc_set_location() is typically called periodically, this is ok)
let mut msg = dc_msg_new(context, Viewtype::Text);
let mut msg = dc_msg_new(context, 10);
(*msg).hidden = 1;
(*msg).param.set_int(Param::Cmd, 9);
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 9);
dc_send_msg(context, chat_id as u32, msg);
dc_msg_unref(msg);
}
@@ -718,13 +706,13 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut dc_job_t) {
// this function is called when location-streaming _might_ have ended for a chat.
// the function checks, if location-streaming is really ended;
// 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=?",
@@ -741,8 +729,14 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
params![chat_id as i32],
).is_ok() {
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
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);
context.call_cb(
Event::CHAT_MODIFIED,
chat_id as usize,
@@ -752,4 +746,5 @@ 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

@@ -4,7 +4,6 @@ use crate::context::Context;
use crate::sql::Sql;
#[derive(Default, Debug)]
#[allow(non_camel_case_types)]
pub struct dc_loginparam_t {
pub addr: String,
pub mail_server: String,

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::*;
@@ -29,7 +29,7 @@ pub struct dc_lot_t {
* An object containing a set of values.
* The meaning of the values is defined by the function returning the object.
* Lot objects are created
* eg. by chatlist.get_summary() or dc_msg_get_summary().
* eg. by dc_chatlist_get_summary() or dc_msg_get_summary().
*
* NB: _Lot_ is used in the meaning _heap_ here.
*/
@@ -125,7 +125,7 @@ pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
/* in practice, the user additionally cuts the string himself pixel-accurate */
pub unsafe fn dc_lot_fill(
mut lot: *mut dc_lot_t,
msg: *mut dc_msg_t,
msg: *const dc_msg_t,
chat: *const Chat,
contact: *const dc_contact_t,
context: &Context,
@@ -134,14 +134,14 @@ pub unsafe fn dc_lot_fill(
return;
}
if (*msg).state == 19i32 {
(*lot).text1 = context.stock_str(StockMessage::Draft).strdup();
(*lot).text1 = dc_stock_str(context, 3i32);
(*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 = context.stock_str(StockMessage::SelfMsg).strdup();
(*lot).text1 = dc_stock_str(context, 2i32);
(*lot).text1_meaning = 3i32
}
} else if chat.is_null() {
@@ -161,7 +161,7 @@ pub unsafe fn dc_lot_fill(
}
}
(*lot).text2 =
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, &mut (*msg).param, 160, context);
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 160i32, context);
(*lot).timestamp = dc_msg_get_timestamp(msg);
(*lot).state = (*msg).state;
}

View File

@@ -1,5 +1,3 @@
use std::ffi::CString;
use chrono::TimeZone;
use mmime::mailimf_types::*;
use mmime::mailimf_types_helper::*;
@@ -17,16 +15,15 @@ use crate::dc_contact::*;
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::param::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
#[derive(Copy, Clone)]
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct dc_mimefactory_t<'a> {
pub from_addr: *mut libc::c_char,
pub from_displayname: *mut libc::c_char,
@@ -50,7 +47,6 @@ pub struct dc_mimefactory_t<'a> {
pub context: &'a Context,
}
#[allow(non_camel_case_types)]
pub type dc_mimefactory_loaded_t = libc::c_uint;
pub const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2;
pub const DC_MF_MSG_LOADED: dc_mimefactory_loaded_t = 1;
@@ -114,6 +110,7 @@ pub unsafe fn dc_mimefactory_load_msg(
msg_id: uint32_t,
) -> libc::c_int {
if factory.is_null() || msg_id <= 9 || !(*factory).msg.is_null() {
info!((*factory).context, 0, "mimefactory: null");
return 0;
}
@@ -128,9 +125,12 @@ pub unsafe fn dc_mimefactory_load_msg(
if dc_msg_load_from_db((*factory).msg, context, msg_id)
&& dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id)
{
info!(context, 0, "mimefactory: loaded msg and chat",);
load_from(factory);
(*factory).req_mdn = 0;
if 0 != dc_chat_is_self_talk((*factory).chat) {
info!(context, 0, "mimefactory: selftalk");
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
@@ -142,6 +142,7 @@ pub unsafe fn dc_mimefactory_load_msg(
dc_strdup((*factory).from_addr) as *mut libc::c_void,
);
} else {
info!(context, 0, "mimefactory: query map");
context
.sql
.query_map(
@@ -156,15 +157,16 @@ pub unsafe fn dc_mimefactory_load_msg(
Ok((authname, addr))
},
|rows| {
info!(context, 0, "mimefactory: processing rows");
for row in rows {
let (authname, addr) = row?;
let addr_c = addr.strdup();
let addr_c = to_cstring(addr);
if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 {
clist_insert_after(
(*factory).recipients_names,
(*(*factory).recipients_names).last,
if !authname.is_empty() {
authname.strdup()
to_cstring(authname)
} else {
std::ptr::null_mut()
} as *mut libc::c_void,
@@ -181,15 +183,14 @@ pub unsafe fn dc_mimefactory_load_msg(
)
.unwrap();
let command = (*(*factory).msg)
.param
.get_int(Param::Cmd)
.unwrap_or_default();
let command = dc_param_get_int((*(*factory).msg).param, DC_PARAM_CMD as i32, 0);
if command == 5 {
let email_to_remove = (*(*factory).msg).param.get(Param::Arg).unwrap_or_default();
let email_to_remove_c = email_to_remove.strdup();
let email_to_remove_c = dc_param_get(
(*(*factory).msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
let email_to_remove = to_string(email_to_remove_c);
let self_addr = context
.sql
.get_config(context, "configured_addr")
@@ -222,6 +223,8 @@ pub unsafe fn dc_mimefactory_load_msg(
(*factory).req_mdn = 1
}
}
info!(context, 0, "mimefactory: loading in reply to");
let row = context.sql.query_row(
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
params![(*(*factory).msg).id as i32],
@@ -234,8 +237,8 @@ pub unsafe fn dc_mimefactory_load_msg(
);
match row {
Ok((in_reply_to, references)) => {
(*factory).in_reply_to = in_reply_to.strdup();
(*factory).references = references.strdup();
(*factory).in_reply_to = to_cstring(in_reply_to);
(*factory).references = to_cstring(references);
}
Err(err) => {
error!(
@@ -259,28 +262,27 @@ pub unsafe fn dc_mimefactory_load_msg(
unsafe fn load_from(mut factory: *mut dc_mimefactory_t) {
let context = (*factory).context;
(*factory).from_addr = context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default()
.strdup();
(*factory).from_addr = to_cstring(
context
.sql
.get_config(context, "configured_addr")
.unwrap_or_default(),
);
(*factory).from_displayname = context
.sql
.get_config(context, "displayname")
.unwrap_or_default()
.strdup();
(*factory).selfstatus = context
.sql
.get_config(context, "selfstatus")
.unwrap_or_default()
.strdup();
(*factory).from_displayname = to_cstring(
context
.sql
.get_config(context, "displayname")
.unwrap_or_default(),
);
(*factory).selfstatus = to_cstring(
context
.sql
.get_config(context, "selfstatus")
.unwrap_or_default(),
);
if (*factory).selfstatus.is_null() {
(*factory).selfstatus = (*factory)
.context
.stock_str(StockMessage::StatusLine)
.strdup();
(*factory).selfstatus = dc_stock_str((*factory).context, 13)
};
}
@@ -368,7 +370,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
// 1=add Autocrypt-header (needed eg. for handshaking), 2=no Autocrypte-header (used for MDN)
let mut force_plaintext: libc::c_int = 0;
let mut do_gossip: libc::c_int = 0;
let mut grpimage = None;
let mut grpimage: *mut libc::c_char = 0 as *mut libc::c_char;
let mut e2ee_helper = dc_e2ee_helper_t {
encryption_successfull: 0,
cdata_to_free: 0 as *mut libc::c_void,
@@ -478,26 +480,26 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
references_list,
0 as *mut libc::c_char,
);
let os_name = &(*factory).context.os_name;
let os_part = os_name
.as_ref()
.map(|s| format!("/{}", s))
.unwrap_or_default();
let os_part = CString::new(os_part).expect("String -> CString conversion failed");
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
strdup(b"X-Mailer\x00" as *const u8 as *const libc::c_char),
dc_mprintf(
b"Delta Chat Core %s%s\x00" as *const u8 as *const libc::c_char,
b"Delta Chat Core %s%s%s\x00" as *const u8 as *const libc::c_char,
DC_VERSION_STR as *const u8 as *const libc::c_char,
os_part.as_ptr(),
if !(*(*factory).context).os_name.is_null() {
b"/\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !(*(*factory).context).os_name.is_null() {
(*(*factory).context).os_name
} else {
b"\x00" as *const u8 as *const libc::c_char
},
),
),
);
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -537,15 +539,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
e2ee_guaranteed = 1;
min_verified = 2
} else {
force_plaintext = (*(*factory).msg)
.param
.get_int(Param::ForcePlaintext)
.unwrap_or_default();
force_plaintext =
dc_param_get_int((*(*factory).msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 0);
if force_plaintext == 0 {
e2ee_guaranteed = (*(*factory).msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
e2ee_guaranteed =
dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
}
}
if (*chat).gossiped_timestamp == 0
@@ -553,9 +551,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
{
do_gossip = 1
}
/* build header etc. */
let command = (*msg).param.get_int(Param::Cmd).unwrap_or_default();
let command: libc::c_int = dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0);
if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
{
@@ -574,8 +571,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
if command == 5 {
let email_to_remove = (*msg).param.get(Param::Arg).unwrap_or_default().strdup();
if strlen(email_to_remove) > 0 {
let email_to_remove: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !email_to_remove.is_null() {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -589,8 +590,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
} else if command == 4 {
do_gossip = 1;
let email_to_add = (*msg).param.get(Param::Arg).unwrap_or_default().strdup();
if strlen(email_to_add) > 0 {
let email_to_add: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !email_to_add.is_null() {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -601,9 +606,13 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
email_to_add,
),
);
grpimage = (*chat).param.get(Param::ProfileImage);
grpimage = dc_param_get(
(*chat).param,
DC_PARAM_PROFILE_IMAGE as i32,
0 as *const libc::c_char,
)
}
if 0 != (*msg).param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
if 0 != dc_param_get_int((*msg).param, DC_PARAM_CMD_ARG2 as i32, 0) & 0x1 {
info!(
(*msg).context,
0,
@@ -619,19 +628,26 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
);
}
} else if command == 2 {
let value_to_add = (*msg).param.get(Param::Arg).unwrap_or_default().strdup();
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
strdup(
b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char,
),
value_to_add,
dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
b"\x00" as *const u8 as *const libc::c_char,
),
),
);
} else if command == 3 {
grpimage = (*msg).param.get(Param::Arg);
if grpimage.is_none() {
grpimage = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if grpimage.is_null() {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -661,14 +677,15 @@ 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 = (*factory)
.context
.stock_str(StockMessage::AcSetupMsgBody)
.strdup();
placeholdertext = dc_stock_str((*factory).context, 43)
}
if command == 7 {
let step = (*msg).param.get(Param::Arg).unwrap_or_default().strdup();
if strlen(step) > 0 {
let step: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG as i32,
0 as *const libc::c_char,
);
if !step.is_null() {
info!(
(*msg).context,
0,
@@ -682,8 +699,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
step,
),
);
let param2 = (*msg).param.get(Param::Arg2).unwrap_or_default().strdup();
if strlen(param2) > 0 {
let param2: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG2 as i32,
0 as *const libc::c_char,
);
if !param2.is_null() {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -710,8 +731,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let fingerprint = (*msg).param.get(Param::Arg3).unwrap_or_default().strdup();
if strlen(fingerprint) > 0 {
let fingerprint: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG3 as i32,
0 as *const libc::c_char,
);
if !fingerprint.is_null() {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -723,10 +748,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let grpid = match (*msg).param.get(Param::Arg4) {
Some(id) => id.strdup(),
None => std::ptr::null_mut(),
};
let grpid: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_CMD_ARG4 as i32,
0 as *const libc::c_char,
);
if !grpid.is_null() {
mailimf_fields_add(
imf_fields,
@@ -740,12 +766,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
}
}
if let Some(grpimage) = grpimage {
let mut meta = dc_msg_new_untyped((*factory).context);
(*meta).type_0 = Viewtype::Image;
(*meta).param.set(Param::File, grpimage);
let mut filename_as_sent = 0 as *mut libc::c_char;
if !grpimage.is_null() {
let mut meta: *mut dc_msg_t = dc_msg_new_untyped((*factory).context);
(*meta).type_0 = DC_MSG_IMAGE as libc::c_int;
dc_param_set((*meta).param, DC_PARAM_FILE as i32, grpimage);
let mut filename_as_sent: *mut libc::c_char = 0 as *mut libc::c_char;
meta_part = build_body_file(
meta,
b"group-image\x00" as *const u8 as *const libc::c_char,
@@ -762,12 +787,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
}
dc_msg_unref(meta);
}
if (*msg).type_0 == Viewtype::Voice
|| (*msg).type_0 == Viewtype::Audio
|| (*msg).type_0 == Viewtype::Video
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
{
if (*msg).type_0 == Viewtype::Voice {
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
mailimf_fields_add(
imf_fields,
mailimf_field_new_custom(
@@ -776,7 +800,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
),
);
}
let duration_ms = (*msg).param.get_int(Param::Duration).unwrap_or_default();
let duration_ms: libc::c_int =
dc_param_get_int((*msg).param, DC_PARAM_DURATION as i32, 0);
if duration_ms > 0 {
mailimf_fields_add(
imf_fields,
@@ -790,15 +815,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
);
}
}
afwd_email = (*msg).param.exists(Param::Forwarded) as libc::c_int;
let mut fwdhint = 0 as *mut libc::c_char;
afwd_email = dc_param_exists((*msg).param, DC_PARAM_FORWARDED as i32);
let mut fwdhint: *mut libc::c_char = 0 as *mut libc::c_char;
if 0 != afwd_email {
fwdhint = dc_strdup(
b"---------- Forwarded message ----------\r\nFrom: Delta Chat\r\n\r\n\x00"
as *const u8 as *const libc::c_char,
)
}
let mut final_text: *const libc::c_char = 0 as *const libc::c_char;
if !placeholdertext.is_null() {
final_text = placeholdertext
@@ -842,10 +866,15 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
parts += 1;
free(fwdhint as *mut libc::c_void);
free(placeholdertext as *mut libc::c_void);
/* add attachment part */
if msgtype_has_file((*msg).type_0) {
if !is_file_size_okay(msg) {
if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|| (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
|| (*msg).type_0 == DC_MSG_FILE as libc::c_int
{
if 0 == is_file_size_okay(msg) {
let error: *mut libc::c_char = dc_mprintf(
b"Message exceeds the recommended %i MB.\x00" as *const u8
as *const libc::c_char,
@@ -879,15 +908,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
if !meta_part.is_null() {
mailmime_smart_add_part(message, meta_part);
}
if (*msg).param.exists(Param::SetLatitude) {
let latitude = (*msg)
.param
.get_float(Param::SetLatitude)
.unwrap_or_default();
let longitude = (*msg)
.param
.get_float(Param::SetLongitude)
.unwrap_or_default();
if 0 != dc_param_exists((*msg).param, DC_PARAM_SET_LATITUDE as libc::c_int)
{
let latitude = dc_param_get_float(
(*msg).param,
DC_PARAM_SET_LATITUDE as libc::c_int,
0.0,
);
let longitude = dc_param_get_float(
(*msg).param,
DC_PARAM_SET_LONGITUDE as libc::c_int,
0.0,
);
let kml_file =
dc_get_message_kml((*msg).timestamp_sort, latitude, longitude);
if !kml_file.is_null() {
@@ -934,7 +966,10 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
mailmime_new_empty(content_type, mime_fields);
mailmime_set_body_text(kml_mime_part, kml_file, strlen(kml_file));
mailmime_smart_add_part(message, kml_mime_part);
if !(*msg).param.exists(Param::SetLatitude) {
if 0 == dc_param_exists(
(*msg).param,
DC_PARAM_SET_LATITUDE as libc::c_int,
) {
// otherwise, the independent location is already filed
(*factory).out_last_added_location_id = last_added_location_id;
}
@@ -960,23 +995,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
) as *mut libc::c_void,
);
mailmime_add_part(message, multipart);
let p1 = if 0
!= (*(*factory).msg)
.param
.get_int(Param::GuranteeE2ee)
.unwrap_or_default()
{
(*factory)
.context
.stock_str(StockMessage::EncryptedMsg)
.into_owned()
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)
} else {
to_string(dc_msg_get_summarytext((*factory).msg, 32))
};
let p2 = (*factory)
.context
.stock_string_repl_str(StockMessage::ReadRcptMailBody, p1);
message_text = format!("{}\r\n", p2).strdup();
p1 = dc_msg_get_summarytext((*factory).msg, 32)
}
p2 = dc_stock_str_repl_string((*factory).context, 32, 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);
let human_mime_part: *mut mailmime = build_body_text(message_text);
mailmime_add_part(multipart, human_mime_part);
message_text2 =
@@ -1002,24 +1031,16 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
);
current_block = 11328123142868406523;
}
match current_block {
11328123142868406523 => {}
_ => {
if (*factory).loaded as libc::c_uint
== DC_MF_MDN_LOADED as libc::c_int as libc::c_uint
{
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(),
);
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);
} else {
subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email)
}
@@ -1083,26 +1104,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
free(message_text as *mut libc::c_void);
free(message_text2 as *mut libc::c_void);
free(subject_str as *mut libc::c_void);
free(grpimage as *mut libc::c_void);
success
}
unsafe fn get_subject(
chat: *const Chat,
msg: *mut dc_msg_t,
msg: *const dc_msg_t,
afwd_email: libc::c_int,
) -> *mut libc::c_char {
let context = (*chat).context;
let ret: *mut libc::c_char;
let raw_subject =
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, &mut (*msg).param, 32, context);
let fwd = if 0 != afwd_email {
let raw_subject: *mut libc::c_char =
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32, context);
let fwd: *const libc::c_char = if 0 != afwd_email {
b"Fwd: \x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
};
if (*msg).param.get_int(Param::Cmd).unwrap_or_default() == 6 {
ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup()
if dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0) == 6 {
ret = dc_stock_str(context, 42)
} 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
{
@@ -1152,7 +1174,6 @@ unsafe fn build_body_text(text: *mut libc::c_char) -> *mut mailmime {
message_part
}
#[allow(non_snake_case)]
unsafe fn build_body_file(
msg: *const dc_msg_t,
mut base_name: *const libc::c_char,
@@ -1162,23 +1183,18 @@ unsafe fn build_body_file(
let mime_fields: *mut mailmime_fields;
let mut mime_sub: *mut mailmime = 0 as *mut mailmime;
let content: *mut mailmime_content;
let pathNfilename = (*msg)
.param
.get(Param::File)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
let mut mimetype = (*msg)
.param
.get(Param::MimeType)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
let suffix = dc_get_filesuffix_lc(pathNfilename);
let mut filename_to_send = 0 as *mut libc::c_char;
let mut filename_encoded = 0 as *mut libc::c_char;
let pathNfilename: *mut libc::c_char =
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
let mut mimetype: *mut libc::c_char = dc_param_get(
(*msg).param,
DC_PARAM_MIMETYPE as i32,
0 as *const libc::c_char,
);
let suffix: *mut libc::c_char = dc_get_filesuffix_lc(pathNfilename);
let mut filename_to_send: *mut libc::c_char = 0 as *mut libc::c_char;
let mut filename_encoded: *mut libc::c_char = 0 as *mut libc::c_char;
if !pathNfilename.is_null() {
if (*msg).type_0 == Viewtype::Voice {
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0);
let suffix = if !suffix.is_null() {
@@ -1189,10 +1205,12 @@ unsafe fn build_body_file(
let res = ts
.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
.to_string();
filename_to_send = res.strdup();
} else if (*msg).type_0 == Viewtype::Audio {
filename_to_send = to_cstring(res);
} else if (*msg).type_0 == DC_MSG_AUDIO as libc::c_int {
filename_to_send = dc_get_filename(pathNfilename)
} else if (*msg).type_0 == Viewtype::Image || (*msg).type_0 == Viewtype::Gif {
} else if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
{
if base_name.is_null() {
base_name = b"image\x00" as *const u8 as *const libc::c_char
}
@@ -1205,7 +1223,7 @@ unsafe fn build_body_file(
b"dat\x00" as *const u8 as *const libc::c_char
},
)
} else if (*msg).type_0 == Viewtype::Video {
} else if (*msg).type_0 == DC_MSG_VIDEO as libc::c_int {
filename_to_send = dc_mprintf(
b"video.%s\x00" as *const u8 as *const libc::c_char,
if !suffix.is_null() {
@@ -1326,16 +1344,16 @@ unsafe fn build_body_file(
/*******************************************************************************
* Render
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> bool {
let mut file_size_okay = true;
let pathNfilename = (*msg).param.get(Param::File).unwrap_or_default().strdup();
let bytes = dc_get_filebytes((*msg).context, pathNfilename);
if bytes > (49 * 1024 * 1024 / 4 * 3) {
file_size_okay = false;
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> libc::c_int {
let mut file_size_okay: libc::c_int = 1;
let pathNfilename: *mut libc::c_char =
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
let bytes: uint64_t = dc_get_filebytes((*msg).context, pathNfilename);
if bytes > (49 * 1024 * 1024 / 4 * 3) as libc::c_ulonglong {
file_size_okay = 0;
}
free(pathNfilename as *mut _);
free(pathNfilename as *mut libc::c_void);
file_size_okay
}

View File

@@ -1,5 +1,5 @@
use std::collections::{HashMap, HashSet};
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use charset::Charset;
use mmime::mailimf::*;
@@ -15,18 +15,18 @@ use crate::context::Context;
use crate::dc_contact::*;
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::param::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
/* Parse MIME body; this is the text part of an IMF, see https://tools.ietf.org/html/rfc5322
dc_mimeparser_t has no deep dependencies to Context or to the database
(Context is used for logging only). */
#[derive(Clone)]
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_mimepart_t {
pub type_0: libc::c_int,
@@ -35,16 +35,15 @@ pub struct dc_mimepart_t {
pub msg: *mut libc::c_char,
pub msg_raw: *mut libc::c_char,
pub bytes: libc::c_int,
pub param: Params,
pub param: *mut dc_param_t,
}
/* *
* @class dc_mimeparser_t
*/
#[derive(Clone)]
#[allow(non_camel_case_types)]
pub struct dc_mimeparser_t<'a> {
pub parts: Vec<dc_mimepart_t>,
pub parts: *mut carray,
pub mimeroot: *mut mailmime,
pub header: HashMap<String, *mut mailimf_field>,
pub header_root: *mut mailimf_fields,
@@ -55,23 +54,23 @@ pub struct dc_mimeparser_t<'a> {
pub e2ee_helper: dc_e2ee_helper_t,
pub is_forwarded: libc::c_int,
pub context: &'a Context,
pub reports: Vec<*mut mailmime>,
pub reports: *mut carray,
pub is_system_message: libc::c_int,
pub location_kml: Option<dc_kml_t>,
pub message_kml: Option<dc_kml_t>,
pub location_kml: *mut dc_kml_t,
pub message_kml: *mut dc_kml_t,
}
// deprecated
pub unsafe fn dc_no_compound_msgs() {
S_GENERATE_COMPOUND_MSGS = 0i32;
s_generate_compound_msgs = 0i32;
}
// deprecated: flag to switch generation of compound messages on and off.
static mut S_GENERATE_COMPOUND_MSGS: libc::c_int = 1i32;
static mut s_generate_compound_msgs: libc::c_int = 1i32;
pub unsafe fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t {
dc_mimeparser_t {
parts: Vec::new(),
parts: carray_new(16i32 as libc::c_uint),
mimeroot: std::ptr::null_mut(),
header: Default::default(),
header_root: std::ptr::null_mut(),
@@ -82,22 +81,38 @@ pub unsafe fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t {
e2ee_helper: Default::default(),
is_forwarded: 0,
context,
reports: Vec::new(),
reports: carray_new(16i32 as libc::c_uint),
is_system_message: 0,
location_kml: None,
message_kml: None,
location_kml: std::ptr::null_mut(),
message_kml: std::ptr::null_mut(),
}
}
pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) {
dc_mimeparser_empty(mimeparser);
if !(*mimeparser).parts.is_null() {
carray_free((*mimeparser).parts);
}
if !(*mimeparser).reports.is_null() {
carray_free((*mimeparser).reports);
}
}
pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
for part in mimeparser.parts.drain(..) {
dc_mimepart_unref(part);
if !(*mimeparser).parts.is_null() {
let mut i: libc::c_int;
let cnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int;
i = 0i32;
while i < cnt {
let part: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t;
if !part.is_null() {
dc_mimepart_unref(part);
}
i += 1
}
carray_set_size((*mimeparser).parts, 0i32 as libc::c_uint);
}
assert!(mimeparser.parts.is_empty());
(*mimeparser).header_root = 0 as *mut mailimf_fields;
(*mimeparser).header.clear();
if !(*mimeparser).header_protected.is_null() {
@@ -113,26 +128,29 @@ pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
(*mimeparser).mimeroot = 0 as *mut mailmime
}
(*mimeparser).is_forwarded = 0i32;
(*mimeparser).reports.clear();
if !(*mimeparser).reports.is_null() {
carray_set_size((*mimeparser).reports, 0i32 as libc::c_uint);
}
(*mimeparser).decrypting_failed = 0i32;
dc_e2ee_thanks(&mut (*mimeparser).e2ee_helper);
if let Some(location_kml) = (*mimeparser).location_kml.as_mut() {
dc_kml_unref(location_kml);
}
(*mimeparser).location_kml = None;
dc_kml_unref((*mimeparser).location_kml);
(*mimeparser).location_kml = 0 as *mut dc_kml_t;
if let Some(message_kml) = (*mimeparser).message_kml.as_mut() {
dc_kml_unref(message_kml);
}
(*mimeparser).message_kml = None;
dc_kml_unref((*mimeparser).message_kml);
(*mimeparser).message_kml = 0 as *mut dc_kml_t;
}
unsafe fn dc_mimepart_unref(mut mimepart: dc_mimepart_t) {
free(mimepart.msg as *mut libc::c_void);
mimepart.msg = 0 as *mut libc::c_char;
free(mimepart.msg_raw as *mut libc::c_void);
mimepart.msg_raw = 0 as *mut libc::c_char;
unsafe fn dc_mimepart_unref(mut mimepart: *mut dc_mimepart_t) {
if mimepart.is_null() {
return;
}
free((*mimepart).msg as *mut libc::c_void);
(*mimepart).msg = 0 as *mut libc::c_char;
free((*mimepart).msg_raw as *mut libc::c_void);
(*mimepart).msg_raw = 0 as *mut libc::c_char;
dc_param_unref((*mimepart).param);
free(mimepart as *mut libc::c_void);
}
pub unsafe fn dc_mimeparser_parse(
@@ -171,34 +189,29 @@ pub unsafe fn dc_mimeparser_parse(
(*mimeparser).is_send_by_messenger = 1i32
}
if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() {
let mut i: libc::c_int;
let mut has_setup_file: libc::c_int = 0i32;
for part in &mimeparser.parts {
if part.int_mimetype == 111i32 {
i = 0i32;
while (i as libc::c_uint) < carray_count((*mimeparser).parts) {
let part: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t;
if (*part).int_mimetype == 111i32 {
has_setup_file = 1i32
}
i += 1
}
if 0 != has_setup_file {
mimeparser.is_system_message = 6i32;
// TODO: replace the following code with this
// once drain_filter stabilizes.
//
// See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter
// and https://github.com/rust-lang/rust/issues/43244
//
// mimeparser
// .parts
// .drain_filter(|part| part.int_mimetype != 111)
// .for_each(|part| dc_mimepart_unref(part));
let mut i = 0;
while i != mimeparser.parts.len() {
if mimeparser.parts[i].int_mimetype != 111 {
let part = mimeparser.parts.remove(i);
dc_mimepart_unref(part);
} else {
i += 1;
(*mimeparser).is_system_message = 6i32;
i = 0i32;
while (i as libc::c_uint) < carray_count((*mimeparser).parts) {
let part_0: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t;
if (*part_0).int_mimetype != 111i32 {
dc_mimepart_unref(part_0);
carray_delete_slow((*mimeparser).parts, i as libc::c_uint);
i -= 1
}
i += 1
}
}
} else {
@@ -217,42 +230,42 @@ pub unsafe fn dc_mimeparser_parse(
}
}
if !dc_mimeparser_lookup_field(mimeparser, "Chat-Group-Image").is_null()
&& !mimeparser.parts.is_empty()
&& carray_count((*mimeparser).parts) >= 1i32 as libc::c_uint
{
let textpart = &mimeparser.parts[0];
if textpart.type_0 == 10i32 {
if mimeparser.parts.len() >= 2 {
let imgpart = &mut mimeparser.parts[1];
if imgpart.type_0 == 20i32 {
imgpart.is_meta = 1i32
let textpart: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t;
if (*textpart).type_0 == 10i32 {
if carray_count((*mimeparser).parts) >= 2i32 as libc::c_uint {
let mut imgpart: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, 1i32 as libc::c_uint) as *mut dc_mimepart_t;
if (*imgpart).type_0 == 20i32 {
(*imgpart).is_meta = 1i32
}
}
}
}
if 0 != (*mimeparser).is_send_by_messenger
&& 0 != S_GENERATE_COMPOUND_MSGS
&& mimeparser.parts.len() == 2
&& 0 != s_generate_compound_msgs
&& carray_count((*mimeparser).parts) == 2i32 as libc::c_uint
{
let need_drop: bool;
let mut textpart_0: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t;
let mut filepart: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, 1i32 as libc::c_uint) as *mut dc_mimepart_t;
if (*textpart_0).type_0 == 10i32
&& ((*filepart).type_0 == 20i32
|| (*filepart).type_0 == 21i32
|| (*filepart).type_0 == 40i32
|| (*filepart).type_0 == 41i32
|| (*filepart).type_0 == 50i32
|| (*filepart).type_0 == 60i32)
&& 0 == (*filepart).is_meta
{
let textpart = &mimeparser.parts[0];
let filepart = &mimeparser.parts[1];
need_drop = textpart.type_0 == 10i32
&& (filepart.type_0 == 20i32
|| filepart.type_0 == 21i32
|| filepart.type_0 == 40i32
|| filepart.type_0 == 41i32
|| filepart.type_0 == 50i32
|| filepart.type_0 == 60i32)
&& 0 == filepart.is_meta;
}
if need_drop {
free(mimeparser.parts[1].msg as *mut libc::c_void);
let mut textpart = mimeparser.parts.swap_remove(0);
mimeparser.parts[1].msg = textpart.msg;
textpart.msg = 0 as *mut libc::c_char;
dc_mimepart_unref(textpart);
free((*filepart).msg as *mut libc::c_void);
(*filepart).msg = (*textpart_0).msg;
(*textpart_0).msg = 0 as *mut libc::c_char;
dc_mimepart_unref(textpart_0);
carray_delete_slow((*mimeparser).parts, 0i32 as libc::c_uint);
}
}
if !(*mimeparser).subject.is_null() {
@@ -279,16 +292,24 @@ pub unsafe fn dc_mimeparser_parse(
}
dc_trim(subj);
if 0 != *subj.offset(0isize) {
for part in mimeparser.parts.iter_mut() {
if part.type_0 == 10i32 {
let mut i_0: libc::c_int;
let icnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int;
i_0 = 0i32;
while i_0 < icnt {
let mut part_1: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i_0 as libc::c_uint)
as *mut dc_mimepart_t;
if (*part_1).type_0 == 10i32 {
let new_txt: *mut libc::c_char = dc_mprintf(
b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char,
subj,
part.msg,
(*part_1).msg,
);
free(part.msg as *mut libc::c_void);
part.msg = new_txt;
free((*part_1).msg as *mut libc::c_void);
(*part_1).msg = new_txt;
break;
} else {
i_0 += 1
}
}
}
@@ -296,33 +317,38 @@ pub unsafe fn dc_mimeparser_parse(
}
}
if 0 != (*mimeparser).is_forwarded {
for part in mimeparser.parts.iter_mut() {
part.param.set_int(Param::Forwarded, 1);
let mut i_1: libc::c_int;
let icnt_0: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int;
i_1 = 0i32;
while i_1 < icnt_0 {
let part_2: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, i_1 as libc::c_uint) as *mut dc_mimepart_t;
dc_param_set_int((*part_2).param, DC_PARAM_FORWARDED as i32, 1);
i_1 += 1
}
}
if mimeparser.parts.len() == 1 {
if mimeparser.parts[0].type_0 == 40i32 {
if carray_count((*mimeparser).parts) == 1i32 as libc::c_uint {
let mut part_3: *mut dc_mimepart_t =
carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t;
if (*part_3).type_0 == 40i32 {
if !dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Voice-Message\x00" as *const u8 as *const libc::c_char,
)
.is_null()
{
let part_mut = &mut mimeparser.parts[0];
part_mut.type_0 = 41i32
(*part_3).type_0 = 41i32
}
}
let part = &mimeparser.parts[0];
if part.type_0 == 40i32 || part.type_0 == 41i32 || part.type_0 == 50i32 {
let field_0 = dc_mimeparser_lookup_optional_field(
if (*part_3).type_0 == 40i32 || (*part_3).type_0 == 41i32 || (*part_3).type_0 == 50i32 {
let field_0: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field(
mimeparser,
b"Chat-Duration\x00" as *const u8 as *const libc::c_char,
);
if !field_0.is_null() {
let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value);
if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 {
let part_mut = &mut mimeparser.parts[0];
part_mut.param.set_int(Param::Duration, duration_ms);
dc_param_set_int((*part_3).param, DC_PARAM_DURATION as i32, duration_ms);
}
}
}
@@ -332,7 +358,7 @@ pub unsafe fn dc_mimeparser_parse(
mimeparser,
b"Chat-Disposition-Notification-To\x00" as *const u8 as *const libc::c_char,
);
if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(mimeparser).is_some() {
if !dn_field.is_null() && !dc_mimeparser_get_last_nonmeta(mimeparser).is_null() {
let mut mb_list: *mut mailimf_mailbox_list = 0 as *mut mailimf_mailbox_list;
let mut index_0: size_t = 0i32 as size_t;
if mailimf_mailbox_list_parse(
@@ -356,9 +382,14 @@ pub unsafe fn dc_mimeparser_parse(
);
if !from_addr.is_null() {
if strcmp(from_addr, dn_to_addr) == 0i32 {
if let Some(part_4) = dc_mimeparser_get_last_nonmeta(mimeparser)
{
part_4.param.set_int(Param::WantsMdn, 1);
let part_4: *mut dc_mimepart_t =
dc_mimeparser_get_last_nonmeta(mimeparser);
if !part_4.is_null() {
dc_param_set_int(
(*part_4).param,
DC_PARAM_WANTS_MDN as i32,
1,
);
}
}
free(from_addr as *mut libc::c_void);
@@ -372,41 +403,52 @@ pub unsafe fn dc_mimeparser_parse(
}
}
/* Cleanup - and try to create at least an empty part if there are no parts yet */
if dc_mimeparser_get_last_nonmeta(mimeparser).is_none() && (*mimeparser).reports.is_empty() {
let mut part_5 = dc_mimepart_new();
part_5.type_0 = 10i32;
if dc_mimeparser_get_last_nonmeta(mimeparser).is_null()
&& carray_count((*mimeparser).reports) == 0i32 as libc::c_uint
{
let mut part_5: *mut dc_mimepart_t = dc_mimepart_new();
(*part_5).type_0 = 10i32;
if !(*mimeparser).subject.is_null() && 0 == (*mimeparser).is_send_by_messenger {
part_5.msg = dc_strdup((*mimeparser).subject)
(*part_5).msg = dc_strdup((*mimeparser).subject)
} else {
part_5.msg = dc_strdup(b"\x00" as *const u8 as *const libc::c_char)
(*part_5).msg = dc_strdup(b"\x00" as *const u8 as *const libc::c_char)
}
mimeparser.parts.push(part_5);
carray_add(
(*mimeparser).parts,
part_5 as *mut libc::c_void,
0 as *mut libc::c_uint,
);
};
}
/*******************************************************************************
* a MIME part
******************************************************************************/
unsafe fn dc_mimepart_new() -> dc_mimepart_t {
dc_mimepart_t {
type_0: 0,
is_meta: 0,
int_mimetype: 0,
msg: std::ptr::null_mut(),
msg_raw: std::ptr::null_mut(),
bytes: 0,
param: Params::new(),
}
unsafe fn dc_mimepart_new() -> *mut dc_mimepart_t {
let mut mimepart: *mut dc_mimepart_t;
mimepart = calloc(1, ::std::mem::size_of::<dc_mimepart_t>()) as *mut dc_mimepart_t;
assert!(!mimepart.is_null());
(*mimepart).type_0 = 0i32;
(*mimepart).param = dc_param_new();
mimepart
}
pub fn dc_mimeparser_get_last_nonmeta<'a>(
mimeparser: &'a mut dc_mimeparser_t,
) -> Option<&'a mut dc_mimepart_t> {
mimeparser
.parts
.iter_mut()
.rev()
.find(|part| part.is_meta == 0)
pub unsafe fn dc_mimeparser_get_last_nonmeta(mimeparser: &dc_mimeparser_t) -> *mut dc_mimepart_t {
if !(*mimeparser).parts.is_null() {
let mut i: libc::c_int;
let icnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int;
i = icnt - 1i32;
while i >= 0i32 {
let part: *mut dc_mimepart_t =
carray_get(mimeparser.parts, i as libc::c_uint) as *mut dc_mimepart_t;
if !part.is_null() && 0 == (*part).is_meta {
return part;
}
i -= 1
}
}
0 as *mut dc_mimepart_t
}
/*the result must be freed*/
@@ -619,21 +661,18 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
}
}
40 => {
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
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(),
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);
(*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,
0 as *mut libc::c_uint,
);
part.msg_raw = dc_strdup(part.msg);
mimeparser.parts.push(part);
any_part_added = 1i32;
(*mimeparser).decrypting_failed = 1i32
}
@@ -663,7 +702,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
b"disposition-notification\x00" as *const u8 as *const libc::c_char,
) == 0i32
{
(*mimeparser).reports.push(mime);
carray_add(
(*mimeparser).reports,
mime as *mut libc::c_void,
0 as *mut libc::c_uint,
);
} else {
any_part_added = dc_mimeparser_parse_mime_recursive(
mimeparser,
@@ -1090,7 +1133,8 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
mime: *mut mailmime,
) -> libc::c_int {
let mut current_block: u64;
let old_part_count = mimeparser.parts.len();
let mut part: *mut dc_mimepart_t = 0 as *mut dc_mimepart_t;
let old_part_count: libc::c_int = carray_count(mimeparser.parts) as libc::c_int;
let mime_type: libc::c_int;
let mime_data: *mut mailmime_data;
let file_suffix: *mut libc::c_char = 0 as *mut libc::c_char;
@@ -1102,7 +1146,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
/* must not be free()'d */
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
let mut decoded_data_bytes = 0;
let mut simplifier: Option<dc_simplify_t> = None;
let mut simplifier: *mut dc_simplify_t = 0 as *mut dc_simplify_t;
if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) {
mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime);
mime_data = (*mime).mm_data.mm_single;
@@ -1123,82 +1167,105 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
/* no always error - but no data */
match mime_type {
60 | 70 => {
if simplifier.is_none() {
simplifier = Some(dc_simplify_t::new());
}
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
let charset: *const libc::c_char =
mailmime_content_charset_get((*mime).mm_content_type);
if !charset.is_null()
&& strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char)
!= 0i32
&& strcmp(charset, b"UTF-8\x00" as *const u8 as *const libc::c_char)
!= 0i32
{
if let Some(encoding) = Charset::for_label(
CStr::from_ptr(charset).to_str().unwrap().as_bytes(),
) {
let data = std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes,
);
let (res, _, _) = encoding.decode(data);
if res.is_empty() {
/* no error - but nothing to add */
current_block = 8795901732489102124;
} else {
decoded_data_bytes = res.len();
decoded_data = res.as_ptr() as *const libc::c_char;
current_block = 17788412896529399552;
}
if simplifier.is_null() {
simplifier = dc_simplify_new();
if simplifier.is_null() {
current_block = 8795901732489102124;
} else {
warn!(
mimeparser.context,
0,
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
decoded_data_bytes as libc::c_int,
as_str(charset),
);
current_block = 17788412896529399552;
current_block = 13797916685926291137;
}
} else {
current_block = 17788412896529399552;
current_block = 13797916685926291137;
}
// TODO match on enums /rtn
match current_block {
8795901732489102124 => {}
_ => {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg: libc::c_int = (dc_mimeparser_lookup_optional_field(
&mimeparser,
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
) != 0 as *mut libc::c_void
as *mut mailimf_optional_field)
as libc::c_int;
let simplified_txt: *mut libc::c_char =
simplifier.unwrap().simplify(
decoded_data,
decoded_data_bytes as libc::c_int,
if mime_type == 70i32 { 1i32 } else { 0i32 },
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
let charset: *const libc::c_char =
mailmime_content_charset_get((*mime).mm_content_type);
if !charset.is_null()
&& strcmp(
charset,
b"utf-8\x00" as *const u8 as *const libc::c_char,
) != 0i32
&& strcmp(
charset,
b"UTF-8\x00" as *const u8 as *const libc::c_char,
) != 0i32
{
let mut part = dc_mimepart_new();
part.type_0 = 10i32;
part.int_mimetype = mime_type;
part.msg = simplified_txt;
part.msg_raw =
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
do_add_single_part(mimeparser, part);
if let Some(encoding) = Charset::for_label(
CStr::from_ptr(charset).to_str().unwrap().as_bytes(),
) {
let data = std::slice::from_raw_parts(
decoded_data as *const u8,
decoded_data_bytes,
);
let (res, _, _) = encoding.decode(data);
if res.is_empty() {
/* no error - but nothing to add */
current_block = 8795901732489102124;
} else {
decoded_data_bytes = res.len();
decoded_data = res.as_ptr() as *const libc::c_char;
current_block = 17788412896529399552;
}
} else {
warn!(
mimeparser.context,
0,
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
decoded_data_bytes as libc::c_int,
as_str(charset),
);
current_block = 17788412896529399552;
}
} else {
free(simplified_txt as *mut libc::c_void);
current_block = 17788412896529399552;
}
if 0 != simplifier.unwrap().is_forwarded {
(*mimeparser).is_forwarded = 1i32
match current_block {
8795901732489102124 => {}
_ => {
/* check header directly as is_send_by_messenger is not yet set up */
let is_msgrmsg: libc::c_int =
(dc_mimeparser_lookup_optional_field(
&mimeparser,
b"Chat-Version\x00" as *const u8
as *const libc::c_char,
) != 0 as *mut libc::c_void
as *mut mailimf_optional_field)
as libc::c_int;
let simplified_txt: *mut libc::c_char =
dc_simplify_simplify(
simplifier,
decoded_data,
decoded_data_bytes as libc::c_int,
if mime_type == 70i32 { 1i32 } else { 0i32 },
is_msgrmsg,
);
if !simplified_txt.is_null()
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
{
part = dc_mimepart_new();
(*part).type_0 = 10i32;
(*part).int_mimetype = mime_type;
(*part).msg = simplified_txt;
(*part).msg_raw = strndup(
decoded_data,
decoded_data_bytes as libc::c_ulong,
);
do_add_single_part(mimeparser, part);
part = 0 as *mut dc_mimepart_t
} else {
free(simplified_txt as *mut libc::c_void);
}
if 0 != (*simplifier).is_forwarded {
(*mimeparser).is_forwarded = 1i32
}
current_block = 10261677128829721533;
}
}
current_block = 10261677128829721533;
}
}
}
@@ -1277,8 +1344,9 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
}
if !filename_parts.is_empty() {
free(desired_filename as *mut libc::c_void);
let parts_c = CString::yolo(filename_parts);
desired_filename = dc_decode_ext_header(parts_c.as_ptr());
let parts_c = to_cstring(filename_parts);
desired_filename = dc_decode_ext_header(parts_c);
free(parts_c as *mut _);
}
if desired_filename.is_null() {
let param = mailmime_find_ct_parameter(
@@ -1324,11 +1392,11 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
4,
) == 0i32
{
(*mimeparser).location_kml = Some(dc_kml_parse(
(*mimeparser).location_kml = dc_kml_parse(
(*mimeparser).context,
decoded_data,
decoded_data_bytes,
));
);
current_block = 8795901732489102124;
} else if strncmp(
desired_filename,
@@ -1343,11 +1411,11 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
4,
) == 0i32
{
(*mimeparser).message_kml = Some(dc_kml_parse(
(*mimeparser).message_kml = dc_kml_parse(
(*mimeparser).context,
decoded_data,
decoded_data_bytes,
));
);
current_block = 8795901732489102124;
} else {
dc_replace_bad_utf8_chars(desired_filename);
@@ -1377,22 +1445,23 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
}
}
/* add object? (we do not add all objects, eg. signatures etc. are ignored) */
dc_simplify_unref(simplifier);
if !transfer_decoding_buffer.is_null() {
mmap_string_unref(transfer_decoding_buffer);
}
free(file_suffix as *mut libc::c_void);
free(desired_filename as *mut libc::c_void);
dc_mimepart_unref(part);
free(raw_mime as *mut libc::c_void);
return if mimeparser.parts.len() > old_part_count {
1
return if carray_count((*mimeparser).parts) > old_part_count as libc::c_uint {
1i32
} else {
0
0i32
};
}
#[allow(non_snake_case)]
unsafe fn do_add_single_file_part(
parser: &mut dc_mimeparser_t,
parser: &dc_mimeparser_t,
msg_type: libc::c_int,
mime_type: libc::c_int,
raw_mime: *const libc::c_char,
@@ -1400,6 +1469,7 @@ unsafe fn do_add_single_file_part(
decoded_data_bytes: size_t,
desired_filename: *const libc::c_char,
) {
let mut part: *mut dc_mimepart_t = 0 as *mut dc_mimepart_t;
let pathNfilename: *mut libc::c_char;
/* create a free file name to use */
pathNfilename = dc_get_fine_pathNfilename(
@@ -1416,38 +1486,44 @@ unsafe fn do_add_single_file_part(
decoded_data_bytes,
) == 0i32)
{
let mut part = dc_mimepart_new();
part.type_0 = msg_type;
part.int_mimetype = mime_type;
part.bytes = decoded_data_bytes as libc::c_int;
part.param.set(Param::File, as_str(pathNfilename));
part.param.set(Param::MimeType, as_str(raw_mime));
if mime_type == 80 {
let mut w = 0;
let mut h = 0;
part = dc_mimepart_new();
(*part).type_0 = msg_type;
(*part).int_mimetype = mime_type;
(*part).bytes = decoded_data_bytes as libc::c_int;
dc_param_set((*part).param, DC_PARAM_FILE as i32, pathNfilename);
dc_param_set((*part).param, DC_PARAM_MIMETYPE as i32, raw_mime);
if mime_type == 80i32 {
let mut w: uint32_t = 0i32 as uint32_t;
let mut h: uint32_t = 0i32 as uint32_t;
if 0 != dc_get_filemeta(
decoded_data as *const libc::c_void,
decoded_data_bytes,
&mut w,
&mut h,
) {
part.param.set_int(Param::Width, w as i32);
part.param.set_int(Param::Height, h as i32);
dc_param_set_int((*part).param, DC_PARAM_WIDTH as i32, w as int32_t);
dc_param_set_int((*part).param, DC_PARAM_HEIGHT as i32, h as int32_t);
}
}
do_add_single_part(parser, part);
part = 0 as *mut dc_mimepart_t
}
}
free(pathNfilename as *mut libc::c_void);
dc_mimepart_unref(part);
}
unsafe fn do_add_single_part(parser: &mut dc_mimeparser_t, mut part: dc_mimepart_t) {
unsafe fn do_add_single_part(parser: &dc_mimeparser_t, part: *mut dc_mimepart_t) {
if 0 != (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 {
part.param.set_int(Param::GuranteeE2ee, 1);
dc_param_set_int((*part).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
} else if 0 != (*parser).e2ee_helper.encrypted {
part.param.set_int(Param::ErroneousE2ee, 0x2);
dc_param_set_int((*part).param, DC_PARAM_ERRONEOUS_E2EE as i32, 0x2);
}
parser.parts.push(part);
carray_add(
(*parser).parts,
part as *mut libc::c_void,
0 as *mut libc::c_uint,
);
}
// TODO should return bool /rtn
@@ -1691,7 +1767,6 @@ pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet<
/* ******************************************************************************
* low-level-tools for getting a list of all recipients
******************************************************************************/
#[allow(non_snake_case)]
unsafe fn mailimf_get_recipients__add_addr(
recipients: &mut HashSet<String>,
mb: *mut mailimf_mailbox,
@@ -1734,20 +1809,27 @@ pub unsafe fn mailimf_find_field(
}
pub unsafe fn dc_mimeparser_repl_msg_by_error(
mimeparser: &mut dc_mimeparser_t,
mimeparser: &dc_mimeparser_t,
error_msg: *const libc::c_char,
) {
if mimeparser.parts.is_empty() {
let mut part: *mut dc_mimepart_t;
let mut i: libc::c_int;
if (*mimeparser).parts.is_null() || carray_count((*mimeparser).parts) <= 0i32 as libc::c_uint {
return;
}
let part = &mut mimeparser.parts[0];
part.type_0 = 10i32;
free(part.msg as *mut libc::c_void);
part.msg = dc_mprintf(b"[%s]\x00" as *const u8 as *const libc::c_char, error_msg);
for part in mimeparser.parts.drain(1..) {
dc_mimepart_unref(part);
part = carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t;
(*part).type_0 = 10i32;
free((*part).msg as *mut libc::c_void);
(*part).msg = dc_mprintf(b"[%s]\x00" as *const u8 as *const libc::c_char, error_msg);
i = 1i32;
while (i as libc::c_uint) < carray_count((*mimeparser).parts) {
part = carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t;
if !part.is_null() {
dc_mimepart_unref(part);
}
i += 1
}
assert_eq!(mimeparser.parts.len(), 1);
carray_set_size((*mimeparser).parts, 1i32 as libc::c_uint);
}
/*the result is a pointer to mime, must not be freed*/

View File

@@ -2,7 +2,6 @@ use crate::constants::*;
use crate::context::*;
use crate::dc_job::*;
use crate::dc_msg::*;
use crate::param::Params;
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
if context
@@ -32,7 +31,13 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
// 1 = dc message, 2 = reply to dc message
if 0 != (*msg).is_dc_message {
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
dc_job_add(
context,
200,
(*msg).id as libc::c_int,
0 as *const libc::c_char,
0,
);
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
}

File diff suppressed because it is too large Load Diff

439
src/dc_param.rs Normal file
View File

@@ -0,0 +1,439 @@
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
/// for msgs and jobs
pub const DC_PARAM_FILE: char = 'f'; // string
/// for msgs
pub const DC_PARAM_WIDTH: char = 'w'; // int
/// for msgs
pub const DC_PARAM_HEIGHT: char = 'h'; // int
/// for msgs
pub const DC_PARAM_DURATION: char = 'd'; // int
/// for msgs
pub const DC_PARAM_MIMETYPE: char = 'm'; // string
/// for msgs: incoming: message is encryoted, outgoing: guarantee E2EE or the message is not send
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c'; // int (bool?)
/// for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e'; // int
/// for msgs: force unencrypted message, either DC_FP_ADD_AUTOCRYPT_HEADER (1), DC_FP_NO_AUTOCRYPT_HEADER (2) or 0
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u'; // int (bool?)
/// for msgs: an incoming message which requests a MDN (aka read receipt)
pub const DC_PARAM_WANTS_MDN: char = 'r'; // int (bool?)
/// for msgs
pub const DC_PARAM_FORWARDED: char = 'a'; // int (bool?)
/// for msgs
pub const DC_PARAM_CMD: char = 'S'; // int
/// for msgs
pub const DC_PARAM_CMD_ARG: char = 'E'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG2: char = 'F'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG3: char = 'G'; // string
/// for msgs
pub const DC_PARAM_CMD_ARG4: char = 'H'; // string
/// for msgs
pub const DC_PARAM_ERROR: char = 'L'; // string
/// for msgs in PREPARING: space-separated list of message IDs of forwarded copies
pub const DC_PARAM_PREP_FORWARDS: char = 'P'; // string
/// for msgs
pub const DC_PARAM_SET_LATITUDE: char = 'l'; // float
/// for msgs
pub const DC_PARAM_SET_LONGITUDE: char = 'n'; // float
/// for jobs
pub const DC_PARAM_SERVER_FOLDER: char = 'Z'; // string
/// for jobs
pub const DC_PARAM_SERVER_UID: char = 'z'; // int
/// for jobs
pub const DC_PARAM_ALSO_MOVE: char = 'M'; // int (bool?)
/// for jobs: space-separated list of message recipients
pub const DC_PARAM_RECIPIENTS: char = 'R'; // stringap
/// for groups
pub const DC_PARAM_UNPROMOTED: char = 'U'; // int (bool?)
/// for groups and contacts
pub const DC_PARAM_PROFILE_IMAGE: char = 'i'; // string (bytes?)
/// for chats
pub const DC_PARAM_SELFTALK: char = 'K';
// missing: 's', 'x', 'g'
// values for DC_PARAM_FORCE_PLAINTEXT
pub const DC_FP_ADD_AUTOCRYPT_HEADER: u8 = 1;
pub const DC_FP_NO_AUTOCRYPT_HEADER: u8 = 2;
/// An object for handling key=value parameter lists; for the key, currently only
/// a single character is allowed.
///
/// The object is used eg. by Chat or dc_msg_t, for readable parameter names,
/// these classes define some DC_PARAM_* constantats.
///
/// Only for library-internal use.
#[derive(Copy, Clone)]
#[repr(C)]
pub struct dc_param_t {
pub packed: *mut libc::c_char,
}
// values for DC_PARAM_FORCE_PLAINTEXT
/* user functions */
pub unsafe fn dc_param_exists(param: *mut dc_param_t, key: libc::c_int) -> libc::c_int {
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
if param.is_null() || key == 0i32 {
return 0i32;
}
return if !find_param((*param).packed, key, &mut p2).is_null() {
1i32
} else {
0i32
};
}
unsafe extern "C" fn find_param(
haystack: *mut libc::c_char,
key: libc::c_int,
ret_p2: *mut *mut libc::c_char,
) -> *mut libc::c_char {
let mut p1: *mut libc::c_char;
let mut p2: *mut libc::c_char;
p1 = haystack;
loop {
if p1.is_null() || *p1 as libc::c_int == 0i32 {
return 0 as *mut libc::c_char;
} else {
if *p1 as libc::c_int == key && *p1.offset(1isize) as libc::c_int == '=' as i32 {
break;
}
p1 = strchr(p1, '\n' as i32);
if !p1.is_null() {
p1 = p1.offset(1isize)
}
}
}
p2 = strchr(p1, '\n' as i32);
if p2.is_null() {
p2 = &mut *p1.offset(strlen(p1) as isize) as *mut libc::c_char
}
*ret_p2 = p2;
p1
}
/* the value may be an empty string, "def" is returned only if the value unset. The result must be free()'d in any case. */
pub unsafe fn dc_param_get(
param: *const dc_param_t,
key: libc::c_int,
def: *const libc::c_char,
) -> *mut libc::c_char {
let mut p1: *mut libc::c_char;
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
let bak: libc::c_char;
let ret: *mut libc::c_char;
if param.is_null() || key == 0i32 {
return if !def.is_null() {
dc_strdup(def)
} else {
0 as *mut libc::c_char
};
}
p1 = find_param((*param).packed, key, &mut p2);
if p1.is_null() {
return if !def.is_null() {
dc_strdup(def)
} else {
0 as *mut libc::c_char
};
}
p1 = p1.offset(2isize);
bak = *p2;
*p2 = 0i32 as libc::c_char;
ret = dc_strdup(p1);
dc_rtrim(ret);
*p2 = bak;
ret
}
pub unsafe fn dc_param_get_int(
param: *const dc_param_t,
key: libc::c_int,
def: int32_t,
) -> int32_t {
if param.is_null() || key == 0i32 {
return def;
}
let s = dc_param_get(param, key, 0 as *const libc::c_char);
if s.is_null() {
return def;
}
let ret = as_str(s).parse().unwrap_or_default();
free(s as *mut libc::c_void);
ret
}
/**
* Get value of a parameter.
*
* @memberof dc_param_t
* @param param Parameter object to query.
* @param key Key of the parameter to get, one of the DC_PARAM_* constants.
* @param def Value to return if the parameter is not set.
* @return The stored value or the default value.
*/
pub unsafe fn dc_param_get_float(
param: *const dc_param_t,
key: libc::c_int,
def: libc::c_double,
) -> libc::c_double {
if param.is_null() || key == 0 {
return def;
}
let str = dc_param_get(param, key, std::ptr::null());
if str.is_null() {
return def;
}
let ret = dc_atof(str) as libc::c_double;
free(str as *mut libc::c_void);
ret
}
pub unsafe fn dc_param_set(
mut param: *mut dc_param_t,
key: libc::c_int,
value: *const libc::c_char,
) {
let mut old1: *mut libc::c_char;
let mut old2: *mut libc::c_char;
let new1: *mut libc::c_char;
if param.is_null() || key == 0i32 {
return;
}
old1 = (*param).packed;
old2 = 0 as *mut libc::c_char;
if !old1.is_null() {
let p1: *mut libc::c_char;
let mut p2: *mut libc::c_char = 0 as *mut libc::c_char;
p1 = find_param(old1, key, &mut p2);
if !p1.is_null() {
*p1 = 0i32 as libc::c_char;
old2 = p2
} else if value.is_null() {
return;
}
}
dc_rtrim(old1);
dc_ltrim(old2);
if !old1.is_null() && *old1.offset(0isize) as libc::c_int == 0i32 {
old1 = 0 as *mut libc::c_char
}
if !old2.is_null() && *old2.offset(0isize) as libc::c_int == 0i32 {
old2 = 0 as *mut libc::c_char
}
if !value.is_null() {
new1 = dc_mprintf(
b"%s%s%c=%s%s%s\x00" as *const u8 as *const libc::c_char,
if !old1.is_null() {
old1
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old1.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
key,
value,
if !old2.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old2.is_null() {
old2
} else {
b"\x00" as *const u8 as *const libc::c_char
},
)
} else {
new1 = dc_mprintf(
b"%s%s%s\x00" as *const u8 as *const libc::c_char,
if !old1.is_null() {
old1
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old1.is_null() && !old2.is_null() {
b"\n\x00" as *const u8 as *const libc::c_char
} else {
b"\x00" as *const u8 as *const libc::c_char
},
if !old2.is_null() {
old2
} else {
b"\x00" as *const u8 as *const libc::c_char
},
)
}
free((*param).packed as *mut libc::c_void);
(*param).packed = new1;
}
pub unsafe fn dc_param_set_int(param: *mut dc_param_t, key: libc::c_int, value: int32_t) {
if param.is_null() || key == 0i32 {
return;
}
let value_str: *mut libc::c_char = dc_mprintf(
b"%i\x00" as *const u8 as *const libc::c_char,
value as libc::c_int,
);
if value_str.is_null() {
return;
}
dc_param_set(param, key, value_str);
free(value_str as *mut libc::c_void);
}
/* library-private */
pub unsafe fn dc_param_new() -> *mut dc_param_t {
let mut param: *mut dc_param_t;
param = calloc(1, ::std::mem::size_of::<dc_param_t>()) as *mut dc_param_t;
assert!(!param.is_null());
(*param).packed = calloc(1, 1) as *mut libc::c_char;
param
}
pub unsafe fn dc_param_empty(param: *mut dc_param_t) {
if param.is_null() {
return;
}
*(*param).packed.offset(0isize) = 0i32 as libc::c_char;
}
pub unsafe fn dc_param_unref(param: *mut dc_param_t) {
if param.is_null() {
return;
}
dc_param_empty(param);
free((*param).packed as *mut libc::c_void);
free(param as *mut libc::c_void);
}
pub unsafe fn dc_param_set_packed(mut param: *mut dc_param_t, packed: *const libc::c_char) {
if param.is_null() {
return;
}
dc_param_empty(param);
if !packed.is_null() {
free((*param).packed as *mut libc::c_void);
(*param).packed = dc_strdup(packed)
};
}
pub unsafe fn dc_param_set_urlencoded(mut param: *mut dc_param_t, urlencoded: *const libc::c_char) {
if param.is_null() {
return;
}
dc_param_empty(param);
if !urlencoded.is_null() {
free((*param).packed as *mut libc::c_void);
(*param).packed = dc_strdup(urlencoded);
dc_str_replace(
&mut (*param).packed,
b"&\x00" as *const u8 as *const libc::c_char,
b"\n\x00" as *const u8 as *const libc::c_char,
);
};
}
/**
* Set parameter to a float.
*
* @memberof dc_param_t
* @param param Parameter object to modify.
* @param key Key of the parameter to modify, one of the DC_PARAM_* constants.
* @param value Value to store for key.
* @return None.
*/
pub unsafe fn dc_param_set_float(param: *mut dc_param_t, key: libc::c_int, value: libc::c_double) {
if param.is_null() || key == 0 {
return;
}
let value_str = dc_ftoa(value);
if value_str.is_null() {
return;
}
dc_param_set(param, key, value_str);
free(value_str as *mut libc::c_void);
}
#[cfg(test)]
mod tests {
use super::*;
use std::ffi::CStr;
#[test]
fn test_dc_param() {
unsafe {
let p1: *mut dc_param_t = dc_param_new();
dc_param_set_packed(
p1,
b"\r\n\r\na=1\nb=2\n\nc = 3 \x00" as *const u8 as *const libc::c_char,
);
assert_eq!(dc_param_get_int(p1, 'a' as i32, 0), 1);
assert_eq!(dc_param_get_int(p1, 'b' as i32, 0), 2);
assert_eq!(dc_param_get_int(p1, 'c' as i32, 0), 0);
assert_eq!(dc_param_exists(p1, 'c' as i32), 0);
dc_param_set_int(p1, 'd' as i32, 4i32);
assert_eq!(dc_param_get_int(p1, 'd' as i32, 0), 4);
dc_param_empty(p1);
dc_param_set(
p1,
'a' as i32,
b"foo\x00" as *const u8 as *const libc::c_char,
);
dc_param_set_int(p1, 'b' as i32, 2i32);
dc_param_set(p1, 'c' as i32, 0 as *const libc::c_char);
dc_param_set_int(p1, 'd' as i32, 4i32);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"a=foo\nb=2\nd=4"
);
dc_param_set(p1, 'b' as i32, 0 as *const libc::c_char);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"a=foo\nd=4",
);
dc_param_set(p1, 'a' as i32, 0 as *const libc::c_char);
dc_param_set(p1, 'd' as i32, 0 as *const libc::c_char);
assert_eq!(
CStr::from_ptr((*p1).packed as *const libc::c_char)
.to_str()
.unwrap(),
"",
);
dc_param_unref(p1);
}
}
}

View File

@@ -1,13 +1,11 @@
use percent_encoding::percent_decode_str;
use crate::context::Context;
use crate::dc_chat::*;
use crate::dc_contact::*;
use crate::dc_lot::*;
use crate::dc_param::*;
use crate::dc_strencode::*;
use crate::dc_tools::*;
use crate::key::*;
use crate::param::*;
use crate::peerstate::*;
use crate::types::*;
use crate::x::*;
@@ -56,40 +54,36 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
if !fragment.is_null() {
*fragment = 0i32 as libc::c_char;
fragment = fragment.offset(1isize);
let param: Params = as_str(fragment).parse().expect("invalid params");
addr = param
.get(Param::Forwarded)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
let param: *mut dc_param_t = dc_param_new();
dc_param_set_urlencoded(param, fragment);
addr = dc_param_get(param, DC_PARAM_FORWARDED as i32, 0 as *const libc::c_char);
if !addr.is_null() {
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
let name_r = percent_decode_str(name_enc)
.decode_utf8()
.expect("invalid name");
name = name_r.strdup();
let mut urlencoded: *mut libc::c_char = dc_param_get(
param,
DC_PARAM_SET_LONGITUDE as i32,
0 as *const libc::c_char,
);
if !urlencoded.is_null() {
name = dc_urldecode(urlencoded);
dc_normalize_name(name);
free(urlencoded as *mut libc::c_void);
}
invitenumber = param
.get(Param::ProfileImage)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
auth = param
.get(Param::Auth)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
grpid = param
.get(Param::GroupId)
.map(|s| s.strdup())
.unwrap_or_else(|| std::ptr::null_mut());
invitenumber = dc_param_get(
param,
DC_PARAM_PROFILE_IMAGE as i32,
0 as *const libc::c_char,
);
auth = dc_param_get(param, 's' as i32, 0 as *const libc::c_char);
grpid = dc_param_get(param, 'x' as i32, 0 as *const libc::c_char);
if !grpid.is_null() {
if let Some(grpname_enc) = param.get(Param::GroupName) {
let grpname_r = percent_decode_str(grpname_enc)
.decode_utf8()
.expect("invalid groupname");
grpname = grpname_r.strdup();
urlencoded = dc_param_get(param, 'g' as i32, 0 as *const libc::c_char);
if !urlencoded.is_null() {
grpname = dc_urldecode(urlencoded);
free(urlencoded as *mut libc::c_void);
}
}
}
dc_param_unref(param);
}
fingerprint = dc_normalize_fingerprint_c(payload);
current_block = 5023038348526654800;
@@ -152,8 +146,11 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
) == 0i32
{
let lines = dc_split_into_lines(qr);
for &key in &lines {
let lines: *mut carray = dc_split_into_lines(qr);
let mut i: libc::c_int = 0i32;
while (i as libc::c_uint) < carray_count(lines) {
let key: *mut libc::c_char =
carray_get(lines, i as libc::c_uint) as *mut libc::c_char;
dc_trim(key);
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
if !value.is_null() {
@@ -189,6 +186,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
dc_normalize_name(name);
}
}
i += 1
}
dc_free_splitted_lines(lines);
}
@@ -249,7 +247,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
if let Some(peerstate) = peerstate {
(*qr_parsed).state = 210i32;
let addr_ptr = if let Some(ref addr) = peerstate.addr {
addr.strdup()
to_cstring(addr)
} else {
std::ptr::null()
};

File diff suppressed because it is too large Load Diff

View File

@@ -12,13 +12,10 @@ pub struct dc_saxparser_t {
}
/* len is only informational, text is already null-terminated */
#[allow(non_camel_case_types)]
pub type dc_saxparser_text_cb_t =
Option<unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: libc::c_int) -> ()>;
#[allow(non_camel_case_types)]
pub type dc_saxparser_endtag_cb_t =
Option<unsafe fn(_: *mut libc::c_void, _: *const libc::c_char) -> ()>;
#[allow(non_camel_case_types)]
pub type dc_saxparser_starttag_cb_t = Option<
unsafe fn(_: *mut libc::c_void, _: *const libc::c_char, _: *mut *mut libc::c_char) -> (),
>;
@@ -495,19 +492,19 @@ unsafe fn xml_decode(mut s: *mut libc::c_char, type_0: libc::c_char) -> *mut lib
&& (type_0 as libc::c_int == '&' as i32 || type_0 as libc::c_int == ' ' as i32)
{
b = 0;
while !S_ENT[b as usize].is_null()
while !s_ent[b as usize].is_null()
&& 0 != strncmp(
s.offset(1isize),
S_ENT[b as usize],
strlen(S_ENT[b as usize]),
s_ent[b as usize],
strlen(s_ent[b as usize]),
)
{
b += 2;
}
let fresh5 = b;
b = b + 1;
if !S_ENT[fresh5 as usize].is_null() {
c = strlen(S_ENT[b as usize]) as isize;
if !s_ent[fresh5 as usize].is_null() {
c = strlen(s_ent[b as usize]) as isize;
e = strchr(s, ';' as i32);
if c - 1 > e.wrapping_offset_from(s) as isize {
d = s.wrapping_offset_from(r) as isize;
@@ -535,7 +532,7 @@ unsafe fn xml_decode(mut s: *mut libc::c_char, type_0: libc::c_char) -> *mut lib
e.offset(1isize) as *const libc::c_void,
strlen(e),
);
strncpy(s, S_ENT[b as usize], c as usize);
strncpy(s, s_ent[b as usize], c as usize);
} else {
s = s.offset(1isize)
}
@@ -569,7 +566,7 @@ NB: SAX = Simple API for XML */
/* ******************************************************************************
* Decoding text
******************************************************************************/
static mut S_ENT: [*const libc::c_char; 508] = [
static mut s_ent: [*const libc::c_char; 508] = [
b"lt;\x00" as *const u8 as *const libc::c_char,
b"<\x00" as *const u8 as *const libc::c_char,
b"gt;\x00" as *const u8 as *const libc::c_char,

View File

@@ -1,10 +1,8 @@
use std::ffi::CString;
use mmime::mailimf_types::*;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use crate::aheader::EncryptPreference;
use crate::constants::*;
use crate::constants::Event;
use crate::context::Context;
use crate::dc_array::*;
use crate::dc_chat::*;
@@ -14,14 +12,14 @@ use crate::dc_e2ee::*;
use crate::dc_lot::*;
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::param::*;
use crate::peerstate::*;
use crate::stock::StockMessage;
use crate::types::*;
use crate::x::*;
@@ -40,17 +38,17 @@ pub unsafe fn dc_get_securejoin_qr(
let mut chat = 0 as *mut Chat;
let mut group_name = 0 as *mut libc::c_char;
let mut group_name_urlencoded = 0 as *mut libc::c_char;
let mut qr: Option<String> = None;
let mut qr = None;
dc_ensure_secret_key_exists(context);
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
if invitenumber.is_null() {
invitenumber = dc_create_id().strdup();
invitenumber = dc_create_id();
dc_token_save(context, DC_TOKEN_INVITENUMBER, group_chat_id, invitenumber);
}
auth = dc_token_lookup(context, DC_TOKEN_AUTH, group_chat_id);
if auth.is_null() {
auth = dc_create_id().strdup();
auth = dc_create_id();
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth);
}
let self_addr = context.sql.get_config(context, "configured_addr");
@@ -64,7 +62,7 @@ pub unsafe fn dc_get_securejoin_qr(
free(group_name_urlencoded as *mut libc::c_void);
if let Some(qr) = qr {
qr.strdup()
to_cstring(qr)
} else {
std::ptr::null_mut()
}
@@ -87,8 +85,8 @@ pub unsafe fn dc_get_securejoin_qr(
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
}
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
let self_addr_urlencoded = utf8_percent_encode(&self_addr, DEFAULT_ENCODE_SET).to_string();
let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string();
qr = if 0 != group_chat_id {
chat = dc_get_chat(context, group_chat_id);
@@ -263,36 +261,29 @@ unsafe fn send_handshake_msg(
grpid: *const libc::c_char,
) {
let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context);
(*msg).type_0 = Viewtype::Text;
(*msg).type_0 = 10i32;
(*msg).text = dc_mprintf(
b"Secure-Join: %s\x00" as *const u8 as *const libc::c_char,
step,
);
(*msg).hidden = 1;
(*msg).param.set_int(Param::Cmd, 7);
if step.is_null() {
(*msg).param.remove(Param::Arg);
} else {
(*msg).param.set(Param::Arg, as_str(step));
}
(*msg).hidden = 1i32;
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 7);
dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, step);
if !param2.is_null() {
(*msg).param.set(Param::Arg2, as_str(param2));
dc_param_set((*msg).param, DC_PARAM_CMD_ARG2 as i32, param2);
}
if !fingerprint.is_null() {
(*msg).param.set(Param::Arg3, as_str(fingerprint));
dc_param_set((*msg).param, DC_PARAM_CMD_ARG3 as i32, fingerprint);
}
if !grpid.is_null() {
(*msg).param.set(Param::Arg4, as_str(grpid));
dc_param_set((*msg).param, DC_PARAM_CMD_ARG4 as i32, grpid);
}
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
{
(*msg).param.set_int(
Param::ForcePlaintext,
ForcePlaintext::AddAutocryptHeader as i32,
);
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 1);
} else {
(*msg).param.set_int(Param::GuranteeE2ee, 1);
dc_param_set_int((*msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
}
dc_send_msg(context, contact_chat_id, msg);
dc_msg_unref(msg);
@@ -806,21 +797,22 @@ 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 = CString::new(context.stock_string_repl_str(
StockMessage::ContactVerified,
let msg: *mut libc::c_char = dc_stock_str_repl_string(
context,
35i32,
if !contact.is_null() {
as_str((*contact).addr)
(*contact).addr
} else {
"?"
b"?\x00" as *const u8 as *const libc::c_char
},
))
.unwrap();
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
);
dc_add_device_msg(context, contact_chat_id, msg);
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);
}
@@ -848,17 +840,18 @@ 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 = context.stock_string_repl_str(
StockMessage::ContactNotVerified,
let msg: *mut libc::c_char = dc_stock_str_repl_string(
context,
36i32,
if !contact.is_null() {
as_str((*contact).addr)
(*contact).addr
} else {
"?"
b"?\x00" as *const u8 as *const libc::c_char
},
);
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_add_device_msg(context, contact_chat_id, msg);
error!(context, 0, "{} ({})", as_str(msg), to_string(details),);
free(msg as *mut libc::c_void);
dc_contact_unref(contact);
}
@@ -946,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 peeraddr: &str = match peerstate.addr {
Some(ref addr) => &addr,
None => "",
let c_addr_ptr = if let Some(ref addr) = peerstate.addr {
to_cstring(addr)
} else {
std::ptr::null_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());
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 _);
context.call_cb(
Event::CHAT_MODIFIED,
contact_chat_id as uintptr_t,

View File

@@ -1,5 +1,6 @@
use crate::dc_dehtml::*;
use crate::dc_tools::*;
use crate::types::*;
use crate::x::*;
#[derive(Copy, Clone)]
@@ -10,210 +11,235 @@ pub struct dc_simplify_t {
pub is_cut_at_end: libc::c_int,
}
impl dc_simplify_t {
pub fn new() -> Self {
dc_simplify_t {
is_forwarded: 0,
is_cut_at_begin: 0,
is_cut_at_end: 0,
}
}
pub unsafe fn dc_simplify_new() -> *mut dc_simplify_t {
let simplify: *mut dc_simplify_t;
simplify = calloc(1, ::std::mem::size_of::<dc_simplify_t>()) as *mut dc_simplify_t;
assert!(!simplify.is_null());
/// Simplify and normalise text: Remove quotes, signatures, unnecessary
/// lineends etc.
/// The data returned from simplify() must be free()'d when no longer used.
pub unsafe fn simplify(
&mut self,
in_unterminated: *const libc::c_char,
in_bytes: libc::c_int,
is_html: libc::c_int,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* create a copy of the given buffer */
let mut out: *mut libc::c_char;
let mut temp: *mut libc::c_char;
self.is_forwarded = 0i32;
self.is_cut_at_begin = 0i32;
self.is_cut_at_end = 0i32;
out = strndup(
in_unterminated as *mut libc::c_char,
in_bytes as libc::c_ulong,
);
if out.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if 0 != is_html {
temp = dc_dehtml(out);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
}
dc_remove_cr_chars(out);
temp = self.simplify_plain_text(out, is_msgrmsg);
simplify
}
pub unsafe fn dc_simplify_unref(simplify: *mut dc_simplify_t) {
if simplify.is_null() {
return;
}
free(simplify as *mut libc::c_void);
}
/* Simplify and normalise text: Remove quotes, signatures, unnecessary
lineends etc.
The data returned from Simplify() must be free()'d when no longer used, private */
pub unsafe fn dc_simplify_simplify(
mut simplify: *mut dc_simplify_t,
in_unterminated: *const libc::c_char,
in_bytes: libc::c_int,
is_html: libc::c_int,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* create a copy of the given buffer */
let mut out: *mut libc::c_char;
let mut temp: *mut libc::c_char;
if simplify.is_null() || in_unterminated.is_null() || in_bytes <= 0i32 {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
(*simplify).is_forwarded = 0i32;
(*simplify).is_cut_at_begin = 0i32;
(*simplify).is_cut_at_end = 0i32;
out = strndup(
in_unterminated as *mut libc::c_char,
in_bytes as libc::c_ulong,
);
if out.is_null() {
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
}
if 0 != is_html {
temp = dc_dehtml(out);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
dc_remove_cr_chars(out);
out
}
dc_remove_cr_chars(out);
temp = dc_simplify_simplify_plain_text(simplify, out, is_msgrmsg);
if !temp.is_null() {
free(out as *mut libc::c_void);
out = temp
}
dc_remove_cr_chars(out);
/**
* Simplify Plain Text
*/
#[allow(non_snake_case)]
unsafe fn simplify_plain_text(
&mut self,
buf_terminated: *const libc::c_char,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* This function ...
... removes all text after the line `-- ` (footer mark)
... removes full quotes at the beginning and at the end of the text -
these are all lines starting with the character `>`
... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
/* split the given buffer into lines */
let lines = dc_split_into_lines(buf_terminated);
let mut l_first: usize = 0;
let mut l_last = lines.len();
let mut line: *mut libc::c_char;
let mut footer_mark: libc::c_int = 0i32;
for l in l_first..l_last {
line = lines[l];
if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32
}
if strcmp(line, b"--\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"---\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32;
self.is_cut_at_end = 1i32
}
if 0 != footer_mark {
l_last = l;
/* done */
out
}
/**
* Simplify Plain Text
*/
unsafe fn dc_simplify_simplify_plain_text(
mut simplify: *mut dc_simplify_t,
buf_terminated: *const libc::c_char,
is_msgrmsg: libc::c_int,
) -> *mut libc::c_char {
/* This function ...
... removes all text after the line `-- ` (footer mark)
... removes full quotes at the beginning and at the end of the text -
these are all lines starting with the character `>`
... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */
/* split the given buffer into lines */
let lines: *mut carray = dc_split_into_lines(buf_terminated);
let mut l: libc::c_int;
let mut l_first: libc::c_int = 0i32;
/* if l_last is -1, there are no lines */
let mut l_last: libc::c_int =
carray_count(lines).wrapping_sub(1i32 as libc::c_uint) as libc::c_int;
let mut line: *mut libc::c_char;
let mut footer_mark: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32
}
if strcmp(line, b"--\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"---\x00" as *const u8 as *const libc::c_char) == 0i32
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
{
footer_mark = 1i32;
(*simplify).is_cut_at_end = 1i32
}
if 0 != footer_mark {
l_last = l - 1i32;
/* done */
break;
} else {
l += 1
}
}
if l_last - l_first + 1i32 >= 3i32 {
let line0: *mut libc::c_char =
carray_get(lines, l_first as libc::c_uint) as *mut libc::c_char;
let line1: *mut libc::c_char =
carray_get(lines, (l_first + 1i32) as libc::c_uint) as *mut libc::c_char;
let line2: *mut libc::c_char =
carray_get(lines, (l_first + 2i32) as libc::c_uint) as *mut libc::c_char;
if strcmp(
line0,
b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char,
) == 0i32
&& strncmp(line1, b"From: \x00" as *const u8 as *const libc::c_char, 6) == 0i32
&& *line2.offset(0isize) as libc::c_int == 0i32
{
(*simplify).is_forwarded = 1i32;
l_first += 3i32
}
}
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
{
l_last = l - 1i32;
(*simplify).is_cut_at_end = 1i32;
/* done */
break;
} else {
l += 1
}
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine: libc::c_int = -1i32;
l = l_last;
while l >= l_first {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_plain_quote(line) {
l_lastQuotedLine = l
} else if !is_empty_line(line) {
break;
}
l -= 1
}
if l_last > l_first + 2 {
let line0: *mut libc::c_char = lines[l_first];
let line1: *mut libc::c_char = lines[l_first + 1];
let line2: *mut libc::c_char = lines[l_first + 2];
if strcmp(
line0,
b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char,
) == 0i32
&& strncmp(line1, b"From: \x00" as *const u8 as *const libc::c_char, 6) == 0i32
&& *line2.offset(0isize) as libc::c_int == 0i32
{
self.is_forwarded = 1i32;
l_first += 3
if l_lastQuotedLine != -1i32 {
l_last = l_lastQuotedLine - 1i32;
(*simplify).is_cut_at_end = 1i32;
if l_last > 0i32 {
if is_empty_line(carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char) {
l_last -= 1
}
}
if l_last > 0i32 {
line = carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char;
if is_quoted_headline(line) {
l_last -= 1
}
}
}
for l in l_first..l_last {
line = lines[l];
if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
{
l_last = l;
self.is_cut_at_end = 1i32;
/* done */
break;
}
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine = None;
for l in (l_first..l_last).rev() {
line = lines[l];
if is_plain_quote(line) {
l_lastQuotedLine = Some(l)
} else if !is_empty_line(line) {
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine_0: libc::c_int = -1i32;
let mut hasQuotedHeadline: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_plain_quote(line) {
l_lastQuotedLine_0 = l
} else if !is_empty_line(line) {
if is_quoted_headline(line) && 0 == hasQuotedHeadline && l_lastQuotedLine_0 == -1i32
{
hasQuotedHeadline = 1i32
} else {
/* non-quoting line found */
break;
}
}
if l_lastQuotedLine.is_some() {
l_last = l_lastQuotedLine.unwrap();
self.is_cut_at_end = 1i32;
if l_last > 1 {
if is_empty_line(lines[l_last - 1]) {
l_last -= 1
}
}
if l_last > 1 {
line = lines[l_last - 1];
if is_quoted_headline(line) {
l_last -= 1
}
}
}
l += 1
}
if 0 == is_msgrmsg {
let mut l_lastQuotedLine_0 = None;
let mut hasQuotedHeadline = 0;
for l in l_first..l_last {
line = lines[l];
if is_plain_quote(line) {
l_lastQuotedLine_0 = Some(l)
} else if !is_empty_line(line) {
if is_quoted_headline(line)
&& 0 == hasQuotedHeadline
&& l_lastQuotedLine_0.is_none()
{
hasQuotedHeadline = 1i32
} else {
/* non-quoting line found */
break;
}
}
}
if l_lastQuotedLine_0.is_some() {
l_first = l_lastQuotedLine_0.unwrap() + 1;
self.is_cut_at_begin = 1i32
}
if l_lastQuotedLine_0 != -1i32 {
l_first = l_lastQuotedLine_0 + 1i32;
(*simplify).is_cut_at_begin = 1i32
}
/* re-create buffer from the remaining lines */
let mut ret = String::new();
if 0 != self.is_cut_at_begin {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
for l in l_first..l_last {
line = lines[l];
if is_empty_line(line) {
pending_linebreaks += 1
} else {
if 0 != content_lines_added {
if pending_linebreaks > 2i32 {
pending_linebreaks = 2i32
}
while 0 != pending_linebreaks {
ret += "\n";
pending_linebreaks -= 1
}
}
// the incoming message might contain invalid UTF8
ret += &to_string_lossy(line);
content_lines_added += 1;
pending_linebreaks = 1i32
}
}
if 0 != self.is_cut_at_end && (0 == self.is_cut_at_begin || 0 != content_lines_added) {
ret += " [...]";
}
dc_free_splitted_lines(lines);
ret.strdup()
}
/* re-create buffer from the remaining lines */
let mut ret = String::new();
if 0 != (*simplify).is_cut_at_begin {
ret += "[...]";
}
/* we write empty lines only in case and non-empty line follows */
let mut pending_linebreaks: libc::c_int = 0i32;
let mut content_lines_added: libc::c_int = 0i32;
l = l_first;
while l <= l_last {
line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char;
if is_empty_line(line) {
pending_linebreaks += 1
} else {
if 0 != content_lines_added {
if pending_linebreaks > 2i32 {
pending_linebreaks = 2i32
}
while 0 != pending_linebreaks {
ret += "\n";
pending_linebreaks -= 1
}
}
// the incoming message might contain invalid UTF8
ret += &to_string_lossy(line);
content_lines_added += 1;
pending_linebreaks = 1i32
}
l += 1
}
if 0 != (*simplify).is_cut_at_end
&& (0 == (*simplify).is_cut_at_begin || 0 != content_lines_added)
{
ret += " [...]";
}
dc_free_splitted_lines(lines);
to_cstring(ret)
}
/**
@@ -264,11 +290,11 @@ mod tests {
#[test]
fn test_simplify_trim() {
unsafe {
let mut simplify = dc_simplify_t::new();
let simplify: *mut dc_simplify_t = dc_simplify_new();
let html: *const libc::c_char =
b"\r\r\nline1<br>\r\n\r\n\r\rline2\n\r\x00" as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -278,17 +304,18 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_parse_href() {
unsafe {
let mut simplify = dc_simplify_t::new();
let simplify: *mut dc_simplify_t = dc_simplify_new();
let html: *const libc::c_char =
b"<a href=url>text</a\x00" as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -298,18 +325,19 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_bold_text() {
unsafe {
let mut simplify = dc_simplify_t::new();
let simplify: *mut dc_simplify_t = dc_simplify_new();
let html: *const libc::c_char =
b"<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>\x00"
as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
CStr::from_ptr(plain as *const libc::c_char)
@@ -319,18 +347,19 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
#[test]
fn test_simplify_html_encoded() {
unsafe {
let mut simplify = dc_simplify_t::new();
let simplify: *mut dc_simplify_t = dc_simplify_new();
let html: *const libc::c_char =
b"&lt;&gt;&quot;&apos;&amp; &auml;&Auml;&ouml;&Ouml;&uuml;&Uuml;&szlig; foo&AElig;&ccedil;&Ccedil; &diams;&noent;&lrm;&rlm;&zwnj;&zwj;\x00"
as *const u8 as *const libc::c_char;
let plain: *mut libc::c_char =
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
assert_eq!(
strcmp(plain,
@@ -340,6 +369,7 @@ mod tests {
);
free(plain as *mut libc::c_void);
dc_simplify_unref(simplify);
}
}
}

338
src/dc_stock.rs Normal file
View File

@@ -0,0 +1,338 @@
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

@@ -1,4 +1,4 @@
use std::ffi::{CStr, CString};
use std::ffi::CStr;
use charset::Charset;
use mmime::mailmime_decode::*;
@@ -64,11 +64,11 @@ pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char
* URL encoding and decoding, RFC 3986
******************************************************************************/
unsafe fn int_2_uppercase_hex(code: libc::c_char) -> libc::c_char {
static mut HEX: [libc::c_char; 17] = [
static mut hex: [libc::c_char; 17] = [
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 65, 66, 67, 68, 69, 70, 0,
];
HEX[(code as libc::c_int & 15i32) as usize]
hex[(code as libc::c_int & 15i32) as usize]
}
pub unsafe fn dc_urldecode(to_decode: *const libc::c_char) -> *mut libc::c_char {
@@ -378,7 +378,7 @@ pub unsafe fn dc_encode_modified_utf7(
if 0 != bitstogo {
let fresh8 = dst;
dst = dst.offset(1);
*fresh8 = BASE64CHARS
*fresh8 = base64chars
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
}
let fresh9 = dst;
@@ -449,7 +449,7 @@ pub unsafe fn dc_encode_modified_utf7(
bitstogo = bitstogo.wrapping_sub(6i32 as libc::c_uint);
let fresh14 = dst;
dst = dst.offset(1);
*fresh14 = BASE64CHARS[(if 0 != bitstogo {
*fresh14 = base64chars[(if 0 != bitstogo {
bitbuf >> bitstogo
} else {
bitbuf
@@ -465,7 +465,7 @@ pub unsafe fn dc_encode_modified_utf7(
if 0 != bitstogo {
let fresh15 = dst;
dst = dst.offset(1);
*fresh15 = BASE64CHARS
*fresh15 = base64chars
[(bitbuf << (6i32 as libc::c_uint).wrapping_sub(bitstogo) & 0x3f) as usize]
}
let fresh16 = dst;
@@ -482,7 +482,7 @@ pub unsafe fn dc_encode_modified_utf7(
******************************************************************************/
// UTF7 modified base64 alphabet
static mut BASE64CHARS: [libc::c_char; 65] = [
static mut base64chars: [libc::c_char; 65] = [
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 44, 0,
@@ -517,7 +517,7 @@ pub unsafe fn dc_decode_modified_utf7(
);
i = 0i32 as libc::c_uint;
while (i as libc::c_ulong) < ::std::mem::size_of::<[libc::c_char; 65]>() as libc::c_ulong {
base64[BASE64CHARS[i as usize] as libc::c_uint as usize] = i as libc::c_uchar;
base64[base64chars[i as usize] as libc::c_uint as usize] = i as libc::c_uchar;
i = i.wrapping_add(1)
}
while *src as libc::c_int != '\u{0}' as i32 {
@@ -710,8 +710,9 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
assert!(!cur.is_null());
let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur));
let raw = CString::yolo(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2]));
libc::memcpy(target as *mut _, raw as *const _, 4);
free(raw as *mut libc::c_void);
}
#[cfg(test)]

View File

@@ -3,7 +3,6 @@ use crate::dc_tools::*;
use crate::sql;
// Token namespaces
#[allow(non_camel_case_types)]
pub type dc_tokennamespc_t = usize;
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
@@ -42,7 +41,7 @@ pub fn dc_token_lookup(
params![namespc as i32, foreign_id as i32],
0,
)
.map(|s| unsafe { s.strdup() })
.map(|s| unsafe { to_cstring(s) })
.unwrap_or_else(|| std::ptr::null_mut())
}

View File

@@ -9,7 +9,6 @@ use rand::{thread_rng, Rng};
use crate::context::Context;
use crate::dc_array::*;
use crate::error::Error;
use crate::types::*;
use crate::x::*;
@@ -17,10 +16,11 @@ const ELLIPSE: &'static str = "[...]";
/* Some tools and enhancements to the used libraries, there should be
no references to Context and other "larger" classes here. */
// for carray etc.
/* ** library-private **********************************************************/
/* math tools */
pub fn dc_exactly_one_bit_set(v: libc::c_int) -> bool {
0 != v && 0 == v & v - 1i32
pub unsafe fn dc_exactly_one_bit_set(v: libc::c_int) -> libc::c_int {
return (0 != v && 0 == v & v - 1i32) as libc::c_int;
}
/* string tools */
@@ -177,13 +177,14 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) {
/* the result must be free()'d */
pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char {
to_string(in_0).to_lowercase().strdup()
to_cstring(to_string(in_0).to_lowercase())
}
pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) {
let raw = CString::yolo(to_string(in_0).to_lowercase());
assert_eq!(strlen(in_0), strlen(raw.as_ptr()));
memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0));
let raw = to_cstring(to_string(in_0).to_lowercase());
assert_eq!(strlen(in_0), strlen(raw));
memcpy(in_0 as *mut _, raw as *const _, strlen(in_0));
free(raw as *mut _);
}
pub unsafe fn dc_str_contains(
@@ -231,7 +232,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li
let buf = std::slice::from_raw_parts(buf, bytes);
let raw = hex::encode_upper(buf);
raw.strdup()
to_cstring(raw)
}
/* remove all \r characters from string */
@@ -261,9 +262,8 @@ pub unsafe fn dc_unify_lineends(buf: *mut libc::c_char) {
}
/* replace bad UTF-8 characters by sequences of `_` (to avoid problems in filenames, we do not use eg. `?`) the function is useful if strings are unexpectingly encoded eg. as ISO-8859-1 */
#[allow(non_snake_case)]
pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
let mut OK_TO_CONTINUE = true;
let current_block: u64;
if buf.is_null() {
return;
}
@@ -279,6 +279,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
ix = p1len;
's_36: loop {
if !(i < ix) {
current_block = 13550086250199790493;
break;
}
c = *p1.offset(i as isize) as libc::c_int;
@@ -291,7 +292,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
&& *p1.offset((i + 1i32) as isize) as libc::c_int & 0xa0i32 == 0xa0i32
{
/* U+d800 to U+dfff */
OK_TO_CONTINUE = false;
current_block = 2775201239069267972;
break;
} else if c & 0xf0i32 == 0xe0i32 {
n = 2i32
@@ -300,7 +301,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
} else {
//else if ((c & 0xFC) == 0xF8) { n=4; } /* 111110bb - not valid in https://tools.ietf.org/html/rfc3629 */
//else if ((c & 0xFE) == 0xFC) { n=5; } /* 1111110b - not valid in https://tools.ietf.org/html/rfc3629 */
OK_TO_CONTINUE = false;
current_block = 2775201239069267972;
break;
}
j = 0i32;
@@ -308,22 +309,25 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) {
/* n bytes matching 10bbbbbb follow ? */
i += 1;
if i == ix || *p1.offset(i as isize) as libc::c_int & 0xc0i32 != 0x80i32 {
OK_TO_CONTINUE = false;
current_block = 2775201239069267972;
break 's_36;
}
j += 1
}
i += 1
}
if OK_TO_CONTINUE == false {
while 0 != *p1 {
if *p1 as libc::c_int > 0x7fi32 {
*p1 = '_' as i32 as libc::c_uchar
match current_block {
13550086250199790493 => return,
_ => {
while 0 != *p1 {
if *p1 as libc::c_int > 0x7fi32 {
*p1 = '_' as i32 as libc::c_uchar
}
p1 = p1.offset(1isize)
}
p1 = p1.offset(1isize)
return;
}
return;
}
};
}
pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t {
@@ -354,7 +358,6 @@ pub fn dc_truncate_str(buf: &str, approx_chars: usize) -> Cow<str> {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_truncate_n_unwrap_str(
buf: *mut libc::c_char,
approx_characters: libc::c_int,
@@ -416,30 +419,47 @@ unsafe fn dc_utf8_strnlen(s: *const libc::c_char, n: size_t) -> size_t {
}
/* split string into lines*/
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> Vec<*mut libc::c_char> {
let mut lines = Vec::new();
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> *mut carray {
let lines: *mut carray = carray_new(1024i32 as libc::c_uint);
let mut line_chars = 0;
let mut p1: *const libc::c_char = buf_terminated;
let mut line_start: *const libc::c_char = p1;
let mut l_indx: libc::c_uint = 0i32 as libc::c_uint;
while 0 != *p1 {
if *p1 as libc::c_int == '\n' as i32 {
lines.push(strndup(line_start, line_chars));
carray_add(
lines,
strndup(line_start, line_chars) as *mut libc::c_void,
&mut l_indx,
);
p1 = p1.offset(1isize);
line_start = p1;
line_chars = 0;
} else {
p1 = p1.offset(1isize);
line_chars += 1;
line_chars = line_chars.wrapping_add(1)
}
}
lines.push(strndup(line_start, line_chars));
carray_add(
lines,
strndup(line_start, line_chars) as *mut libc::c_void,
&mut l_indx,
);
lines
}
pub unsafe fn dc_free_splitted_lines(lines: Vec<*mut libc::c_char>) {
for s in lines {
free(s as *mut libc::c_void);
}
pub unsafe fn dc_free_splitted_lines(lines: *mut carray) {
if !lines.is_null() {
let mut i: libc::c_int;
let cnt: libc::c_int = carray_count(lines) as libc::c_int;
i = 0i32;
while i < cnt {
free(carray_get(lines, i as libc::c_uint));
i += 1
}
carray_free(lines);
};
}
/* insert a break every n characters, the return must be free()'d */
@@ -508,7 +528,7 @@ pub unsafe fn dc_str_from_clist(
}
}
res.strdup()
to_cstring(res)
}
pub unsafe fn dc_str_to_clist(
@@ -547,7 +567,7 @@ pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
- being noticeable on a typical map
- harmonize together while being different enough
(therefore, we cannot just use random rgb colors :) */
static mut COLORS: [uint32_t; 16] = [
static mut colors: [uint32_t; 16] = [
0xe56555i32 as uint32_t,
0xf28c48i32 as uint32_t,
0x8e85eei32 as uint32_t,
@@ -579,7 +599,7 @@ pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
) as libc::c_int;
free(str_lower as *mut libc::c_void);
COLORS[color_index as usize] as libc::c_int
colors[color_index as usize] as libc::c_int
}
/* clist tools */
@@ -647,7 +667,13 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
* date/time tools
******************************************************************************/
pub fn dc_timestamp_to_str(wanted: i64) -> String {
/* the return value must be free()'d */
pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char {
let res = dc_timestamp_to_str_safe(wanted);
to_cstring(res)
}
pub fn dc_timestamp_to_str_safe(wanted: i64) -> String {
let ts = chrono::Utc.timestamp(wanted, 0);
ts.format("%Y.%m.%d %H:%M:%S").to_string()
}
@@ -698,7 +724,7 @@ pub unsafe fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int
}
/* Message-ID tools */
pub fn dc_create_id() -> String {
pub unsafe fn dc_create_id() -> *mut libc::c_char {
/* generate an id. the generated ID should be as short and as unique as possible:
- short, because it may also used as part of Message-ID headers or in QR codes
- unique as two IDs generated on two devices should not be the same. However, collisions are not world-wide but only by the few contacts.
@@ -716,26 +742,39 @@ pub fn dc_create_id() -> String {
encode_66bits_as_base64(buf[0usize], buf[1usize], buf[2usize])
}
/// Encode 66 bits as a base64 string.
/// This is useful for ID generating with short strings as we save 5 character
/// in each id compared to 64 bit hex encoding. For a typical group ID, these
/// are 10 characters (grpid+msgid):
/// hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters
/// base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits)
/// Only the lower 2 bits of `fill` are used.
fn encode_66bits_as_base64(v1: u32, v2: u32, fill: u32) -> String {
use byteorder::{BigEndian, WriteBytesExt};
/* ******************************************************************************
* generate Message-IDs
******************************************************************************/
unsafe fn encode_66bits_as_base64(v1: uint32_t, v2: uint32_t, fill: uint32_t) -> *mut libc::c_char {
/* encode 66 bits as a base64 string. This is useful for ID generating with short strings as
we save 5 character in each id compared to 64 bit hex encoding, for a typical group ID, these are 10 characters (grpid+msgid):
hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters
base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) */
let ret: *mut libc::c_char = malloc(12) as *mut libc::c_char;
assert!(!ret.is_null());
let mut wrapped_writer = Vec::new();
{
let mut enc = base64::write::EncoderWriter::new(&mut wrapped_writer, base64::URL_SAFE);
enc.write_u32::<BigEndian>(v1).unwrap();
enc.write_u32::<BigEndian>(v2).unwrap();
enc.write_u8(((fill & 0x3) as u8) << 6).unwrap();
enc.finish().unwrap();
}
assert_eq!(wrapped_writer.pop(), Some('A' as u8)); // Remove last "A"
String::from_utf8(wrapped_writer).unwrap()
static mut chars: [libc::c_char; 65] = [
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
45, 95, 0,
];
*ret.offset(0isize) = chars[(v1 >> 26i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(1isize) = chars[(v1 >> 20i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(2isize) = chars[(v1 >> 14i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(3isize) = chars[(v1 >> 8i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(4isize) = chars[(v1 >> 2i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(5isize) = chars
[(v1 << 4i32 & 0x30i32 as libc::c_uint | v2 >> 28i32 & 0xfi32 as libc::c_uint) as usize];
*ret.offset(6isize) = chars[(v2 >> 22i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(7isize) = chars[(v2 >> 16i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(8isize) = chars[(v2 >> 10i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(9isize) = chars[(v2 >> 4i32 & 0x3fi32 as libc::c_uint) as usize];
*ret.offset(10isize) =
chars[(v2 << 2i32 & 0x3ci32 as libc::c_uint | fill & 0x3i32 as libc::c_uint) as usize];
*ret.offset(11isize) = 0i32 as libc::c_char;
ret
}
pub unsafe fn dc_create_incoming_rfc724_mid(
@@ -775,7 +814,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
- the message ID should be globally unique
- do not add a counter or any private data as as this may give unneeded information to the receiver */
let mut rand1: *mut libc::c_char = 0 as *mut libc::c_char;
let rand2: *mut libc::c_char = dc_create_id().strdup();
let rand2: *mut libc::c_char = dc_create_id();
let ret: *mut libc::c_char;
let mut at_hostname: *const libc::c_char = strchr(from_addr, '@' as i32);
if at_hostname.is_null() {
@@ -789,7 +828,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
at_hostname,
)
} else {
rand1 = dc_create_id().strdup();
rand1 = dc_create_id();
ret = dc_mprintf(
b"Mr.%s.%s%s\x00" as *const u8 as *const libc::c_char,
rand1,
@@ -803,49 +842,52 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
ret
}
/// Extract the group id (grpid) from a message id (mid)
///
/// # Arguments
///
/// * `mid` - A string that holds the message id
///
/// # Examples
///
/// ```
/// use deltachat::dc_tools::dc_extract_grpid_from_rfc724_mid;
/// let mid = "Gr.12345678901.morerandom@domain.de";
/// let grpid = dc_extract_grpid_from_rfc724_mid(mid);
/// assert_eq!(grpid, Some("12345678901"));
/// ```
pub fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
if mid.len() < 9 || !mid.starts_with("Gr.") {
return None;
}
if let Some(mid_without_offset) = mid.get(3..) {
if let Some(grpid_len) = mid_without_offset.find('.') {
/* strict length comparison, the 'Gr.' magic is weak enough */
if grpid_len == 11 || grpid_len == 16 {
return Some(mid_without_offset.get(0..grpid_len).unwrap());
pub unsafe fn dc_extract_grpid_from_rfc724_mid(mid: *const libc::c_char) -> *mut libc::c_char {
/* extract our group ID from Message-IDs as `Gr.12345678901.morerandom@domain.de`; "12345678901" is the wanted ID in this example. */
let mut success: libc::c_int = 0i32;
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
let p1: *mut libc::c_char;
let grpid_len: libc::c_int;
if !(mid.is_null()
|| strlen(mid) < 8
|| *mid.offset(0isize) as libc::c_int != 'G' as i32
|| *mid.offset(1isize) as libc::c_int != 'r' as i32
|| *mid.offset(2isize) as libc::c_int != '.' as i32)
{
grpid = dc_strdup(&*mid.offset(3isize));
p1 = strchr(grpid, '.' as i32);
if !p1.is_null() {
*p1 = 0i32 as libc::c_char;
grpid_len = strlen(grpid) as libc::c_int;
if !(grpid_len != 11i32 && grpid_len != 16i32) {
/* strict length comparison, the 'Gr.' magic is weak enough */
success = 1i32
}
}
}
None
if success == 0i32 {
free(grpid as *mut libc::c_void);
grpid = 0 as *mut libc::c_char
}
return if 0 != success {
grpid
} else {
0 as *mut libc::c_char
};
}
pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut libc::c_char {
if !list.is_null() {
let mut cur: *mut clistiter = (*list).first;
while !cur.is_null() {
let mid = if !cur.is_null() {
as_str((*cur).data as *const libc::c_char)
let mid: *const libc::c_char = (if !cur.is_null() {
(*cur).data
} else {
""
};
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) {
return grpid.strdup();
0 as *mut libc::c_void
}) as *const libc::c_char;
let grpid: *mut libc::c_char = dc_extract_grpid_from_rfc724_mid(mid);
if !grpid.is_null() {
return grpid;
}
cur = if !cur.is_null() {
(*cur).next
@@ -859,7 +901,6 @@ pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut
}
/* file tools */
#[allow(non_snake_case)]
pub unsafe fn dc_ensure_no_slash(pathNfilename: *mut libc::c_char) {
let path_len = strlen(pathNfilename);
if path_len > 0 {
@@ -892,7 +933,6 @@ pub unsafe fn dc_validate_filename(filename: *mut libc::c_char) {
}
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_filename(pathNfilename: *const libc::c_char) -> *mut libc::c_char {
let mut p: *const libc::c_char = strrchr(pathNfilename, '/' as i32);
if p.is_null() {
@@ -907,7 +947,6 @@ pub unsafe fn dc_get_filename(pathNfilename: *const libc::c_char) -> *mut libc::
}
// the case of the suffix is preserved
#[allow(non_snake_case)]
pub unsafe fn dc_split_filename(
pathNfilename: *const libc::c_char,
ret_basename: *mut *mut libc::c_char,
@@ -940,7 +979,6 @@ pub unsafe fn dc_split_filename(
}
// the returned suffix is lower-case
#[allow(non_snake_case)]
pub unsafe fn dc_get_filesuffix_lc(pathNfilename: *const libc::c_char) -> *mut libc::c_char {
if !pathNfilename.is_null() {
let mut p: *const libc::c_char = strrchr(pathNfilename, '.' as i32);
@@ -1036,7 +1074,6 @@ pub unsafe fn dc_get_filemeta(
0
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_abs_path(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1066,7 +1103,6 @@ pub unsafe fn dc_get_abs_path(
pathNfilename_abs
}
#[allow(non_snake_case)]
pub unsafe fn dc_file_exist(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int {
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
@@ -1082,7 +1118,6 @@ pub unsafe fn dc_file_exist(context: &Context, pathNfilename: *const libc::c_cha
exist as libc::c_int
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_filebytes(context: &Context, pathNfilename: *const libc::c_char) -> uint64_t {
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
if pathNfilename_abs.is_null() {
@@ -1103,7 +1138,6 @@ pub unsafe fn dc_get_filebytes(context: &Context, pathNfilename: *const libc::c_
filebytes as uint64_t
}
#[allow(non_snake_case)]
pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
@@ -1135,7 +1169,6 @@ pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_ch
success
}
#[allow(non_snake_case)]
pub unsafe fn dc_copy_file(
context: &Context,
src: *const libc::c_char,
@@ -1167,7 +1200,6 @@ pub unsafe fn dc_copy_file(
success
}
#[allow(non_snake_case)]
pub unsafe fn dc_create_folder(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1199,7 +1231,6 @@ pub unsafe fn dc_create_folder(
success
}
#[allow(non_snake_case)]
pub unsafe fn dc_write_file(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1211,11 +1242,12 @@ pub unsafe fn dc_write_file(
dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int
}
#[allow(non_snake_case)]
pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool {
let pathNfilename_abs = unsafe {
let n = CString::yolo(pathNfilename.as_ref());
dc_get_abs_path(context, n.as_ptr())
let n = to_cstring(pathNfilename.as_ref());
let res = dc_get_abs_path(context, n);
free(n as *mut _);
res
};
if pathNfilename_abs.is_null() {
return false;
@@ -1240,7 +1272,6 @@ pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf
success
}
#[allow(non_snake_case)]
pub unsafe fn dc_read_file(
context: &Context,
pathNfilename: *const libc::c_char,
@@ -1260,11 +1291,12 @@ pub unsafe fn dc_read_file(
}
}
#[allow(non_snake_case)]
pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> {
let pathNfilename_abs = unsafe {
let n = CString::yolo(pathNfilename.as_ref());
dc_get_abs_path(context, n.as_ptr())
let n = to_cstring(pathNfilename.as_ref());
let p = dc_get_abs_path(context, n);
free(n as *mut _);
p
};
if pathNfilename_abs.is_null() {
@@ -1290,7 +1322,6 @@ pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> O
res
}
#[allow(non_snake_case)]
pub unsafe fn dc_get_fine_pathNfilename(
context: &Context,
pathNfolder: *const libc::c_char,
@@ -1344,9 +1375,15 @@ pub unsafe fn dc_get_fine_pathNfilename(
ret
}
pub unsafe fn dc_is_blobdir_path(context: &Context, path: *const libc::c_char) -> bool {
return strncmp(path, context.get_blobdir(), strlen(context.get_blobdir())) == 0
|| strncmp(path, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, 8) == 0;
// TODO should return bool /rtn
pub unsafe fn dc_is_blobdir_path(context: &Context, path: *const libc::c_char) -> libc::c_int {
if strncmp(path, context.get_blobdir(), strlen(context.get_blobdir())) == 0i32
|| strncmp(path, b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, 8) == 0i32
{
return 1i32;
}
0
}
pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char) {
@@ -1362,14 +1399,15 @@ pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char)
};
}
pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> bool {
let mut success = false;
// TODO should return bool /rtn
pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
let mut blobdir_path: *mut libc::c_char = 0 as *mut libc::c_char;
if !(path.is_null() || (*path).is_null()) {
if dc_is_blobdir_path(context, *path) {
if 0 != dc_is_blobdir_path(context, *path) {
dc_make_rel_path(context, path);
success = true;
success = 1i32
} else {
filename = dc_get_filename(*path);
if !(filename.is_null()
@@ -1387,7 +1425,7 @@ pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_ch
*path = blobdir_path;
blobdir_path = 0 as *mut libc::c_char;
dc_make_rel_path(context, path);
success = true;
success = 1i32
}
}
}
@@ -1497,49 +1535,10 @@ fn os_str_to_c_string_unicode(
}
}
/// Convenience methods/associated functions for working with [CString]
///
/// This is helps transitioning from unsafe code.
pub trait CStringExt {
/// Create a new [CString], yolo style
///
/// This unwrap the result, panicking when there are embedded NULL
/// bytes.
fn yolo<T: Into<Vec<u8>>>(t: T) -> CString {
CString::new(t).expect("String contains null byte, can not be CString")
}
}
impl CStringExt for CString {}
/// Convenience methods to make transitioning from raw C strings easier.
///
/// To interact with (legacy) C APIs we often need to convert from
/// Rust strings to raw C strings. This can be clumsy to do correctly
/// and the compiler sometimes allows it in an unsafe way. These
/// methods make it more succinct and help you get it right.
pub trait StrExt {
/// Allocate a new raw C `*char` version of this string.
///
/// This allocates a new raw C string which must be freed using
/// `free`. It takes care of some common pitfalls with using
/// [CString::as_ptr].
///
/// [CString::as_ptr]: std::ffi::CString::as_ptr
///
/// # Panics
///
/// This function will panic when the original string contains an
/// interior null byte as this can not be represented in raw C
/// strings.
unsafe fn strdup(&self) -> *mut libc::c_char;
}
impl<T: AsRef<str>> StrExt for T {
unsafe fn strdup(&self) -> *mut libc::c_char {
let tmp = CString::yolo(self.as_ref());
dc_strdup(tmp.as_ptr())
}
/// Needs to free the result after use!
pub unsafe fn to_cstring<S: AsRef<str>>(s: S) -> *mut libc::c_char {
let cstr = CString::new(s.as_ref()).expect("invalid string converted");
dc_strdup(cstr.as_ref().as_ptr())
}
pub fn to_string(s: *const libc::c_char) -> String {
@@ -1571,16 +1570,13 @@ pub fn to_string_lossy(s: *const libc::c_char) -> String {
}
pub fn as_str<'a>(s: *const libc::c_char) -> &'a str {
as_str_safe(s).unwrap_or_else(|err| panic!("{}", err))
}
pub fn as_str_safe<'a>(s: *const libc::c_char) -> Result<&'a str, Error> {
assert!(!s.is_null(), "cannot be used on null pointers");
let cstr = unsafe { CStr::from_ptr(s) };
cstr.to_str()
.map_err(|err| format_err!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err))
cstr.to_str().unwrap_or_else(|err| {
panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err);
})
}
/// Convert a C `*char` pointer to a [std::path::Path] slice.
@@ -1978,28 +1974,11 @@ mod tests {
#[test]
fn test_dc_create_id() {
let buf = dc_create_id();
assert_eq!(buf.len(), 11);
}
#[test]
fn test_encode_66bits_as_base64() {
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 0),
"ASNFZ4mrze8"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 1),
"ASNFZ4mrze9"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 2),
"ASNFZ4mrze-"
);
assert_eq!(
encode_66bits_as_base64(0x01234567, 0x89abcdef, 3),
"ASNFZ4mrze_"
);
unsafe {
let buf = dc_create_id();
assert_eq!(strlen(buf), 11);
free(buf as *mut libc::c_void);
}
}
#[test]
@@ -2098,53 +2077,4 @@ mod tests {
let ptr = some_path.as_ptr();
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
}
#[test]
fn test_cstring_yolo() {
assert_eq!(CString::new("hello").unwrap(), CString::yolo("hello"));
}
#[test]
fn test_strdup_str() {
unsafe {
let s = "hello".strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_strdup_string() {
unsafe {
let s = String::from("hello").strdup();
let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char);
free(s as *mut libc::c_void);
assert_eq!(cmp, 0);
}
}
#[test]
fn test_dc_extract_grpid_from_rfc724_mid() {
// Should return None if we pass invalid mid
let mid = "foobar";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, None);
// Should return None if grpid has a length which is not 11 or 16
let mid = "Gr.12345678.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, None);
// Should return extracted grpid for grpid with length of 11
let mid = "Gr.12345678901.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, Some("12345678901"));
// Should return extracted grpid for grpid with length of 11
let mid = "Gr.1234567890123456.morerandom@domain.de";
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
assert_eq!(grpid, Some("1234567890123456"));
}
}

View File

@@ -16,8 +16,6 @@ pub enum Error {
SqlFailedToOpen,
#[fail(display = "{:?}", _0)]
Io(std::io::Error),
#[fail(display = "{:?}", _0)]
Message(String),
}
pub type Result<T> = std::result::Result<T, Error>;
@@ -45,67 +43,3 @@ impl From<std::io::Error> for Error {
Error::Io(err)
}
}
#[macro_export]
macro_rules! bail {
($e:expr) => {
return Err($crate::error::Error::Message($e.to_string()));
};
($fmt:expr, $($arg:tt)+) => {
return Err($crate::error::Error::Message(format!($fmt, $($arg)+)));
};
}
#[macro_export]
macro_rules! format_err {
($e:expr) => {
$crate::error::Error::Message($e.to_string());
};
($fmt:expr, $($arg:tt)+) => {
$crate::error::Error::Message(format!($fmt, $($arg)+));
};
}
#[macro_export(local_inner_macros)]
macro_rules! ensure {
($cond:expr, $e:expr) => {
if !($cond) {
bail!($e);
}
};
($cond:expr, $fmt:expr, $($arg:tt)+) => {
if !($cond) {
bail!($fmt, $($arg)+);
}
};
}
#[macro_export]
macro_rules! ensure_eq {
($left:expr, $right:expr) => ({
match (&$left, &$right) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
bail!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`"#, left_val, right_val)
}
}
}
});
($left:expr, $right:expr,) => ({
ensure_eq!($left, $right)
});
($left:expr, $right:expr, $($arg:tt)+) => ({
match (&($left), &($right)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
bail!(r#"assertion failed: `(left == right)`
left: `{:?}`,
right: `{:?}`: {}"#, left_val, right_val,
format_args!($($arg)+))
}
}
}
});
}

View File

@@ -1,4 +1,3 @@
use std::ffi::CString;
use std::net;
use std::sync::{Arc, Condvar, Mutex, RwLock};
use std::time::{Duration, SystemTime};
@@ -6,9 +5,10 @@ use std::time::{Duration, SystemTime};
use crate::constants::*;
use crate::context::Context;
use crate::dc_loginparam::*;
use crate::dc_tools::CStringExt;
use crate::dc_tools::{as_str, to_cstring};
use crate::oauth2::dc_get_oauth2_access_token;
use crate::types::*;
use crate::x::free;
pub const DC_IMAP_SEEN: usize = 0x0001;
pub const DC_REGENERATE: usize = 0x01;
@@ -705,16 +705,26 @@ impl Imap {
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
let key = format!("imap.mailbox.{}", folder.as_ref());
if let Some(entry) = (self.get_config)(context, &key) {
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
)
} else {
(0, 0)
let val1 = unsafe {
let key_c = to_cstring(key);
let val = (self.get_config)(context, key_c, 0 as *const libc::c_char);
free(key_c as *mut _);
val
};
if val1.is_null() {
return (0, 0);
}
let entry = as_str(val1);
if entry.is_empty() {
return (0, 0);
}
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
let mut parts = entry.split(':');
(
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
parts.next().unwrap().parse().unwrap_or_else(|_| 0),
)
}
fn fetch_from_single_folder<S: AsRef<str>>(&self, context: &Context, folder: S) -> usize {
@@ -843,8 +853,10 @@ impl Imap {
.expect("missing message id");
if 0 == unsafe {
let message_id_c = CString::yolo(message_id);
(self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid)
let message_id_c = to_cstring(message_id);
let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid);
free(message_id_c as *mut _);
res
} {
// check passed, go fetch the rest
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
@@ -912,7 +924,13 @@ impl Imap {
let key = format!("imap.mailbox.{}", folder.as_ref());
let val = format!("{}:{}", uidvalidity, lastseenuid);
(self.set_config)(context, &key, Some(&val));
unsafe {
let key_c = to_cstring(key);
let val_c = to_cstring(val);
(self.set_config)(context, key_c, val_c);
free(key_c as *mut _);
free(val_c as *mut _);
};
}
fn fetch_single_msg<S: AsRef<str>>(

View File

@@ -216,16 +216,22 @@ impl Key {
}
}
/// Each header line must be terminated by `\r\n`
pub fn to_asc(&self, header: Option<(&str, &str)>) -> String {
/// Each header line must be terminated by `\r\n`, the result must be freed.
pub fn to_asc_c(&self, header: Option<(&str, &str)>) -> *mut libc::c_char {
let headers = header.map(|(key, value)| {
let mut m = BTreeMap::new();
m.insert(key.to_string(), value.to_string());
m
});
self.to_armored_string(headers.as_ref())
.expect("failed to serialize key")
let buf = self
.to_armored_string(headers.as_ref())
.expect("failed to serialize key");
let buf_c = CString::new(buf).unwrap();
// need to use strdup to allocate the result with malloc
// so it can be `free`d later.
unsafe { strdup(buf_c.as_ptr()) }
}
pub fn write_asc_to_file(&self, file: *const libc::c_char, context: &Context) -> bool {
@@ -233,16 +239,15 @@ impl Key {
return false;
}
let file_content = self.to_asc(None);
let file_content_c = CString::new(file_content).unwrap();
let file_content = self.to_asc_c(None);
let success = if 0
== unsafe {
dc_write_file(
context,
file,
file_content_c.as_ptr() as *const libc::c_void,
file_content_c.as_bytes().len(),
file_content as *const libc::c_void,
strlen(file_content),
)
} {
error!(context, 0, "Cannot write key to {}", to_string(file));
@@ -251,6 +256,8 @@ impl Key {
true
};
unsafe { free(file_content as *mut libc::c_void) };
success
}

View File

@@ -1,4 +1,12 @@
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
#![allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
non_upper_case_globals,
non_camel_case_types,
non_snake_case
)]
#![feature(c_variadic, ptr_wrapping_offset_from)]
#[macro_use]
extern crate failure_derive;
@@ -8,36 +16,30 @@ extern crate num_derive;
extern crate smallvec;
#[macro_use]
extern crate rusqlite;
extern crate strum;
#[macro_use]
extern crate strum_macros;
#[macro_use]
mod log;
#[macro_use]
pub mod error;
pub mod aheader;
pub mod chatlist;
pub mod config;
pub mod constants;
pub mod context;
pub mod error;
pub mod imap;
pub mod key;
pub mod keyhistory;
pub mod keyring;
pub mod oauth2;
pub mod param;
pub mod peerstate;
pub mod pgp;
pub mod smtp;
pub mod sql;
pub mod stock;
pub mod types;
pub mod x;
pub mod dc_array;
pub mod dc_chat;
pub mod dc_chatlist;
pub mod dc_configure;
pub mod dc_contact;
pub mod dc_dehtml;
@@ -52,16 +54,15 @@ pub mod dc_mimefactory;
pub mod dc_mimeparser;
pub mod dc_move;
pub mod dc_msg;
pub mod dc_param;
pub mod dc_qr;
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;

View File

@@ -7,9 +7,10 @@ macro_rules! info {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
formatted_c.as_ptr() as libc::uintptr_t);
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}
@@ -22,9 +23,10 @@ macro_rules! warn {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
formatted_c.as_ptr() as libc::uintptr_t);
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void) ;
}};
}
@@ -37,9 +39,10 @@ macro_rules! error {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
formatted_c.as_ptr() as libc::uintptr_t);
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}
@@ -52,8 +55,9 @@ macro_rules! log_event {
#[allow(unused_unsafe)]
unsafe {
let formatted = format!($msg, $($args),*);
let formatted_c = std::ffi::CString::new(formatted).unwrap();
let formatted_c = $crate::dc_tools::to_cstring(formatted);
$ctx.call_cb($event, $data1 as libc::uintptr_t,
formatted_c.as_ptr() as libc::uintptr_t);
formatted_c as libc::uintptr_t);
libc::free(formatted_c as *mut libc::c_void);
}};
}

View File

@@ -1,6 +1,6 @@
use std::collections::HashMap;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use serde::Deserialize;
use crate::context::Context;
@@ -321,7 +321,7 @@ fn is_expired(context: &Context) -> bool {
}
fn replace_in_uri(uri: impl AsRef<str>, key: impl AsRef<str>, value: impl AsRef<str>) -> String {
let value_urlencoded = utf8_percent_encode(value.as_ref(), NON_ALPHANUMERIC).to_string();
let value_urlencoded = utf8_percent_encode(value.as_ref(), DEFAULT_ENCODE_SET).to_string();
uri.as_ref().replace(key.as_ref(), &value_urlencoded)
}
@@ -344,7 +344,7 @@ mod tests {
fn test_replace_in_uri() {
assert_eq!(
replace_in_uri("helloworld", "world", "a-b c"),
"helloa%2Db%20c"
"helloa-b%20c"
);
}

View File

@@ -1,238 +0,0 @@
use std::collections::BTreeMap;
use std::fmt;
use std::str;
use num_traits::FromPrimitive;
use crate::error;
/// Available param keys.
#[derive(PartialEq, Eq, Debug, Clone, Copy, Hash, PartialOrd, Ord, FromPrimitive)]
#[repr(u8)]
pub enum Param {
/// For messages and jobs
File = 'f' as u8,
/// For Messages
Width = 'w' as u8,
/// For Messages
Height = 'h' as u8,
/// For Messages
Duration = 'd' as u8,
/// For Messages
MimeType = 'm' as u8,
/// For Messages: message is encryoted, outgoing: guarantee E2EE or the message is not send
GuranteeE2ee = 'c' as u8,
/// For Messages: decrypted with validation errors or without mutual set, if neither
/// 'c' nor 'e' are preset, the messages is only transport encrypted.
ErroneousE2ee = 'e' as u8,
/// For Messages: force unencrypted message, either `ForcePlaintext::AddAutocryptHeader` (1),
/// `ForcePlaintext::NoAutocryptHeader` (2) or 0.
ForcePlaintext = 'u' as u8,
/// For Messages
WantsMdn = 'r' as u8,
/// For Messages
Forwarded = 'a' as u8,
/// For Messages
Cmd = 'S' as u8,
/// For Messages
Arg = 'E' as u8,
/// For Messages
Arg2 = 'F' as u8,
/// For Messages
Arg3 = 'G' as u8,
/// For Messages
Arg4 = 'H' as u8,
/// For Messages
Error = 'L' as u8,
/// For Messages: space-separated list of messaged IDs of forwarded copies.
PrepForwards = 'P' as u8,
/// For Jobs
SetLatitude = 'l' as u8,
/// For Jobs
SetLongitude = 'n' as u8,
/// For Jobs
ServerFolder = 'Z' as u8,
/// For Jobs
ServerUid = 'z' as u8,
/// For Jobs
AlsoMove = 'M' as u8,
/// For Jobs: space-separated list of message recipients
Recipients = 'R' as u8,
// For Groups
Unpromoted = 'U' as u8,
// For Groups and Contacts
ProfileImage = 'i' as u8,
// For Chats
Selftalk = 'K' as u8,
// For QR
Auth = 's' as u8,
// For QR
GroupId = 'x' as u8,
// For QR
GroupName = 'g' as u8,
}
/// Possible values for `Param::ForcePlaintext`.
#[derive(PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)]
#[repr(u8)]
pub enum ForcePlaintext {
AddAutocryptHeader = 1,
NoAutocryptHeader = 2,
}
/// An object for handling key=value parameter lists.
///
/// The structure is serialized by calling `to_string()` on it.
///
/// Only for library-internal use.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Params {
inner: BTreeMap<Param, String>,
}
impl fmt::Display for Params {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (i, (key, value)) in self.inner.iter().enumerate() {
if i > 0 {
write!(f, "\n")?;
}
write!(f, "{}={}", *key as u8 as char, value)?;
}
Ok(())
}
}
impl str::FromStr for Params {
type Err = error::Error;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut inner = BTreeMap::new();
for pair in s.trim().lines() {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
// TODO: probably nicer using a regex
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
let mut split = pair.splitn(2, '=');
let key = split.next();
let value = split.next();
ensure!(key.is_some(), "Missing key");
ensure!(value.is_some(), "Missing value");
let key = key.unwrap().trim();
let value = value.unwrap().trim();
if let Some(key) = Param::from_u8(key.as_bytes()[0]) {
inner.insert(key, value.to_string());
} else {
bail!("Unknown key: {}", key);
}
}
Ok(Params { inner })
}
}
impl Params {
/// Create new empty params.
pub fn new() -> Self {
Default::default()
}
/// Get the value of the given key, return `None` if no value is set.
pub fn get(&self, key: Param) -> Option<&str> {
self.inner.get(&key).map(|s| s.as_str())
}
/// Check if the given key is set.
pub fn exists(&self, key: Param) -> bool {
self.inner.contains_key(&key)
}
/// Set the given key to the passed in value.
pub fn set(&mut self, key: Param, value: impl AsRef<str>) -> &mut Self {
self.inner.insert(key, value.as_ref().to_string());
self
}
/// Removes the given key, if it exists.
pub fn remove(&mut self, key: Param) -> &mut Self {
self.inner.remove(&key);
self
}
/// Check if there are any values in this.
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Returns how many key-value pairs are set.
pub fn len(&self) -> usize {
self.inner.len()
}
/// Get the given parameter and parse as `i32`.
pub fn get_int(&self, key: Param) -> Option<i32> {
self.get(key).and_then(|s| s.parse().ok())
}
/// Get the given parameter and parse as `f64`.
pub fn get_float(&self, key: Param) -> Option<f64> {
self.get(key).and_then(|s| s.parse().ok())
}
/// Set the given paramter to the passed in `i32`.
pub fn set_int(&mut self, key: Param, value: i32) -> &mut Self {
self.set(key, format!("{}", value));
self
}
/// Set the given parameter to the passed in `f64` .
pub fn set_float(&mut self, key: Param, value: f64) -> &mut Self {
self.set(key, format!("{}", value));
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dc_param() {
let mut p1: Params = "\r\n\r\na=1\nf=2\n\nc = 3 ".parse().unwrap();
assert_eq!(p1.get_int(Param::Forwarded), Some(1));
assert_eq!(p1.get_int(Param::File), Some(2));
assert_eq!(p1.get_int(Param::Height), None);
assert!(!p1.exists(Param::Height));
p1.set_int(Param::Duration, 4);
assert_eq!(p1.get_int(Param::Duration), Some(4));
let mut p1 = Params::new();
p1.set(Param::Forwarded, "foo")
.set_int(Param::File, 2)
.remove(Param::GuranteeE2ee)
.set_int(Param::Duration, 4);
assert_eq!(p1.to_string(), "a=foo\nd=4\nf=2");
p1.remove(Param::File);
assert_eq!(p1.to_string(), "a=foo\nd=4",);
assert_eq!(p1.len(), 2);
p1.remove(Param::Forwarded);
p1.remove(Param::Duration);
assert_eq!(p1.to_string(), "",);
assert!(p1.is_empty());
assert_eq!(p1.len(), 0)
}
}

View File

@@ -462,11 +462,16 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
use std::ffi::CStr;
use tempfile::{tempdir, TempDir};
use crate::context::*;
use crate::dc_tools::to_cstring;
use crate::x::free;
#[test]
fn test_peerstate_save_to_db() {
let ctx = crate::test_utils::dummy_context();
let ctx = unsafe { create_test_context() };
let addr = "hello@mail.com";
let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap();
@@ -504,4 +509,29 @@ mod tests {
ctx: Context,
dir: TempDir,
}
unsafe extern "C" fn cb(
_context: &Context,
_event: Event,
_data1: libc::uintptr_t,
_data2: libc::uintptr_t,
) -> libc::uintptr_t {
0
}
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
"Failed to open {}",
CStr::from_ptr(dbfile as *const _).to_str().unwrap()
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
}

View File

@@ -17,14 +17,15 @@ use crate::keyring::*;
use crate::types::*;
use crate::x::*;
// TODO should return bool /rtn
pub unsafe fn dc_split_armored_data(
buf: *mut libc::c_char,
ret_headerline: *mut *const libc::c_char,
ret_setupcodebegin: *mut *const libc::c_char,
ret_preferencrypt: *mut *const libc::c_char,
ret_base64: *mut *const libc::c_char,
) -> bool {
let mut success = false;
) -> libc::c_int {
let mut success: libc::c_int = 0i32;
let mut line_chars: size_t = 0i32 as size_t;
let mut line: *mut libc::c_char = buf;
let mut p1: *mut libc::c_char = buf;
@@ -127,7 +128,7 @@ pub unsafe fn dc_split_armored_data(
if !ret_base64.is_null() {
*ret_base64 = base64
}
success = true;
success = 1i32
}
}
}

View File

@@ -6,10 +6,11 @@ use thread_local_object::ThreadLocal;
use crate::constants::*;
use crate::context::Context;
use crate::dc_param::*;
use crate::dc_tools::*;
use crate::error::{Error, Result};
use crate::param::*;
use crate::peerstate::*;
use crate::x::*;
const DC_OPEN_READONLY: usize = 0x01;
@@ -339,7 +340,7 @@ fn open(
info!(
context,
0,
"First time init: creating tables in {:?}.",
"First time init: creating tables in \"{:?}\".",
dbfile.as_ref(),
);
sql.execute(
@@ -815,7 +816,7 @@ fn open(
}
}
info!(context, 0, "Opened {:?}.", dbfile.as_ref(),);
info!(context, 0, "Opened \"{:?}\".", dbfile.as_ref(),);
Ok(())
}
@@ -953,25 +954,25 @@ pub fn housekeeping(context: &Context) {
context,
&mut files_in_use,
"SELECT param FROM msgs WHERE chat_id!=3 AND type!=10;",
Param::File,
'f' as i32,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM jobs;",
Param::File,
'f' as i32,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM chats;",
Param::ProfileImage,
'i' as i32,
);
maybe_add_from_param(
context,
&mut files_in_use,
"SELECT param FROM contacts;",
Param::ProfileImage,
'i' as i32,
);
context
@@ -1006,15 +1007,35 @@ pub fn housekeeping(context: &Context) {
}
let entry = entry.unwrap();
let name_f = entry.file_name();
let name_s = name_f.to_string_lossy();
let name_c = unsafe { to_cstring(name_f.to_string_lossy()) };
if is_file_in_use(&mut files_in_use, None, &name_s)
|| is_file_in_use(&mut files_in_use, Some(".increation"), &name_s)
|| is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s)
|| is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s)
if unsafe { is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c) }
|| unsafe {
is_file_in_use(
&mut files_in_use,
b".increation\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
|| unsafe {
is_file_in_use(
&mut files_in_use,
b".waveform\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
|| unsafe {
is_file_in_use(
&mut files_in_use,
b"-preview.jpg\x00" as *const u8 as *const libc::c_char,
name_c,
)
}
{
unsafe { free(name_c as *mut _) };
continue;
}
unsafe { free(name_c as *mut _) };
unreferenced_count += 1;
@@ -1048,8 +1069,9 @@ pub fn housekeeping(context: &Context) {
entry.file_name()
);
unsafe {
let path = entry.path().to_c_string().unwrap();
dc_delete_file(context, path.as_ptr());
let path = to_cstring(entry.path().to_str().unwrap());
dc_delete_file(context, path);
free(path as *mut _);
}
}
}
@@ -1067,18 +1089,26 @@ pub fn housekeeping(context: &Context) {
info!(context, 0, "Housekeeping done.",);
}
fn is_file_in_use(files_in_use: &HashSet<String>, namespc_opt: Option<&str>, name: &str) -> bool {
let name_to_check = if let Some(namespc) = namespc_opt {
let name_len = name.len();
let namespc_len = namespc.len();
if name_len <= namespc_len || !name.ends_with(namespc) {
unsafe fn is_file_in_use(
files_in_use: &HashSet<String>,
namespc: *const libc::c_char,
name: *const libc::c_char,
) -> bool {
let name_to_check = dc_strdup(name);
if !namespc.is_null() {
let name_len: libc::c_int = strlen(name) as libc::c_int;
let namespc_len: libc::c_int = strlen(namespc) as libc::c_int;
if name_len <= namespc_len
|| strcmp(&*name.offset((name_len - namespc_len) as isize), namespc) != 0
{
return false;
}
&name[..name_len - namespc_len]
} else {
name
};
files_in_use.contains(name_to_check)
*name_to_check.offset((name_len - namespc_len) as isize) = 0 as libc::c_char
}
let contains = files_in_use.contains(as_str(name_to_check));
free(name_to_check as *mut libc::c_void);
contains
}
fn maybe_add_file(files_in_use: &mut HashSet<String>, file: impl AsRef<str>) {
@@ -1093,20 +1123,31 @@ fn maybe_add_from_param(
context: &Context,
files_in_use: &mut HashSet<String>,
query: &str,
param_id: Param,
param_id: libc::c_int,
) {
let param = unsafe { dc_param_new() };
context
.sql
.query_row(query, NO_PARAMS, |row| {
let param: Params = row.get::<_, String>(0)?.parse().unwrap_or_default();
if let Some(file) = param.get(param_id) {
maybe_add_file(files_in_use, file);
unsafe {
let v = to_cstring(row.get::<_, String>(0)?);
dc_param_set_packed(param, v as *const _);
let file = dc_param_get(param, param_id, 0 as *const _);
if !file.is_null() {
maybe_add_file(files_in_use, as_str(file));
free(file as *mut libc::c_void);
}
free(v as *mut _);
}
Ok(())
})
.unwrap_or_else(|err| {
warn!(context, 0, "sql: failed to add_from_param: {}", err);
});
unsafe { dc_param_unref(param) };
}
#[cfg(test)]
@@ -1132,12 +1173,26 @@ mod test {
maybe_add_file(&mut files, "$BLOBDIR/world.txt");
maybe_add_file(&mut files, "world2.txt");
assert!(is_file_in_use(&mut files, None, "hello"));
assert!(!is_file_in_use(&mut files, Some(".txt"), "hello"));
assert!(is_file_in_use(
&mut files,
Some("-suffix"),
"world.txt-suffix"
));
assert!(unsafe {
is_file_in_use(
&mut files,
std::ptr::null(),
b"hello\x00" as *const u8 as *const _,
)
});
assert!(!unsafe {
is_file_in_use(
&mut files,
b".txt\x00" as *const u8 as *const _,
b"hello\x00" as *const u8 as *const _,
)
});
assert!(unsafe {
is_file_in_use(
&mut files,
b"-suffix\x00" as *const u8 as *const _,
b"world.txt-suffix\x00" as *const u8 as *const _,
)
});
}
}

View File

@@ -1,423 +0,0 @@
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(), None);
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(), None);
// 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(), None);
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(), None);
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(), None);
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(), None);
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."
)
}
}

View File

@@ -1,46 +0,0 @@
//! 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;
/// 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(), None);
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
"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)
}

View File

@@ -2,14 +2,12 @@
import os
import re
if __name__ == "__main__":
filestats = []
for fn in os.listdir():
if fn.endswith(".rs"):
s = open(fn).read()
s = re.sub(r'(?m)///.*$', '', s) # remove comments
unsafe = s.count("unsafe")
free = s.count("free(")
gotoblocks = s.count("current_block =")

View File

@@ -1,7 +1,7 @@
#![allow(non_camel_case_types)]
use crate::constants::Event;
use crate::context::Context;
pub use mmime::carray::*;
pub use mmime::clist::*;
pub use rusqlite::ffi::*;
@@ -33,8 +33,10 @@ the online state. */
pub type dc_precheck_imf_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int;
pub type dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> ();
pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>;
pub type dc_set_config_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> ();
pub type dc_get_config_t =
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char;
pub type sqlite_int64 = i64;
pub type sqlite3_int64 = sqlite_int64;

View File

@@ -1,3 +1,5 @@
use crate::types::*;
pub use libc::{
calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, strcmp,
strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system,
@@ -35,6 +37,14 @@ pub fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char {
extern "C" {
pub fn clock() -> libc::clock_t;
pub fn qsort(
__base: *mut libc::c_void,
__nel: size_t,
__width: size_t,
__compar: Option<
unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int,
>,
);
// -- DC Methods
pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char;

View File

@@ -1,7 +1,6 @@
//! Stress some functions for testing; if used as a lib, this file is obsolete.
use std::collections::HashSet;
use std::ffi::CString;
use mmime::mailimf_types::*;
use tempfile::{tempdir, TempDir};
@@ -124,15 +123,21 @@ unsafe fn stress_functions(context: &Context) {
context.get_blobdir(),
b"foobar\x00" as *const u8 as *const libc::c_char,
);
assert!(dc_is_blobdir_path(context, abs_path));
assert!(dc_is_blobdir_path(
context,
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert!(!dc_is_blobdir_path(
context,
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
));
assert_ne!(0, dc_is_blobdir_path(context, abs_path));
assert_ne!(
0,
dc_is_blobdir_path(
context,
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
)
);
assert_eq!(
0,
dc_is_blobdir_path(
context,
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
)
);
assert_ne!(0, dc_file_exist(context, abs_path));
free(abs_path as *mut libc::c_void);
assert_ne!(
@@ -273,6 +278,7 @@ unsafe fn stress_functions(context: &Context) {
assert!(res.contains(" configured_send_port "));
assert!(res.contains(" configured_server_flags "));
let mut ok: libc::c_int;
let mut buf_0: *mut libc::c_char;
let mut headerline: *const libc::c_char = 0 as *const libc::c_char;
let mut setupcodebegin: *const libc::c_char = 0 as *const libc::c_char;
@@ -282,14 +288,14 @@ unsafe fn stress_functions(context: &Context) {
b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\ndata\n-----END PGP MESSAGE-----\x00" as *const u8
as *const libc::c_char,
);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
0 as *mut *const libc::c_char,
&mut base64,
);
assert!(ok);
assert_eq!(ok, 1);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -307,7 +313,7 @@ unsafe fn stress_functions(context: &Context) {
buf_0 =
strdup(b"-----BEGIN PGP MESSAGE-----\n\ndat1\n-----END PGP MESSAGE-----\n-----BEGIN PGP MESSAGE-----\n\ndat2\n-----END PGP MESSAGE-----\x00"
as *const u8 as *const libc::c_char);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -315,7 +321,7 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert!(ok);
assert_eq!(ok, 1);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -334,7 +340,7 @@ unsafe fn stress_functions(context: &Context) {
b"foo \n -----BEGIN PGP MESSAGE----- \n base64-123 \n -----END PGP MESSAGE-----\x00"
as *const u8 as *const libc::c_char,
);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -342,7 +348,7 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert!(ok);
assert_eq!(ok, 1);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -359,7 +365,7 @@ unsafe fn stress_functions(context: &Context) {
free(buf_0 as *mut libc::c_void);
buf_0 = strdup(b"foo-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
@@ -367,19 +373,19 @@ unsafe fn stress_functions(context: &Context) {
&mut base64,
);
assert!(!ok);
assert_eq!(ok, 0);
free(buf_0 as *mut libc::c_void);
buf_0 =
strdup(b"foo \n -----BEGIN PGP MESSAGE-----\n Passphrase-BeGIN : 23 \n \n base64-567 \r\n abc \n -----END PGP MESSAGE-----\n\n\n\x00"
as *const u8 as *const libc::c_char);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
&mut setupcodebegin,
0 as *mut *const libc::c_char,
&mut base64,
);
assert!(ok);
assert_eq!(ok, 1);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -406,14 +412,14 @@ unsafe fn stress_functions(context: &Context) {
buf_0 =
strdup(b"-----BEGIN PGP PRIVATE KEY BLOCK-----\n Autocrypt-Prefer-Encrypt : mutual \n\nbase64\n-----END PGP PRIVATE KEY BLOCK-----\x00"
as *const u8 as *const libc::c_char);
let ok = dc_split_armored_data(
ok = dc_split_armored_data(
buf_0,
&mut headerline,
0 as *mut *const libc::c_char,
&mut preferencrypt,
&mut base64,
);
assert!(ok);
assert_eq!(ok, 1);
assert!(!headerline.is_null());
assert_eq!(
strcmp(
@@ -469,13 +475,16 @@ unsafe fn stress_functions(context: &Context) {
let mut setupcodebegin_0: *const libc::c_char = 0 as *const libc::c_char;
let mut preferencrypt_0: *const libc::c_char = 0 as *const libc::c_char;
buf_1 = strdup(S_EM_SETUPFILE);
assert!(dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
));
assert_ne!(
0,
dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
)
);
assert!(!headerline_0.is_null());
assert_eq!(
0,
@@ -495,13 +504,16 @@ unsafe fn stress_functions(context: &Context) {
free(buf_1 as *mut libc::c_void);
buf_1 = dc_decrypt_setup_file(context, S_EM_SETUPCODE, S_EM_SETUPFILE);
assert!(!buf_1.is_null());
assert!(dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
));
assert_ne!(
0,
dc_split_armored_data(
buf_1,
&mut headerline_0,
&mut setupcodebegin_0,
&mut preferencrypt_0,
0 as *mut *const libc::c_char,
)
);
assert!(!headerline_0.is_null());
assert_eq!(
strcmp(
@@ -521,21 +533,65 @@ unsafe fn stress_functions(context: &Context) {
);
free(buf_1 as *mut libc::c_void);
if 0 != dc_is_configured(context) {
let setupcode = dc_create_setup_code(context);
let setupcode_c = CString::yolo(setupcode.clone());
let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
let setupfile_c = CString::yolo(setupfile);
let setupcode: *mut libc::c_char;
let setupfile: *mut libc::c_char;
setupcode = dc_create_setup_code(context);
assert!(!setupcode.is_null());
assert_eq!(strlen(setupcode), 44);
assert!(
0 != !(*setupcode.offset(4isize) as libc::c_int == '-' as i32
&& *setupcode.offset(9isize) as libc::c_int == '-' as i32
&& *setupcode.offset(14isize) as libc::c_int == '-' as i32
&& *setupcode.offset(19isize) as libc::c_int == '-' as i32
&& *setupcode.offset(24isize) as libc::c_int == '-' as i32
&& *setupcode.offset(29isize) as libc::c_int == '-' as i32
&& *setupcode.offset(34isize) as libc::c_int == '-' as i32
&& *setupcode.offset(39isize) as libc::c_int == '-' as i32)
as usize
);
setupfile = dc_render_setup_file(context, setupcode);
assert!(!setupfile.is_null());
let buf_2: *mut libc::c_char = dc_strdup(setupfile);
let mut headerline_1: *const libc::c_char = 0 as *const libc::c_char;
let mut setupcodebegin_1: *const libc::c_char = 0 as *const libc::c_char;
assert_eq!(
0,
dc_split_armored_data(
buf_2,
&mut headerline_1,
&mut setupcodebegin_1,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
)
);
assert!(!headerline_1.is_null());
assert_eq!(
strcmp(
headerline_1,
b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char,
),
0
);
assert!(
!(!setupcodebegin_1.is_null()
&& strlen(setupcodebegin_1) == 2
&& strncmp(setupcodebegin_1, setupcode, 2) == 0i32)
);
free(buf_2 as *mut libc::c_void);
let payload: *mut libc::c_char;
let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
payload = dc_decrypt_setup_file(context, setupcode, setupfile);
assert!(payload.is_null());
assert!(!dc_split_armored_data(
payload,
&mut headerline_2,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
));
assert_eq!(
0,
dc_split_armored_data(
payload,
&mut headerline_2,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
0 as *mut *const libc::c_char,
)
);
assert!(!headerline_2.is_null());
assert_eq!(
strcmp(
@@ -545,6 +601,8 @@ unsafe fn stress_functions(context: &Context) {
0
);
free(payload as *mut libc::c_void);
free(setupfile as *mut libc::c_void);
free(setupcode as *mut libc::c_void);
}
if 0 != dc_is_configured(context) {
@@ -632,7 +690,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_signed_bytes = ctext.len();
let ctext_signed = CString::yolo(ctext);
let ctext_signed = to_cstring(ctext);
let ctext = dc_pgp_pk_encrypt(
original_text as *const libc::c_void,
@@ -645,7 +703,7 @@ fn test_encryption_decryption() {
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
let ctext_unsigned_bytes = ctext.len();
let ctext_unsigned = CString::yolo(ctext);
let ctext_unsigned = to_cstring(ctext);
let mut keyring = Keyring::default();
keyring.add_owned(private_key);
@@ -659,7 +717,7 @@ fn test_encryption_decryption() {
let mut valid_signatures: HashSet<String> = Default::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
@@ -674,7 +732,7 @@ fn test_encryption_decryption() {
let empty_keyring = Keyring::default();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&empty_keyring,
@@ -687,7 +745,7 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -702,7 +760,7 @@ fn test_encryption_decryption() {
public_keyring2.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring2,
@@ -715,7 +773,7 @@ fn test_encryption_decryption() {
valid_signatures.clear();
let plain = dc_pgp_pk_decrypt(
ctext_unsigned.as_ptr() as *const _,
ctext_unsigned as *const _,
ctext_unsigned_bytes,
&keyring,
&public_keyring,
@@ -723,6 +781,7 @@ fn test_encryption_decryption() {
)
.unwrap();
free(ctext_unsigned as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
valid_signatures.clear();
@@ -733,7 +792,7 @@ fn test_encryption_decryption() {
public_keyring.add_ref(&public_key);
let plain = dc_pgp_pk_decrypt(
ctext_signed.as_ptr() as *const _,
ctext_signed as *const _,
ctext_signed_bytes,
&keyring,
&public_keyring,
@@ -741,6 +800,7 @@ fn test_encryption_decryption() {
)
.unwrap();
free(ctext_signed as *mut _);
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
}
}
@@ -761,14 +821,16 @@ struct TestContext {
}
unsafe fn create_test_context() -> TestContext {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
assert!(
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
assert_eq!(
dc_open(&mut ctx, dbfile, std::ptr::null()),
1,
"Failed to open {}",
dbfile.display()
as_str(dbfile as *const libc::c_char)
);
free(dbfile as *mut _);
TestContext { ctx: ctx, dir: dir }
}
@@ -781,31 +843,34 @@ fn test_dc_kml_parse() {
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</wHeN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></Placemark>\n</Document>\n</kml>\x00"
as *const u8 as *const libc::c_char;
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
let kml: *mut dc_kml_t = dc_kml_parse(&context.ctx, xml, strlen(xml));
assert!(!kml.addr.is_null());
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
assert!(!(*kml).addr.is_null());
assert_eq!(
as_str((*kml).addr as *const libc::c_char),
"user@example.org",
);
let locations_ref = &kml.locations.as_ref().unwrap();
assert_eq!(locations_ref.len(), 2);
assert_eq!(dc_array_get_cnt((*kml).locations), 2);
assert!(locations_ref[0].latitude > 53.6f64);
assert!(locations_ref[0].latitude < 53.8f64);
assert!(locations_ref[0].longitude > 9.3f64);
assert!(locations_ref[0].longitude < 9.5f64);
assert!(locations_ref[0].accuracy > 31.9f64);
assert!(locations_ref[0].accuracy < 32.1f64);
assert_eq!(locations_ref[0].timestamp, 1551906597);
assert!(dc_array_get_latitude((*kml).locations, 0) > 53.6f64);
assert!(dc_array_get_latitude((*kml).locations, 0) < 53.8f64);
assert!(dc_array_get_longitude((*kml).locations, 0) > 9.3f64);
assert!(dc_array_get_longitude((*kml).locations, 0) < 9.5f64);
assert!(dc_array_get_accuracy((*kml).locations, 0) > 31.9f64);
assert!(dc_array_get_accuracy((*kml).locations, 0) < 32.1f64);
assert_eq!(dc_array_get_timestamp((*kml).locations, 0), 1551906597);
assert!(locations_ref[1].latitude > 63.6f64);
assert!(locations_ref[1].latitude < 63.8f64);
assert!(locations_ref[1].longitude > 19.3f64);
assert!(locations_ref[1].longitude < 19.5f64);
assert!(locations_ref[1].accuracy > 2.4f64);
assert!(locations_ref[1].accuracy < 2.6f64);
assert_eq!(locations_ref[1].timestamp, 1544739072);
assert!(dc_array_get_latitude((*kml).locations, 1) > 63.6f64);
assert!(dc_array_get_latitude((*kml).locations, 1) < 63.8f64);
assert!(dc_array_get_longitude((*kml).locations, 1) > 19.3f64);
assert!(dc_array_get_longitude((*kml).locations, 1) < 19.5f64);
assert!(dc_array_get_accuracy((*kml).locations, 1) > 2.4f64);
assert!(dc_array_get_accuracy((*kml).locations, 1) < 2.6f64);
dc_kml_unref(&mut kml);
assert_eq!(dc_array_get_timestamp((*kml).locations, 1), 1544739072);
dc_kml_unref(kml);
}
}
@@ -842,7 +907,7 @@ fn test_dc_mimeparser_with_context() {
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
);
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",);
assert_eq!(mimeparser.parts.len(), 1);
assert_eq!(carray_count(mimeparser.parts), 1);
dc_mimeparser_unref(&mut mimeparser);
}
@@ -855,7 +920,7 @@ fn test_dc_get_oauth2_url() {
let redirect_uri = "chat.delta:/com.b44t.messenger";
let res = dc_get_oauth2_url(&ctx.ctx, addr, redirect_uri);
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878%2D4mvtgf6feshskf7695nfln6002mom908%2Eapps%2Egoogleusercontent%2Ecom&redirect_uri=chat%2Edelta%3A%2Fcom%2Eb44t%2Emessenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
assert_eq!(res, Some("https://accounts.google.com/o/oauth2/auth?client_id=959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com&redirect_uri=chat.delta:/com.b44t.messenger&response_type=code&scope=https%3A%2F%2Fmail.google.com%2F%20email&access_type=offline".into()));
}
#[test]
@@ -890,24 +955,29 @@ fn test_stress_tests() {
fn test_get_contacts() {
unsafe {
let context = create_test_context();
let name = CString::yolo("some2");
let contacts = dc_get_contacts(&context.ctx, 0, name.as_ptr());
let name = to_cstring("some2");
let contacts = dc_get_contacts(&context.ctx, 0, name);
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
let name = CString::yolo("bob");
let email = CString::yolo("bob@mail.de");
let id = dc_create_contact(&context.ctx, name.as_ptr(), email.as_ptr());
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let id = dc_create_contact(&context.ctx, name, email);
assert_ne!(id, 0);
let contacts = dc_get_contacts(&context.ctx, 0, name.as_ptr());
let contacts = dc_get_contacts(&context.ctx, 0, name);
assert_eq!(dc_array_get_cnt(contacts), 1);
dc_array_unref(contacts);
let name2 = CString::yolo("alice");
let contacts = dc_get_contacts(&context.ctx, 0, name2.as_ptr());
let name2 = to_cstring("alice");
let contacts = dc_get_contacts(&context.ctx, 0, name2);
assert_eq!(dc_array_get_cnt(contacts), 0);
dc_array_unref(contacts);
free(name as *mut _);
free(name2 as *mut _);
free(email as *mut _);
}
}
@@ -915,10 +985,12 @@ fn test_get_contacts() {
fn test_chat() {
unsafe {
let context = create_test_context();
let name = CString::yolo("bob");
let email = CString::yolo("bob@mail.de");
let name = to_cstring("bob");
let email = to_cstring("bob@mail.de");
let contact1 = dc_create_contact(&context.ctx, name.as_ptr(), email.as_ptr());
let contact1 = dc_create_contact(&context.ctx, name, email);
free(name as *mut _);
free(email as *mut _);
assert_ne!(contact1, 0);
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
@@ -938,12 +1010,28 @@ fn test_chat() {
#[test]
fn test_wrong_db() {
unsafe {
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
let dir = tempdir().unwrap();
let dbfile = dir.path().join("db.sqlite");
std::fs::write(&dbfile, b"123").unwrap();
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
assert!(!res);
let dbfile_c = to_cstring(dbfile.to_str().unwrap());
let res = dc_open(&mut ctx, dbfile_c, std::ptr::null());
free(dbfile_c as *mut _);
assert_eq!(res, 0);
}
}
#[test]
fn test_arr_to_string() {
let arr2: [uint32_t; 4] = [
0i32 as uint32_t,
12i32 as uint32_t,
133i32 as uint32_t,
1999999i32 as uint32_t,
];
let str_0 = unsafe { dc_arr_to_string(arr2.as_ptr(), 4i32) };
assert_eq!(to_string(str_0), "0,12,133,1999999");
unsafe { free(str_0 as *mut _) };
}