mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 21:42:10 +03:00
Compare commits
197 Commits
fix_unshow
...
fix/str-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d47d25c1e | ||
|
|
e4f9446e3c | ||
|
|
abbbd0cb67 | ||
|
|
ae9f52e001 | ||
|
|
2414a618e2 | ||
|
|
7c34806125 | ||
|
|
c1f19aa9d3 | ||
|
|
aab2336223 | ||
|
|
7863f5719c | ||
|
|
8c933f8fa8 | ||
|
|
3d83409375 | ||
|
|
76dbb37609 | ||
|
|
e9ff02bdc5 | ||
|
|
602d145348 | ||
|
|
ee264117d3 | ||
|
|
721dcd7ccd | ||
|
|
cb138fdcb7 | ||
|
|
a993c256e2 | ||
|
|
ea6972118a | ||
|
|
70f6aa9d3a | ||
|
|
e1d7871754 | ||
|
|
655884b559 | ||
|
|
e0dbd47185 | ||
|
|
925759a922 | ||
|
|
9034f316ba | ||
|
|
0276f97d96 | ||
|
|
ac2f9fdee5 | ||
|
|
76b3c532b1 | ||
|
|
760332262d | ||
|
|
bc2314586c | ||
|
|
765ac2005e | ||
|
|
d4650ba4a9 | ||
|
|
b715df9e97 | ||
|
|
acb3d14d7d | ||
|
|
c0e3c7a9df | ||
|
|
164130a83f | ||
|
|
4d9bd46c9d | ||
|
|
76d3d86a9e | ||
|
|
c0b1e41820 | ||
|
|
10689f45f4 | ||
|
|
205e2d4e99 | ||
|
|
5b368960c0 | ||
|
|
9ecd0f80dc | ||
|
|
45eec35e6e | ||
|
|
b74a86f617 | ||
|
|
ff95c44d51 | ||
|
|
d5168916df | ||
|
|
282f964f2f | ||
|
|
7b5073b634 | ||
|
|
59bb9add2a | ||
|
|
ed95752f8f | ||
|
|
81719c4b33 | ||
|
|
fc6019e3c9 | ||
|
|
5811248bfc | ||
|
|
47b76ceb3e | ||
|
|
0051720d1b | ||
|
|
d814dffbb0 | ||
|
|
4e1e32df65 | ||
|
|
d2b23b727b | ||
|
|
d37dda6f50 | ||
|
|
c568d5dcac | ||
|
|
227ef1b467 | ||
|
|
a8cea64f39 | ||
|
|
294e855bbe | ||
|
|
c5eef21645 | ||
|
|
d64fcece5b | ||
|
|
0a9f3ae160 | ||
|
|
acbedbd352 | ||
|
|
e78b879c9e | ||
|
|
1145c3533b | ||
|
|
0d41a78182 | ||
|
|
a4f94dbf86 | ||
|
|
b6b0849bce | ||
|
|
e7428887d0 | ||
|
|
a7269e7096 | ||
|
|
7394666266 | ||
|
|
8eb5cec9ce | ||
|
|
b2807429cc | ||
|
|
07d7316a9f | ||
|
|
1f9807ccfe | ||
|
|
3358d09148 | ||
|
|
c04c8ff103 | ||
|
|
e7456248a0 | ||
|
|
3370b9cfcb | ||
|
|
022c7c2f07 | ||
|
|
9e50e77031 | ||
|
|
39e9cfd908 | ||
|
|
c29c893426 | ||
|
|
16028cc5dc | ||
|
|
39e530f759 | ||
|
|
8274c6bf17 | ||
|
|
566d255b33 | ||
|
|
9fb9fb0fc1 | ||
|
|
164a8fe39a | ||
|
|
0b679f8b95 | ||
|
|
8267ffb8d2 | ||
|
|
7bc338fc72 | ||
|
|
d6dae0a9e8 | ||
|
|
a41c0614cc | ||
|
|
73298c0273 | ||
|
|
b44c7928f2 | ||
|
|
dd9b83dc24 | ||
|
|
e79c168279 | ||
|
|
707c8c2830 | ||
|
|
f87c98d6ea | ||
|
|
76e76470e0 | ||
|
|
ae6c41a019 | ||
|
|
81a84620eb | ||
|
|
14e42b48bd | ||
|
|
2688a397aa | ||
|
|
3ace4fcc2f | ||
|
|
9314a5a8fd | ||
|
|
5bf2d7c5ac | ||
|
|
1fbd16310a | ||
|
|
7ba1a6f79f | ||
|
|
2c66837df4 | ||
|
|
21b4147d15 | ||
|
|
0c082fac7b | ||
|
|
03603a48a0 | ||
|
|
27342f50b5 | ||
|
|
2d5b04148f | ||
|
|
dfce34f275 | ||
|
|
188da2a020 | ||
|
|
3f445a3a6c | ||
|
|
669ed0e0df | ||
|
|
c6ccfd824e | ||
|
|
39cc93240f | ||
|
|
6c818c6123 | ||
|
|
3eaab07b0b | ||
|
|
0cffbaf1e9 | ||
|
|
c34e66adb6 | ||
|
|
0132106c7e | ||
|
|
5a8b4748b8 | ||
|
|
3033d8100d | ||
|
|
0e6b12d7ae | ||
|
|
2407604e37 | ||
|
|
b23ca26908 | ||
|
|
21d94b1d09 | ||
|
|
ae1cbc9596 | ||
|
|
86d047f618 | ||
|
|
1cd7cb541c | ||
|
|
f27dda86ff | ||
|
|
51319f89e8 | ||
|
|
8b4acbb63a | ||
|
|
928361429e | ||
|
|
c17632188a | ||
|
|
ea3c89e913 | ||
|
|
ea84edf13a | ||
|
|
c335348f20 | ||
|
|
1e91f6a204 | ||
|
|
dfd43cbb97 | ||
|
|
c7a6b3caae | ||
|
|
f3eea41914 | ||
|
|
8d43ad4809 | ||
|
|
1f63753a8b | ||
|
|
e796a4c438 | ||
|
|
85dfd65e48 | ||
|
|
a323fe68a6 | ||
|
|
05aca2c529 | ||
|
|
1dfad65afd | ||
|
|
e15e3a1e84 | ||
|
|
252697b174 | ||
|
|
7764ab3ff3 | ||
|
|
7585dc49e3 | ||
|
|
f0ae5fcd7c | ||
|
|
7cba2b3f66 | ||
|
|
a0594338b2 | ||
|
|
4902310138 | ||
|
|
44b8629811 | ||
|
|
30668aaecf | ||
|
|
6dfc019163 | ||
|
|
b584b7eb58 | ||
|
|
36b5f4da53 | ||
|
|
d1968d8ccb | ||
|
|
a9c714748d | ||
|
|
7a07629d68 | ||
|
|
67d9515033 | ||
|
|
f63e79cd6d | ||
|
|
83346722fd | ||
|
|
9836e73683 | ||
|
|
c7ebf6de09 | ||
|
|
2f204fd2aa | ||
|
|
63ed5c4009 | ||
|
|
9f75a5049e | ||
|
|
ec6cc5c355 | ||
|
|
b0ef825e67 | ||
|
|
a791f76e4b | ||
|
|
2cf227571a | ||
|
|
9a9b49f8f0 | ||
|
|
9d87f2f10b | ||
|
|
5de7c35622 | ||
|
|
004cdf6491 | ||
|
|
72ad8b5199 | ||
|
|
cb75ac3842 | ||
|
|
a5553f98af | ||
|
|
648d3d78aa | ||
|
|
afcf48f833 |
@@ -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:
|
||||
|
||||
343
Cargo.lock
generated
343
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@@ -25,7 +25,7 @@ imap = "1.0.1"
|
||||
mmime = "0.1.0"
|
||||
base64 = "0.10"
|
||||
charset = "0.1"
|
||||
percent-encoding = "1.0"
|
||||
percent-encoding = "2.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
chrono = "0.4.6"
|
||||
@@ -35,14 +35,16 @@ failure_derive = "0.1.5"
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.19", features = ["bundled"] }
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
addr = "0.2.0"
|
||||
r2d2_sqlite = "0.11.0"
|
||||
r2d2_sqlite = "0.12.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"
|
||||
itertools = "0.8.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -54,10 +56,6 @@ 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"
|
||||
|
||||
@@ -63,6 +63,11 @@ 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:
|
||||
|
||||
```
|
||||
|
||||
@@ -4,6 +4,7 @@ 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"
|
||||
|
||||
@@ -18,6 +18,7 @@ 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"]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,14 @@
|
||||
use std::ffi::CString;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_chatlist::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_contact::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_job::*;
|
||||
use deltachat::dc_location::*;
|
||||
@@ -146,7 +147,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
} else {
|
||||
current_block = 7149356873433890176;
|
||||
}
|
||||
real_spec = to_cstring(rs.unwrap_or_default());
|
||||
real_spec = rs.unwrap_or_default().strdup();
|
||||
}
|
||||
match current_block {
|
||||
8522321847195001863 => {}
|
||||
@@ -183,11 +184,10 @@ 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 = to_cstring(path_plus_name);
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c) {
|
||||
let path_plus_name_c = CString::yolo(path_plus_name);
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
||||
read_cnt += 1
|
||||
}
|
||||
free(path_plus_name_c as *mut _);
|
||||
}
|
||||
}
|
||||
current_block = 1622411330066726685;
|
||||
@@ -218,18 +218,19 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
}
|
||||
|
||||
unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t) {
|
||||
let contact: *mut dc_contact_t = dc_get_contact(context, dc_msg_get_from_id(msg));
|
||||
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 contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact");
|
||||
let contact_name = contact.get_name();
|
||||
let contact_id = contact.get_id();
|
||||
|
||||
let statestr = match dc_msg_get_state(msg) {
|
||||
20 => " o",
|
||||
26 => " √",
|
||||
28 => " √√",
|
||||
24 => " !!",
|
||||
DC_STATE_OUT_PENDING => " o",
|
||||
DC_STATE_OUT_DELIVERED => " √",
|
||||
DC_STATE_OUT_MDN_RCVD => " √√",
|
||||
DC_STATE_OUT_FAILED => " !!",
|
||||
_ => "",
|
||||
};
|
||||
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);
|
||||
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
|
||||
let msgtext = dc_msg_get_text(msg);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
@@ -242,19 +243,15 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
""
|
||||
},
|
||||
if dc_msg_has_location(msg) { "📍" } else { "" },
|
||||
as_str(contact_name),
|
||||
&contact_name,
|
||||
contact_id,
|
||||
as_str(msgtext),
|
||||
if 0 != dc_msg_is_starred(msg) {
|
||||
"★"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
if 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) == 16 {
|
||||
} else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN {
|
||||
"[SEEN]"
|
||||
} else if dc_msg_get_state(msg) == 13 {
|
||||
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
|
||||
"[NOTICED]"
|
||||
} else {
|
||||
"[FRESH]"
|
||||
@@ -265,12 +262,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
""
|
||||
},
|
||||
statestr,
|
||||
as_str(temp2),
|
||||
&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);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
@@ -308,7 +302,6 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
}
|
||||
|
||||
unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
|
||||
let mut contact: *mut dc_contact_t;
|
||||
if !dc_array_search_id(contacts, 1 as uint32_t, 0 as *mut size_t) {
|
||||
dc_array_add_id(contacts, 1 as uint32_t);
|
||||
}
|
||||
@@ -317,13 +310,12 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
|
||||
let contact_id = dc_array_get_id(contacts, i as size_t);
|
||||
let line;
|
||||
let mut line2 = "".to_string();
|
||||
contact = dc_get_contact(context, contact_id);
|
||||
if !contact.is_null() {
|
||||
let name: *mut libc::c_char = dc_contact_get_name(contact);
|
||||
let addr: *mut libc::c_char = dc_contact_get_addr(contact);
|
||||
let verified_state: libc::c_int = dc_contact_is_verified(contact);
|
||||
let verified_str = if 0 != verified_state {
|
||||
if verified_state == 2 {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
let name = contact.get_name();
|
||||
let addr = contact.get_addr();
|
||||
let verified_state = contact.is_verified();
|
||||
let verified_str = if VerifiedStatus::Unverified != verified_state {
|
||||
if verified_state == VerifiedStatus::BidirectVerified {
|
||||
" √√"
|
||||
} else {
|
||||
" √"
|
||||
@@ -333,28 +325,26 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) {
|
||||
};
|
||||
line = format!(
|
||||
"{}{} <{}>",
|
||||
if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int {
|
||||
as_str(name)
|
||||
if !name.is_empty() {
|
||||
&name
|
||||
} else {
|
||||
"<name unset>"
|
||||
},
|
||||
verified_str,
|
||||
if !addr.is_null() && 0 != *addr.offset(0isize) as libc::c_int {
|
||||
as_str(addr)
|
||||
if !addr.is_empty() {
|
||||
&addr
|
||||
} else {
|
||||
"addr unset"
|
||||
}
|
||||
);
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, as_str(addr));
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, &addr);
|
||||
if peerstate.is_some() && contact_id != 1 as libc::c_uint {
|
||||
line2 = format!(
|
||||
", prefer-encrypt={}",
|
||||
peerstate.as_ref().unwrap().prefer_encrypt
|
||||
);
|
||||
}
|
||||
dc_contact_unref(contact);
|
||||
free(name as *mut libc::c_void);
|
||||
free(addr as *mut libc::c_void);
|
||||
|
||||
info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2);
|
||||
}
|
||||
}
|
||||
@@ -390,13 +380,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let arg1_c = if arg1.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
to_cstring(arg1) as *const _
|
||||
arg1.strdup() as *const _
|
||||
};
|
||||
let arg2 = args.next().unwrap_or_default();
|
||||
let arg2_c = if arg2.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
to_cstring(arg2) as *const _
|
||||
arg2.strdup() as *const _
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
@@ -450,6 +440,7 @@ 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\
|
||||
@@ -501,10 +492,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"open" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing");
|
||||
dc_close(context);
|
||||
ensure!(
|
||||
0 != dc_open(context, arg1_c, 0 as *const libc::c_char),
|
||||
"Open failed"
|
||||
);
|
||||
ensure!(dc_open(context, arg1, None), "Open failed");
|
||||
}
|
||||
"close" => {
|
||||
dc_close(context);
|
||||
@@ -523,7 +511,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().unwrap();
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
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);
|
||||
@@ -543,7 +531,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().unwrap(), arg2_c) {
|
||||
if 0 == dc_continue_key_transfer(context, arg1.parse()?, arg2_c) {
|
||||
bail!("Continue key transfer failed");
|
||||
}
|
||||
}
|
||||
@@ -567,39 +555,26 @@ 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: *mut libc::c_char = dc_create_setup_code(context);
|
||||
let setup_code = 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: *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);
|
||||
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,
|
||||
);
|
||||
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().unwrap();
|
||||
let bits: i32 = arg1.parse()?;
|
||||
ensure!(bits < 16, "<bits> must be lower than 16.");
|
||||
ensure!(0 != dc_reset_tables(context, bits), "Reset failed");
|
||||
}
|
||||
@@ -629,11 +604,10 @@ 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 = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t);
|
||||
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
|
||||
let chatlist = Chatlist::try_load(context, listflags, Some(arg1), None)?;
|
||||
|
||||
let mut i: libc::c_int;
|
||||
let cnt = dc_chatlist_get_cnt(chatlist) as libc::c_int;
|
||||
let mut i: usize;
|
||||
let cnt = chatlist.len();
|
||||
if cnt > 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
@@ -642,8 +616,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, dc_chatlist_get_chat_id(chatlist, i as size_t));
|
||||
while i > 0 {
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
let temp_subtitle = dc_chat_get_subtitle(chat);
|
||||
let temp_name = dc_chat_get_name(chat);
|
||||
info!(
|
||||
@@ -658,7 +632,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 = dc_chatlist_get_summary(chatlist, i as size_t, chat);
|
||||
let lot = chatlist.get_summary(i, chat);
|
||||
let statestr = if 0 != dc_chat_get_archived(chat) {
|
||||
" [Archived]"
|
||||
} else {
|
||||
@@ -681,7 +655,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
if !text1.is_null() { ": " } else { "" },
|
||||
to_string(text2),
|
||||
statestr,
|
||||
as_str(timestr),
|
||||
×tr,
|
||||
if 0 != dc_chat_is_sending_locations(chat) {
|
||||
"📍"
|
||||
} else {
|
||||
@@ -690,7 +664,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
free(text1 as *mut libc::c_void);
|
||||
free(text2 as *mut libc::c_void);
|
||||
free(timestr as *mut libc::c_void);
|
||||
dc_lot_unref(lot);
|
||||
dc_chat_unref(chat);
|
||||
info!(
|
||||
@@ -705,7 +678,6 @@ 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() {
|
||||
@@ -715,7 +687,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().unwrap();
|
||||
let chat_id = arg1.parse()?;
|
||||
println!("Selecting chat #{}", chat_id);
|
||||
sel_chat = dc_get_chat(context, chat_id);
|
||||
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
||||
@@ -759,7 +731,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().unwrap();
|
||||
let contact_id: libc::c_int = arg1.parse()?;
|
||||
let chat_id: libc::c_int =
|
||||
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
|
||||
if chat_id != 0 {
|
||||
@@ -770,7 +742,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().unwrap();
|
||||
let msg_id_0: libc::c_int = arg1.parse()?;
|
||||
let chat_id_0: libc::c_int =
|
||||
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
|
||||
if chat_id_0 != 0 {
|
||||
@@ -807,7 +779,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().unwrap();
|
||||
let contact_id_0: libc::c_int = arg1.parse()?;
|
||||
if 0 != dc_add_contact_to_chat(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
@@ -821,7 +793,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().unwrap();
|
||||
let contact_id_1: libc::c_int = arg1.parse()?;
|
||||
if 0 != dc_remove_contact_from_chat(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
@@ -886,7 +858,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),
|
||||
as_str(timestr_0),
|
||||
×tr_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),
|
||||
@@ -899,7 +871,6 @@ 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
|
||||
}
|
||||
@@ -912,7 +883,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().unwrap();
|
||||
let seconds = arg1.parse()?;
|
||||
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
|
||||
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
|
||||
}
|
||||
@@ -921,8 +892,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().unwrap();
|
||||
let longitude = arg2.parse().unwrap();
|
||||
let latitude = arg1.parse()?;
|
||||
let longitude = arg2.parse()?;
|
||||
|
||||
let continue_streaming = dc_set_location(context, latitude, longitude, 0.);
|
||||
if 0 != continue_streaming {
|
||||
@@ -938,23 +909,17 @@ 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 = to_cstring(format!("{} {}", arg1, arg2));
|
||||
let msg = format!("{} {}", arg1, arg2);
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
"sendempty" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
if 0 != dc_send_text_msg(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
) {
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
|
||||
println!("Message sent.");
|
||||
} else {
|
||||
bail!("Sending failed.");
|
||||
@@ -964,7 +929,14 @@ 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" { 20 } else { 60 });
|
||||
let msg_0 = dc_msg_new(
|
||||
context,
|
||||
if arg0 == "sendimage" {
|
||||
Viewtype::Image
|
||||
} else {
|
||||
Viewtype::File
|
||||
},
|
||||
);
|
||||
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);
|
||||
@@ -991,7 +963,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, 10);
|
||||
let draft_0 = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(draft_0, arg1_c);
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
|
||||
dc_msg_unref(draft_0);
|
||||
@@ -1004,7 +976,13 @@ 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), 20, 21, 50);
|
||||
let images = dc_get_chat_media(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
Viewtype::Image,
|
||||
Viewtype::Gif,
|
||||
Viewtype::Video,
|
||||
);
|
||||
let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int;
|
||||
println!("{} images or videos: ", icnt);
|
||||
for i in 0..icnt {
|
||||
@@ -1020,17 +998,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().unwrap();
|
||||
let chat_id = arg1.parse()?;
|
||||
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().unwrap();
|
||||
let chat_id = arg1.parse()?;
|
||||
dc_delete_chat(context, chat_id);
|
||||
}
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let id = arg1.parse().unwrap();
|
||||
let id = arg1.parse()?;
|
||||
let res = dc_get_msg_info(context, id);
|
||||
println!("{}", as_str(res));
|
||||
}
|
||||
@@ -1049,20 +1027,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
|
||||
let mut msg_ids = [0; 1];
|
||||
let chat_id = arg2.parse().unwrap();
|
||||
msg_ids[0] = arg1.parse().unwrap();
|
||||
let chat_id = arg2.parse()?;
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
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().unwrap();
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
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().unwrap();
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
dc_star_msgs(
|
||||
context,
|
||||
msg_ids.as_mut_ptr(),
|
||||
@@ -1073,19 +1051,19 @@ 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().unwrap();
|
||||
ids[0] = arg1.parse()?;
|
||||
dc_delete_msgs(context, ids.as_mut_ptr(), 1);
|
||||
}
|
||||
"listcontacts" | "contacts" | "listverified" => {
|
||||
let contacts = dc_get_contacts(
|
||||
let contacts = Contact::get_all(
|
||||
context,
|
||||
if arg0 == "listverified" {
|
||||
0x1 | 0x2
|
||||
} else {
|
||||
0x2
|
||||
},
|
||||
arg1_c,
|
||||
);
|
||||
Some(arg1),
|
||||
)?;
|
||||
if !contacts.is_null() {
|
||||
log_contactlist(context, contacts);
|
||||
println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,);
|
||||
@@ -1098,36 +1076,25 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty(), "Arguments [<name>] <addr> expected.");
|
||||
|
||||
if !arg2.is_empty() {
|
||||
let book = dc_mprintf(
|
||||
b"%s\n%s\x00" as *const u8 as *const libc::c_char,
|
||||
arg1_c,
|
||||
arg2_c,
|
||||
);
|
||||
dc_add_address_book(context, book);
|
||||
free(book as *mut libc::c_void);
|
||||
let book = format!("{}\n{}", arg1, arg2);
|
||||
Contact::add_address_book(context, book)?;
|
||||
} else {
|
||||
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) {
|
||||
bail!("Failed to create contact");
|
||||
}
|
||||
Contact::create(context, "", arg1)?;
|
||||
}
|
||||
}
|
||||
"contactinfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
|
||||
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);
|
||||
let contact_id = arg1.parse()?;
|
||||
let contact = Contact::get_by_id(context, contact_id)?;
|
||||
let name_n_addr = contact.get_name_n_addr();
|
||||
|
||||
let mut res = format!("Contact info for: {}:\n\n", as_str(name_n_addr),);
|
||||
free(name_n_addr as *mut libc::c_void);
|
||||
dc_contact_unref(contact);
|
||||
let mut res = format!("Contact info for: {}:\n\n", name_n_addr);
|
||||
|
||||
let encrinfo = dc_get_contact_encrinfo(context, contact_id);
|
||||
res += as_str(encrinfo);
|
||||
free(encrinfo as *mut libc::c_void);
|
||||
res += &Contact::get_encrinfo(context, contact_id);
|
||||
|
||||
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;
|
||||
let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?;
|
||||
let chatlist_cnt = chatlist.len();
|
||||
if chatlist_cnt > 0 {
|
||||
res += &format!(
|
||||
"\n\n{} chats shared with Contact#{}: ",
|
||||
@@ -1137,19 +1104,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
if 0 != i {
|
||||
res += ", ";
|
||||
}
|
||||
let chat = dc_get_chat(context, dc_chatlist_get_chat_id(chatlist, i as size_t));
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
|
||||
dc_chat_unref(chat);
|
||||
}
|
||||
}
|
||||
dc_chatlist_unref(chatlist);
|
||||
|
||||
println!("{}", res);
|
||||
}
|
||||
"delcontact" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
if !dc_delete_contact(context, arg1.parse().unwrap()) {
|
||||
bail!("Failed to delete contact");
|
||||
}
|
||||
Contact::delete(context, arg1.parse()?)?;
|
||||
}
|
||||
"checkqr" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
||||
@@ -1165,7 +1130,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"event" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||
let event = Event::from_u32(arg1.parse().unwrap()).unwrap();
|
||||
let event = arg1.parse()?;
|
||||
let event = Event::from_u32(event).ok_or(format_err!("Event::from_u32({})", event))?;
|
||||
let r = context.call_cb(event, 0 as uintptr_t, 0 as uintptr_t);
|
||||
println!(
|
||||
"Sending event {:?}({}), received value {}.",
|
||||
@@ -1192,6 +1158,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
bail!("Command failed.");
|
||||
}
|
||||
}
|
||||
"" => (),
|
||||
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
//!
|
||||
//! 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;
|
||||
@@ -391,18 +392,13 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
let mut context = dc_context_new(
|
||||
Some(receive_event),
|
||||
0 as *mut libc::c_void,
|
||||
b"CLI\x00" as *const u8 as *const libc::c_char,
|
||||
Some("CLI".into()),
|
||||
);
|
||||
|
||||
unsafe { dc_cmdline_skip_auth() };
|
||||
|
||||
if args.len() == 2 {
|
||||
if 0 == unsafe {
|
||||
let a = to_cstring(&args[1]);
|
||||
let res = dc_open(&mut context, a, 0 as *const _);
|
||||
free(a as *mut _);
|
||||
res
|
||||
} {
|
||||
if unsafe { !dc_open(&mut context, &args[1], None) } {
|
||||
println!("Error: Cannot open {}.", args[0],);
|
||||
}
|
||||
} else if args.len() != 1 {
|
||||
@@ -484,7 +480,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
let arg1_c = if arg1.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
to_cstring(arg1)
|
||||
arg1.strdup()
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
extern crate deltachat;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::constants::Event;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_chatlist::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_contact::*;
|
||||
use deltachat::dc_job::{
|
||||
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
|
||||
dc_perform_smtp_jobs,
|
||||
@@ -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(), std::ptr::null_mut());
|
||||
let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
||||
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 = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
|
||||
println!("opening database {:?}", dbfile);
|
||||
|
||||
assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1);
|
||||
assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None));
|
||||
|
||||
println!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
@@ -93,18 +93,17 @@ fn main() {
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
let email = CString::new("dignifiedquire@gmail.com").unwrap();
|
||||
println!("sending a message");
|
||||
let contact_id = dc_create_contact(&ctx, std::ptr::null(), email.as_ptr());
|
||||
let contact_id =
|
||||
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
|
||||
let msg_text = CString::new("Hi, here is my first message!").unwrap();
|
||||
dc_send_text_msg(&ctx, chat_id, msg_text.as_ptr());
|
||||
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = dc_get_chatlist(&ctx, 0, std::ptr::null(), 0);
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..dc_chatlist_get_cnt(chats) {
|
||||
let summary = dc_chatlist_get_summary(chats, 0, std::ptr::null_mut());
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(0, std::ptr::null_mut());
|
||||
let text1 = dc_lot_get_text1(summary);
|
||||
let text2 = dc_lot_get_text2(summary);
|
||||
|
||||
@@ -121,7 +120,6 @@ fn main() {
|
||||
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
|
||||
dc_lot_unref(summary);
|
||||
}
|
||||
dc_chatlist_unref(chats);
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ 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
|
||||
-------
|
||||
|
||||
@@ -288,10 +288,6 @@ 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):
|
||||
|
||||
@@ -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', 'attrs', 'six'],
|
||||
install_requires=['cffi>=1.0.0', 'six'],
|
||||
packages=setuptools.find_packages('src'),
|
||||
package_dir={'': 'src'},
|
||||
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
||||
|
||||
@@ -7,9 +7,9 @@ import re
|
||||
import time
|
||||
from array import array
|
||||
try:
|
||||
from queue import Queue
|
||||
from queue import Queue, Empty
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
from Queue import Queue, Empty
|
||||
|
||||
import deltachat
|
||||
from . import const
|
||||
@@ -142,15 +142,6 @@ 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
|
||||
@@ -166,6 +157,17 @@ 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.
|
||||
|
||||
@@ -201,7 +203,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._dc_context, chat_id)
|
||||
return Chat(self, chat_id)
|
||||
|
||||
def create_chat_by_message(self, message):
|
||||
""" create or get an existing chat object for the
|
||||
@@ -218,7 +220,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._dc_context, chat_id)
|
||||
return Chat(self, chat_id)
|
||||
|
||||
def create_group_chat(self, name, verified=False):
|
||||
""" create a new group chat object.
|
||||
@@ -230,7 +232,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._dc_context, chat_id)
|
||||
return Chat(self, chat_id)
|
||||
|
||||
def get_chats(self):
|
||||
""" return list of chats.
|
||||
@@ -246,15 +248,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._dc_context, chat_id))
|
||||
chatlist.append(Chat(self, chat_id))
|
||||
return chatlist
|
||||
|
||||
def get_deaddrop_chat(self):
|
||||
return Chat(self._dc_context, const.DC_CHAT_ID_DEADDROP)
|
||||
return Chat(self, const.DC_CHAT_ID_DEADDROP)
|
||||
|
||||
def get_message_by_id(self, msg_id):
|
||||
""" return Message instance. """
|
||||
return Message.from_db(self._dc_context, msg_id)
|
||||
return Message.from_db(self, msg_id)
|
||||
|
||||
def mark_seen_messages(self, messages):
|
||||
""" mark the given set of messages as seen.
|
||||
@@ -437,6 +439,17 @@ 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))
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
""" chatting related objects: Contact, Chat, Message. """
|
||||
|
||||
import os
|
||||
|
||||
import mimetypes
|
||||
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`.
|
||||
"""
|
||||
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
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)
|
||||
|
||||
@property
|
||||
def _dc_contact(self):
|
||||
@@ -46,14 +52,26 @@ 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`.
|
||||
"""
|
||||
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
id = attr.ib(validator=v.instance_of(int))
|
||||
|
||||
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)
|
||||
|
||||
@property
|
||||
def _dc_chat(self):
|
||||
@@ -126,7 +144,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._dc_context, msg_id)
|
||||
return Message.from_db(self.account, msg_id)
|
||||
|
||||
def send_file(self, path, mime_type="application/octet-stream"):
|
||||
""" send a file and return the resulting Message instance.
|
||||
@@ -136,14 +154,9 @@ class Chat(object):
|
||||
:raises ValueError: if message can not be send/chat does not exist.
|
||||
:returns: the resulting :class:`deltachat.message.Message` instance
|
||||
"""
|
||||
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)
|
||||
msg = self.prepare_message_file(path=path, mime_type=mime_type)
|
||||
self.send_prepared(msg)
|
||||
return msg
|
||||
|
||||
def send_image(self, path):
|
||||
""" send an image message and return the resulting Message instance.
|
||||
@@ -152,14 +165,25 @@ class Chat(object):
|
||||
:raises ValueError: if message can not be send/chat does not exist.
|
||||
:returns: the resulting :class:`deltachat.message.Message` instance
|
||||
"""
|
||||
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)
|
||||
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
|
||||
|
||||
def prepare_file(self, path, mime_type=None, view_type="file"):
|
||||
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"):
|
||||
""" prepare a message for sending and return the resulting Message instance.
|
||||
|
||||
To actually send the message, call :meth:`send_prepared`.
|
||||
@@ -167,18 +191,13 @@ 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: passed to :meth:`MessageType.new`.
|
||||
:param view_type: "text", "image", "gif", "audio", "video", "file"
|
||||
:raises ValueError: if message can not be prepared/chat does not exist.
|
||||
:returns: the resulting :class:`Message` instance
|
||||
"""
|
||||
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)
|
||||
msg = Message.new_empty(self.account, view_type)
|
||||
msg.set_file(path, mime_type)
|
||||
return self.prepare_message(msg)
|
||||
|
||||
def send_prepared(self, message):
|
||||
""" send a previously prepared message.
|
||||
@@ -186,12 +205,42 @@ 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 with updated state
|
||||
:returns: a :class:`deltachat.message.Message` instance as sent out.
|
||||
"""
|
||||
msg_id = lib.dc_send_msg(self._dc_context, 0, message._dc_msg)
|
||||
if msg_id == 0:
|
||||
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:
|
||||
raise ValueError("message could not be sent")
|
||||
return Message.from_db(self._dc_context, msg_id)
|
||||
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)
|
||||
|
||||
def get_messages(self):
|
||||
""" return list of messages in this chat.
|
||||
@@ -202,7 +251,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._dc_context, x)))
|
||||
return list(iter_array(dc_array, lambda x: Message.from_db(self.account, x)))
|
||||
|
||||
def count_fresh_messages(self):
|
||||
""" return number of fresh messages in this chat.
|
||||
|
||||
@@ -1,59 +1,55 @@
|
||||
""" 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`.
|
||||
"""
|
||||
_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 __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)
|
||||
|
||||
@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
|
||||
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, _dc_context, id):
|
||||
def from_db(cls, account, id):
|
||||
assert id > 0
|
||||
return cls(_dc_context, id)
|
||||
return cls(account, ffi.gc(
|
||||
lib.dc_get_msg(account._dc_context, id),
|
||||
lib.dc_msg_unref
|
||||
))
|
||||
|
||||
@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 new_empty(cls, account, view_type):
|
||||
""" create a non-persistent message.
|
||||
|
||||
def get_state(self):
|
||||
""" get the message in/out state.
|
||||
|
||||
:returns: :class:`deltachat.message.MessageState`
|
||||
:param: view_type is "text", "audio", "video", "file"
|
||||
"""
|
||||
return MessageState(self)
|
||||
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),
|
||||
lib.dc_msg_unref
|
||||
))
|
||||
|
||||
@props.with_doc
|
||||
def text(self):
|
||||
@@ -62,7 +58,9 @@ class Message(object):
|
||||
|
||||
def set_text(self, text):
|
||||
"""set text of this message. """
|
||||
return lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
|
||||
assert self.id > 0, "message not prepared"
|
||||
assert self.is_out_preparing()
|
||||
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
|
||||
|
||||
@props.with_doc
|
||||
def filename(self):
|
||||
@@ -70,9 +68,23 @@ 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. """
|
||||
mtype = ffi.NULL if mime_type is None else mime_type
|
||||
assert os.path.exists(path)
|
||||
"""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
|
||||
lib.dc_msg_set_file(self._dc_msg, as_dc_charpointer(path), mtype)
|
||||
|
||||
@props.with_doc
|
||||
@@ -85,18 +97,17 @@ 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))
|
||||
@@ -144,7 +155,7 @@ class Message(object):
|
||||
"""
|
||||
from .chatting import Chat
|
||||
chat_id = lib.dc_msg_get_chat_id(self._dc_msg)
|
||||
return Chat(self._dc_context, chat_id)
|
||||
return Chat(self.account, chat_id)
|
||||
|
||||
def get_sender_contact(self):
|
||||
"""return the contact of who wrote the message.
|
||||
@@ -155,66 +166,20 @@ class Message(object):
|
||||
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
|
||||
return Contact(self._dc_context, contact_id)
|
||||
|
||||
|
||||
@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))
|
||||
|
||||
#
|
||||
# Message State query methods
|
||||
#
|
||||
@property
|
||||
def _msgstate(self):
|
||||
return lib.dc_msg_get_state(self.message._dc_msg)
|
||||
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)
|
||||
|
||||
def is_in_fresh(self):
|
||||
""" return True if Message is incoming fresh message (un-noticed).
|
||||
@@ -268,3 +233,56 @@ class MessageState(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())))
|
||||
|
||||
@@ -2,11 +2,12 @@ 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 TestOfflineAccount:
|
||||
class TestOfflineAccountBasic:
|
||||
def test_wrong_db(self, tmpdir):
|
||||
p = tmpdir.join("hello.db")
|
||||
p.write("123")
|
||||
@@ -57,16 +58,22 @@ class TestOfflineAccount:
|
||||
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(self, acfactory):
|
||||
def test_get_contacts_and_delete(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact(email="some1@hello.com", name="some1")
|
||||
contacts = ac1.get_contacts()
|
||||
@@ -79,26 +86,48 @@ class TestOfflineAccount:
|
||||
contacts = ac1.get_contacts(with_self=True)
|
||||
assert len(contacts) == 2
|
||||
|
||||
def test_chat(self, acfactory):
|
||||
assert ac1.delete_contact(contact1)
|
||||
assert contact1 not in ac1.get_contacts()
|
||||
|
||||
def test_get_contacts_and_delete_fails(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 == chat.id
|
||||
assert chat2.get_name() == chat.get_name()
|
||||
assert chat == chat2
|
||||
assert not (chat != chat2)
|
||||
assert chat2.id == chat1.id
|
||||
assert chat2.get_name() == chat1.get_name()
|
||||
assert chat1 == chat2
|
||||
assert not (chat1 != chat2)
|
||||
|
||||
for ichat in ac1.get_chats():
|
||||
if ichat.id == chat.id:
|
||||
if ichat.id == chat1.id:
|
||||
break
|
||||
else:
|
||||
pytest.fail("could not find chat")
|
||||
|
||||
def test_group_chat_creation(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
def test_group_chat_creation(self, ac1):
|
||||
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")
|
||||
@@ -111,64 +140,73 @@ class TestOfflineAccount:
|
||||
chat.set_name("title2")
|
||||
assert chat.get_name() == "title2"
|
||||
|
||||
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()
|
||||
def test_delete_and_send_fails(self, ac1, chat1):
|
||||
chat1.delete()
|
||||
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
with pytest.raises(ValueError):
|
||||
chat.send_text("msg1")
|
||||
chat1.send_text("msg1")
|
||||
|
||||
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_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_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")
|
||||
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")
|
||||
assert msg
|
||||
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 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()
|
||||
|
||||
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)
|
||||
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_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)
|
||||
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):
|
||||
with pytest.raises(ValueError):
|
||||
chat.send_image(path="notexists")
|
||||
chat1.send_image(path="notexists")
|
||||
fn = data.get_path("d.png")
|
||||
lp.sec("sending image")
|
||||
msg = chat.send_image(fn)
|
||||
assert msg.view_type.name == "image"
|
||||
msg = chat1.send_image(fn)
|
||||
assert msg.is_image()
|
||||
assert msg
|
||||
assert msg.id > 0
|
||||
assert os.path.exists(msg.filename)
|
||||
@@ -179,20 +217,19 @@ class TestOfflineAccount:
|
||||
("text/plain", "text/plain"),
|
||||
("image/png", "image/png"),
|
||||
])
|
||||
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)
|
||||
def test_message_file(self, ac1, chat1, data, lp, typein, typeout):
|
||||
lp.sec("sending file")
|
||||
fn = data.get_path("r.txt")
|
||||
msg = chat.send_file(fn, typein)
|
||||
msg = chat1.send_file(fn, typein)
|
||||
assert msg
|
||||
assert msg.id > 0
|
||||
assert msg.view_type.name == "file"
|
||||
assert msg.view_type.is_file()
|
||||
assert msg.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()
|
||||
@@ -205,12 +242,9 @@ class TestOfflineAccount:
|
||||
with pytest.raises(ValueError):
|
||||
ac2.create_chat_by_message(msg)
|
||||
|
||||
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)
|
||||
def test_chat_message_distinctions(self, ac1, chat1):
|
||||
past1s = datetime.utcnow() - timedelta(seconds=1)
|
||||
msg = chat.send_text("msg1")
|
||||
msg = chat1.send_text("msg1")
|
||||
ts = msg.time_sent
|
||||
assert msg.time_received is None
|
||||
assert ts.strftime("Y")
|
||||
@@ -218,8 +252,7 @@ class TestOfflineAccount:
|
||||
contact = msg.get_sender_contact()
|
||||
assert contact == ac1.get_self_contact()
|
||||
|
||||
def test_basic_configure_ok_addr_setting_forbidden(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
def test_basic_configure_ok_addr_setting_forbidden(self, ac1):
|
||||
assert ac1.get_config("mail_pw")
|
||||
assert ac1.is_configured()
|
||||
with pytest.raises(ValueError):
|
||||
@@ -258,11 +291,21 @@ class TestOfflineAccount:
|
||||
assert messages[0].text == "msg1"
|
||||
assert os.path.exists(messages[1].filename)
|
||||
|
||||
def test_ac_setup_message_fails(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
def test_ac_setup_message_fails(self, ac1):
|
||||
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):
|
||||
@@ -294,48 +337,13 @@ class TestOnlineAccount:
|
||||
wait_successful_IMAP_SMTP_connection(ac2)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
|
||||
txt = "message1"
|
||||
msg_out = chat.send_text(txt)
|
||||
msg_out = chat.send_text("message1")
|
||||
|
||||
# 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 == 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
|
||||
assert msg_in.text == "message1"
|
||||
|
||||
def test_forward_messages(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
@@ -386,7 +394,7 @@ class TestOnlineAccount:
|
||||
evt_name, data1, data2 = ev
|
||||
assert data1 == chat.id
|
||||
assert data2 == msg_out.id
|
||||
assert msg_out.get_state().is_out_delivered()
|
||||
assert msg_out.is_out_delivered()
|
||||
|
||||
lp.sec("wait for ac2 to receive message")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
@@ -413,10 +421,11 @@ class TestOnlineAccount:
|
||||
lp.sec("mark message as seen on ac2, wait for changes on ac1")
|
||||
ac2.mark_seen_messages([msg_in])
|
||||
lp.step("1")
|
||||
ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ")
|
||||
assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL
|
||||
lp.step("2")
|
||||
# ac1._evlogger.get_info_matching("Message marked as seen")
|
||||
assert msg_out.get_state().is_out_mdn_received()
|
||||
assert msg_out.is_out_mdn_received()
|
||||
|
||||
def test_saved_mime_on_received_message(self, acfactory, lp):
|
||||
lp.sec("starting accounts, waiting for configuration")
|
||||
@@ -456,13 +465,13 @@ class TestOnlineAccount:
|
||||
evt_name, data1, data2 = ev
|
||||
assert data1 == chat.id
|
||||
assert data2 == msg_out.id
|
||||
assert msg_out.get_state().is_out_delivered()
|
||||
assert msg_out.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.view_type.is_image()
|
||||
assert msg_in.is_image()
|
||||
assert os.path.exists(msg_in.filename)
|
||||
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
|
||||
|
||||
@@ -498,7 +507,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(10)
|
||||
ac2._evlogger.set_timeout(30)
|
||||
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()
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
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
|
||||
@@ -13,18 +11,15 @@ 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 = os.path.join(blobdir, "d.png")
|
||||
open(path, 'a').close()
|
||||
prepared_original = chat.prepare_file(path)
|
||||
assert prepared_original.get_state().is_out_preparing()
|
||||
path = data.get_path("d.png")
|
||||
prepared_original = chat.prepare_message_file(path)
|
||||
assert prepared_original.is_out_preparing()
|
||||
wait_msgs_changed(ac1, chat.id, prepared_original.id)
|
||||
|
||||
lp.sec("forward the message while still in creation")
|
||||
@@ -37,35 +32,36 @@ 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.get_state().is_out_preparing()
|
||||
assert forwarded_msg.is_out_preparing()
|
||||
|
||||
lp.sec("finish creating the file and send it")
|
||||
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)
|
||||
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)
|
||||
|
||||
lp.sec("expect the forwarded message to be sent now too")
|
||||
wait_msgs_changed(ac1, chat2.id, forwarded_id)
|
||||
state = ac1.get_message_by_id(forwarded_id).get_state()
|
||||
assert state.is_out_pending() or state.is_out_delivered()
|
||||
fwd_msg = ac1.get_message_by_id(forwarded_id)
|
||||
assert fwd_msg.is_out_pending() or fwd_msg.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] == sent_original.id
|
||||
assert ev[2] == prepared_original.id
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
|
||||
assert ev[1] == chat2.id
|
||||
assert ev[2] == forwarded_id
|
||||
|
||||
lp.sec("wait for both messages to arrive")
|
||||
lp.sec("wait1 for original or forwarded 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]
|
||||
|
||||
@@ -64,3 +64,14 @@ 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")
|
||||
|
||||
@@ -8,7 +8,7 @@ envlist =
|
||||
|
||||
[testenv]
|
||||
commands =
|
||||
pytest -rsXx {posargs:tests}
|
||||
pytest -v -rsXx {posargs:tests}
|
||||
python tests/package_wheels.py {toxworkdir}/wheelhouse
|
||||
passenv =
|
||||
TRAVIS
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::{fmt, str};
|
||||
use mmime::mailimf_types::*;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_tools::as_str;
|
||||
use crate::key::*;
|
||||
|
||||
@@ -94,7 +94,7 @@ impl Aheader {
|
||||
|
||||
match Self::from_str(value) {
|
||||
Ok(test) => {
|
||||
if dc_addr_cmp(&test.addr, as_str(wanted_from)) {
|
||||
if addr_cmp(&test.addr, as_str(wanted_from)) {
|
||||
if fine_header.is_none() {
|
||||
fine_header = Some(test);
|
||||
} else {
|
||||
|
||||
334
src/chatlist.rs
Normal file
334
src/chatlist.rs
Normal file
@@ -0,0 +1,334 @@
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
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 = None;
|
||||
|
||||
if chat.is_null() {
|
||||
chat = dc_chat_new(self.context);
|
||||
let chat_to_delete = chat;
|
||||
if !dc_chat_load_from_db(chat, self.ids[index].0) {
|
||||
(*ret).text2 = "ErrCannotReadChat".strdup();
|
||||
dc_chat_unref(chat_to_delete);
|
||||
|
||||
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 = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
|
||||
}
|
||||
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_UNDEFINED as u32 {
|
||||
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
|
||||
} else {
|
||||
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
|
||||
}
|
||||
|
||||
dc_msg_unref(lastmsg);
|
||||
|
||||
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()
|
||||
}
|
||||
@@ -4,10 +4,9 @@ use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
use crate::constants::DC_VERSION_STR;
|
||||
use crate::context::Context;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::x::*;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// The available configuration keys.
|
||||
#[derive(
|
||||
@@ -70,17 +69,7 @@ impl Context {
|
||||
let value = match key {
|
||||
Config::Selfavatar => {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
rel_path.map(|p| {
|
||||
let v = unsafe {
|
||||
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 _) };
|
||||
r
|
||||
})
|
||||
rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string())
|
||||
}
|
||||
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
@@ -94,12 +83,7 @@ impl Context {
|
||||
|
||||
// Default values
|
||||
match key {
|
||||
Config::Selfstatus => {
|
||||
let s = unsafe { dc_stock_str(self, 13) };
|
||||
let res = to_string(s);
|
||||
unsafe { free(s as *mut _) };
|
||||
Some(res)
|
||||
}
|
||||
Config::Selfstatus => Some(self.stock_str(StockMessage::StatusLine).into_owned()),
|
||||
_ => key.get_str("default").map(|s| s.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -129,15 +113,14 @@ impl Context {
|
||||
ret
|
||||
}
|
||||
Config::Selfstatus => {
|
||||
let def = unsafe { dc_stock_str(self, 13) };
|
||||
let val = if value.is_none() || value.unwrap() == as_str(def) {
|
||||
let def = self.stock_str(StockMessage::StatusLine);
|
||||
let val = if value.is_none() || value.unwrap() == def {
|
||||
None
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
let ret = self.sql.set_config(self, key, val);
|
||||
unsafe { free(def as *mut libc::c_void) };
|
||||
ret
|
||||
}
|
||||
_ => self.sql.set_config(self, key, value),
|
||||
|
||||
145
src/constants.rs
145
src/constants.rs
@@ -1,4 +1,8 @@
|
||||
//! 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";
|
||||
|
||||
@@ -59,32 +63,33 @@ 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: 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_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_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: 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;
|
||||
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;
|
||||
/// to check if a mail was sent, use dc_msg_is_sent()
|
||||
pub const DC_STATE_OUT_DELIVERED: usize = 26;
|
||||
pub const DC_STATE_OUT_MDN_RCVD: usize = 28;
|
||||
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
|
||||
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
|
||||
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
|
||||
/// approx. max. length returned by dc_get_msg_info()
|
||||
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
|
||||
|
||||
pub const DC_CONTACT_ID_UNDEFINED: usize = 0;
|
||||
pub const DC_CONTACT_ID_SELF: usize = 1;
|
||||
pub const DC_CONTACT_ID_DEVICE: usize = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
|
||||
@@ -95,46 +100,6 @@ 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.
|
||||
@@ -182,6 +147,78 @@ 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,
|
||||
|
||||
1093
src/contact.rs
Normal file
1093
src/contact.rs
Normal file
File diff suppressed because it is too large
Load Diff
140
src/context.rs
140
src/context.rs
@@ -1,9 +1,9 @@
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_jobthread::*;
|
||||
use crate::dc_loginparam::*;
|
||||
@@ -14,6 +14,7 @@ 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::*;
|
||||
@@ -26,15 +27,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<i32>>,
|
||||
pub probe_imap_network: Arc<RwLock<i32>>,
|
||||
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
|
||||
pub probe_imap_network: Arc<RwLock<bool>>,
|
||||
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: *mut libc::c_char,
|
||||
pub os_name: Option<String>,
|
||||
pub cmdline_sel_chat_id: Arc<RwLock<u32>>,
|
||||
pub bob: Arc<RwLock<BobStatus>>,
|
||||
pub last_smeared_timestamp: Arc<RwLock<i64>>,
|
||||
@@ -105,33 +106,17 @@ impl Default for BobStatus {
|
||||
#[derive(Default, Debug)]
|
||||
pub struct SmtpState {
|
||||
pub idle: bool,
|
||||
pub suspended: i32,
|
||||
pub doing_jobs: i32,
|
||||
pub suspended: bool,
|
||||
pub doing_jobs: bool,
|
||||
pub perform_jobs_needed: i32,
|
||||
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,
|
||||
pub probe_network: bool,
|
||||
}
|
||||
|
||||
// create/open/config/information
|
||||
pub fn dc_context_new(
|
||||
cb: Option<dc_callback_t>,
|
||||
userdata: *mut libc::c_void,
|
||||
os_name: *const libc::c_char,
|
||||
os_name: Option<String>,
|
||||
) -> Context {
|
||||
Context {
|
||||
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
|
||||
@@ -146,7 +131,7 @@ pub fn dc_context_new(
|
||||
})),
|
||||
userdata,
|
||||
cb,
|
||||
os_name: unsafe { dc_strdup_keep_null(os_name) },
|
||||
os_name: os_name,
|
||||
running_state: Arc::new(RwLock::new(Default::default())),
|
||||
sql: Sql::new(),
|
||||
smtp: Arc::new(Mutex::new(Smtp::new())),
|
||||
@@ -175,8 +160,18 @@ pub fn dc_context_new(
|
||||
cb_receive_imf,
|
||||
),
|
||||
))),
|
||||
probe_imap_network: Arc::new(RwLock::new(0)),
|
||||
perform_inbox_jobs_needed: Arc::new(RwLock::new(0)),
|
||||
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) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,26 +236,15 @@ unsafe fn cb_precheck_imf(
|
||||
}
|
||||
dc_do_heuristics_moves(context, server_folder, msg_id);
|
||||
if 0 != mark_seen {
|
||||
dc_job_add(
|
||||
context,
|
||||
130i32,
|
||||
msg_id as libc::c_int,
|
||||
0 as *const libc::c_char,
|
||||
0i32,
|
||||
);
|
||||
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
|
||||
}
|
||||
}
|
||||
free(old_server_folder as *mut libc::c_void);
|
||||
return rfc724_mid_exists;
|
||||
}
|
||||
|
||||
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();
|
||||
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
|
||||
context.sql.set_config(context, key, value).ok();
|
||||
}
|
||||
|
||||
/* *
|
||||
@@ -270,24 +254,14 @@ unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *con
|
||||
*
|
||||
* @private @memberof Context
|
||||
*/
|
||||
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)
|
||||
fn cb_get_config(context: &Context, key: &str) -> Option<String> {
|
||||
context.sql.get_config(context, key)
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -331,32 +305,26 @@ 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: *const libc::c_char,
|
||||
blobdir: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool {
|
||||
let mut success = false;
|
||||
if 0 != dc_is_open(context) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
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
|
||||
}
|
||||
*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 0 == success {
|
||||
// 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 {
|
||||
dc_close(context);
|
||||
}
|
||||
success
|
||||
@@ -378,7 +346,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
let chats = dc_get_chat_cnt(context) as usize;
|
||||
let real_msgs = dc_get_real_msg_cnt(context) as usize;
|
||||
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
|
||||
let contacts = dc_get_real_contact_cnt(context) as usize;
|
||||
let contacts = Contact::get_real_cnt(context) as usize;
|
||||
let is_configured = context
|
||||
.sql
|
||||
.get_config_int(context, "configured")
|
||||
@@ -515,7 +483,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
fingerprint_str,
|
||||
);
|
||||
|
||||
to_cstring(res)
|
||||
res.strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
|
||||
@@ -537,18 +505,19 @@ 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 ret = unsafe { dc_array_new(128 as size_t) };
|
||||
let mut ret = dc_array_t::new(128);
|
||||
|
||||
for row in rows {
|
||||
let id = row?;
|
||||
unsafe { dc_array_add_id(ret, id) };
|
||||
ret.add_id(id);
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(ret.into_raw())
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn dc_search_msgs(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
@@ -576,7 +545,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 ret = unsafe { dc_array_new(100 as size_t) };
|
||||
let mut ret = dc_array_t::new(100);
|
||||
|
||||
let success = context
|
||||
.sql
|
||||
@@ -586,7 +555,7 @@ pub fn dc_search_msgs(
|
||||
|row| row.get::<_, i32>(0),
|
||||
|rows| {
|
||||
for id in rows {
|
||||
unsafe { dc_array_add_id(ret, id? as u32) };
|
||||
ret.add_id(id? as u32);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -594,12 +563,9 @@ pub fn dc_search_msgs(
|
||||
.is_ok();
|
||||
|
||||
if success {
|
||||
return ret;
|
||||
return ret.into_raw();
|
||||
}
|
||||
|
||||
if !ret.is_null() {
|
||||
unsafe { dc_array_unref(ret) };
|
||||
}
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
|
||||
571
src/dc_array.rs
571
src/dc_array.rs
@@ -1,78 +1,175 @@
|
||||
use crate::context::*;
|
||||
use crate::dc_location::dc_location;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
const DC_ARRAY_MAGIC: uint32_t = 0x000a11aa;
|
||||
|
||||
/* * the structure behind dc_array_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,
|
||||
#[derive(Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum dc_array_t {
|
||||
Locations(Vec<dc_location>),
|
||||
Uint(Vec<uintptr_t>),
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
impl dc_array_t {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
dc_array_t::Uint(Vec::with_capacity(capacity))
|
||||
}
|
||||
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_free_ptr(array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return;
|
||||
/// 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))
|
||||
}
|
||||
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,
|
||||
);
|
||||
|
||||
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");
|
||||
}
|
||||
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 {
|
||||
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() {
|
||||
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
|
||||
Box::from_raw(array);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_uint(array: *mut dc_array_t, item: uintptr_t) {
|
||||
if !array.is_null() {
|
||||
(*array).add_uint(item);
|
||||
}
|
||||
*(*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) {
|
||||
dc_array_add_uint(array, item as uintptr_t);
|
||||
if !array.is_null() {
|
||||
(*array).add_id(item);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
|
||||
@@ -80,130 +177,107 @@ 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() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return 0i32 as size_t;
|
||||
if array.is_null() {
|
||||
0
|
||||
} else {
|
||||
(*array).len()
|
||||
}
|
||||
(*array).count
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0i32 as uintptr_t;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_uint(index)
|
||||
}
|
||||
*(*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() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0i32 as uint32_t;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_id(index)
|
||||
}
|
||||
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() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0 as *mut libc::c_void;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
(*array).get_ptr(index)
|
||||
}
|
||||
*(*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()
|
||||
|| (*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;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_latitude(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*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;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_longitude(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*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;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_accuracy(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
{
|
||||
return 0;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_timestamp(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
{
|
||||
return 0i32 as uint32_t;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_chat_id(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
{
|
||||
return 0i32 as uint32_t;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_contact_id(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
{
|
||||
return 0i32 as uint32_t;
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_msg_id(index)
|
||||
}
|
||||
(*(*(*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()
|
||||
|| (*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;
|
||||
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()
|
||||
}
|
||||
dc_strdup_keep_null((*(*(*array).array.offset(index as isize) as *mut _dc_location)).marker)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,16 +291,15 @@ 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()
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
{
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*(*(*array).array.offset(index as isize) as *mut _dc_location)).independent as libc::c_int
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_search_id(
|
||||
@@ -234,176 +307,84 @@ pub unsafe fn dc_array_search_id(
|
||||
needle: uint32_t,
|
||||
ret_index: *mut size_t,
|
||||
) -> bool {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
if array.is_null() {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
if let Some(i) = (*array).search_id(needle as uintptr_t) {
|
||||
if !ret_index.is_null() {
|
||||
*ret_index = i
|
||||
}
|
||||
i = i.wrapping_add(1)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
if array.is_null() {
|
||||
return 0 as *const uintptr_t;
|
||||
}
|
||||
(*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);
|
||||
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
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
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() {
|
||||
return;
|
||||
}
|
||||
(*array).count = 0i32 as size_t;
|
||||
(*array).clear()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
|
||||
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
|
||||
if array.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} 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;
|
||||
(*array).clone().into_raw()
|
||||
}
|
||||
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() || (*array).magic != DC_ARRAY_MAGIC || sep.is_null() {
|
||||
if array.is_null() || sep.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let cnt = (*array).count as usize;
|
||||
let slice = std::slice::from_raw_parts((*array).array, cnt);
|
||||
let sep = as_str(sep);
|
||||
if let dc_array_t::Uint(v) = &*array {
|
||||
let cnt = v.len();
|
||||
let sep = as_str(sep);
|
||||
|
||||
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 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 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]
|
||||
@@ -439,7 +420,7 @@ mod tests {
|
||||
dc_array_add_id(arr, 0 as uint32_t);
|
||||
dc_array_add_id(arr, 5000 as uint32_t);
|
||||
|
||||
dc_array_sort_ids(arr);
|
||||
(*arr).sort_ids();
|
||||
|
||||
assert_eq!(dc_array_get_id(arr, 0 as size_t), 0);
|
||||
assert_eq!(dc_array_get_id(arr, 1 as size_t), 7);
|
||||
@@ -453,38 +434,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
1215
src/dc_chat.rs
1215
src/dc_chat.rs
File diff suppressed because it is too large
Load Diff
@@ -1,389 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
@@ -9,6 +9,7 @@ use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
@@ -60,9 +61,10 @@ pub unsafe fn dc_configure(context: &Context) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
dc_job_kill_action(context, 900i32);
|
||||
dc_job_add(context, 900i32, 0i32, 0 as *const libc::c_char, 0i32);
|
||||
dc_job_kill_action(context, 900);
|
||||
dc_job_add(context, 900, 0, Params::new(), 0);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_has_ongoing(context: &Context) -> libc::c_int {
|
||||
let s_a = context.running_state.clone();
|
||||
let s = s_a.read().unwrap();
|
||||
@@ -99,6 +101,7 @@ 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;
|
||||
@@ -207,8 +210,7 @@ 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(¶m.addr, DEFAULT_ENCODE_SET)
|
||||
.to_string();
|
||||
utf8_percent_encode(¶m.addr, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
if !s.shall_stop_ongoing {
|
||||
context.call_cb(
|
||||
@@ -570,13 +572,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 0 == dc_exactly_one_bit_set(
|
||||
if !dc_exactly_one_bit_set(
|
||||
param.server_flags & (0x2 | 0x4),
|
||||
) {
|
||||
param.server_flags &= !(0x2 | 0x4);
|
||||
param.server_flags |= 0x4
|
||||
}
|
||||
if 0 == dc_exactly_one_bit_set(
|
||||
if !dc_exactly_one_bit_set(
|
||||
param.server_flags & (0x100 | 0x200 | 0x400),
|
||||
) {
|
||||
param.server_flags &= !(0x100 | 0x200 | 0x400);
|
||||
@@ -586,7 +588,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context, _job: *mut dc_j
|
||||
0x200
|
||||
}
|
||||
}
|
||||
if 0 == dc_exactly_one_bit_set(
|
||||
if !dc_exactly_one_bit_set(
|
||||
param.server_flags & (0x10000 | 0x20000 | 0x40000),
|
||||
) {
|
||||
param.server_flags &=
|
||||
@@ -1100,14 +1102,14 @@ unsafe fn moz_autoconfigure(
|
||||
tag_config: 0,
|
||||
};
|
||||
|
||||
let url_c = to_cstring(url);
|
||||
let url_c = url.strdup();
|
||||
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 = to_cstring(¶m_in.addr);
|
||||
moz_ac.in_emaillocalpart = param_in.addr.strdup();
|
||||
let p = strchr(moz_ac.in_emaillocalpart, '@' as i32);
|
||||
|
||||
if p.is_null() {
|
||||
@@ -1164,7 +1166,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 = to_cstring(&(*moz_ac).in_0.addr);
|
||||
let addr = (*moz_ac).in_0.addr.strdup();
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1304,7 +1306,7 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc:
|
||||
.send()
|
||||
.and_then(|mut res| res.text())
|
||||
{
|
||||
Ok(res) => unsafe { to_cstring(res) },
|
||||
Ok(res) => unsafe { res.strdup() },
|
||||
Err(_err) => {
|
||||
info!(context, 0, "Can\'t read file.",);
|
||||
|
||||
@@ -1320,7 +1322,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 = to_cstring(url__);
|
||||
let mut url = url__.strdup();
|
||||
let mut outlk_ad = outlk_autodiscover_t {
|
||||
in_0: param_in,
|
||||
out: dc_loginparam_new(),
|
||||
|
||||
1149
src/dc_contact.rs
1149
src/dc_contact.rs
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ lazy_static! {
|
||||
struct Dehtml {
|
||||
strbuilder: String,
|
||||
add_text: AddText,
|
||||
last_href: *mut libc::c_char,
|
||||
last_href: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -32,7 +32,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
|
||||
let mut dehtml = Dehtml {
|
||||
strbuilder: String::with_capacity(strlen(buf_terminated)),
|
||||
add_text: AddText::YesRemoveLineEnds,
|
||||
last_href: 0 as *mut libc::c_char,
|
||||
last_href: None,
|
||||
};
|
||||
let mut saxparser = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
@@ -51,9 +51,8 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, buf_terminated);
|
||||
free(dehtml.last_href as *mut libc::c_void);
|
||||
|
||||
to_cstring(dehtml.strbuilder)
|
||||
dehtml.strbuilder.strdup()
|
||||
}
|
||||
|
||||
unsafe fn dehtml_text_cb(
|
||||
@@ -66,7 +65,11 @@ unsafe fn dehtml_text_cb(
|
||||
if dehtml.add_text == AddText::YesPreserveLineEnds
|
||||
|| dehtml.add_text == AddText::YesRemoveLineEnds
|
||||
{
|
||||
let last_added = std::ffi::CStr::from_ptr(text).to_string_lossy();
|
||||
let last_added = std::ffi::CStr::from_ptr(text)
|
||||
.to_str()
|
||||
.expect("invalid utf8");
|
||||
// TODO: why does len does not match?
|
||||
// assert_eq!(last_added.len(), len as usize);
|
||||
|
||||
if dehtml.add_text == AddText::YesRemoveLineEnds {
|
||||
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
|
||||
@@ -86,14 +89,10 @@ unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char
|
||||
dehtml.add_text = AddText::YesRemoveLineEnds;
|
||||
}
|
||||
"a" => {
|
||||
if !dehtml.last_href.is_null() {
|
||||
if let Some(ref last_href) = dehtml.last_href.take() {
|
||||
dehtml.strbuilder += "](";
|
||||
dehtml.strbuilder += std::ffi::CStr::from_ptr((*dehtml).last_href)
|
||||
.to_string_lossy()
|
||||
.as_ref();
|
||||
dehtml.strbuilder += last_href;
|
||||
dehtml.strbuilder += ")";
|
||||
free(dehtml.last_href as *mut libc::c_void);
|
||||
dehtml.last_href = 0 as *mut libc::c_char;
|
||||
}
|
||||
}
|
||||
"b" | "strong" => {
|
||||
@@ -131,12 +130,13 @@ unsafe fn dehtml_starttag_cb(
|
||||
dehtml.add_text = AddText::YesPreserveLineEnds;
|
||||
}
|
||||
"a" => {
|
||||
free(dehtml.last_href as *mut libc::c_void);
|
||||
dehtml.last_href = dc_strdup_keep_null(dc_attr_find(
|
||||
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
|
||||
attr,
|
||||
b"href\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
if !dehtml.last_href.is_null() {
|
||||
let text_r = text_c.to_str().expect("invalid utf8");
|
||||
if !text_r.is_empty() {
|
||||
dehtml.last_href = Some(text_r.to_string());
|
||||
dehtml.strbuilder += "[";
|
||||
}
|
||||
}
|
||||
|
||||
282
src/dc_e2ee.rs
282
src/dc_e2ee.rs
@@ -34,6 +34,7 @@ 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,
|
||||
@@ -54,6 +55,7 @@ impl Default for dc_e2ee_helper_t {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_e2ee_encrypt(
|
||||
context: &Context,
|
||||
recipients_addr: *const clist,
|
||||
@@ -64,7 +66,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
mut in_out_message: *mut mailmime,
|
||||
helper: &mut dc_e2ee_helper_t,
|
||||
) {
|
||||
let mut current_block: u64 = 0;
|
||||
let mut ok_to_continue = true;
|
||||
let mut col: libc::c_int = 0i32;
|
||||
let mut do_encrypt: libc::c_int = 0i32;
|
||||
/*just a pointer into mailmime structure, must not be freed*/
|
||||
@@ -175,16 +177,12 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
let p = peerstates[i as usize]
|
||||
.render_gossip_header(min_verified as usize);
|
||||
|
||||
if p.is_some() {
|
||||
let header = to_cstring(p.unwrap());
|
||||
if let Some(header) = p {
|
||||
mailimf_fields_add(
|
||||
imffields_encrypted,
|
||||
mailimf_field_new_custom(
|
||||
strdup(
|
||||
b"Autocrypt-Gossip\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
),
|
||||
header,
|
||||
"Autocrypt-Gossip".strdup(),
|
||||
header.strdup(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -285,7 +283,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
);
|
||||
mailmime_write_mem(plain, &mut col, message_to_encrypt);
|
||||
if (*plain).str_0.is_null() || (*plain).len <= 0 {
|
||||
current_block = 14181132614457621749;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
if let Some(ctext_v) = dc_pgp_pk_encrypt(
|
||||
(*plain).str_0 as *const libc::c_void,
|
||||
@@ -294,8 +292,8 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
sign_key.as_ref(),
|
||||
) {
|
||||
let ctext_bytes = ctext_v.len();
|
||||
let ctext = to_cstring(ctext_v);
|
||||
(*helper).cdata_to_free = ctext as *mut _;
|
||||
let ctext = ctext_v.strdup();
|
||||
helper.cdata_to_free = ctext as *mut _;
|
||||
|
||||
/* create MIME-structure that will contain the encrypted text */
|
||||
let mut encrypted_part: *mut mailmime = new_data_part(
|
||||
@@ -318,11 +316,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,
|
||||
@@ -341,27 +339,19 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part;
|
||||
(*encrypted_part).mm_parent = in_out_message;
|
||||
mailmime_free(message_to_encrypt);
|
||||
(*helper).encryption_successfull = 1i32;
|
||||
current_block = 13824533195664196414;
|
||||
helper.encryption_successfull = 1i32;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current_block = 13824533195664196414;
|
||||
}
|
||||
match current_block {
|
||||
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(
|
||||
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
|
||||
rendered,
|
||||
),
|
||||
);
|
||||
}
|
||||
if ok_to_continue {
|
||||
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
|
||||
mailimf_fields_add(
|
||||
imffields_unprotected,
|
||||
mailimf_field_new_custom(
|
||||
"Autocrypt".strdup(),
|
||||
aheader.to_string().strdup(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,7 +372,7 @@ unsafe fn new_data_part(
|
||||
default_content_type: *mut libc::c_char,
|
||||
default_encoding: libc::c_int,
|
||||
) -> *mut mailmime {
|
||||
let mut current_block: u64;
|
||||
let mut ok_to_continue = true;
|
||||
//char basename_buf[PATH_MAX];
|
||||
let mut encoding: *mut mailmime_mechanism;
|
||||
let content: *mut mailmime_content;
|
||||
@@ -402,7 +392,7 @@ unsafe fn new_data_part(
|
||||
}
|
||||
content = mailmime_content_new_with_str(content_type_str);
|
||||
if content.is_null() {
|
||||
current_block = 16266721588079097885;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
do_encoding = 1i32;
|
||||
if (*(*content).ct_type).tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int {
|
||||
@@ -430,54 +420,44 @@ unsafe fn new_data_part(
|
||||
}
|
||||
encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char);
|
||||
if encoding.is_null() {
|
||||
current_block = 16266721588079097885;
|
||||
} else {
|
||||
current_block = 11057878835866523405;
|
||||
ok_to_continue = false;
|
||||
}
|
||||
} else {
|
||||
current_block = 11057878835866523405;
|
||||
}
|
||||
match current_block {
|
||||
16266721588079097885 => {}
|
||||
_ => {
|
||||
mime_fields = mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut mailmime_disposition,
|
||||
0 as *mut mailmime_language,
|
||||
);
|
||||
if mime_fields.is_null() {
|
||||
current_block = 16266721588079097885;
|
||||
if ok_to_continue {
|
||||
mime_fields = mailmime_fields_new_with_data(
|
||||
encoding,
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut mailmime_disposition,
|
||||
0 as *mut mailmime_language,
|
||||
);
|
||||
if mime_fields.is_null() {
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
mime = mailmime_new_empty(content, mime_fields);
|
||||
if mime.is_null() {
|
||||
mailmime_fields_free(mime_fields);
|
||||
mailmime_content_free(content);
|
||||
} else {
|
||||
mime = mailmime_new_empty(content, mime_fields);
|
||||
if mime.is_null() {
|
||||
mailmime_fields_free(mime_fields);
|
||||
mailmime_content_free(content);
|
||||
} else {
|
||||
if !data.is_null()
|
||||
&& data_bytes > 0
|
||||
&& (*mime).mm_type == MAILMIME_SINGLE as libc::c_int
|
||||
{
|
||||
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
|
||||
}
|
||||
return mime;
|
||||
if !data.is_null()
|
||||
&& data_bytes > 0
|
||||
&& (*mime).mm_type == MAILMIME_SINGLE as libc::c_int
|
||||
{
|
||||
mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes);
|
||||
}
|
||||
current_block = 13668317689588454213;
|
||||
return mime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
16266721588079097885 => {
|
||||
if !encoding.is_null() {
|
||||
mailmime_mechanism_free(encoding);
|
||||
}
|
||||
if !content.is_null() {
|
||||
mailmime_content_free(content);
|
||||
}
|
||||
|
||||
if ok_to_continue == false {
|
||||
if !encoding.is_null() {
|
||||
mailmime_mechanism_free(encoding);
|
||||
}
|
||||
if !content.is_null() {
|
||||
mailmime_content_free(content);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return 0 as *mut mailmime;
|
||||
}
|
||||
@@ -491,7 +471,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() {
|
||||
@@ -499,11 +479,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!(
|
||||
@@ -537,7 +517,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
|
||||
@@ -832,14 +812,14 @@ unsafe fn decrypt_recursive(
|
||||
}
|
||||
|
||||
unsafe fn decrypt_part(
|
||||
_context: &Context,
|
||||
context: &Context,
|
||||
mime: *mut mailmime,
|
||||
private_keyring: &Keyring,
|
||||
public_keyring_for_validate: &Keyring,
|
||||
ret_valid_signatures: &mut HashSet<String>,
|
||||
ret_decrypted_mime: *mut *mut mailmime,
|
||||
) -> libc::c_int {
|
||||
let current_block: u64;
|
||||
let mut ok_to_continue = true;
|
||||
let mime_data: *mut mailmime_data;
|
||||
let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int;
|
||||
/* mmap_string_unref()'d if set */
|
||||
@@ -887,9 +867,7 @@ unsafe fn decrypt_part(
|
||||
decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length;
|
||||
if decoded_data.is_null() || decoded_data_bytes <= 0 {
|
||||
/* no error - but no data */
|
||||
current_block = 2554982661806928548;
|
||||
} else {
|
||||
current_block = 4488286894823169796;
|
||||
ok_to_continue = false;
|
||||
}
|
||||
} else {
|
||||
let r: libc::c_int;
|
||||
@@ -906,53 +884,49 @@ unsafe fn decrypt_part(
|
||||
|| transfer_decoding_buffer.is_null()
|
||||
|| decoded_data_bytes <= 0
|
||||
{
|
||||
current_block = 2554982661806928548;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
decoded_data = transfer_decoding_buffer;
|
||||
current_block = 4488286894823169796;
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
2554982661806928548 => {}
|
||||
_ => {
|
||||
/* encrypted, decoded data in decoded_data now ... */
|
||||
if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int))
|
||||
{
|
||||
let add_signatures = if ret_valid_signatures.is_empty() {
|
||||
Some(ret_valid_signatures)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if ok_to_continue {
|
||||
/* encrypted, decoded data in decoded_data now ... */
|
||||
if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int)) {
|
||||
let add_signatures = if ret_valid_signatures.is_empty() {
|
||||
Some(ret_valid_signatures)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
|
||||
if let Some(plain) = dc_pgp_pk_decrypt(
|
||||
decoded_data as *const libc::c_void,
|
||||
decoded_data_bytes,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
add_signatures,
|
||||
) {
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
/*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */
|
||||
if let Some(plain) = dc_pgp_pk_decrypt(
|
||||
decoded_data as *const libc::c_void,
|
||||
decoded_data_bytes,
|
||||
&private_keyring,
|
||||
&public_keyring_for_validate,
|
||||
add_signatures,
|
||||
) {
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index: size_t = 0i32 as size_t;
|
||||
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
|
||||
if mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
) != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
mailmime_free(decrypted_mime);
|
||||
}
|
||||
} else {
|
||||
*ret_decrypted_mime = decrypted_mime;
|
||||
sth_decrypted = 1i32
|
||||
let mut index: size_t = 0i32 as size_t;
|
||||
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
|
||||
if mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
) != MAIL_NO_ERROR as libc::c_int
|
||||
|| decrypted_mime.is_null()
|
||||
{
|
||||
if !decrypted_mime.is_null() {
|
||||
mailmime_free(decrypted_mime);
|
||||
}
|
||||
} else {
|
||||
*ret_decrypted_mime = decrypted_mime;
|
||||
sth_decrypted = 1i32
|
||||
}
|
||||
std::mem::forget(plain);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1082,3 +1056,69 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_mailmime_parse() {
|
||||
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
|
||||
Chat-Group-ID: CovhGgau8M-
|
||||
Chat-Group-Name: Delta Chat Dev
|
||||
Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for
|
||||
=?utf-8?Q?all=3A?= rust core master ...
|
||||
Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\"
|
||||
Content-Transfer-Encoding: quoted-printable
|
||||
|
||||
sidenote for all: rust core master is broken currently ... so dont recomm=
|
||||
end to try to run with desktop or ios unless you are ready to hunt bugs
|
||||
|
||||
-- =20
|
||||
Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
let plain_bytes = plain.len();
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index = 0;
|
||||
let mut decrypted_mime = std::ptr::null_mut();
|
||||
|
||||
let res = unsafe {
|
||||
mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime;
|
||||
let mut decoded_data = 0 as *const libc::c_char;
|
||||
let mut decoded_data_bytes = 0;
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
|
||||
assert_eq!(
|
||||
mailmime_transfer_decode(
|
||||
msg1,
|
||||
&mut decoded_data,
|
||||
&mut decoded_data_bytes,
|
||||
&mut transfer_decoding_buffer,
|
||||
),
|
||||
1
|
||||
);
|
||||
println!(
|
||||
"{:?}",
|
||||
String::from_utf8_lossy(std::slice::from_raw_parts(
|
||||
decoded_data as *const u8,
|
||||
decoded_data_bytes as usize,
|
||||
))
|
||||
);
|
||||
|
||||
free(decoded_data as *mut _);
|
||||
}
|
||||
|
||||
assert_eq!(res, 0);
|
||||
assert!(!decrypted_mime.is_null());
|
||||
|
||||
unsafe { free(decrypted_mime as *mut _) };
|
||||
}
|
||||
}
|
||||
|
||||
693
src/dc_imex.rs
693
src/dc_imex.rs
File diff suppressed because it is too large
Load Diff
254
src/dc_job.rs
254
src/dc_job.rs
@@ -5,7 +5,7 @@ use std::time::Duration;
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_configure::*;
|
||||
@@ -15,14 +15,16 @@ 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 ...
|
||||
@@ -33,7 +35,7 @@ use crate::x::*;
|
||||
// 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(Copy, Clone)]
|
||||
#[derive(Clone)]
|
||||
#[repr(C)]
|
||||
pub struct dc_job_t {
|
||||
pub job_id: uint32_t,
|
||||
@@ -42,7 +44,7 @@ pub struct dc_job_t {
|
||||
pub desired_timestamp: i64,
|
||||
pub added_timestamp: i64,
|
||||
pub tries: libc::c_int,
|
||||
pub param: *mut dc_param_t,
|
||||
pub param: Params,
|
||||
pub try_again: libc::c_int,
|
||||
pub pending_error: *mut libc::c_char,
|
||||
}
|
||||
@@ -51,15 +53,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() = 0;
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = 0;
|
||||
*context.probe_imap_network.write().unwrap() = false;
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = false;
|
||||
|
||||
dc_job_perform(context, 100, probe_imap_network);
|
||||
dc_job_perform(context, DC_IMAP_THREAD, probe_imap_network);
|
||||
info!(context, 0, "dc_perform_imap_jobs ended.",);
|
||||
}
|
||||
|
||||
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) {
|
||||
let query = if probe_network == 0 {
|
||||
unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: bool) {
|
||||
let query = if !probe_network {
|
||||
// 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 \
|
||||
@@ -74,7 +76,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 == 0 {
|
||||
let params: &[&dyn rusqlite::ToSql] = if !probe_network {
|
||||
params_no_probe
|
||||
} else {
|
||||
params_probe
|
||||
@@ -91,15 +93,11 @@ 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: dc_param_new(),
|
||||
param: row.get::<_, String>(3)?.parse().unwrap_or_default(),
|
||||
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| {
|
||||
@@ -120,7 +118,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
|
||||
context,
|
||||
0,
|
||||
"{}-job #{}, action {} started...",
|
||||
if thread == 100 { "INBOX" } else { "SMTP" },
|
||||
if thread == DC_IMAP_THREAD {
|
||||
"INBOX"
|
||||
} else {
|
||||
"SMTP"
|
||||
},
|
||||
job.job_id,
|
||||
job.action,
|
||||
);
|
||||
@@ -133,7 +135,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, 1);
|
||||
dc_suspend_smtp_thread(context, true);
|
||||
}
|
||||
|
||||
let mut tries = 0;
|
||||
@@ -171,7 +173,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, 0);
|
||||
dc_suspend_smtp_thread(context, false);
|
||||
break;
|
||||
} else if job.try_again == 2 {
|
||||
// just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready
|
||||
@@ -179,7 +181,11 @@ 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 == 100 { "INBOX" } else { "SMTP" },
|
||||
if thread == DC_IMAP_THREAD {
|
||||
"INBOX"
|
||||
} else {
|
||||
"SMTP"
|
||||
},
|
||||
job.job_id
|
||||
);
|
||||
} else if job.try_again == -1 || job.try_again == 3 {
|
||||
@@ -193,13 +199,17 @@ 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 == 100 { "INBOX" } else { "SMTP" },
|
||||
if thread == DC_IMAP_THREAD {
|
||||
"INBOX"
|
||||
} else {
|
||||
"SMTP"
|
||||
},
|
||||
job.job_id as libc::c_int,
|
||||
tries,
|
||||
time_offset,
|
||||
job.added_timestamp + time_offset - time()
|
||||
);
|
||||
if thread == 5000 && tries < 17 - 1 {
|
||||
if thread == DC_SMTP_THREAD && tries < 17 - 1 {
|
||||
context
|
||||
.smtp_state
|
||||
.clone()
|
||||
@@ -214,7 +224,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network:
|
||||
}
|
||||
dc_job_delete(context, &mut job);
|
||||
}
|
||||
if 0 == probe_network {
|
||||
if !probe_network {
|
||||
continue;
|
||||
}
|
||||
// on dc_maybe_network() we stop trying here;
|
||||
@@ -225,7 +235,6 @@ 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);
|
||||
}
|
||||
}
|
||||
@@ -240,7 +249,8 @@ fn dc_job_delete(context: &Context, job: &dc_job_t) -> bool {
|
||||
/* ******************************************************************************
|
||||
* Tools
|
||||
******************************************************************************/
|
||||
unsafe fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 {
|
||||
#[allow(non_snake_case)]
|
||||
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;
|
||||
@@ -261,30 +271,31 @@ fn dc_job_update(context: &Context, job: &dc_job_t) -> bool {
|
||||
params![
|
||||
job.desired_timestamp,
|
||||
job.tries as i64,
|
||||
as_str(unsafe { (*job.param).packed }),
|
||||
job.param.to_string(),
|
||||
job.job_id as i32,
|
||||
],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: libc::c_int) {
|
||||
unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: bool) {
|
||||
context.smtp_state.0.lock().unwrap().suspended = suspend;
|
||||
if 0 != suspend {
|
||||
if suspend {
|
||||
loop {
|
||||
if context.smtp_state.0.lock().unwrap().doing_jobs == 0 {
|
||||
if !context.smtp_state.0.lock().unwrap().doing_jobs {
|
||||
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() {
|
||||
@@ -302,20 +313,15 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) {
|
||||
}
|
||||
match current_block {
|
||||
13109137661213826276 => {
|
||||
filename = dc_param_get(job.param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
|
||||
if filename.is_null() {
|
||||
filename = job.param.get(Param::File).unwrap_or_default().strdup();
|
||||
if strlen(filename) == 0 {
|
||||
warn!(context, 0, "Missing file name for job {}", job.job_id,);
|
||||
} else if !(0 == dc_read_file(context, filename, &mut buf, &mut buf_bytes)) {
|
||||
recipients = dc_param_get(
|
||||
job.param,
|
||||
DC_PARAM_RECIPIENTS as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if recipients.is_null() {
|
||||
let recipients = job.param.get(Param::Recipients);
|
||||
if recipients.is_none() {
|
||||
warn!(context, 0, "Missing recipients for job {}", job.job_id,);
|
||||
} else {
|
||||
let recipients_list = std::ffi::CStr::from_ptr(recipients)
|
||||
.to_str()
|
||||
let recipients_list = recipients
|
||||
.unwrap()
|
||||
.split("\x1e")
|
||||
.filter_map(|addr| match lettre::EmailAddress::new(addr.to_string()) {
|
||||
@@ -365,7 +371,11 @@ 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, 26i32);
|
||||
dc_update_msg_state(
|
||||
context,
|
||||
job.foreign_id,
|
||||
DC_STATE_OUT_DELIVERED,
|
||||
);
|
||||
let chat_id: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
@@ -389,7 +399,6 @@ 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);
|
||||
}
|
||||
@@ -405,6 +414,7 @@ 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);
|
||||
@@ -501,21 +511,22 @@ 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: *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 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 inbox = context.inbox.read().unwrap();
|
||||
|
||||
if !inbox.is_connected() {
|
||||
connect_to_inbox(context, &inbox);
|
||||
if !inbox.is_connected() {
|
||||
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
|
||||
dc_job_try_again_later(job, 3, 0 as *const libc::c_char);
|
||||
current_block = 2670689566614003383;
|
||||
} else {
|
||||
current_block = 11006700562992250127;
|
||||
@@ -525,11 +536,10 @@ unsafe fn dc_job_do_DC_JOB_MARKSEEN_MDN_ON_IMAP(context: &Context, job: &mut dc_
|
||||
}
|
||||
match current_block {
|
||||
11006700562992250127 => {
|
||||
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 {
|
||||
if inbox.set_seen(context, &folder, uid) == 0 {
|
||||
dc_job_try_again_later(job, 3i32, 0 as *const libc::c_char);
|
||||
}
|
||||
if 0 != dc_param_get_int(job.param, DC_PARAM_ALSO_MOVE as i32, 0i32) {
|
||||
if 0 != job.param.get_int(Param::AlsoMove).unwrap_or_default() {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
@@ -550,9 +560,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);
|
||||
@@ -582,12 +592,8 @@ 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 != dc_param_get_int(
|
||||
(*msg).param,
|
||||
DC_PARAM_WANTS_MDN as i32,
|
||||
0i32,
|
||||
) && 0
|
||||
!= context
|
||||
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
@@ -640,7 +646,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 != dc_param_get_int((*msg).param, DC_PARAM_WANTS_MDN as i32, 0)
|
||||
if 0 != (*msg).param.get_int(Param::WantsMdn).unwrap_or_default()
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
@@ -737,6 +743,7 @@ 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,
|
||||
@@ -745,7 +752,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 param: *mut dc_param_t = dc_param_new();
|
||||
let mut param = Params::new();
|
||||
pathNfilename = dc_get_fine_pathNfilename(
|
||||
context,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -778,8 +785,8 @@ unsafe fn dc_add_smtp_job(
|
||||
(*mimefactory).recipients_addr,
|
||||
b"\x1e\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_param_set(param, DC_PARAM_FILE as i32, pathNfilename);
|
||||
dc_param_set(param, DC_PARAM_RECIPIENTS as i32, recipients);
|
||||
param.set(Param::File, as_str(pathNfilename));
|
||||
param.set(Param::Recipients, as_str(recipients));
|
||||
dc_job_add(
|
||||
context,
|
||||
action,
|
||||
@@ -788,30 +795,30 @@ unsafe fn dc_add_smtp_job(
|
||||
{
|
||||
(*(*mimefactory).msg).id
|
||||
} else {
|
||||
0i32 as libc::c_uint
|
||||
0
|
||||
}) as libc::c_int,
|
||||
(*param).packed,
|
||||
0i32,
|
||||
param,
|
||||
0,
|
||||
);
|
||||
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: *const libc::c_char,
|
||||
param: Params,
|
||||
delay_seconds: libc::c_int,
|
||||
) {
|
||||
let timestamp = time();
|
||||
let thread = if action >= 100 && action < 100 + 1000 {
|
||||
100
|
||||
} else if action >= 5000 && action < 5000 + 1000 {
|
||||
5000
|
||||
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
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
@@ -825,16 +832,12 @@ pub unsafe fn dc_job_add(
|
||||
thread,
|
||||
action,
|
||||
foreign_id,
|
||||
if !param.is_null() {
|
||||
as_str(param)
|
||||
} else {
|
||||
""
|
||||
},
|
||||
param.to_string(),
|
||||
(timestamp + delay_seconds as i64)
|
||||
]
|
||||
).ok();
|
||||
|
||||
if thread == 100 {
|
||||
if thread == DC_IMAP_THREAD {
|
||||
dc_interrupt_imap_idle(context);
|
||||
} else {
|
||||
dc_interrupt_smtp_idle(context);
|
||||
@@ -855,10 +858,11 @@ 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() = 1;
|
||||
*context.perform_inbox_jobs_needed.write().unwrap() = true;
|
||||
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;
|
||||
@@ -962,7 +966,7 @@ pub fn dc_perform_imap_idle(context: &Context) {
|
||||
|
||||
connect_to_inbox(context, &inbox);
|
||||
|
||||
if 0 != *context.perform_inbox_jobs_needed.clone().read().unwrap() {
|
||||
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
|
||||
info!(
|
||||
context,
|
||||
0, "INBOX-IDLE will not be started because of waiting jobs."
|
||||
@@ -1037,26 +1041,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 = 0;
|
||||
state.probe_network = false;
|
||||
state.perform_jobs_needed = 0;
|
||||
|
||||
if 0 != state.suspended {
|
||||
if state.suspended {
|
||||
info!(context, 0, "SMTP-jobs suspended.",);
|
||||
return;
|
||||
}
|
||||
state.doing_jobs = 1;
|
||||
state.doing_jobs = true;
|
||||
probe_smtp_network
|
||||
};
|
||||
|
||||
info!(context, 0, "SMTP-jobs started...",);
|
||||
dc_job_perform(context, 5000, probe_smtp_network);
|
||||
dc_job_perform(context, DC_SMTP_THREAD, 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 = 0;
|
||||
state.doing_jobs = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,7 +1076,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, 5000);
|
||||
let dur = get_next_wakeup_time(context, DC_SMTP_THREAD);
|
||||
|
||||
loop {
|
||||
let res = cvar.wait_timeout(state, dur).unwrap();
|
||||
@@ -1118,9 +1122,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 = 1;
|
||||
state.probe_network = true;
|
||||
|
||||
*context.probe_imap_network.write().unwrap() = 1;
|
||||
*context.probe_imap_network.write().unwrap() = true;
|
||||
}
|
||||
|
||||
dc_interrupt_smtp_idle(context);
|
||||
@@ -1137,6 +1141,7 @@ 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 {
|
||||
@@ -1170,28 +1175,23 @@ 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 (*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)
|
||||
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)
|
||||
{
|
||||
let mut buf: *mut libc::c_uchar = 0 as *mut libc::c_uchar;
|
||||
let mut buf = 0 as *mut libc::c_uchar;
|
||||
let mut buf_bytes: size_t = 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);
|
||||
let mut w = 0;
|
||||
let mut h = 0;
|
||||
(*mimefactory.msg).param.set_int(Param::Width, 0);
|
||||
(*mimefactory.msg).param.set_int(Param::Height, 0);
|
||||
if 0 != dc_read_file(
|
||||
context,
|
||||
pathNfilename,
|
||||
@@ -1204,16 +1204,8 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
|
||||
&mut w,
|
||||
&mut h,
|
||||
) {
|
||||
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,
|
||||
);
|
||||
(*mimefactory.msg).param.set_int(Param::Width, w as i32);
|
||||
(*mimefactory.msg).param.set_int(Param::Height, h as i32);
|
||||
}
|
||||
}
|
||||
free(buf as *mut libc::c_void);
|
||||
@@ -1225,15 +1217,19 @@ 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 != dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
|
||||
} else if 0
|
||||
!= (*mimefactory.msg)
|
||||
.param
|
||||
.get_int(Param::GuranteeE2ee)
|
||||
.unwrap_or_default()
|
||||
&& 0 == mimefactory.out_encrypted
|
||||
{
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"e2e encryption unavailable {} - {}",
|
||||
"e2e encryption unavailable {} - {:?}",
|
||||
msg_id,
|
||||
dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0),
|
||||
(*mimefactory.msg).param.get_int(Param::GuranteeE2ee),
|
||||
);
|
||||
dc_set_msg_failed(
|
||||
context,
|
||||
@@ -1271,19 +1267,15 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in
|
||||
}
|
||||
}
|
||||
if 0 != mimefactory.out_encrypted
|
||||
&& dc_param_get_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
|
||||
&& (*mimefactory.msg)
|
||||
.param
|
||||
.get_int(Param::GuranteeE2ee)
|
||||
.unwrap_or_default()
|
||||
== 0
|
||||
{
|
||||
dc_param_set_int((*mimefactory.msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
|
||||
(*mimefactory.msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
dc_msg_save_param_to_disk(mimefactory.msg);
|
||||
}
|
||||
dc_add_to_keyhistory(
|
||||
context,
|
||||
0 as *const libc::c_char,
|
||||
0,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
success = dc_add_smtp_job(context, 5901i32, &mut mimefactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
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(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct dc_location_t {
|
||||
#[derive(Clone, Default)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct dc_location {
|
||||
pub location_id: uint32_t,
|
||||
pub latitude: libc::c_double,
|
||||
pub longitude: libc::c_double,
|
||||
@@ -24,16 +27,45 @@ pub struct dc_location_t {
|
||||
pub contact_id: uint32_t,
|
||||
pub msg_id: uint32_t,
|
||||
pub chat_id: uint32_t,
|
||||
pub marker: *mut libc::c_char,
|
||||
pub marker: Option<String>,
|
||||
pub independent: uint32_t,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
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)]
|
||||
pub struct dc_kml_t {
|
||||
pub addr: *mut libc::c_char,
|
||||
pub locations: *mut dc_array_t,
|
||||
pub locations: Option<Vec<dc_location>>,
|
||||
pub tag: libc::c_int,
|
||||
pub curr: dc_location_t,
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// location streaming
|
||||
@@ -44,7 +76,6 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
) {
|
||||
let now = time();
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let mut stock_str: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let is_sending_locations_before: bool;
|
||||
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
|
||||
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
|
||||
@@ -68,25 +99,20 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
.is_ok()
|
||||
{
|
||||
if 0 != seconds && !is_sending_locations_before {
|
||||
msg = dc_msg_new(context, 10i32);
|
||||
(*msg).text = dc_stock_system_msg(
|
||||
context,
|
||||
64,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 8);
|
||||
msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
(*msg).param.set_int(Param::Cmd, 8);
|
||||
dc_send_msg(context, chat_id, msg);
|
||||
} else if 0 == seconds && is_sending_locations_before {
|
||||
stock_str = dc_stock_system_msg(
|
||||
context,
|
||||
65i32,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0i32 as uint32_t,
|
||||
);
|
||||
dc_add_device_msg(context, chat_id, stock_str);
|
||||
let stock_str = CString::new(context.stock_system_msg(
|
||||
StockMessage::MsgLocationDisabled,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
))
|
||||
.unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
@@ -99,22 +125,22 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
context,
|
||||
5007i32,
|
||||
chat_id as libc::c_int,
|
||||
0 as *const libc::c_char,
|
||||
Params::new(),
|
||||
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, 0 as *const libc::c_char, 60);
|
||||
dc_job_add(context, 5005, 0, Params::new(), 60);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -197,54 +223,43 @@ pub fn dc_get_locations(
|
||||
timestamp_from,
|
||||
timestamp_to,
|
||||
],
|
||||
|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");
|
||||
|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
|
||||
};
|
||||
|
||||
(*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 _);
|
||||
}
|
||||
}
|
||||
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,
|
||||
};
|
||||
Ok(loc)
|
||||
},
|
||||
|locations| {
|
||||
let ret = unsafe { dc_array_new_typed(1, 500) };
|
||||
let mut ret = Vec::new();
|
||||
|
||||
for location in locations {
|
||||
unsafe { dc_array_add_ptr(ret, location? as *mut libc::c_void) };
|
||||
ret.push(location?);
|
||||
}
|
||||
Ok(ret)
|
||||
Ok(dc_array_t::from(ret).into_raw())
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
// 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
|
||||
fn is_marker(txt: &str) -> bool {
|
||||
txt.len() == 1 && txt.chars().next().unwrap() != ' '
|
||||
}
|
||||
|
||||
pub fn dc_delete_all_locations(context: &Context) -> bool {
|
||||
@@ -332,7 +347,7 @@ pub fn dc_get_location_kml(
|
||||
}
|
||||
|
||||
if 0 != success {
|
||||
unsafe { to_cstring(ret) }
|
||||
unsafe { ret.strdup() }
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
@@ -346,7 +361,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();
|
||||
to_cstring(res)
|
||||
res.strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_message_kml(
|
||||
@@ -404,13 +419,14 @@ pub unsafe fn dc_save_locations(
|
||||
context: &Context,
|
||||
chat_id: u32,
|
||||
contact_id: u32,
|
||||
locations: *const dc_array_t,
|
||||
locations_opt: &Option<Vec<dc_location>>,
|
||||
independent: libc::c_int,
|
||||
) -> u32 {
|
||||
if chat_id <= 9 || locations.is_null() {
|
||||
if chat_id <= 9 || locations_opt.is_none() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let locations = locations_opt.as_ref().unwrap();
|
||||
context
|
||||
.sql
|
||||
.prepare2(
|
||||
@@ -422,31 +438,29 @@ pub unsafe fn dc_save_locations(
|
||||
let mut newest_timestamp = 0;
|
||||
let mut newest_location_id = 0;
|
||||
|
||||
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;
|
||||
|
||||
for location in locations {
|
||||
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,
|
||||
);
|
||||
@@ -463,8 +477,8 @@ pub unsafe fn dc_kml_parse(
|
||||
context: &Context,
|
||||
content: *const libc::c_char,
|
||||
content_bytes: size_t,
|
||||
) -> *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;
|
||||
) -> dc_kml_t {
|
||||
let mut kml = dc_kml_t::new();
|
||||
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,
|
||||
@@ -481,8 +495,11 @@ 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 = dc_array_new_typed(1, 100 as size_t);
|
||||
dc_saxparser_init(&mut saxparser, kml as *mut libc::c_void);
|
||||
kml.locations = Some(Vec::with_capacity(100));
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut kml as *mut dc_kml_t as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(kml_starttag_cb),
|
||||
@@ -563,10 +580,8 @@ 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: *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);
|
||||
let location = (*kml).curr.clone();
|
||||
((*kml).locations.as_mut().unwrap()).push(location);
|
||||
}
|
||||
(*kml).tag = 0
|
||||
};
|
||||
@@ -617,15 +632,11 @@ unsafe fn kml_starttag_cb(
|
||||
};
|
||||
}
|
||||
|
||||
pub unsafe fn dc_kml_unref(kml: *mut dc_kml_t) {
|
||||
if kml.is_null() {
|
||||
return;
|
||||
}
|
||||
dc_array_unref((*kml).locations);
|
||||
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
|
||||
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;
|
||||
@@ -688,9 +699,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, 10);
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).hidden = 1;
|
||||
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 9);
|
||||
(*msg).param.set_int(Param::Cmd, 9);
|
||||
dc_send_msg(context, chat_id as u32, msg);
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
@@ -706,13 +717,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=?",
|
||||
@@ -729,14 +740,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
|
||||
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
).is_ok() {
|
||||
stock_str = dc_stock_system_msg(
|
||||
context,
|
||||
65,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
dc_add_device_msg(context, chat_id, stock_str);
|
||||
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as usize,
|
||||
@@ -746,5 +751,4 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
|
||||
}
|
||||
}
|
||||
}
|
||||
free(stock_str as *mut libc::c_void);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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,
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use crate::contact::*;
|
||||
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::*;
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
/* * Structure behind dc_lot_t */
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -29,7 +31,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 dc_chatlist_get_summary() or dc_msg_get_summary().
|
||||
* eg. by chatlist.get_summary() or dc_msg_get_summary().
|
||||
*
|
||||
* NB: _Lot_ is used in the meaning _heap_ here.
|
||||
*/
|
||||
@@ -125,43 +127,59 @@ 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: *const dc_msg_t,
|
||||
msg: *mut dc_msg_t,
|
||||
chat: *const Chat,
|
||||
contact: *const dc_contact_t,
|
||||
contact: Option<&Contact>,
|
||||
context: &Context,
|
||||
) {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint || msg.is_null() {
|
||||
return;
|
||||
}
|
||||
if (*msg).state == 19i32 {
|
||||
(*lot).text1 = dc_stock_str(context, 3i32);
|
||||
(*lot).text1 = context.stock_str(StockMessage::Draft).strdup();
|
||||
(*lot).text1_meaning = 1i32
|
||||
} else if (*msg).from_id == 1i32 as libc::c_uint {
|
||||
if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else {
|
||||
(*lot).text1 = dc_stock_str(context, 2i32);
|
||||
(*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup();
|
||||
(*lot).text1_meaning = 3i32
|
||||
}
|
||||
} else if chat.is_null() {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
|
||||
if 0 != dc_msg_is_info(msg) || contact.is_null() {
|
||||
if 0 != dc_msg_is_info(msg) || contact.is_none() {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else {
|
||||
if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint {
|
||||
(*lot).text1 = dc_contact_get_display_name(contact)
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_display_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
(*lot).text1 = dc_contact_get_first_name(contact)
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_first_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
(*lot).text1_meaning = 2i32
|
||||
(*lot).text1_meaning = 2i32;
|
||||
}
|
||||
}
|
||||
|
||||
let msgtext_c = (*msg)
|
||||
.text
|
||||
.as_ref()
|
||||
.map(|s| CString::yolo(String::as_str(s)));
|
||||
let msgtext_ptr = msgtext_c.map_or(ptr::null(), |s| s.as_ptr());
|
||||
|
||||
(*lot).text2 =
|
||||
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 160i32, context);
|
||||
dc_msg_get_summarytext_by_raw((*msg).type_0, msgtext_ptr, &mut (*msg).param, 160, context);
|
||||
|
||||
(*lot).timestamp = dc_msg_get_timestamp(msg);
|
||||
(*lot).state = (*msg).state;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ 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
|
||||
@@ -31,13 +32,7 @@ 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,
|
||||
0 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
|
||||
}
|
||||
|
||||
|
||||
681
src/dc_msg.rs
681
src/dc_msg.rs
File diff suppressed because it is too large
Load Diff
439
src/dc_param.rs
439
src/dc_param.rs
@@ -1,439 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
119
src/dc_qr.rs
119
src/dc_qr.rs
@@ -1,11 +1,13 @@
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
use crate::contact::*;
|
||||
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::*;
|
||||
@@ -54,36 +56,39 @@ 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: *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);
|
||||
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());
|
||||
if !addr.is_null() {
|
||||
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);
|
||||
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
|
||||
let name_r = percent_decode_str(name_enc)
|
||||
.decode_utf8()
|
||||
.expect("invalid name");
|
||||
name = normalize_name(name_r).strdup();
|
||||
}
|
||||
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);
|
||||
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());
|
||||
if !grpid.is_null() {
|
||||
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);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_param_unref(param);
|
||||
}
|
||||
fingerprint = dc_normalize_fingerprint_c(payload);
|
||||
current_block = 5023038348526654800;
|
||||
@@ -146,11 +151,8 @@ 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: *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;
|
||||
let lines = dc_split_into_lines(qr);
|
||||
for &key in &lines {
|
||||
dc_trim(key);
|
||||
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
|
||||
if !value.is_null() {
|
||||
@@ -183,10 +185,9 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
b";\x00" as *const u8 as *const libc::c_char,
|
||||
b",\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_normalize_name(name);
|
||||
name = normalize_name(as_str(name)).strdup();
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
}
|
||||
@@ -202,10 +203,10 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
let mut temp: *mut libc::c_char = dc_urldecode(addr);
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
temp = dc_addr_normalize(addr);
|
||||
temp = addr_normalize(as_str(addr)).strdup();
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
if !dc_may_be_valid_addr(addr) {
|
||||
if !may_be_valid_addr(as_str(addr)) {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 = dc_strdup(
|
||||
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -246,19 +247,19 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
|
||||
if let Some(peerstate) = peerstate {
|
||||
(*qr_parsed).state = 210i32;
|
||||
let addr_ptr = if let Some(ref addr) = peerstate.addr {
|
||||
to_cstring(addr)
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
let addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
0 as *const libc::c_char,
|
||||
addr_ptr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
free(addr_ptr as *mut _);
|
||||
"",
|
||||
addr,
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
(*qr_parsed).id,
|
||||
@@ -284,26 +285,28 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
} else {
|
||||
(*qr_parsed).state = 200i32
|
||||
}
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
as_str(name),
|
||||
as_str(addr),
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
|
||||
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
|
||||
(*qr_parsed).auth = dc_strdup(auth)
|
||||
}
|
||||
} else if !addr.is_null() {
|
||||
(*qr_parsed).state = 320i32;
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
as_str(name),
|
||||
as_str(addr),
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
} else if strstr(
|
||||
qr,
|
||||
b"http://\x00" as *const u8 as *const libc::c_char,
|
||||
|
||||
@@ -8,22 +8,22 @@ use mmime::other::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_move::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_param::*;
|
||||
use crate::dc_securejoin::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
@@ -38,7 +38,7 @@ pub unsafe fn dc_receive_imf(
|
||||
let mut current_block: u64;
|
||||
/* the function returns the number of created messages in the database */
|
||||
let mut incoming: libc::c_int = 1;
|
||||
let mut incoming_origin: libc::c_int = 0;
|
||||
let mut incoming_origin = Origin::Unknown;
|
||||
let mut to_self: libc::c_int = 0;
|
||||
let mut from_id: uint32_t = 0 as uint32_t;
|
||||
let mut from_id_blocked: libc::c_int = 0;
|
||||
@@ -51,8 +51,6 @@ pub unsafe fn dc_receive_imf(
|
||||
let mut add_delete_job: libc::c_int = 0;
|
||||
let mut insert_msg_id: uint32_t = 0 as uint32_t;
|
||||
|
||||
let mut i: size_t;
|
||||
let mut icnt: size_t;
|
||||
/* Message-ID from the header */
|
||||
let mut rfc724_mid = 0 as *mut libc::c_char;
|
||||
let mut sort_timestamp = 0;
|
||||
@@ -105,7 +103,7 @@ pub unsafe fn dc_receive_imf(
|
||||
dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
context,
|
||||
(*fld_from).frm_mb_list,
|
||||
0x10,
|
||||
Origin::IncomingUnknownFrom,
|
||||
from_list,
|
||||
&mut check_self,
|
||||
);
|
||||
@@ -116,7 +114,8 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
} else if dc_array_get_cnt(from_list) >= 1 {
|
||||
from_id = dc_array_get_id(from_list, 0 as size_t);
|
||||
incoming_origin = dc_get_contact_origin(context, from_id, &mut from_id_blocked)
|
||||
incoming_origin =
|
||||
Contact::get_origin_by_id(context, from_id, &mut from_id_blocked)
|
||||
}
|
||||
dc_array_unref(from_list);
|
||||
}
|
||||
@@ -129,18 +128,18 @@ pub unsafe fn dc_receive_imf(
|
||||
context,
|
||||
(*fld_to).to_addr_list,
|
||||
if 0 == incoming {
|
||||
0x4000
|
||||
} else if incoming_origin >= 0x100 {
|
||||
0x400
|
||||
Origin::OutgoingTo
|
||||
} else if incoming_origin.is_verified() {
|
||||
Origin::IncomingTo
|
||||
} else {
|
||||
0x40
|
||||
Origin::IncomingUnknownTo
|
||||
},
|
||||
to_ids,
|
||||
&mut to_self,
|
||||
);
|
||||
}
|
||||
}
|
||||
if !dc_mimeparser_get_last_nonmeta(&mime_parser).is_null() {
|
||||
if dc_mimeparser_get_last_nonmeta(&mut mime_parser).is_some() {
|
||||
field = dc_mimeparser_lookup_field(&mime_parser, "Cc");
|
||||
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_CC as libc::c_int {
|
||||
let fld_cc: *mut mailimf_cc = (*field).fld_data.fld_cc;
|
||||
@@ -149,11 +148,11 @@ pub unsafe fn dc_receive_imf(
|
||||
context,
|
||||
(*fld_cc).cc_addr_list,
|
||||
if 0 == incoming {
|
||||
0x2000
|
||||
} else if incoming_origin >= 0x100 {
|
||||
0x200
|
||||
Origin::OutgoingCc
|
||||
} else if incoming_origin.is_verified() {
|
||||
Origin::IncomingCc
|
||||
} else {
|
||||
0x20
|
||||
Origin::IncomingUnknownCc
|
||||
},
|
||||
to_ids,
|
||||
0 as *mut libc::c_int,
|
||||
@@ -255,7 +254,7 @@ pub unsafe fn dc_receive_imf(
|
||||
if chat_id == 0 as libc::c_uint {
|
||||
let create_blocked: libc::c_int = if 0 != test_normal_chat_id
|
||||
&& test_normal_chat_id_blocked == 0
|
||||
|| incoming_origin >= 0x7fffffff
|
||||
|| incoming_origin.is_start_new_chat()
|
||||
{
|
||||
0
|
||||
} else {
|
||||
@@ -287,7 +286,7 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
if chat_id == 0 as libc::c_uint {
|
||||
let create_blocked_0: libc::c_int =
|
||||
if incoming_origin >= 0x7fffffff || from_id == to_id {
|
||||
if incoming_origin.is_start_new_chat() || from_id == to_id {
|
||||
0
|
||||
} else {
|
||||
2
|
||||
@@ -311,16 +310,20 @@ pub unsafe fn dc_receive_imf(
|
||||
} else if 0
|
||||
!= dc_is_reply_to_known_message(context, &mime_parser)
|
||||
{
|
||||
dc_scaleup_contact_origin(context, from_id, 0x100);
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
from_id,
|
||||
Origin::IncomingReplyTo,
|
||||
);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Message is a reply to a known message, mark sender as known.",
|
||||
);
|
||||
incoming_origin = if incoming_origin > 0x100 {
|
||||
incoming_origin = if incoming_origin.is_verified() {
|
||||
incoming_origin
|
||||
} else {
|
||||
0x100
|
||||
Origin::IncomingReplyTo
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -329,8 +332,8 @@ pub unsafe fn dc_receive_imf(
|
||||
chat_id = 3 as uint32_t
|
||||
}
|
||||
if 0 != chat_id_blocked && state == 10 {
|
||||
if incoming_origin < 0x100 && msgrmsg == 0 {
|
||||
state = 13
|
||||
if !incoming_origin.is_verified() && msgrmsg == 0 {
|
||||
state = 13;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -355,12 +358,13 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
}
|
||||
if chat_id == 0 as libc::c_uint && 0 != allow_creation {
|
||||
let create_blocked_1: libc::c_int =
|
||||
if 0 != msgrmsg && !dc_is_contact_blocked(context, to_id) {
|
||||
0
|
||||
} else {
|
||||
2
|
||||
};
|
||||
let create_blocked_1: libc::c_int = if 0 != msgrmsg
|
||||
&& !Contact::is_blocked_load(context, to_id)
|
||||
{
|
||||
0
|
||||
} else {
|
||||
2
|
||||
};
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
to_id,
|
||||
@@ -437,7 +441,7 @@ pub unsafe fn dc_receive_imf(
|
||||
)
|
||||
}
|
||||
}
|
||||
icnt = carray_count(mime_parser.parts) as size_t;
|
||||
let icnt = mime_parser.parts.len();
|
||||
|
||||
context.sql.prepare(
|
||||
"INSERT INTO msgs \
|
||||
@@ -452,23 +456,23 @@ pub unsafe fn dc_receive_imf(
|
||||
current_block = 2756754640271984560;
|
||||
break;
|
||||
}
|
||||
let part = carray_get(mime_parser.parts, i as libc::c_uint) as *mut dc_mimepart_t;
|
||||
if !(0 != (*part).is_meta) {
|
||||
if !mime_parser.location_kml.is_null()
|
||||
let part = &mut mime_parser.parts[i];
|
||||
if part.is_meta == 0 {
|
||||
if !mime_parser.location_kml.is_none()
|
||||
&& icnt == 1
|
||||
&& !(*part).msg.is_null()
|
||||
&& !part.msg.is_null()
|
||||
&& (strcmp(
|
||||
(*part).msg,
|
||||
part.msg,
|
||||
b"-location-\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
|| *(*part).msg.offset(0isize) as libc::c_int == 0)
|
||||
|| *part.msg.offset(0isize) as libc::c_int == 0)
|
||||
{
|
||||
hidden = 1;
|
||||
if state == 10 {
|
||||
state = 13
|
||||
}
|
||||
}
|
||||
if (*part).type_0 == 10 {
|
||||
if part.type_0 == 10 {
|
||||
txt_raw = dc_mprintf(
|
||||
b"%s\n\n%s\x00" as *const u8 as *const libc::c_char,
|
||||
if !mime_parser.subject.is_null() {
|
||||
@@ -476,13 +480,12 @@ pub unsafe fn dc_receive_imf(
|
||||
} else {
|
||||
b"\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
(*part).msg_raw,
|
||||
part.msg_raw,
|
||||
)
|
||||
}
|
||||
if 0 != mime_parser.is_system_message {
|
||||
dc_param_set_int(
|
||||
(*part).param,
|
||||
'S' as i32,
|
||||
|
||||
part.param.set_int( Param::Cmd,
|
||||
mime_parser.is_system_message,
|
||||
);
|
||||
}
|
||||
@@ -497,11 +500,11 @@ pub unsafe fn dc_receive_imf(
|
||||
sort_timestamp,
|
||||
sent_timestamp,
|
||||
rcvd_timestamp,
|
||||
(*part).type_0,
|
||||
part.type_0,
|
||||
state,
|
||||
msgrmsg,
|
||||
if !(*part).msg.is_null() {
|
||||
as_str((*part).msg)
|
||||
if !part.msg.is_null() {
|
||||
as_str(part.msg)
|
||||
} else {
|
||||
""
|
||||
},
|
||||
@@ -511,8 +514,8 @@ pub unsafe fn dc_receive_imf(
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
as_str((*(*part).param).packed),
|
||||
(*part).bytes,
|
||||
part.param.to_string(),
|
||||
part.bytes,
|
||||
hidden,
|
||||
if 0 != save_mime_headers {
|
||||
let body_string = std::str::from_utf8(std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes)).unwrap();
|
||||
@@ -589,17 +592,13 @@ pub unsafe fn dc_receive_imf(
|
||||
match current_block {
|
||||
16282941964262048061 => {}
|
||||
_ => {
|
||||
if carray_count(mime_parser.reports) > 0 as libc::c_uint {
|
||||
if !mime_parser.reports.is_empty() {
|
||||
let mdns_enabled = context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1);
|
||||
icnt = carray_count(mime_parser.reports) as size_t;
|
||||
i = 0 as size_t;
|
||||
while i < icnt {
|
||||
for report_root in mime_parser.reports {
|
||||
let mut mdn_consumed: libc::c_int = 0;
|
||||
let report_root: *mut mailmime =
|
||||
carray_get(mime_parser.reports, i as libc::c_uint) as *mut mailmime;
|
||||
let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter(
|
||||
report_root,
|
||||
b"report-type\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -724,8 +723,7 @@ pub unsafe fn dc_receive_imf(
|
||||
&mut msg_id,
|
||||
) {
|
||||
rr_event_to_send
|
||||
.push((chat_id_0, 0));
|
||||
rr_event_to_send.push((msg_id, 0));
|
||||
.push((chat_id_0, msg_id));
|
||||
}
|
||||
mdn_consumed = (msg_id
|
||||
!= 0 as libc::c_uint)
|
||||
@@ -743,47 +741,35 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
}
|
||||
if 0 != mime_parser.is_send_by_messenger || 0 != mdn_consumed {
|
||||
let param = dc_param_new();
|
||||
let server_folder_c = to_cstring(server_folder.as_ref());
|
||||
dc_param_set(
|
||||
param,
|
||||
DC_PARAM_SERVER_FOLDER as i32,
|
||||
server_folder_c,
|
||||
);
|
||||
free(server_folder_c as *mut _);
|
||||
dc_param_set_int(
|
||||
param,
|
||||
DC_PARAM_SERVER_UID as i32,
|
||||
server_uid as i32,
|
||||
);
|
||||
let mut param = Params::new();
|
||||
param.set(Param::ServerFolder, server_folder.as_ref());
|
||||
param.set_int(Param::ServerUid, server_uid as i32);
|
||||
if 0 != mime_parser.is_send_by_messenger
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
dc_param_set_int(param, DC_PARAM_ALSO_MOVE as i32, 1);
|
||||
param.set_int(Param::AlsoMove, 1);
|
||||
}
|
||||
dc_job_add(context, 120, 0, (*param).packed, 0);
|
||||
dc_param_unref(param);
|
||||
dc_job_add(context, 120, 0, param, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
i = i.wrapping_add(1)
|
||||
}
|
||||
}
|
||||
if !mime_parser.message_kml.is_null() && chat_id > 9 as libc::c_uint {
|
||||
if !mime_parser.message_kml.is_none() && chat_id > 9 as libc::c_uint {
|
||||
let mut location_id_written = false;
|
||||
let mut send_event = false;
|
||||
|
||||
if !mime_parser.message_kml.is_null()
|
||||
if !mime_parser.message_kml.is_none()
|
||||
&& chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint
|
||||
{
|
||||
let newest_location_id: uint32_t = dc_save_locations(
|
||||
context,
|
||||
chat_id,
|
||||
from_id,
|
||||
(*mime_parser.message_kml).locations,
|
||||
&mime_parser.message_kml.unwrap().locations,
|
||||
1,
|
||||
);
|
||||
if 0 != newest_location_id && 0 == hidden {
|
||||
@@ -793,28 +779,37 @@ pub unsafe fn dc_receive_imf(
|
||||
}
|
||||
}
|
||||
|
||||
if !mime_parser.location_kml.is_null()
|
||||
if !mime_parser.location_kml.is_none()
|
||||
&& chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint
|
||||
{
|
||||
let contact = dc_get_contact(context, from_id);
|
||||
if !(*mime_parser.location_kml).addr.is_null()
|
||||
&& !contact.is_null()
|
||||
&& !(*contact).addr.is_null()
|
||||
&& strcasecmp((*contact).addr, (*mime_parser.location_kml).addr) == 0
|
||||
{
|
||||
let newest_location_id = dc_save_locations(
|
||||
context,
|
||||
chat_id,
|
||||
from_id,
|
||||
(*mime_parser.location_kml).locations,
|
||||
0,
|
||||
);
|
||||
if newest_location_id != 0 && hidden == 0 && !location_id_written {
|
||||
dc_set_msg_location_id(context, insert_msg_id, newest_location_id);
|
||||
if !mime_parser.location_kml.as_ref().unwrap().addr.is_null() {
|
||||
if let Ok(contact) = Contact::get_by_id(context, from_id) {
|
||||
if !contact.get_addr().is_empty()
|
||||
&& contact.get_addr().to_lowercase()
|
||||
== as_str(mime_parser.location_kml.as_ref().unwrap().addr)
|
||||
.to_lowercase()
|
||||
{
|
||||
let newest_location_id = dc_save_locations(
|
||||
context,
|
||||
chat_id,
|
||||
from_id,
|
||||
&mime_parser.location_kml.as_ref().unwrap().locations,
|
||||
0,
|
||||
);
|
||||
if newest_location_id != 0
|
||||
&& hidden == 0
|
||||
&& !location_id_written
|
||||
{
|
||||
dc_set_msg_location_id(
|
||||
context,
|
||||
insert_msg_id,
|
||||
newest_location_id,
|
||||
);
|
||||
}
|
||||
send_event = true;
|
||||
}
|
||||
}
|
||||
send_event = true;
|
||||
}
|
||||
dc_contact_unref(contact);
|
||||
}
|
||||
if send_event {
|
||||
context.call_cb(
|
||||
@@ -830,7 +825,7 @@ pub unsafe fn dc_receive_imf(
|
||||
context,
|
||||
DC_JOB_DELETE_MSG_ON_IMAP,
|
||||
created_db_entries[0].1 as i32,
|
||||
0 as *const libc::c_char,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
}
|
||||
@@ -914,6 +909,7 @@ which tries to create or find out the chat_id by:
|
||||
|
||||
So when the function returns, the caller has the group id matching the current
|
||||
state of the group. */
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn create_or_lookup_group(
|
||||
context: &Context,
|
||||
mime_parser: &mut dc_mimeparser_t,
|
||||
@@ -941,18 +937,13 @@ unsafe fn create_or_lookup_group(
|
||||
let mut X_MrAddToGrp: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut X_MrGrpNameChanged: libc::c_int = 0;
|
||||
let mut X_MrGrpImageChanged: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut better_msg: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut better_msg: String = From::from("");
|
||||
let mut failure_reason: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if mime_parser.is_system_message == 8 {
|
||||
better_msg = dc_stock_system_msg(
|
||||
context,
|
||||
64,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
from_id as uint32_t,
|
||||
)
|
||||
if mime_parser.is_system_message == 8i32 {
|
||||
better_msg =
|
||||
context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32)
|
||||
}
|
||||
set_better_msg(mime_parser, &mut better_msg);
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
/* search the grpid in the header */
|
||||
let mut field: *mut mailimf_field;
|
||||
let mut optional_field: *mut mailimf_optional_field;
|
||||
@@ -968,7 +959,13 @@ unsafe fn create_or_lookup_group(
|
||||
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_MESSAGE_ID as libc::c_int {
|
||||
let fld_message_id: *mut mailimf_message_id = (*field).fld_data.fld_message_id;
|
||||
if !fld_message_id.is_null() {
|
||||
grpid = dc_extract_grpid_from_rfc724_mid((*fld_message_id).mid_value)
|
||||
if let Some(extracted_grpid) =
|
||||
dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value))
|
||||
{
|
||||
grpid = extracted_grpid.strdup();
|
||||
} else {
|
||||
grpid = 0 as *mut libc::c_char;
|
||||
}
|
||||
}
|
||||
}
|
||||
if grpid.is_null() {
|
||||
@@ -1028,15 +1025,17 @@ unsafe fn create_or_lookup_group(
|
||||
if !optional_field.is_null() {
|
||||
X_MrRemoveFromGrp = (*optional_field).fld_value;
|
||||
mime_parser.is_system_message = 5;
|
||||
let left_group: libc::c_int =
|
||||
(dc_lookup_contact_id_by_addr(context, X_MrRemoveFromGrp)
|
||||
== from_id as libc::c_uint) as libc::c_int;
|
||||
better_msg = dc_stock_system_msg(
|
||||
context,
|
||||
if 0 != left_group { 19 } else { 18 },
|
||||
X_MrRemoveFromGrp,
|
||||
0 as *const libc::c_char,
|
||||
from_id as uint32_t,
|
||||
let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp))
|
||||
== from_id as u32) as libc::c_int;
|
||||
better_msg = context.stock_system_msg(
|
||||
if 0 != left_group {
|
||||
StockMessage::MsgGroupLeft
|
||||
} else {
|
||||
StockMessage::MsgDelMember
|
||||
},
|
||||
as_str(X_MrRemoveFromGrp),
|
||||
"",
|
||||
from_id as u32,
|
||||
)
|
||||
} else {
|
||||
optional_field = dc_mimeparser_lookup_optional_field(
|
||||
@@ -1053,12 +1052,11 @@ unsafe fn create_or_lookup_group(
|
||||
if !optional_field.is_null() {
|
||||
X_MrGrpImageChanged = (*optional_field).fld_value
|
||||
}
|
||||
better_msg = dc_stock_system_msg(
|
||||
context,
|
||||
17,
|
||||
X_MrAddToGrp,
|
||||
0 as *const libc::c_char,
|
||||
from_id as uint32_t,
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
as_str(X_MrAddToGrp),
|
||||
"",
|
||||
from_id as u32,
|
||||
)
|
||||
} else {
|
||||
optional_field = dc_mimeparser_lookup_optional_field(
|
||||
@@ -1066,14 +1064,13 @@ unsafe fn create_or_lookup_group(
|
||||
b"Chat-Group-Name-Changed\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if !optional_field.is_null() {
|
||||
X_MrGrpNameChanged = 1;
|
||||
mime_parser.is_system_message = 2;
|
||||
better_msg = dc_stock_system_msg(
|
||||
context,
|
||||
15,
|
||||
(*optional_field).fld_value,
|
||||
grpname,
|
||||
from_id as uint32_t,
|
||||
X_MrGrpNameChanged = 1i32;
|
||||
mime_parser.is_system_message = 2i32;
|
||||
better_msg = context.stock_system_msg(
|
||||
StockMessage::MsgGrpName,
|
||||
as_str((*optional_field).fld_value),
|
||||
as_str(grpname),
|
||||
from_id as u32,
|
||||
)
|
||||
} else {
|
||||
optional_field = dc_mimeparser_lookup_optional_field(
|
||||
@@ -1083,26 +1080,25 @@ unsafe fn create_or_lookup_group(
|
||||
if !optional_field.is_null() {
|
||||
X_MrGrpImageChanged = (*optional_field).fld_value;
|
||||
mime_parser.is_system_message = 3;
|
||||
better_msg = dc_stock_system_msg(
|
||||
context,
|
||||
better_msg = context.stock_system_msg(
|
||||
if strcmp(
|
||||
X_MrGrpImageChanged,
|
||||
b"0\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
33
|
||||
StockMessage::MsgGrpImgDeleted
|
||||
} else {
|
||||
16
|
||||
StockMessage::MsgGrpImgChanged
|
||||
},
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
from_id as uint32_t,
|
||||
"",
|
||||
"",
|
||||
from_id as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
set_better_msg(mime_parser, &mut better_msg);
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
chat_id = dc_get_chat_id_by_grpid(
|
||||
context,
|
||||
grpid,
|
||||
@@ -1139,7 +1135,7 @@ unsafe fn create_or_lookup_group(
|
||||
&& !grpname.is_null()
|
||||
&& X_MrRemoveFromGrp.is_null()
|
||||
&& (0 == group_explicitly_left
|
||||
|| !X_MrAddToGrp.is_null() && dc_addr_cmp(&self_addr, as_str(X_MrAddToGrp)))
|
||||
|| !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp)))
|
||||
{
|
||||
/*otherwise, a pending "quit" message may pop up*/
|
||||
/*re-create explicitly left groups only if ourself is re-added*/
|
||||
@@ -1221,24 +1217,19 @@ unsafe fn create_or_lookup_group(
|
||||
{
|
||||
ok = 1
|
||||
} else {
|
||||
let mut i_0: libc::c_int = 0;
|
||||
while (i_0 as libc::c_uint) < carray_count(mime_parser.parts) {
|
||||
let part: *mut dc_mimepart_t =
|
||||
carray_get(mime_parser.parts, i_0 as libc::c_uint)
|
||||
as *mut dc_mimepart_t;
|
||||
if (*part).type_0 == 20 {
|
||||
grpimage = dc_param_get(
|
||||
(*part).param,
|
||||
DC_PARAM_FILE as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
for part in &mut mime_parser.parts {
|
||||
if part.type_0 == 20 {
|
||||
grpimage = part
|
||||
.param
|
||||
.get(Param::File)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
ok = 1
|
||||
}
|
||||
i_0 += 1
|
||||
}
|
||||
}
|
||||
if 0 != ok {
|
||||
let chat: *mut Chat = dc_chat_new(context);
|
||||
let chat = dc_chat_new(context);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
@@ -1250,11 +1241,11 @@ unsafe fn create_or_lookup_group(
|
||||
},
|
||||
);
|
||||
dc_chat_load_from_db(chat, chat_id);
|
||||
dc_param_set(
|
||||
(*chat).param,
|
||||
DC_PARAM_PROFILE_IMAGE as i32,
|
||||
grpimage,
|
||||
);
|
||||
if grpimage.is_null() {
|
||||
(*chat).param.remove(Param::ProfileImage);
|
||||
} else {
|
||||
(*chat).param.set(Param::ProfileImage, as_str(grpimage));
|
||||
}
|
||||
dc_chat_update_param(chat);
|
||||
dc_chat_unref(chat);
|
||||
free(grpimage as *mut libc::c_void);
|
||||
@@ -1274,17 +1265,20 @@ unsafe fn create_or_lookup_group(
|
||||
params![chat_id as i32],
|
||||
)
|
||||
.ok();
|
||||
if skip.is_null() || !dc_addr_cmp(&self_addr, as_str(skip)) {
|
||||
if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) {
|
||||
dc_add_to_chat_contacts_table(context, chat_id, 1);
|
||||
}
|
||||
if from_id > 9 {
|
||||
if !dc_addr_equals_contact(context, &self_addr, from_id as u32)
|
||||
&& (skip.is_null()
|
||||
|| !dc_addr_equals_contact(
|
||||
context,
|
||||
to_string(skip),
|
||||
from_id as u32,
|
||||
))
|
||||
if !Contact::addr_equals_contact(
|
||||
context,
|
||||
&self_addr,
|
||||
from_id as u32,
|
||||
) && (skip.is_null()
|
||||
|| !Contact::addr_equals_contact(
|
||||
context,
|
||||
to_string(skip),
|
||||
from_id as u32,
|
||||
))
|
||||
{
|
||||
dc_add_to_chat_contacts_table(
|
||||
context,
|
||||
@@ -1296,9 +1290,13 @@ unsafe fn create_or_lookup_group(
|
||||
i = 0;
|
||||
while i < to_ids_cnt {
|
||||
let to_id = dc_array_get_id(to_ids, i as size_t);
|
||||
if !dc_addr_equals_contact(context, &self_addr, to_id)
|
||||
if !Contact::addr_equals_contact(context, &self_addr, to_id)
|
||||
&& (skip.is_null()
|
||||
|| !dc_addr_equals_contact(context, to_string(skip), to_id))
|
||||
|| !Contact::addr_equals_contact(
|
||||
context,
|
||||
to_string(skip),
|
||||
to_id,
|
||||
))
|
||||
{
|
||||
dc_add_to_chat_contacts_table(context, chat_id, to_id);
|
||||
}
|
||||
@@ -1342,8 +1340,6 @@ unsafe fn create_or_lookup_group(
|
||||
}
|
||||
free(grpid as *mut libc::c_void);
|
||||
free(grpname as *mut libc::c_void);
|
||||
|
||||
free(better_msg as *mut libc::c_void);
|
||||
free(failure_reason as *mut libc::c_void);
|
||||
if !ret_chat_id.is_null() {
|
||||
*ret_chat_id = chat_id
|
||||
@@ -1429,11 +1425,12 @@ unsafe fn create_or_lookup_adhoc_group(
|
||||
{
|
||||
grpname = dc_strdup(mime_parser.subject)
|
||||
} else {
|
||||
grpname = dc_stock_str_repl_int(
|
||||
context,
|
||||
4,
|
||||
dc_array_get_cnt(member_ids) as libc::c_int,
|
||||
)
|
||||
grpname = context
|
||||
.stock_string_repl_int(
|
||||
StockMessage::Member,
|
||||
dc_array_get_cnt(member_ids) as libc::c_int,
|
||||
)
|
||||
.strdup();
|
||||
}
|
||||
chat_id =
|
||||
create_group_record(context, grpid, grpname, create_blocked, 0);
|
||||
@@ -1542,9 +1539,10 @@ fn hex_hash(s: impl AsRef<str>) -> *const libc::c_char {
|
||||
let bytes = s.as_ref().as_bytes();
|
||||
let result = Sha256::digest(bytes);
|
||||
let result_hex = hex::encode(&result[..8]);
|
||||
unsafe { to_cstring(result_hex) as *const _ }
|
||||
unsafe { result_hex.strdup() as *const _ }
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn search_chat_ids_by_contact_ids(
|
||||
context: &Context,
|
||||
unsorted_contact_ids: *const dc_array_t,
|
||||
@@ -1569,7 +1567,7 @@ unsafe fn search_chat_ids_by_contact_ids(
|
||||
i += 1
|
||||
}
|
||||
if !(dc_array_get_cnt(contact_ids) == 0) {
|
||||
dc_array_sort_ids(contact_ids);
|
||||
(*contact_ids).sort_ids();
|
||||
contact_ids_str =
|
||||
dc_array_get_string(contact_ids, b",\x00" as *const u8 as *const libc::c_char);
|
||||
|
||||
@@ -1623,26 +1621,21 @@ unsafe fn check_verified_properties(
|
||||
to_ids: *const dc_array_t,
|
||||
failure_reason: *mut *mut libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let contact = dc_contact_new(context);
|
||||
|
||||
let verify_fail = |reason: String| {
|
||||
*failure_reason = to_cstring(format!("{}. See \"Info\" for details.", reason));
|
||||
*failure_reason = format!("{}. See \"Info\" for details.", reason).strdup();
|
||||
warn!(context, 0, "{}", reason);
|
||||
};
|
||||
|
||||
let cleanup = || {
|
||||
dc_contact_unref(contact);
|
||||
let contact = match Contact::load_from_db(context, from_id) {
|
||||
Ok(contact) => contact,
|
||||
Err(_err) => {
|
||||
verify_fail("Internal Error; cannot load contact".into());
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
if !dc_contact_load_from_db(contact, &context.sql, from_id) {
|
||||
verify_fail("Internal Error; cannot load contact".into());
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if 0 == mimeparser.e2ee_helper.encrypted {
|
||||
verify_fail("This message is not encrypted".into());
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1651,18 +1644,18 @@ unsafe fn check_verified_properties(
|
||||
// this check is skipped for SELF as there is no proper SELF-peerstate
|
||||
// and results in group-splits otherwise.
|
||||
if from_id != 1 {
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr));
|
||||
let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr());
|
||||
|
||||
if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 {
|
||||
if peerstate.is_none()
|
||||
|| contact.is_verified_ex(peerstate.as_ref()) != VerifiedStatus::BidirectVerified
|
||||
{
|
||||
verify_fail("The sender of this message is not verified.".into());
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if let Some(peerstate) = peerstate {
|
||||
if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) {
|
||||
verify_fail("The message was sent with non-verified encryption.".into());
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1684,7 +1677,6 @@ unsafe fn check_verified_properties(
|
||||
);
|
||||
|
||||
if rows.is_err() {
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
for (to_addr, mut is_verified) in rows.unwrap().into_iter() {
|
||||
@@ -1705,7 +1697,7 @@ unsafe fn check_verified_properties(
|
||||
context,
|
||||
0,
|
||||
"{} has verfied {}.",
|
||||
as_str((*contact).addr),
|
||||
contact.get_addr(),
|
||||
to_addr,
|
||||
);
|
||||
let fp = peerstate.gossip_key_fingerprint.clone();
|
||||
@@ -1721,7 +1713,6 @@ unsafe fn check_verified_properties(
|
||||
"{} is not a member of this verified group",
|
||||
to_addr
|
||||
));
|
||||
cleanup();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -1729,14 +1720,13 @@ unsafe fn check_verified_properties(
|
||||
1
|
||||
}
|
||||
|
||||
unsafe fn set_better_msg(mime_parser: &dc_mimeparser_t, better_msg: *mut *mut libc::c_char) {
|
||||
if !(*better_msg).is_null() && carray_count((*mime_parser).parts) > 0 as libc::c_uint {
|
||||
let mut part: *mut dc_mimepart_t =
|
||||
carray_get(mime_parser.parts, 0 as libc::c_uint) as *mut dc_mimepart_t;
|
||||
unsafe fn set_better_msg<T: AsRef<str>>(mime_parser: &mut dc_mimeparser_t, better_msg: T) {
|
||||
let msg = better_msg.as_ref();
|
||||
if msg.len() > 0 && !mime_parser.parts.is_empty() {
|
||||
let part = &mut mime_parser.parts[0];
|
||||
if (*part).type_0 == 10 {
|
||||
free((*part).msg as *mut libc::c_void);
|
||||
(*part).msg = *better_msg;
|
||||
*better_msg = 0 as *mut libc::c_char
|
||||
free(part.msg as *mut libc::c_void);
|
||||
part.msg = msg.strdup();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1903,7 +1893,7 @@ fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) ->
|
||||
unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
context: &Context,
|
||||
adr_list: *const mailimf_address_list,
|
||||
origin: libc::c_int,
|
||||
origin: Origin,
|
||||
ids: *mut dc_array_t,
|
||||
check_self: *mut libc::c_int,
|
||||
) {
|
||||
@@ -1953,7 +1943,7 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list(
|
||||
unsafe fn dc_add_or_lookup_contacts_by_mailbox_list(
|
||||
context: &Context,
|
||||
mb_list: *const mailimf_mailbox_list,
|
||||
origin: libc::c_int,
|
||||
origin: Origin,
|
||||
ids: *mut dc_array_t,
|
||||
check_self: *mut libc::c_int,
|
||||
) {
|
||||
@@ -1991,7 +1981,7 @@ unsafe fn add_or_lookup_contact_by_addr(
|
||||
context: &Context,
|
||||
display_name_enc: *const libc::c_char,
|
||||
addr_spec: *const libc::c_char,
|
||||
origin: libc::c_int,
|
||||
origin: Origin,
|
||||
ids: *mut dc_array_t,
|
||||
mut check_self: *mut libc::c_int,
|
||||
) {
|
||||
@@ -2009,7 +1999,7 @@ unsafe fn add_or_lookup_contact_by_addr(
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
|
||||
if dc_addr_cmp(self_addr, as_str(addr_spec)) {
|
||||
if addr_cmp(self_addr, as_str(addr_spec)) {
|
||||
*check_self = 1;
|
||||
}
|
||||
|
||||
@@ -2017,20 +2007,15 @@ unsafe fn add_or_lookup_contact_by_addr(
|
||||
return;
|
||||
}
|
||||
/* add addr_spec if missing, update otherwise */
|
||||
let mut display_name_dec = 0 as *mut libc::c_char;
|
||||
let mut display_name_dec = "".to_string();
|
||||
if !display_name_enc.is_null() {
|
||||
display_name_dec = dc_decode_header_words(display_name_enc);
|
||||
dc_normalize_name(display_name_dec);
|
||||
let tmp = as_str(dc_decode_header_words(display_name_enc));
|
||||
display_name_dec = normalize_name(&tmp);
|
||||
}
|
||||
/*can be NULL*/
|
||||
let row_id = dc_add_or_lookup_contact(
|
||||
context,
|
||||
display_name_dec,
|
||||
addr_spec,
|
||||
origin,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
free(display_name_dec as *mut libc::c_void);
|
||||
let row_id = Contact::add_or_lookup(context, display_name_dec, as_str(addr_spec), origin)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
if 0 != row_id {
|
||||
if !dc_array_search_id(ids, row_id, 0 as *mut size_t) {
|
||||
dc_array_add_id(ids, row_id);
|
||||
|
||||
@@ -12,10 +12,13 @@ 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) -> (),
|
||||
>;
|
||||
@@ -77,7 +80,7 @@ pub unsafe fn dc_saxparser_set_text_handler(
|
||||
}
|
||||
|
||||
pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *const libc::c_char) {
|
||||
let current_block: u64;
|
||||
let mut is_valid = false;
|
||||
let mut bak: libc::c_char;
|
||||
let buf_start: *mut libc::c_char;
|
||||
let mut last_text_start: *mut libc::c_char;
|
||||
@@ -96,7 +99,7 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
p = buf_start;
|
||||
loop {
|
||||
if !(0 != *p) {
|
||||
current_block = 13425230902034816933;
|
||||
is_valid = true;
|
||||
break;
|
||||
}
|
||||
if *p as libc::c_int == '<' as i32 {
|
||||
@@ -110,7 +113,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
if strncmp(p, b"!--\x00" as *const u8 as *const libc::c_char, 3) == 0i32 {
|
||||
p = strstr(p, b"-->\x00" as *const u8 as *const libc::c_char);
|
||||
if p.is_null() {
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
}
|
||||
p = p.offset(3isize)
|
||||
@@ -134,7 +136,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
strlen(text_beg),
|
||||
'c' as i32 as libc::c_char,
|
||||
);
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
}
|
||||
} else if strncmp(p, b"!DOCTYPE\x00" as *const u8 as *const libc::c_char, 8) == 0i32 {
|
||||
@@ -146,13 +147,11 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
}
|
||||
if *p as libc::c_int == 0i32 {
|
||||
/* unclosed doctype */
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
} else if *p as libc::c_int == '[' as i32 {
|
||||
p = strstr(p, b"]>\x00" as *const u8 as *const libc::c_char);
|
||||
if p.is_null() {
|
||||
/* unclosed inline doctype */
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
} else {
|
||||
p = p.offset(2isize)
|
||||
@@ -164,7 +163,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
p = strstr(p, b"?>\x00" as *const u8 as *const libc::c_char);
|
||||
if p.is_null() {
|
||||
/* unclosed processing instruction */
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
} else {
|
||||
p = p.offset(2isize)
|
||||
@@ -325,7 +323,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
p = strchr(p, '>' as i32);
|
||||
if p.is_null() {
|
||||
/* unclosed start-tag or end-tag */
|
||||
current_block = 7627180618761592946;
|
||||
break;
|
||||
} else {
|
||||
p = p.offset(1isize)
|
||||
@@ -336,16 +333,13 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c
|
||||
p = p.offset(1isize)
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
13425230902034816933 => {
|
||||
call_text_cb(
|
||||
saxparser,
|
||||
last_text_start,
|
||||
p.wrapping_offset_from(last_text_start) as size_t,
|
||||
'&' as i32 as libc::c_char,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
if is_valid {
|
||||
call_text_cb(
|
||||
saxparser,
|
||||
last_text_start,
|
||||
p.wrapping_offset_from(last_text_start) as size_t,
|
||||
'&' as i32 as libc::c_char,
|
||||
);
|
||||
}
|
||||
do_free_attr(attr.as_mut_ptr(), free_attr.as_mut_ptr());
|
||||
free(buf_start as *mut libc::c_void);
|
||||
@@ -492,19 +486,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;
|
||||
@@ -532,7 +526,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)
|
||||
}
|
||||
@@ -566,7 +560,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,
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use mmime::mailimf_types::*;
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_contact::*;
|
||||
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::*;
|
||||
|
||||
@@ -38,17 +40,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 = None;
|
||||
let mut qr: Option<String> = 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();
|
||||
invitenumber = dc_create_id().strdup();
|
||||
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();
|
||||
auth = dc_create_id().strdup();
|
||||
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth);
|
||||
}
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
@@ -62,7 +64,7 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
free(group_name_urlencoded as *mut libc::c_void);
|
||||
|
||||
if let Some(qr) = qr {
|
||||
to_cstring(qr)
|
||||
qr.strdup()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
@@ -85,8 +87,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, DEFAULT_ENCODE_SET).to_string();
|
||||
let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string();
|
||||
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
|
||||
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
qr = if 0 != group_chat_id {
|
||||
chat = dc_get_chat(context, group_chat_id);
|
||||
@@ -261,29 +263,33 @@ 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 = 10i32;
|
||||
(*msg).text = dc_mprintf(
|
||||
b"Secure-Join: %s\x00" as *const u8 as *const libc::c_char,
|
||||
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);
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
(*msg).text = Some(format!("Secure-Join: {}", to_string(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));
|
||||
}
|
||||
if !param2.is_null() {
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG2 as i32, param2);
|
||||
(*msg).param.set(Param::Arg2, as_str(param2));
|
||||
}
|
||||
if !fingerprint.is_null() {
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG3 as i32, fingerprint);
|
||||
(*msg).param.set(Param::Arg3, as_str(fingerprint));
|
||||
}
|
||||
if !grpid.is_null() {
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG4 as i32, grpid);
|
||||
(*msg).param.set(Param::Arg4, as_str(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
|
||||
{
|
||||
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 1);
|
||||
(*msg).param.set_int(
|
||||
Param::ForcePlaintext,
|
||||
ForcePlaintext::AddAutocryptHeader as i32,
|
||||
);
|
||||
} else {
|
||||
dc_param_set_int((*msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
|
||||
(*msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
}
|
||||
dc_send_msg(context, contact_chat_id, msg);
|
||||
dc_msg_unref(msg);
|
||||
@@ -309,30 +315,23 @@ unsafe fn fingerprint_equals_sender(
|
||||
return 0;
|
||||
}
|
||||
let mut fingerprint_equal: libc::c_int = 0i32;
|
||||
let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id);
|
||||
let contact: *mut dc_contact_t = dc_contact_new(context);
|
||||
let contacts = dc_get_chat_contacts(context, contact_chat_id);
|
||||
|
||||
if !(dc_array_get_cnt(contacts) != 1) {
|
||||
if !dc_contact_load_from_db(
|
||||
contact,
|
||||
&context.sql,
|
||||
dc_array_get_id(contacts, 0i32 as size_t),
|
||||
) {
|
||||
if let Ok(contact) = Contact::load_from_db(context, dc_array_get_id(contacts, 0)) {
|
||||
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
|
||||
{
|
||||
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
|
||||
if peerstate.public_key_fingerprint.is_some()
|
||||
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
|
||||
{
|
||||
fingerprint_equal = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if let Some(peerstate) =
|
||||
Peerstate::from_addr(context, &context.sql, as_str((*contact).addr))
|
||||
{
|
||||
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
|
||||
if peerstate.public_key_fingerprint.is_some()
|
||||
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
|
||||
{
|
||||
fingerprint_equal = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_contact_unref(contact);
|
||||
dc_array_unref(contacts);
|
||||
|
||||
fingerprint_equal
|
||||
@@ -354,7 +353,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
let mut contact_chat_id_blocked: libc::c_int = 0i32;
|
||||
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut ret: libc::c_int = 0i32;
|
||||
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
|
||||
if !(contact_id <= 9i32 as libc::c_uint) {
|
||||
step = lookup_field(mimeparser, "Secure-Join");
|
||||
if !step.is_null() {
|
||||
@@ -566,7 +565,11 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_scaleup_contact_origin(context, contact_id, 0x1000000i32);
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
contact_id,
|
||||
Origin::SecurejoinInvited,
|
||||
);
|
||||
info!(context, 0, "Auth verified.",);
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.call_cb(
|
||||
@@ -693,16 +696,23 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_scaleup_contact_origin(context, contact_id, 0x2000000i32);
|
||||
Contact::scaleup_origin_by_id(
|
||||
context,
|
||||
contact_id,
|
||||
Origin::SecurejoinJoined,
|
||||
);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
0i32 as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
if 0 != join_vg {
|
||||
if 0 == dc_addr_equals_self(
|
||||
if !addr_equals_self(
|
||||
context,
|
||||
lookup_field(mimeparser, "Chat-Group-Member-Added"),
|
||||
as_str(lookup_field(
|
||||
mimeparser,
|
||||
"Chat-Group-Member-Added",
|
||||
)),
|
||||
) {
|
||||
info!(
|
||||
context,
|
||||
@@ -750,22 +760,26 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
==== Alice - the inviter side ====
|
||||
==== Step 8 in "Out-of-band verified groups" protocol ====
|
||||
============================================================ */
|
||||
contact = dc_get_contact(context, contact_id);
|
||||
if contact.is_null() || 0 == dc_contact_is_verified(contact) {
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
if contact.is_verified() == VerifiedStatus::Unverified {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
contact_id as uintptr_t,
|
||||
800i32 as uintptr_t,
|
||||
);
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
contact_id as uintptr_t,
|
||||
1000i32 as uintptr_t,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
contact_id as uintptr_t,
|
||||
800i32 as uintptr_t,
|
||||
);
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
contact_id as uintptr_t,
|
||||
1000i32 as uintptr_t,
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
}
|
||||
} else {
|
||||
current_block = 10256747982273457880;
|
||||
@@ -780,7 +794,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_contact_unref(contact);
|
||||
|
||||
free(scanned_fingerprint_of_alice as *mut libc::c_void);
|
||||
free(auth as *mut libc::c_void);
|
||||
free(own_fingerprint as *mut libc::c_void);
|
||||
@@ -796,24 +810,20 @@ unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
|
||||
|
||||
unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) {
|
||||
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
|
||||
let contact: *mut dc_contact_t = dc_get_contact(context, contact_id);
|
||||
let msg: *mut libc::c_char = dc_stock_str_repl_string(
|
||||
context,
|
||||
35i32,
|
||||
if !contact.is_null() {
|
||||
(*contact).addr
|
||||
} else {
|
||||
b"?\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
);
|
||||
dc_add_device_msg(context, contact_chat_id, msg);
|
||||
let contact = Contact::get_by_id(context, contact_id);
|
||||
let addr = if let Ok(ref contact) = contact {
|
||||
contact.get_addr()
|
||||
} else {
|
||||
"?"
|
||||
};
|
||||
let msg =
|
||||
CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap();
|
||||
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
contact_chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
);
|
||||
free(msg as *mut libc::c_void);
|
||||
dc_contact_unref(contact);
|
||||
}
|
||||
|
||||
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
|
||||
@@ -839,20 +849,18 @@ unsafe fn could_not_establish_secure_connection(
|
||||
details: *const libc::c_char,
|
||||
) {
|
||||
let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id);
|
||||
let contact = dc_get_contact(context, contact_id);
|
||||
let msg: *mut libc::c_char = dc_stock_str_repl_string(
|
||||
context,
|
||||
36i32,
|
||||
if !contact.is_null() {
|
||||
(*contact).addr
|
||||
let contact = Contact::get_by_id(context, contact_id);
|
||||
let msg = context.stock_string_repl_str(
|
||||
StockMessage::ContactNotVerified,
|
||||
if let Ok(ref contact) = contact {
|
||||
contact.get_addr()
|
||||
} else {
|
||||
b"?\x00" as *const u8 as *const libc::c_char
|
||||
"?"
|
||||
},
|
||||
);
|
||||
dc_add_device_msg(context, contact_chat_id, msg);
|
||||
error!(context, 0, "{} ({})", as_str(msg), to_string(details),);
|
||||
free(msg as *mut libc::c_void);
|
||||
dc_contact_unref(contact);
|
||||
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));
|
||||
}
|
||||
|
||||
unsafe fn mark_peer_as_verified(
|
||||
@@ -939,15 +947,15 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
|
||||
&mut contact_chat_id,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
let c_addr_ptr = if let Some(ref addr) = peerstate.addr {
|
||||
to_cstring(addr)
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
let peeraddr: &str = match peerstate.addr {
|
||||
Some(ref addr) => &addr,
|
||||
None => "",
|
||||
};
|
||||
let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr);
|
||||
dc_add_device_msg(context, contact_chat_id, msg);
|
||||
free(msg as *mut libc::c_void);
|
||||
free(c_addr_ptr as *mut _);
|
||||
let msg = CString::new(
|
||||
context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr),
|
||||
)
|
||||
.unwrap();
|
||||
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
contact_chat_id as uintptr_t,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::dc_dehtml::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -11,235 +10,214 @@ pub struct dc_simplify_t {
|
||||
pub is_cut_at_end: libc::c_int,
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
pub unsafe fn dc_simplify_unref(simplify: *mut dc_simplify_t) {
|
||||
if simplify.is_null() {
|
||||
return;
|
||||
impl dc_simplify_t {
|
||||
pub fn new() -> Self {
|
||||
dc_simplify_t {
|
||||
is_forwarded: 0,
|
||||
is_cut_at_begin: 0,
|
||||
is_cut_at_end: 0,
|
||||
}
|
||||
}
|
||||
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);
|
||||
/// 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 {
|
||||
if in_bytes <= 0 {
|
||||
return "".strdup();
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if !temp.is_null() {
|
||||
free(out as *mut libc::c_void);
|
||||
out = temp
|
||||
}
|
||||
}
|
||||
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);
|
||||
dc_remove_cr_chars(out);
|
||||
|
||||
out
|
||||
}
|
||||
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) {
|
||||
/**
|
||||
* 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 */
|
||||
break;
|
||||
}
|
||||
l -= 1
|
||||
}
|
||||
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
|
||||
}
|
||||
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 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 */
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
l += 1
|
||||
}
|
||||
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 != (*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
|
||||
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
|
||||
}
|
||||
}
|
||||
while 0 != pending_linebreaks {
|
||||
ret += "\n";
|
||||
pending_linebreaks -= 1
|
||||
if l_last > 1 {
|
||||
line = lines[l_last - 1];
|
||||
if is_quoted_headline(line) {
|
||||
l_last -= 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);
|
||||
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
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
|
||||
to_cstring(ret)
|
||||
ret.strdup()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,11 +268,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simplify_trim() {
|
||||
unsafe {
|
||||
let simplify: *mut dc_simplify_t = dc_simplify_new();
|
||||
let mut simplify = dc_simplify_t::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 =
|
||||
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -304,18 +282,17 @@ mod tests {
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
dc_simplify_unref(simplify);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_parse_href() {
|
||||
unsafe {
|
||||
let simplify: *mut dc_simplify_t = dc_simplify_new();
|
||||
let mut simplify = dc_simplify_t::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 =
|
||||
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -325,19 +302,18 @@ mod tests {
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
dc_simplify_unref(simplify);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_bold_text() {
|
||||
unsafe {
|
||||
let simplify: *mut dc_simplify_t = dc_simplify_new();
|
||||
let mut simplify = dc_simplify_t::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 =
|
||||
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -347,19 +323,18 @@ mod tests {
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
dc_simplify_unref(simplify);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simplify_html_encoded() {
|
||||
unsafe {
|
||||
let simplify: *mut dc_simplify_t = dc_simplify_new();
|
||||
let mut simplify = dc_simplify_t::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<>"'& äÄöÖüÜß fooÆçÇ ♦&noent;‎‏‌‍\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
dc_simplify_simplify(simplify, html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
|
||||
assert_eq!(
|
||||
strcmp(plain,
|
||||
@@ -369,7 +344,6 @@ mod tests {
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
dc_simplify_unref(simplify);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
338
src/dc_stock.rs
338
src/dc_stock.rs
@@ -1,338 +0,0 @@
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
/* Return the string with the given ID by calling DC_EVENT_GET_STRING.
|
||||
The result must be free()'d! */
|
||||
pub unsafe fn dc_stock_str(context: &Context, id: libc::c_int) -> *mut libc::c_char {
|
||||
return get_string(context, id, 0i32);
|
||||
}
|
||||
|
||||
unsafe fn get_string(context: &Context, id: libc::c_int, qty: libc::c_int) -> *mut libc::c_char {
|
||||
let mut ret: *mut libc::c_char;
|
||||
|
||||
ret =
|
||||
context.call_cb(Event::GET_STRING, id as uintptr_t, qty as uintptr_t) as *mut libc::c_char;
|
||||
|
||||
if ret.is_null() {
|
||||
ret = default_string(id)
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/* Add translated strings that are used by the messager backend.
|
||||
As the logging functions may use these strings, do not log any
|
||||
errors from here. */
|
||||
unsafe fn default_string(id: libc::c_int) -> *mut libc::c_char {
|
||||
// TODO match on enum values /rtn
|
||||
match id {
|
||||
1 => {
|
||||
return dc_strdup(b"No messages.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
2 => {
|
||||
return dc_strdup(b"Me\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
3 => {
|
||||
return dc_strdup(b"Draft\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
4 => {
|
||||
return dc_strdup(b"%1$s member(s)\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
6 => {
|
||||
return dc_strdup(b"%1$s contact(s)\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
7 => {
|
||||
return dc_strdup(b"Voice message\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
8 => {
|
||||
return dc_strdup(b"Contact requests\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
9 => {
|
||||
return dc_strdup(b"Image\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
23 => {
|
||||
return dc_strdup(b"GIF\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
10 => {
|
||||
return dc_strdup(b"Video\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
11 => {
|
||||
return dc_strdup(b"Audio\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
12 => {
|
||||
return dc_strdup(b"File\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
66 => {
|
||||
return dc_strdup(b"Location\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
24 => {
|
||||
return dc_strdup(b"Encrypted message\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
13 => {
|
||||
return dc_strdup(b"Sent with my Delta Chat Messenger: https://delta.chat\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
14 => {
|
||||
return dc_strdup(b"Hello, I\'ve just created the group \"%1$s\" for us.\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
15 => {
|
||||
return dc_strdup(b"Group name changed from \"%1$s\" to \"%2$s\".\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
16 => {
|
||||
return dc_strdup(b"Group image changed.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
17 => {
|
||||
return dc_strdup(b"Member %1$s added.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
18 => {
|
||||
return dc_strdup(b"Member %1$s removed.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
19 => {
|
||||
return dc_strdup(b"Group left.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
64 => {
|
||||
return dc_strdup(b"Location streaming enabled.\x00" as *const u8
|
||||
as *const libc::c_char)
|
||||
}
|
||||
65 => {
|
||||
return dc_strdup(b"Location streaming disabled.\x00" as *const u8
|
||||
as *const libc::c_char)
|
||||
}
|
||||
62 => {
|
||||
return dc_strdup(b"%1$s by %2$s.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
63 => {
|
||||
return dc_strdup(b"%1$s by me.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
25 => {
|
||||
return dc_strdup(b"End-to-end encryption available.\x00" as
|
||||
*const u8 as *const libc::c_char)
|
||||
}
|
||||
27 => {
|
||||
return dc_strdup(b"Transport-encryption.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
28 => {
|
||||
return dc_strdup(b"No encryption.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
30 => {
|
||||
return dc_strdup(b"Fingerprints\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
31 => {
|
||||
return dc_strdup(b"Return receipt\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
32 => {
|
||||
return dc_strdup(b"This is a return receipt for the message \"%1$s\".\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
33 => {
|
||||
return dc_strdup(b"Group image deleted.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
34 => {
|
||||
return dc_strdup(b"End-to-end encryption preferred.\x00" as
|
||||
*const u8 as *const libc::c_char)
|
||||
}
|
||||
35 => {
|
||||
return dc_strdup(b"%1$s verified.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
36 => {
|
||||
return dc_strdup(b"Cannot verifiy %1$s\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
37 => {
|
||||
return dc_strdup(b"Changed setup for %1$s\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
40 => {
|
||||
return dc_strdup(b"Archived chats\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
41 => {
|
||||
return dc_strdup(b"Starred messages\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
42 => {
|
||||
return dc_strdup(b"Autocrypt Setup Message\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
43 => {
|
||||
return dc_strdup(b"This is the Autocrypt Setup Message used to transfer your key between clients.\n\nTo decrypt and use your key, open the message in an Autocrypt-compliant client and enter the setup code presented on the generating device.\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
50 => {
|
||||
return dc_strdup(b"Messages I sent to myself\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
29 => {
|
||||
return dc_strdup(b"This message was encrypted for another setup.\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
}
|
||||
60 => {
|
||||
return dc_strdup(b"Cannot login as %1$s.\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
61 => {
|
||||
return dc_strdup(b"Response from %1$s: %2$s\x00" as *const u8 as
|
||||
*const libc::c_char)
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
|
||||
dc_strdup(b"ErrStr\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
|
||||
/* Replaces the first `%1$s` in the given String-ID by the given value.
|
||||
The result must be free()'d! */
|
||||
pub unsafe fn dc_stock_str_repl_string(
|
||||
context: &Context,
|
||||
id: libc::c_int,
|
||||
to_insert: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let mut ret: *mut libc::c_char = get_string(context, id, 0i32);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$s\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$d\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert,
|
||||
);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub unsafe fn dc_stock_str_repl_int(
|
||||
context: &Context,
|
||||
id: libc::c_int,
|
||||
to_insert_int: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
let mut ret: *mut libc::c_char = get_string(context, id, to_insert_int);
|
||||
let to_insert_str: *mut libc::c_char = dc_mprintf(
|
||||
b"%i\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert_int as libc::c_int,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$s\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert_str,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$d\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert_str,
|
||||
);
|
||||
free(to_insert_str as *mut libc::c_void);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/* Replaces the first `%1$s` and `%2$s` in the given String-ID by the two given strings.
|
||||
The result must be free()'d! */
|
||||
pub unsafe fn dc_stock_str_repl_string2(
|
||||
context: &Context,
|
||||
id: libc::c_int,
|
||||
to_insert: *const libc::c_char,
|
||||
to_insert2: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let mut ret: *mut libc::c_char = get_string(context, id, 0i32);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$s\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%1$d\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%2$s\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert2,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut ret,
|
||||
b"%2$d\x00" as *const u8 as *const libc::c_char,
|
||||
to_insert2,
|
||||
);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/* Misc. */
|
||||
pub unsafe fn dc_stock_system_msg(
|
||||
context: &Context,
|
||||
str_id: libc::c_int,
|
||||
mut param1: *const libc::c_char,
|
||||
param2: *const libc::c_char,
|
||||
from_id: uint32_t,
|
||||
) -> *mut libc::c_char {
|
||||
let ret: *mut libc::c_char;
|
||||
let mut mod_contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
let mut mod_displayname: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut from_contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
let mut from_displayname: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if str_id == 17i32 || str_id == 18i32 {
|
||||
let mod_contact_id: uint32_t = dc_lookup_contact_id_by_addr(context, param1);
|
||||
if mod_contact_id != 0i32 as libc::c_uint {
|
||||
mod_contact = dc_get_contact(context, mod_contact_id);
|
||||
mod_displayname = dc_contact_get_name_n_addr(mod_contact);
|
||||
param1 = mod_displayname
|
||||
}
|
||||
}
|
||||
let action: *mut libc::c_char = dc_stock_str_repl_string2(context, str_id, param1, param2);
|
||||
if 0 != from_id {
|
||||
if 0 != strlen(action)
|
||||
&& *action.offset(strlen(action).wrapping_sub(1) as isize) as libc::c_int == '.' as i32
|
||||
{
|
||||
*action.offset(strlen(action).wrapping_sub(1) as isize) = 0i32 as libc::c_char
|
||||
}
|
||||
from_contact = dc_get_contact(context, from_id);
|
||||
from_displayname = dc_contact_get_display_name(from_contact);
|
||||
ret = dc_stock_str_repl_string2(
|
||||
context,
|
||||
if from_id == 1i32 as libc::c_uint {
|
||||
63i32
|
||||
} else {
|
||||
62i32
|
||||
},
|
||||
action,
|
||||
from_displayname,
|
||||
)
|
||||
} else {
|
||||
ret = dc_strdup(action)
|
||||
}
|
||||
free(action as *mut libc::c_void);
|
||||
free(from_displayname as *mut libc::c_void);
|
||||
free(mod_displayname as *mut libc::c_void);
|
||||
dc_contact_unref(from_contact);
|
||||
dc_contact_unref(mod_contact);
|
||||
|
||||
ret
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
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 {
|
||||
@@ -120,97 +120,90 @@ fn hex_2_int(ch: libc::c_char) -> libc::c_char {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut current_block: u64;
|
||||
let mut ok_to_continue = true;
|
||||
let mut ret_str: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut cur: *const libc::c_char = to_encode;
|
||||
let mmapstr: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
if to_encode.is_null() || mmapstr.is_null() {
|
||||
current_block = 8550051112593613029;
|
||||
} else {
|
||||
current_block = 4644295000439058019;
|
||||
ok_to_continue = false;
|
||||
}
|
||||
loop {
|
||||
match current_block {
|
||||
8550051112593613029 => {
|
||||
if !mmapstr.is_null() {
|
||||
mmap_string_free(mmapstr);
|
||||
}
|
||||
break;
|
||||
if !ok_to_continue {
|
||||
if !mmapstr.is_null() {
|
||||
mmap_string_free(mmapstr);
|
||||
}
|
||||
_ => {
|
||||
if *cur as libc::c_int != '\u{0}' as i32 {
|
||||
let begin: *const libc::c_char;
|
||||
let mut end: *const libc::c_char;
|
||||
let mut do_quote: bool;
|
||||
let mut quote_words: libc::c_int;
|
||||
begin = cur;
|
||||
end = begin;
|
||||
quote_words = 0i32;
|
||||
do_quote = true;
|
||||
while *cur as libc::c_int != '\u{0}' as i32 {
|
||||
get_word(cur, &mut cur, &mut do_quote);
|
||||
if !do_quote {
|
||||
break;
|
||||
}
|
||||
quote_words = 1i32;
|
||||
end = cur;
|
||||
if *cur as libc::c_int != '\u{0}' as i32 {
|
||||
cur = cur.offset(1isize)
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
if *cur as libc::c_int != '\u{0}' as i32 {
|
||||
let begin: *const libc::c_char;
|
||||
let mut end: *const libc::c_char;
|
||||
let mut do_quote: bool;
|
||||
let mut quote_words: libc::c_int;
|
||||
begin = cur;
|
||||
end = begin;
|
||||
quote_words = 0i32;
|
||||
do_quote = true;
|
||||
while *cur as libc::c_int != '\u{0}' as i32 {
|
||||
get_word(cur, &mut cur, &mut do_quote);
|
||||
if !do_quote {
|
||||
break;
|
||||
}
|
||||
if 0 != quote_words {
|
||||
if !quote_word(
|
||||
b"utf-8\x00" as *const u8 as *const libc::c_char,
|
||||
mmapstr,
|
||||
begin,
|
||||
end.wrapping_offset_from(begin) as size_t,
|
||||
) {
|
||||
current_block = 8550051112593613029;
|
||||
continue;
|
||||
}
|
||||
if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 {
|
||||
if mmap_string_append_c(mmapstr, *end).is_null() {
|
||||
current_block = 8550051112593613029;
|
||||
continue;
|
||||
}
|
||||
end = end.offset(1isize)
|
||||
}
|
||||
if *end as libc::c_int != '\u{0}' as i32 {
|
||||
if mmap_string_append_len(
|
||||
mmapstr,
|
||||
end,
|
||||
cur.wrapping_offset_from(end) as size_t,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
current_block = 8550051112593613029;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else if mmap_string_append_len(
|
||||
quote_words = 1i32;
|
||||
end = cur;
|
||||
if *cur as libc::c_int != '\u{0}' as i32 {
|
||||
cur = cur.offset(1isize)
|
||||
}
|
||||
}
|
||||
if 0 != quote_words {
|
||||
if !quote_word(
|
||||
b"utf-8\x00" as *const u8 as *const libc::c_char,
|
||||
mmapstr,
|
||||
begin,
|
||||
cur.wrapping_offset_from(begin) as size_t,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
current_block = 8550051112593613029;
|
||||
end.wrapping_offset_from(begin) as size_t,
|
||||
) {
|
||||
ok_to_continue = false;
|
||||
continue;
|
||||
}
|
||||
if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) {
|
||||
current_block = 4644295000439058019;
|
||||
continue;
|
||||
if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 {
|
||||
if mmap_string_append_c(mmapstr, *end).is_null() {
|
||||
ok_to_continue = false;
|
||||
continue;
|
||||
}
|
||||
end = end.offset(1isize)
|
||||
}
|
||||
if mmap_string_append_c(mmapstr, *cur).is_null() {
|
||||
current_block = 8550051112593613029;
|
||||
continue;
|
||||
if *end as libc::c_int != '\u{0}' as i32 {
|
||||
if mmap_string_append_len(
|
||||
mmapstr,
|
||||
end,
|
||||
cur.wrapping_offset_from(end) as size_t,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
ok_to_continue = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
cur = cur.offset(1isize);
|
||||
current_block = 4644295000439058019;
|
||||
} else {
|
||||
ret_str = strdup((*mmapstr).str_0);
|
||||
current_block = 8550051112593613029;
|
||||
} else if mmap_string_append_len(
|
||||
mmapstr,
|
||||
begin,
|
||||
cur.wrapping_offset_from(begin) as size_t,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
ok_to_continue = false;
|
||||
continue;
|
||||
}
|
||||
if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) {
|
||||
continue;
|
||||
}
|
||||
if mmap_string_append_c(mmapstr, *cur).is_null() {
|
||||
ok_to_continue = false;
|
||||
continue;
|
||||
}
|
||||
cur = cur.offset(1isize);
|
||||
} else {
|
||||
ret_str = strdup((*mmapstr).str_0);
|
||||
ok_to_continue = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -378,7 +371,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 +442,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 +458,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 +475,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 +510,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,9 +703,8 @@ 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 = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2]));
|
||||
libc::memcpy(target as *mut _, raw as *const _, 4);
|
||||
free(raw as *mut libc::c_void);
|
||||
let raw = CString::yolo(format!("={}", &hex::encode_upper(bytes)[..2]));
|
||||
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -3,6 +3,7 @@ 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;
|
||||
@@ -41,7 +42,7 @@ pub fn dc_token_lookup(
|
||||
params![namespc as i32, foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.map(|s| unsafe { to_cstring(s) })
|
||||
.map(|s| unsafe { s.strdup() })
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
|
||||
649
src/dc_tools.rs
649
src/dc_tools.rs
@@ -9,6 +9,7 @@ use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::error::Error;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
@@ -16,11 +17,10 @@ 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 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;
|
||||
pub fn dc_exactly_one_bit_set(v: libc::c_int) -> bool {
|
||||
0 != v && 0 == v & v - 1i32
|
||||
}
|
||||
|
||||
/* string tools */
|
||||
@@ -177,14 +177,13 @@ 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_cstring(to_string(in_0).to_lowercase())
|
||||
to_string(in_0).to_lowercase().strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) {
|
||||
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 _);
|
||||
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));
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_contains(
|
||||
@@ -232,7 +231,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);
|
||||
to_cstring(raw)
|
||||
raw.strdup()
|
||||
}
|
||||
|
||||
/* remove all \r characters from string */
|
||||
@@ -262,8 +261,9 @@ 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 current_block: u64;
|
||||
let mut OK_TO_CONTINUE = true;
|
||||
if buf.is_null() {
|
||||
return;
|
||||
}
|
||||
@@ -279,7 +279,6 @@ 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;
|
||||
@@ -292,7 +291,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 */
|
||||
current_block = 2775201239069267972;
|
||||
OK_TO_CONTINUE = false;
|
||||
break;
|
||||
} else if c & 0xf0i32 == 0xe0i32 {
|
||||
n = 2i32
|
||||
@@ -301,7 +300,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 */
|
||||
current_block = 2775201239069267972;
|
||||
OK_TO_CONTINUE = false;
|
||||
break;
|
||||
}
|
||||
j = 0i32;
|
||||
@@ -309,25 +308,22 @@ 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 {
|
||||
current_block = 2775201239069267972;
|
||||
OK_TO_CONTINUE = false;
|
||||
break 's_36;
|
||||
}
|
||||
j += 1
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
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)
|
||||
if OK_TO_CONTINUE == false {
|
||||
while 0 != *p1 {
|
||||
if *p1 as libc::c_int > 0x7fi32 {
|
||||
*p1 = '_' as i32 as libc::c_uchar
|
||||
}
|
||||
return;
|
||||
p1 = p1.offset(1isize)
|
||||
}
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t {
|
||||
@@ -358,6 +354,7 @@ 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,
|
||||
@@ -419,47 +416,30 @@ 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) -> *mut carray {
|
||||
let lines: *mut carray = carray_new(1024i32 as libc::c_uint);
|
||||
pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> Vec<*mut libc::c_char> {
|
||||
let mut lines = Vec::new();
|
||||
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 {
|
||||
carray_add(
|
||||
lines,
|
||||
strndup(line_start, line_chars) as *mut libc::c_void,
|
||||
&mut l_indx,
|
||||
);
|
||||
lines.push(strndup(line_start, line_chars));
|
||||
p1 = p1.offset(1isize);
|
||||
line_start = p1;
|
||||
line_chars = 0;
|
||||
} else {
|
||||
p1 = p1.offset(1isize);
|
||||
line_chars = line_chars.wrapping_add(1)
|
||||
line_chars += 1;
|
||||
}
|
||||
}
|
||||
carray_add(
|
||||
lines,
|
||||
strndup(line_start, line_chars) as *mut libc::c_void,
|
||||
&mut l_indx,
|
||||
);
|
||||
|
||||
lines.push(strndup(line_start, line_chars));
|
||||
lines
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
pub unsafe fn dc_free_splitted_lines(lines: Vec<*mut libc::c_char>) {
|
||||
for s in lines {
|
||||
free(s as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
|
||||
/* insert a break every n characters, the return must be free()'d */
|
||||
@@ -528,7 +508,7 @@ pub unsafe fn dc_str_from_clist(
|
||||
}
|
||||
}
|
||||
|
||||
to_cstring(res)
|
||||
res.strdup()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_to_clist(
|
||||
@@ -559,32 +539,32 @@ pub unsafe fn dc_str_to_clist(
|
||||
list
|
||||
}
|
||||
|
||||
/* the colors must fulfill some criterions as:
|
||||
- contrast to black and to white
|
||||
- work as a text-color
|
||||
- being noticeable on a typical map
|
||||
- harmonize together while being different enough
|
||||
(therefore, we cannot just use random rgb colors :) */
|
||||
const COLORS: [u32; 16] = [
|
||||
0xe56555, 0xf28c48, 0x8e85ee, 0x76c84d, 0x5bb6cc, 0x549cdd, 0xd25c99, 0xb37800, 0xf23030,
|
||||
0x39b249, 0xbb243b, 0x964078, 0x66874f, 0x308ab9, 0x127ed0, 0xbe450c,
|
||||
];
|
||||
|
||||
pub fn dc_str_to_color_safe(s: impl AsRef<str>) -> u32 {
|
||||
let str_lower = s.as_ref().to_lowercase();
|
||||
let mut checksum = 0;
|
||||
let bytes = str_lower.as_bytes();
|
||||
for i in 0..str_lower.len() {
|
||||
checksum += (i + 1) * bytes[i] as usize;
|
||||
checksum %= 0xffffff;
|
||||
}
|
||||
let color_index = checksum % COLORS.len();
|
||||
|
||||
COLORS[color_index]
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
|
||||
let str_lower: *mut libc::c_char = dc_strlower(str);
|
||||
/* the colors must fulfill some criterions as:
|
||||
- contrast to black and to white
|
||||
- work as a text-color
|
||||
- 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] = [
|
||||
0xe56555i32 as uint32_t,
|
||||
0xf28c48i32 as uint32_t,
|
||||
0x8e85eei32 as uint32_t,
|
||||
0x76c84di32 as uint32_t,
|
||||
0x5bb6cci32 as uint32_t,
|
||||
0x549cddi32 as uint32_t,
|
||||
0xd25c99i32 as uint32_t,
|
||||
0xb37800i32 as uint32_t,
|
||||
0xf23030i32 as uint32_t,
|
||||
0x39b249i32 as uint32_t,
|
||||
0xbb243bi32 as uint32_t,
|
||||
0x964078i32 as uint32_t,
|
||||
0x66874fi32 as uint32_t,
|
||||
0x308ab9i32 as uint32_t,
|
||||
0x127ed0i32 as uint32_t,
|
||||
0xbe450ci32 as uint32_t,
|
||||
];
|
||||
let mut checksum: libc::c_int = 0i32;
|
||||
let str_len: libc::c_int = strlen(str_lower) as libc::c_int;
|
||||
let mut i: libc::c_int = 0i32;
|
||||
@@ -599,7 +579,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 */
|
||||
@@ -667,13 +647,7 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
* date/time tools
|
||||
******************************************************************************/
|
||||
|
||||
/* 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 {
|
||||
pub fn dc_timestamp_to_str(wanted: i64) -> String {
|
||||
let ts = chrono::Utc.timestamp(wanted, 0);
|
||||
ts.format("%Y.%m.%d %H:%M:%S").to_string()
|
||||
}
|
||||
@@ -724,7 +698,7 @@ pub unsafe fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int
|
||||
}
|
||||
|
||||
/* Message-ID tools */
|
||||
pub unsafe fn dc_create_id() -> *mut libc::c_char {
|
||||
pub fn dc_create_id() -> String {
|
||||
/* 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.
|
||||
@@ -742,39 +716,26 @@ pub unsafe fn dc_create_id() -> *mut libc::c_char {
|
||||
encode_66bits_as_base64(buf[0usize], buf[1usize], buf[2usize])
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* 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());
|
||||
/// 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};
|
||||
|
||||
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
|
||||
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()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_create_incoming_rfc724_mid(
|
||||
@@ -814,7 +775,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();
|
||||
let rand2: *mut libc::c_char = dc_create_id().strdup();
|
||||
let ret: *mut libc::c_char;
|
||||
let mut at_hostname: *const libc::c_char = strchr(from_addr, '@' as i32);
|
||||
if at_hostname.is_null() {
|
||||
@@ -828,7 +789,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
|
||||
at_hostname,
|
||||
)
|
||||
} else {
|
||||
rand1 = dc_create_id();
|
||||
rand1 = dc_create_id().strdup();
|
||||
ret = dc_mprintf(
|
||||
b"Mr.%s.%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
rand1,
|
||||
@@ -842,52 +803,49 @@ pub unsafe fn dc_create_outgoing_rfc724_mid(
|
||||
ret
|
||||
}
|
||||
|
||||
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
|
||||
/// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
};
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
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: *const libc::c_char = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
let mid = if !cur.is_null() {
|
||||
as_str((*cur).data as *const libc::c_char)
|
||||
} else {
|
||||
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;
|
||||
""
|
||||
};
|
||||
|
||||
if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) {
|
||||
return grpid.strdup();
|
||||
}
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
@@ -901,6 +859,7 @@ 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 {
|
||||
@@ -933,6 +892,7 @@ 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() {
|
||||
@@ -947,6 +907,7 @@ 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,
|
||||
@@ -979,6 +940,7 @@ 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);
|
||||
@@ -1074,6 +1036,27 @@ pub unsafe fn dc_get_filemeta(
|
||||
0
|
||||
}
|
||||
|
||||
/// Expand paths relative to $BLOBDIR into absolute paths.
|
||||
///
|
||||
/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path.
|
||||
/// Otherwise, returns path as is.
|
||||
pub fn dc_get_abs_path_safe<P: AsRef<std::path::Path>>(
|
||||
context: &Context,
|
||||
path: P,
|
||||
) -> std::path::PathBuf {
|
||||
let p: &std::path::Path = path.as_ref();
|
||||
if let Ok(p) = p.strip_prefix("$BLOBDIR") {
|
||||
assert!(
|
||||
context.has_blobdir(),
|
||||
"Expected context to have blobdir to substitute $BLOBDIR",
|
||||
);
|
||||
std::path::PathBuf::from(as_str(context.get_blobdir())).join(p)
|
||||
} else {
|
||||
p.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_get_abs_path(
|
||||
context: &Context,
|
||||
pathNfilename: *const libc::c_char,
|
||||
@@ -1103,134 +1086,82 @@ pub unsafe fn dc_get_abs_path(
|
||||
pathNfilename_abs
|
||||
}
|
||||
|
||||
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() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let exist = {
|
||||
let p = std::path::Path::new(as_str(pathNfilename_abs));
|
||||
p.exists()
|
||||
};
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
exist as libc::c_int
|
||||
pub fn dc_file_exist(context: &Context, path: *const libc::c_char) -> libc::c_int {
|
||||
dc_get_abs_path_safe(context, as_path(path)).exists() as libc::c_int
|
||||
}
|
||||
|
||||
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() {
|
||||
return 0;
|
||||
pub fn dc_get_filebytes(context: &Context, path: *const libc::c_char) -> uint64_t {
|
||||
let path_abs = dc_get_abs_path_safe(context, as_path(path));
|
||||
match fs::metadata(&path_abs) {
|
||||
Ok(meta) => meta.len() as uint64_t,
|
||||
Err(_err) => 0,
|
||||
}
|
||||
|
||||
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let filebytes = match fs::metadata(p) {
|
||||
Ok(meta) => meta.len(),
|
||||
Err(_err) => {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
filebytes as uint64_t
|
||||
}
|
||||
|
||||
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);
|
||||
if pathNfilename_abs.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let p = std::path::Path::new(
|
||||
std::ffi::CStr::from_ptr(pathNfilename_abs)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let res = if p.is_file() {
|
||||
fs::remove_file(p)
|
||||
pub fn dc_delete_file(context: &Context, path: *const libc::c_char) -> libc::c_int {
|
||||
let path = as_path(path);
|
||||
let path_abs = dc_get_abs_path_safe(context, path);
|
||||
let res = if path_abs.is_file() {
|
||||
fs::remove_file(path_abs)
|
||||
} else {
|
||||
fs::remove_dir_all(p)
|
||||
fs::remove_dir_all(path_abs)
|
||||
};
|
||||
|
||||
match res {
|
||||
Ok(_) => {
|
||||
success = 1;
|
||||
}
|
||||
Ok(_) => 1,
|
||||
Err(_err) => {
|
||||
warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),);
|
||||
warn!(context, 0, "Cannot delete \"{}\".", path.display());
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
success
|
||||
}
|
||||
|
||||
pub unsafe fn dc_copy_file(
|
||||
pub fn dc_copy_file(
|
||||
context: &Context,
|
||||
src: *const libc::c_char,
|
||||
dest: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
|
||||
let src_abs = dc_get_abs_path(context, src);
|
||||
let dest_abs = dc_get_abs_path(context, dest);
|
||||
|
||||
if src_abs.is_null() || dest_abs.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let src_p = std::ffi::CStr::from_ptr(src_abs).to_str().unwrap();
|
||||
let dest_p = std::ffi::CStr::from_ptr(dest_abs).to_str().unwrap();
|
||||
|
||||
match fs::copy(src_p, dest_p) {
|
||||
Ok(_) => {
|
||||
success = 1;
|
||||
}
|
||||
let src = as_path(src);
|
||||
let dest = as_path(dest);
|
||||
let src_abs = dc_get_abs_path_safe(context, src);
|
||||
let dest_abs = dc_get_abs_path_safe(context, dest);
|
||||
match fs::copy(&src_abs, &dest_abs) {
|
||||
Ok(_) => 1,
|
||||
Err(_) => {
|
||||
error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,);
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Cannot copy \"{}\" to \"{}\".",
|
||||
src.display(),
|
||||
dest.display(),
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
free(src_abs as *mut libc::c_void);
|
||||
free(dest_abs as *mut libc::c_void);
|
||||
success
|
||||
}
|
||||
|
||||
pub unsafe fn dc_create_folder(
|
||||
context: &Context,
|
||||
pathNfilename: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
|
||||
{
|
||||
let p = std::path::Path::new(as_str(pathNfilename_abs));
|
||||
if !p.exists() {
|
||||
match fs::create_dir_all(p) {
|
||||
Ok(_) => {
|
||||
success = 1;
|
||||
}
|
||||
Err(_err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create directory \"{}\".",
|
||||
as_str(pathNfilename),
|
||||
);
|
||||
}
|
||||
pub fn dc_create_folder(context: &Context, path: *const libc::c_char) -> libc::c_int {
|
||||
let path = as_path(path);
|
||||
let path_abs = dc_get_abs_path_safe(context, path);
|
||||
if !path_abs.exists() {
|
||||
match fs::create_dir_all(path_abs) {
|
||||
Ok(_) => 1,
|
||||
Err(_err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot create directory \"{}\".",
|
||||
path.display(),
|
||||
);
|
||||
0
|
||||
}
|
||||
} else {
|
||||
success = 1;
|
||||
}
|
||||
} else {
|
||||
1
|
||||
}
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
success
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_write_file(
|
||||
context: &Context,
|
||||
pathNfilename: *const libc::c_char,
|
||||
@@ -1242,36 +1173,27 @@ pub unsafe fn dc_write_file(
|
||||
dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int
|
||||
}
|
||||
|
||||
pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool {
|
||||
let pathNfilename_abs = unsafe {
|
||||
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;
|
||||
}
|
||||
|
||||
let p = as_str(pathNfilename_abs);
|
||||
|
||||
let success = if let Err(_err) = fs::write(p, buf) {
|
||||
pub fn dc_write_file_safe<P: AsRef<std::path::Path>>(
|
||||
context: &Context,
|
||||
path: P,
|
||||
buf: &[u8],
|
||||
) -> bool {
|
||||
let path_abs = dc_get_abs_path_safe(context, &path);
|
||||
if let Err(_err) = fs::write(&path_abs, buf) {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot write {} bytes to \"{}\".",
|
||||
buf.len(),
|
||||
pathNfilename.as_ref(),
|
||||
path.as_ref().display(),
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
|
||||
success
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_read_file(
|
||||
context: &Context,
|
||||
pathNfilename: *const libc::c_char,
|
||||
@@ -1291,37 +1213,23 @@ pub unsafe fn dc_read_file(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> {
|
||||
let pathNfilename_abs = unsafe {
|
||||
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() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let p = as_str(pathNfilename_abs);
|
||||
let res = match fs::read(p) {
|
||||
pub fn dc_read_file_safe<P: AsRef<std::path::Path>>(context: &Context, path: P) -> Option<Vec<u8>> {
|
||||
let path_abs = dc_get_abs_path_safe(context, &path);
|
||||
match fs::read(&path_abs) {
|
||||
Ok(bytes) => Some(bytes),
|
||||
Err(_err) => {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot read \"{}\" or file is empty.",
|
||||
pathNfilename.as_ref(),
|
||||
path.as_ref().display()
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_get_fine_pathNfilename(
|
||||
context: &Context,
|
||||
pathNfolder: *const libc::c_char,
|
||||
@@ -1375,15 +1283,9 @@ pub unsafe fn dc_get_fine_pathNfilename(
|
||||
ret
|
||||
}
|
||||
|
||||
// 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_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;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char) {
|
||||
@@ -1399,15 +1301,14 @@ pub unsafe fn dc_make_rel_path(context: &Context, path: *mut *mut libc::c_char)
|
||||
};
|
||||
}
|
||||
|
||||
// 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;
|
||||
pub unsafe fn dc_make_rel_and_copy(context: &Context, path: *mut *mut libc::c_char) -> bool {
|
||||
let mut success = false;
|
||||
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 0 != dc_is_blobdir_path(context, *path) {
|
||||
if dc_is_blobdir_path(context, *path) {
|
||||
dc_make_rel_path(context, path);
|
||||
success = 1i32
|
||||
success = true;
|
||||
} else {
|
||||
filename = dc_get_filename(*path);
|
||||
if !(filename.is_null()
|
||||
@@ -1425,7 +1326,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 = 1i32
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1535,10 +1436,49 @@ fn os_str_to_c_string_unicode(
|
||||
}
|
||||
}
|
||||
|
||||
/// 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())
|
||||
/// 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())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string(s: *const libc::c_char) -> String {
|
||||
@@ -1570,13 +1510,16 @@ 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().unwrap_or_else(|err| {
|
||||
panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err);
|
||||
})
|
||||
cstr.to_str()
|
||||
.map_err(|err| format_err!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err))
|
||||
}
|
||||
|
||||
/// Convert a C `*char` pointer to a [std::path::Path] slice.
|
||||
@@ -1974,11 +1917,28 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_dc_create_id() {
|
||||
unsafe {
|
||||
let buf = dc_create_id();
|
||||
assert_eq!(strlen(buf), 11);
|
||||
free(buf as *mut libc::c_void);
|
||||
}
|
||||
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_"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2077,4 +2037,53 @@ 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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
66
src/error.rs
66
src/error.rs
@@ -16,6 +16,8 @@ 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>;
|
||||
@@ -43,3 +45,67 @@ 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)+))
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
46
src/imap.rs
46
src/imap.rs
@@ -1,3 +1,4 @@
|
||||
use std::ffi::CString;
|
||||
use std::net;
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::time::{Duration, SystemTime};
|
||||
@@ -5,10 +6,9 @@ use std::time::{Duration, SystemTime};
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::{as_str, to_cstring};
|
||||
use crate::dc_tools::CStringExt;
|
||||
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,26 +705,16 @@ 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());
|
||||
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);
|
||||
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 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 {
|
||||
@@ -853,10 +843,8 @@ impl Imap {
|
||||
.expect("missing message id");
|
||||
|
||||
if 0 == unsafe {
|
||||
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
|
||||
let message_id_c = CString::yolo(message_id);
|
||||
(self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid)
|
||||
} {
|
||||
// check passed, go fetch the rest
|
||||
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
|
||||
@@ -924,13 +912,7 @@ impl Imap {
|
||||
let key = format!("imap.mailbox.{}", folder.as_ref());
|
||||
let val = format!("{}:{}", uidvalidity, lastseenuid);
|
||||
|
||||
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 _);
|
||||
};
|
||||
(self.set_config)(context, &key, Some(&val));
|
||||
}
|
||||
|
||||
fn fetch_single_msg<S: AsRef<str>>(
|
||||
|
||||
23
src/key.rs
23
src/key.rs
@@ -216,22 +216,16 @@ impl Key {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
/// Each header line must be terminated by `\r\n`
|
||||
pub fn to_asc(&self, header: Option<(&str, &str)>) -> String {
|
||||
let headers = header.map(|(key, value)| {
|
||||
let mut m = BTreeMap::new();
|
||||
m.insert(key.to_string(), value.to_string());
|
||||
m
|
||||
});
|
||||
|
||||
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()) }
|
||||
self.to_armored_string(headers.as_ref())
|
||||
.expect("failed to serialize key")
|
||||
}
|
||||
|
||||
pub fn write_asc_to_file(&self, file: *const libc::c_char, context: &Context) -> bool {
|
||||
@@ -239,15 +233,16 @@ impl Key {
|
||||
return false;
|
||||
}
|
||||
|
||||
let file_content = self.to_asc_c(None);
|
||||
let file_content = self.to_asc(None);
|
||||
let file_content_c = CString::new(file_content).unwrap();
|
||||
|
||||
let success = if 0
|
||||
== unsafe {
|
||||
dc_write_file(
|
||||
context,
|
||||
file,
|
||||
file_content as *const libc::c_void,
|
||||
strlen(file_content),
|
||||
file_content_c.as_ptr() as *const libc::c_void,
|
||||
file_content_c.as_bytes().len(),
|
||||
)
|
||||
} {
|
||||
error!(context, 0, "Cannot write key to {}", to_string(file));
|
||||
@@ -256,8 +251,6 @@ impl Key {
|
||||
true
|
||||
};
|
||||
|
||||
unsafe { free(file_content as *mut libc::c_void) };
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
use crate::context::Context;
|
||||
|
||||
/* yes: uppercase */
|
||||
/* library private: key-history */
|
||||
pub fn dc_add_to_keyhistory(
|
||||
_context: &Context,
|
||||
_rfc724_mid: *const libc::c_char,
|
||||
_sending_time: u64,
|
||||
_addr: *const libc::c_char,
|
||||
_fingerprint: *const libc::c_char,
|
||||
) {
|
||||
|
||||
}
|
||||
28
src/lib.rs
28
src/lib.rs
@@ -1,12 +1,4 @@
|
||||
#![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)]
|
||||
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
@@ -16,32 +8,37 @@ 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 contact;
|
||||
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;
|
||||
pub mod dc_e2ee;
|
||||
pub mod dc_imex;
|
||||
@@ -54,15 +51,16 @@ 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;
|
||||
|
||||
20
src/log.rs
20
src/log.rs
@@ -7,10 +7,9 @@ macro_rules! info {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -23,10 +22,9 @@ macro_rules! warn {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void) ;
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -39,10 +37,9 @@ macro_rules! error {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -55,9 +52,8 @@ macro_rules! log_event {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
let formatted_c = std::ffi::CString::new(formatted).unwrap();
|
||||
$ctx.call_cb($event, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
formatted_c.as_ptr() as libc::uintptr_t);
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
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(), DEFAULT_ENCODE_SET).to_string();
|
||||
let value_urlencoded = utf8_percent_encode(value.as_ref(), NON_ALPHANUMERIC).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-b%20c"
|
||||
"helloa%2Db%20c"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
238
src/param.rs
Normal file
238
src/param.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -462,16 +462,11 @@ mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use std::ffi::CStr;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::context::*;
|
||||
use crate::dc_tools::to_cstring;
|
||||
use crate::x::free;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn test_peerstate_save_to_db() {
|
||||
let ctx = unsafe { create_test_context() };
|
||||
let ctx = crate::test_utils::dummy_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();
|
||||
@@ -509,29 +504,4 @@ 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 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,15 +17,14 @@ 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,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
) -> bool {
|
||||
let mut success = false;
|
||||
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;
|
||||
@@ -128,7 +127,7 @@ pub unsafe fn dc_split_armored_data(
|
||||
if !ret_base64.is_null() {
|
||||
*ret_base64 = base64
|
||||
}
|
||||
success = 1i32
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
127
src/sql.rs
127
src/sql.rs
@@ -6,11 +6,10 @@ 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;
|
||||
|
||||
@@ -340,7 +339,7 @@ fn open(
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"First time init: creating tables in \"{:?}\".",
|
||||
"First time init: creating tables in {:?}.",
|
||||
dbfile.as_ref(),
|
||||
);
|
||||
sql.execute(
|
||||
@@ -816,7 +815,7 @@ fn open(
|
||||
}
|
||||
}
|
||||
|
||||
info!(context, 0, "Opened \"{:?}\".", dbfile.as_ref(),);
|
||||
info!(context, 0, "Opened {:?}.", dbfile.as_ref(),);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -954,25 +953,25 @@ pub fn housekeeping(context: &Context) {
|
||||
context,
|
||||
&mut files_in_use,
|
||||
"SELECT param FROM msgs WHERE chat_id!=3 AND type!=10;",
|
||||
'f' as i32,
|
||||
Param::File,
|
||||
);
|
||||
maybe_add_from_param(
|
||||
context,
|
||||
&mut files_in_use,
|
||||
"SELECT param FROM jobs;",
|
||||
'f' as i32,
|
||||
Param::File,
|
||||
);
|
||||
maybe_add_from_param(
|
||||
context,
|
||||
&mut files_in_use,
|
||||
"SELECT param FROM chats;",
|
||||
'i' as i32,
|
||||
Param::ProfileImage,
|
||||
);
|
||||
maybe_add_from_param(
|
||||
context,
|
||||
&mut files_in_use,
|
||||
"SELECT param FROM contacts;",
|
||||
'i' as i32,
|
||||
Param::ProfileImage,
|
||||
);
|
||||
|
||||
context
|
||||
@@ -1007,35 +1006,15 @@ pub fn housekeeping(context: &Context) {
|
||||
}
|
||||
let entry = entry.unwrap();
|
||||
let name_f = entry.file_name();
|
||||
let name_c = unsafe { to_cstring(name_f.to_string_lossy()) };
|
||||
let name_s = name_f.to_string_lossy();
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
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)
|
||||
{
|
||||
unsafe { free(name_c as *mut _) };
|
||||
continue;
|
||||
}
|
||||
unsafe { free(name_c as *mut _) };
|
||||
|
||||
unreferenced_count += 1;
|
||||
|
||||
@@ -1068,11 +1047,8 @@ pub fn housekeeping(context: &Context) {
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
unsafe {
|
||||
let path = to_cstring(entry.path().to_str().unwrap());
|
||||
dc_delete_file(context, path);
|
||||
free(path as *mut _);
|
||||
}
|
||||
let path = entry.path().to_c_string().unwrap();
|
||||
dc_delete_file(context, path.as_ptr());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -1089,26 +1065,18 @@ pub fn housekeeping(context: &Context) {
|
||||
info!(context, 0, "Housekeeping done.",);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
*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
|
||||
&name[..name_len - namespc_len]
|
||||
} else {
|
||||
name
|
||||
};
|
||||
files_in_use.contains(name_to_check)
|
||||
}
|
||||
|
||||
fn maybe_add_file(files_in_use: &mut HashSet<String>, file: impl AsRef<str>) {
|
||||
@@ -1123,31 +1091,20 @@ fn maybe_add_from_param(
|
||||
context: &Context,
|
||||
files_in_use: &mut HashSet<String>,
|
||||
query: &str,
|
||||
param_id: libc::c_int,
|
||||
param_id: Param,
|
||||
) {
|
||||
let param = unsafe { dc_param_new() };
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_row(query, NO_PARAMS, |row| {
|
||||
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 _);
|
||||
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);
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap_or_else(|err| {
|
||||
warn!(context, 0, "sql: failed to add_from_param: {}", err);
|
||||
});
|
||||
|
||||
unsafe { dc_param_unref(param) };
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -1173,26 +1130,12 @@ mod test {
|
||||
maybe_add_file(&mut files, "$BLOBDIR/world.txt");
|
||||
maybe_add_file(&mut files, "world2.txt");
|
||||
|
||||
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 _,
|
||||
)
|
||||
});
|
||||
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"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
394
src/stock.rs
Normal file
394
src/stock.rs
Normal file
@@ -0,0 +1,394 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use strum::EnumProperty;
|
||||
use strum_macros::EnumProperty;
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
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 {
|
||||
let contact_id = Contact::lookup_id_by_addr(self, param1.as_ref());
|
||||
if contact_id != 0 {
|
||||
Contact::get_by_id(self, contact_id)
|
||||
.map(|contact| contact.get_name_n_addr())
|
||||
.unwrap_or_default()
|
||||
} 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
|
||||
_ => {
|
||||
let displayname = Contact::get_by_id(self, from_id)
|
||||
.map(|contact| contact.get_name_n_addr())
|
||||
.unwrap_or_default();
|
||||
|
||||
self.stock_string_repl_str2(StockMessage::MsgActionByUser, action1, &displayname)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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();
|
||||
Contact::create(&t.ctx, "Alice", "alice@example.com").expect("failed to create contact");
|
||||
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 = {
|
||||
Contact::create(&t.ctx, "Alice", "alice@example.com")
|
||||
.expect("Failed to create contact Alice");
|
||||
let id =
|
||||
Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob");
|
||||
id
|
||||
};
|
||||
assert_eq!(
|
||||
t.ctx.stock_system_msg(
|
||||
StockMessage::MsgAddMember,
|
||||
"alice@example.com",
|
||||
"",
|
||||
contact_id,
|
||||
),
|
||||
"Member Alice (alice@example.com) added by Bob (bob@example.com)."
|
||||
);
|
||||
}
|
||||
|
||||
#[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 id = Contact::create(&t.ctx, "Alice", "alice@example.com")
|
||||
.expect("failed to create contact");
|
||||
|
||||
assert_eq!(
|
||||
t.ctx
|
||||
.stock_system_msg(StockMessage::MsgGrpName, "Some chat", "Other chat", id,),
|
||||
"Group name changed from \"Some chat\" to \"Other chat\" by Alice (alice@example.com)."
|
||||
)
|
||||
}
|
||||
}
|
||||
46
src/test_utils.rs
Normal file
46
src/test_utils.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! 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)
|
||||
}
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
|
||||
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 =")
|
||||
|
||||
@@ -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,10 +33,8 @@ 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 =
|
||||
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 dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> ();
|
||||
pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>;
|
||||
|
||||
pub type sqlite_int64 = i64;
|
||||
pub type sqlite3_int64 = sqlite_int64;
|
||||
|
||||
10
src/x.rs
10
src/x.rs
@@ -1,5 +1,3 @@
|
||||
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,
|
||||
@@ -37,14 +35,6 @@ 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;
|
||||
|
||||
279
tests/stress.rs
279
tests/stress.rs
@@ -1,17 +1,18 @@
|
||||
//! 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};
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_contact::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
@@ -123,21 +124,15 @@ unsafe fn stress_functions(context: &Context) {
|
||||
context.get_blobdir(),
|
||||
b"foobar\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!(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_file_exist(context, abs_path));
|
||||
free(abs_path as *mut libc::c_void);
|
||||
assert_ne!(
|
||||
@@ -278,7 +273,6 @@ 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;
|
||||
@@ -288,14 +282,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,
|
||||
);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
assert_eq!(ok, 1);
|
||||
assert!(ok);
|
||||
assert!(!headerline.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
@@ -313,7 +307,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);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
@@ -321,7 +315,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
assert_eq!(ok, 1);
|
||||
assert!(ok);
|
||||
assert!(!headerline.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
@@ -340,7 +334,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,
|
||||
);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
@@ -348,7 +342,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
assert_eq!(ok, 1);
|
||||
assert!(ok);
|
||||
assert!(!headerline.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
@@ -365,7 +359,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);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
@@ -373,19 +367,19 @@ unsafe fn stress_functions(context: &Context) {
|
||||
&mut base64,
|
||||
);
|
||||
|
||||
assert_eq!(ok, 0);
|
||||
assert!(!ok);
|
||||
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);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
&mut setupcodebegin,
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut base64,
|
||||
);
|
||||
assert_eq!(ok, 1);
|
||||
assert!(ok);
|
||||
assert!(!headerline.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
@@ -412,14 +406,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);
|
||||
ok = dc_split_armored_data(
|
||||
let ok = dc_split_armored_data(
|
||||
buf_0,
|
||||
&mut headerline,
|
||||
0 as *mut *const libc::c_char,
|
||||
&mut preferencrypt,
|
||||
&mut base64,
|
||||
);
|
||||
assert_eq!(ok, 1);
|
||||
assert!(ok);
|
||||
assert!(!headerline.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
@@ -475,16 +469,13 @@ 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_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!(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,
|
||||
@@ -504,16 +495,13 @@ 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_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!(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(
|
||||
@@ -533,65 +521,21 @@ unsafe fn stress_functions(context: &Context) {
|
||||
);
|
||||
free(buf_1 as *mut libc::c_void);
|
||||
if 0 != dc_is_configured(context) {
|
||||
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 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 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, setupfile);
|
||||
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
assert!(payload.is_null());
|
||||
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!(!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(
|
||||
@@ -601,8 +545,6 @@ 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) {
|
||||
@@ -690,7 +632,7 @@ fn test_encryption_decryption() {
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_signed_bytes = ctext.len();
|
||||
let ctext_signed = to_cstring(ctext);
|
||||
let ctext_signed = CString::yolo(ctext);
|
||||
|
||||
let ctext = dc_pgp_pk_encrypt(
|
||||
original_text as *const libc::c_void,
|
||||
@@ -703,7 +645,7 @@ fn test_encryption_decryption() {
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_unsigned_bytes = ctext.len();
|
||||
let ctext_unsigned = to_cstring(ctext);
|
||||
let ctext_unsigned = CString::yolo(ctext);
|
||||
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(private_key);
|
||||
@@ -717,7 +659,7 @@ fn test_encryption_decryption() {
|
||||
let mut valid_signatures: HashSet<String> = Default::default();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed as *const _,
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
@@ -732,7 +674,7 @@ fn test_encryption_decryption() {
|
||||
|
||||
let empty_keyring = Keyring::default();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed as *const _,
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
@@ -745,7 +687,7 @@ fn test_encryption_decryption() {
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed as *const _,
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
@@ -760,7 +702,7 @@ fn test_encryption_decryption() {
|
||||
public_keyring2.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed as *const _,
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
@@ -773,7 +715,7 @@ fn test_encryption_decryption() {
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_unsigned as *const _,
|
||||
ctext_unsigned.as_ptr() as *const _,
|
||||
ctext_unsigned_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
@@ -781,7 +723,6 @@ 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();
|
||||
@@ -792,7 +733,7 @@ fn test_encryption_decryption() {
|
||||
public_keyring.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed as *const _,
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
@@ -800,7 +741,6 @@ fn test_encryption_decryption() {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
free(ctext_signed as *mut _);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
}
|
||||
}
|
||||
@@ -821,16 +761,14 @@ struct TestContext {
|
||||
}
|
||||
|
||||
unsafe fn create_test_context() -> TestContext {
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
||||
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,
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
assert!(
|
||||
dc_open(&mut ctx, dbfile.to_str().unwrap(), None),
|
||||
"Failed to open {}",
|
||||
as_str(dbfile as *const libc::c_char)
|
||||
dbfile.display()
|
||||
);
|
||||
free(dbfile as *mut _);
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
|
||||
@@ -843,34 +781,31 @@ 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 kml: *mut dc_kml_t = dc_kml_parse(&context.ctx, xml, strlen(xml));
|
||||
let mut kml = 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",);
|
||||
|
||||
assert_eq!(dc_array_get_cnt((*kml).locations), 2);
|
||||
let locations_ref = &kml.locations.as_ref().unwrap();
|
||||
assert_eq!(locations_ref.len(), 2);
|
||||
|
||||
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[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, 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);
|
||||
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_eq!(dc_array_get_timestamp((*kml).locations, 1), 1544739072);
|
||||
|
||||
dc_kml_unref(kml);
|
||||
dc_kml_unref(&mut kml);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -907,7 +842,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!(carray_count(mimeparser.parts), 1);
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
|
||||
dc_mimeparser_unref(&mut mimeparser);
|
||||
}
|
||||
@@ -920,7 +855,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-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()));
|
||||
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()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -955,29 +890,20 @@ fn test_stress_tests() {
|
||||
fn test_get_contacts() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
let name = to_cstring("some2");
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name);
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
free(name as *mut _);
|
||||
|
||||
let name = to_cstring("bob");
|
||||
let email = to_cstring("bob@mail.de");
|
||||
let id = dc_create_contact(&context.ctx, name, email);
|
||||
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||
assert_ne!(id, 0);
|
||||
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name);
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 1);
|
||||
dc_array_unref(contacts);
|
||||
|
||||
let name2 = to_cstring("alice");
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name2);
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
|
||||
free(name as *mut _);
|
||||
free(name2 as *mut _);
|
||||
free(email as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -985,12 +911,7 @@ fn test_get_contacts() {
|
||||
fn test_chat() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
let name = to_cstring("bob");
|
||||
let email = to_cstring("bob@mail.de");
|
||||
|
||||
let contact1 = dc_create_contact(&context.ctx, name, email);
|
||||
free(name as *mut _);
|
||||
free(email as *mut _);
|
||||
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||
assert_ne!(contact1, 0);
|
||||
|
||||
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
@@ -1010,28 +931,12 @@ fn test_chat() {
|
||||
#[test]
|
||||
fn test_wrong_db() {
|
||||
unsafe {
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None);
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
std::fs::write(&dbfile, b"123").unwrap();
|
||||
|
||||
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);
|
||||
let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None);
|
||||
assert!(!res);
|
||||
}
|
||||
}
|
||||
|
||||
#[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 _) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user