mirror of
https://github.com/chatmail/core.git
synced 2026-04-06 15:42:10 +03:00
Compare commits
153 Commits
local_hpk_
...
refactor-j
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8294b5eb28 | ||
|
|
1a8e08e429 | ||
|
|
bf99c0f2ba | ||
|
|
d47a693611 | ||
|
|
886262539a | ||
|
|
401c5a7cb0 | ||
|
|
b5c66dd52a | ||
|
|
e7cf5a546a | ||
|
|
c06cf4eba2 | ||
|
|
fb7c095dad | ||
|
|
d55f6ee7c7 | ||
|
|
8a49ae2361 | ||
|
|
05ec266d9b | ||
|
|
9ec7833a50 | ||
|
|
8c10aa287c | ||
|
|
6b2fe03d08 | ||
|
|
799d362654 | ||
|
|
64beb17fa6 | ||
|
|
2bddb8409c | ||
|
|
b1a056082a | ||
|
|
7dc82ba4d7 | ||
|
|
4645a4300e | ||
|
|
13d8306a7b | ||
|
|
9b1a74cc22 | ||
|
|
25e97df641 | ||
|
|
6b3245ddfc | ||
|
|
001880e1f0 | ||
|
|
ddfd067e97 | ||
|
|
64117c2964 | ||
|
|
c8ce099f22 | ||
|
|
4878192a25 | ||
|
|
c901ab844f | ||
|
|
eb2b4028ab | ||
|
|
64a0718032 | ||
|
|
b2a6876a50 | ||
|
|
5ea2da4245 | ||
|
|
ff1f286882 | ||
|
|
e013532e27 | ||
|
|
1a42a1e6b1 | ||
|
|
d946774741 | ||
|
|
3d080d2733 | ||
|
|
aa7a149560 | ||
|
|
6beea86df7 | ||
|
|
5917d05305 | ||
|
|
ceb4464a9b | ||
|
|
8f0e554bc4 | ||
|
|
7c7a1b64df | ||
|
|
ea661896a1 | ||
|
|
2bb2ef07e9 | ||
|
|
c71934215b | ||
|
|
f31884fc15 | ||
|
|
2b1ffb5fb9 | ||
|
|
fadcd43fee | ||
|
|
5af800b16a | ||
|
|
37622af55a | ||
|
|
d4dfc5443c | ||
|
|
18b70bff0e | ||
|
|
d4bd9187d6 | ||
|
|
712f5a9782 | ||
|
|
29d4f6888d | ||
|
|
5b47409fb0 | ||
|
|
8077fdeddb | ||
|
|
6f290e249f | ||
|
|
a655f2cbba | ||
|
|
0cb42f840d | ||
|
|
99aabef7f3 | ||
|
|
7d51c6e4f4 | ||
|
|
f463fb3759 | ||
|
|
cb0eb0e68e | ||
|
|
8009f220fc | ||
|
|
4f1551b91f | ||
|
|
6ba37a135e | ||
|
|
dab514d8bc | ||
|
|
8342b29618 | ||
|
|
e05944c6cb | ||
|
|
88a81f5737 | ||
|
|
9cf6ca045c | ||
|
|
ba381d0d0b | ||
|
|
033ebc7ce3 | ||
|
|
6292219551 | ||
|
|
ed237c8d25 | ||
|
|
d46a5345d2 | ||
|
|
523141597e | ||
|
|
cfed5c914c | ||
|
|
20f9bb3b14 | ||
|
|
6067160582 | ||
|
|
3175c4f7ba | ||
|
|
a29f06a730 | ||
|
|
c713474d1f | ||
|
|
89c874d4a9 | ||
|
|
5e3cba9b70 | ||
|
|
a7894fd785 | ||
|
|
c638a770f9 | ||
|
|
6ced6ac23b | ||
|
|
d0b77b61eb | ||
|
|
b440c3636b | ||
|
|
dfd58961f7 | ||
|
|
139c9f37b1 | ||
|
|
2445b12898 | ||
|
|
4d402f3a06 | ||
|
|
ab022ccc33 | ||
|
|
fb7bbac524 | ||
|
|
39fbff5fb6 | ||
|
|
3ac1eaf7d2 | ||
|
|
6c95d008e0 | ||
|
|
16f891c290 | ||
|
|
650bddd54b | ||
|
|
9e30df4b43 | ||
|
|
50c592e41f | ||
|
|
bdf8cd2dd5 | ||
|
|
5554df29fd | ||
|
|
2dd3088f50 | ||
|
|
b9bd128c7a | ||
|
|
adb67d1910 | ||
|
|
ce3b815bd8 | ||
|
|
b94f9ef496 | ||
|
|
77db475663 | ||
|
|
a3683be047 | ||
|
|
9dca19d6c9 | ||
|
|
3ba847ece2 | ||
|
|
91bf948d1e | ||
|
|
91fec77f4b | ||
|
|
8fb25a6340 | ||
|
|
cf49acff67 | ||
|
|
4f1a25e1bf | ||
|
|
8608daa7dc | ||
|
|
828e6e3fd0 | ||
|
|
ff021fed1f | ||
|
|
ed66f36cb5 | ||
|
|
b7ff996b15 | ||
|
|
faf53fe11e | ||
|
|
b23c4b4da6 | ||
|
|
966bb2271a | ||
|
|
5438be891b | ||
|
|
f31f603c8b | ||
|
|
ff39fa0fed | ||
|
|
cdf3809634 | ||
|
|
b2a2791f6f | ||
|
|
125df968d2 | ||
|
|
1c5d07a29f | ||
|
|
24b025f573 | ||
|
|
d323bd3593 | ||
|
|
b7174783f1 | ||
|
|
e3269616bd | ||
|
|
64051fca10 | ||
|
|
14ce55b1a8 | ||
|
|
be605d8ea5 | ||
|
|
4d8d5f4e1e | ||
|
|
750d6e99a8 | ||
|
|
a67892d414 | ||
|
|
1cd2a62caf | ||
|
|
6772d6f66c | ||
|
|
89531dfb62 |
@@ -13,7 +13,7 @@ restore-workspace: &restore-workspace
|
||||
restore-cache: &restore-cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- repo-source-{{ .Branch }}-{{ .Revision }}
|
||||
|
||||
commands:
|
||||
@@ -53,10 +53,11 @@ jobs:
|
||||
command: cargo generate-lockfile
|
||||
- restore_cache:
|
||||
keys:
|
||||
- cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
- run: rustup install $(cat rust-toolchain)
|
||||
- run: rustup default $(cat rust-toolchain)
|
||||
- run: rustup component add --toolchain $(cat rust-toolchain) rustfmt
|
||||
- run: rustup component add --toolchain $(cat rust-toolchain) clippy-preview
|
||||
- run: cargo update
|
||||
- run: cargo fetch
|
||||
- run: rustc +stable --version
|
||||
@@ -67,7 +68,7 @@ jobs:
|
||||
paths:
|
||||
- crate
|
||||
- save_cache:
|
||||
key: cargo-v0-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
key: cargo-v1-{{ checksum "rust-toolchain" }}-{{ checksum "Cargo.toml" }}-{{ checksum "Cargo.lock" }}-{{ arch }}
|
||||
paths:
|
||||
- "~/.cargo"
|
||||
- "~/.rustup"
|
||||
@@ -160,6 +161,15 @@ jobs:
|
||||
- run: ls -laR workspace
|
||||
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
|
||||
|
||||
clippy:
|
||||
executor: default
|
||||
steps:
|
||||
- *restore-workspace
|
||||
- *restore-cache
|
||||
- run:
|
||||
name: Run cargo clippy
|
||||
command: cargo clippy --all
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2.1
|
||||
@@ -175,6 +185,10 @@ workflows:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
- clippy:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
# Linux Desktop 64bit
|
||||
- test_x86_64-unknown-linux-gnu:
|
||||
requires:
|
||||
|
||||
689
Cargo.lock
generated
689
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
11
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-alpha.3"
|
||||
version = "1.0.0-alpha.4"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
@@ -10,11 +10,13 @@ cc = "1.0.35"
|
||||
pkg-config = "0.3"
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
libc = "0.2.51"
|
||||
pgp = { version = "0.2", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
phf = { git = "https://github.com/sfackler/rust-phf", rev = "0d00821", features = ["macros"] }
|
||||
smallvec = "0.6.9"
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
@@ -36,7 +38,6 @@ rustyline = "4.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.20", features = ["bundled"] }
|
||||
addr = "0.2.0"
|
||||
r2d2_sqlite = "0.12.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.15.0"
|
||||
@@ -45,6 +46,9 @@ thread-local-object = "0.1.0"
|
||||
backtrace = "0.3.33"
|
||||
byteorder = "1.3.1"
|
||||
itertools = "0.8.0"
|
||||
image-meta = "0.1.0"
|
||||
quick-xml = "0.15.0"
|
||||
escaper = "0.1.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -53,7 +57,8 @@ pretty_env_logger = "0.3.0"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"deltachat-ffi"
|
||||
"deltachat-ffi",
|
||||
"deltachat_derive",
|
||||
]
|
||||
|
||||
[[example]]
|
||||
|
||||
@@ -89,6 +89,14 @@ $ cargo test --all
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
```
|
||||
|
||||
### Expensive tests
|
||||
|
||||
Some tests are expensive and marked with `#[ignore]`, to run these
|
||||
use the `--ignored` argument to the test binary (not to cargo itself):
|
||||
```sh
|
||||
$ cargo test -- --ignored
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
||||
|
||||
6
Xargo.toml
Normal file
6
Xargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[dependencies.std]
|
||||
features = ["panic-unwind"]
|
||||
|
||||
# if using `cargo test`
|
||||
[dependencies.test]
|
||||
stage = 1
|
||||
@@ -16,7 +16,7 @@ export BRANCH=${CIRCLE_BRANCH:-test7}
|
||||
#fi
|
||||
|
||||
# run everything else inside docker (TESTS, DOCS, WHEELS)
|
||||
docker run -e BRANCH -e TESTS -e DOCS \
|
||||
docker run -e DCC_PY_LIVECONFIG -e BRANCH -e TESTS -e DOCS \
|
||||
--rm -it -v $(pwd):/mnt -w /mnt \
|
||||
deltachat/coredeps ci_scripts/run_all.sh
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ export BRANCH=${CIRCLE_BRANCH:?specify branch for uploading purposes}
|
||||
|
||||
|
||||
# python docs to py.delta.chat
|
||||
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null delta@py.delta.chat mkdir -p build/${BRANCH}
|
||||
rsync -avz \
|
||||
-e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" \
|
||||
"$PYDOCDIR/html/" \
|
||||
@@ -37,11 +38,13 @@ pip install devpi-client
|
||||
devpi use https://m.devpi.net
|
||||
devpi login dc --password $DEVPI_LOGIN
|
||||
|
||||
devpi use dc/$BRANCH || {
|
||||
devpi index -c $BRANCH
|
||||
devpi use dc/$BRANCH
|
||||
N_BRANCH=${BRANCH//[\/]}
|
||||
|
||||
devpi use dc/$N_BRANCH || {
|
||||
devpi index -c $N_BRANCH
|
||||
devpi use dc/$N_BRANCH
|
||||
}
|
||||
devpi index $BRANCH bases=/root/pypi
|
||||
devpi index $N_BRANCH bases=/root/pypi
|
||||
devpi upload deltachat*.whl
|
||||
|
||||
popd
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
set -e -x
|
||||
|
||||
# Install Rust
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-04-19 -y
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly-2019-07-10 -y
|
||||
export PATH=/root/.cargo/bin:$PATH
|
||||
rustc --version
|
||||
|
||||
# remove some 300-400 MB that we don't need for automated builds
|
||||
rm -rf /root/.rustup/toolchains/nightly-2019-04-19-x86_64-unknown-linux-gnu/share/
|
||||
rm -rf /root/.rustup/toolchains/nightly-2019-07-10-x86_64-unknown-linux-gnu/share/
|
||||
|
||||
@@ -37,6 +37,10 @@ if [ -n "$TESTS" ]; then
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox
|
||||
# XXX we don't run liveconfig tests because they hang sometimes
|
||||
# see https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
# unset DCC_PY_LIVECONFIG
|
||||
|
||||
tox --workdir "$TOXWORKDIR" -e lint,py27,py35,py36,py37,auditwheels
|
||||
popd
|
||||
fi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-alpha.3"
|
||||
version = "1.0.0-alpha.4"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
@@ -25,4 +25,3 @@ default = ["vendored", "nightly", "ringbuf"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
nightly = ["deltachat/nightly"]
|
||||
ringbuf = ["deltachat/ringbuf"]
|
||||
|
||||
|
||||
@@ -372,14 +372,10 @@ void dc_delete_all_locations (dc_context_t*);
|
||||
*/
|
||||
void dc_array_unref (dc_array_t*);
|
||||
|
||||
void dc_array_add_uint (dc_array_t*, uintptr_t);
|
||||
void dc_array_add_id (dc_array_t*, uint32_t);
|
||||
void dc_array_add_ptr (dc_array_t*, void*);
|
||||
|
||||
size_t dc_array_get_cnt (const dc_array_t*);
|
||||
uintptr_t dc_array_get_uint (const dc_array_t*, size_t index);
|
||||
uint32_t dc_array_get_id (const dc_array_t*, size_t index);
|
||||
void* dc_array_get_ptr (const dc_array_t*, size_t index);
|
||||
double dc_array_get_latitude (const dc_array_t*, size_t index);
|
||||
double dc_array_get_longitude (const dc_array_t*, size_t index);
|
||||
double dc_array_get_accuracy (const dc_array_t*, size_t index);
|
||||
@@ -391,7 +387,7 @@ char* dc_array_get_marker (const dc_array_t*, size_t index);
|
||||
int dc_array_is_independent (const dc_array_t*, size_t index);
|
||||
|
||||
int dc_array_search_id (const dc_array_t*, uint32_t needle, size_t* indx);
|
||||
const uintptr_t* dc_array_get_raw (const dc_array_t*);
|
||||
const uint32_t* dc_array_get_raw (const dc_array_t*);
|
||||
|
||||
|
||||
/**
|
||||
@@ -598,8 +594,6 @@ int dc_contact_is_verified (dc_contact_t*);
|
||||
#define DC_TEXT1_SELF 3
|
||||
|
||||
|
||||
dc_lot_t* dc_lot_new ();
|
||||
void dc_lot_empty (dc_lot_t*);
|
||||
void dc_lot_unref (dc_lot_t*);
|
||||
char* dc_lot_get_text1 (const dc_lot_t*);
|
||||
char* dc_lot_get_text2 (const dc_lot_t*);
|
||||
|
||||
@@ -12,6 +12,8 @@ extern crate human_panic;
|
||||
extern crate num_traits;
|
||||
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use std::convert::TryInto;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::contact::Contact;
|
||||
@@ -44,11 +46,14 @@ pub unsafe extern "C" fn dc_context_new(
|
||||
Box::into_raw(Box::new(ctx))
|
||||
}
|
||||
|
||||
/// Release the context structure.
|
||||
///
|
||||
/// This function releases the memory of the `dc_context_t` structure.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &mut *context;
|
||||
context::dc_context_unref(context);
|
||||
context::dc_close(context);
|
||||
Box::from_raw(context);
|
||||
}
|
||||
|
||||
@@ -184,7 +189,7 @@ pub unsafe extern "C" fn dc_perform_imap_jobs(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_imap_jobs(context)
|
||||
job::perform_imap_jobs(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -192,7 +197,7 @@ pub unsafe extern "C" fn dc_perform_imap_fetch(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_imap_fetch(context)
|
||||
job::perform_imap_fetch(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -200,7 +205,7 @@ pub unsafe extern "C" fn dc_perform_imap_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_imap_idle(context)
|
||||
job::perform_imap_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -208,7 +213,7 @@ pub unsafe extern "C" fn dc_interrupt_imap_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_interrupt_imap_idle(context)
|
||||
job::interrupt_imap_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -216,7 +221,7 @@ pub unsafe extern "C" fn dc_perform_mvbox_fetch(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_mvbox_fetch(context)
|
||||
job::perform_mvbox_fetch(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -224,7 +229,7 @@ pub unsafe extern "C" fn dc_perform_mvbox_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_mvbox_idle(context)
|
||||
job::perform_mvbox_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -232,7 +237,7 @@ pub unsafe extern "C" fn dc_interrupt_mvbox_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_interrupt_mvbox_idle(context)
|
||||
job::interrupt_mvbox_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -240,7 +245,7 @@ pub unsafe extern "C" fn dc_perform_sentbox_fetch(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_sentbox_fetch(context)
|
||||
job::perform_sentbox_fetch(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -248,7 +253,7 @@ pub unsafe extern "C" fn dc_perform_sentbox_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_sentbox_idle(context)
|
||||
job::perform_sentbox_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -256,7 +261,7 @@ pub unsafe extern "C" fn dc_interrupt_sentbox_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_interrupt_sentbox_idle(context)
|
||||
job::interrupt_sentbox_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -264,7 +269,7 @@ pub unsafe extern "C" fn dc_perform_smtp_jobs(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_smtp_jobs(context)
|
||||
job::perform_smtp_jobs(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -272,7 +277,7 @@ pub unsafe extern "C" fn dc_perform_smtp_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_perform_smtp_idle(context)
|
||||
job::perform_smtp_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -280,7 +285,7 @@ pub unsafe extern "C" fn dc_interrupt_smtp_idle(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_interrupt_smtp_idle(context)
|
||||
job::interrupt_smtp_idle(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -288,7 +293,7 @@ pub unsafe extern "C" fn dc_maybe_network(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_job::dc_maybe_network(context)
|
||||
job::maybe_network(context)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -318,7 +323,7 @@ pub unsafe extern "C" fn dc_create_chat_by_msg_id(context: *mut dc_context_t, ms
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_create_chat_by_msg_id(context, msg_id)
|
||||
chat::create_by_msg_id(context, msg_id).unwrap_or_log_default(context, "Failed to create chat")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -329,7 +334,8 @@ pub unsafe extern "C" fn dc_create_chat_by_contact_id(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_create_chat_by_contact_id(context, contact_id)
|
||||
chat::create_by_contact_id(context, contact_id)
|
||||
.unwrap_or_log_default(context, "Failed to create chat")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -340,7 +346,8 @@ pub unsafe extern "C" fn dc_get_chat_id_by_contact_id(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_chat_id_by_contact_id(context, contact_id)
|
||||
chat::get_by_contact_id(context, contact_id)
|
||||
.unwrap_or_log_default(context, "Failed to get chat")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -353,7 +360,8 @@ pub unsafe extern "C" fn dc_prepare_msg(
|
||||
assert!(!msg.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_prepare_msg(context, chat_id, msg)
|
||||
chat::prepare_msg(context, chat_id, msg)
|
||||
.unwrap_or_log_default(context, "Failed to prepare message")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -366,7 +374,7 @@ pub unsafe extern "C" fn dc_send_msg(
|
||||
assert!(!msg.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_send_msg(context, chat_id, msg)
|
||||
chat::send_msg(context, chat_id, msg).unwrap_or_log_default(context, "Failed to send message")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -380,7 +388,8 @@ pub unsafe extern "C" fn dc_send_text_msg(
|
||||
let context = &*context;
|
||||
let text_to_send = dc_tools::to_string_lossy(text_to_send);
|
||||
|
||||
dc_chat::dc_send_text_msg(context, chat_id, text_to_send)
|
||||
chat::send_text_msg(context, chat_id, text_to_send)
|
||||
.unwrap_or_log_default(context, "Failed to send text message")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -392,7 +401,7 @@ pub unsafe extern "C" fn dc_set_draft(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_set_draft(context, chat_id, msg)
|
||||
chat::set_draft(context, chat_id, msg)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -403,7 +412,7 @@ pub unsafe extern "C" fn dc_get_draft<'a>(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_draft(context, chat_id)
|
||||
chat::get_draft(context, chat_id)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -416,7 +425,7 @@ pub unsafe extern "C" fn dc_get_chat_msgs(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_chat_msgs(context, chat_id, flags, marker1before)
|
||||
dc_array_t::from(chat::get_chat_msgs(context, chat_id, flags, marker1before)).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -424,7 +433,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_msg_cnt(context, chat_id)
|
||||
chat::get_msg_cnt(context, chat_id) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -435,7 +444,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_fresh_msg_cnt(context, chat_id)
|
||||
chat::get_fresh_msg_cnt(context, chat_id) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -445,7 +454,7 @@ pub unsafe extern "C" fn dc_get_fresh_msgs(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
context::dc_get_fresh_msgs(context)
|
||||
dc_array_t::from(context::dc_get_fresh_msgs(context)).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -453,7 +462,7 @@ pub unsafe extern "C" fn dc_marknoticed_chat(context: *mut dc_context_t, chat_id
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_marknoticed_chat(context, chat_id);
|
||||
chat::marknoticed_chat(context, chat_id).log_err(context, "Failed marknoticed chat");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -461,7 +470,7 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_marknoticed_all_chats(context);
|
||||
chat::marknoticed_all_chats(context).log_err(context, "Failed marknoticed all chats");
|
||||
}
|
||||
|
||||
fn from_prim<S, T>(s: S) -> Option<T>
|
||||
@@ -489,7 +498,14 @@ pub unsafe extern "C" fn dc_get_chat_media(
|
||||
let or_msg_type3 =
|
||||
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
|
||||
|
||||
dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3)
|
||||
dc_array_t::from(chat::get_chat_media(
|
||||
context,
|
||||
chat_id,
|
||||
msg_type,
|
||||
or_msg_type2,
|
||||
or_msg_type3,
|
||||
))
|
||||
.into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -510,7 +526,7 @@ pub unsafe extern "C" fn dc_get_next_media(
|
||||
let or_msg_type3 =
|
||||
from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3));
|
||||
|
||||
dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
|
||||
chat::get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -522,7 +538,15 @@ pub unsafe extern "C" fn dc_archive_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_archive_chat(context, chat_id, archive);
|
||||
let archive = if archive == 0 {
|
||||
false
|
||||
} else if archive == 1 {
|
||||
true
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
chat::archive(context, chat_id, archive).log_err(context, "Failed archive chat");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -530,8 +554,7 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
// TODO: update to indicate public api success/failure of deletion
|
||||
dc_chat::dc_delete_chat(context, chat_id);
|
||||
chat::delete(context, chat_id).log_err(context, "Failed chat delete");
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -542,7 +565,7 @@ pub unsafe extern "C" fn dc_get_chat_contacts(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_chat_contacts(context, chat_id)
|
||||
dc_array_t::from(chat::get_chat_contacts(context, chat_id)).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -555,7 +578,7 @@ pub unsafe extern "C" fn dc_search_msgs(
|
||||
assert!(!query.is_null());
|
||||
let context = &*context;
|
||||
|
||||
context::dc_search_msgs(context, chat_id, query)
|
||||
dc_array_t::from(context::dc_search_msgs(context, chat_id, query)).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -566,7 +589,10 @@ pub unsafe extern "C" fn dc_get_chat<'a>(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_get_chat(context, chat_id)
|
||||
match chat::Chat::load_from_db(context, chat_id) {
|
||||
Ok(chat) => Box::into_raw(Box::new(chat)),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -579,7 +605,14 @@ pub unsafe extern "C" fn dc_create_group_chat(
|
||||
assert!(!name.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_create_group_chat(context, verified, name)
|
||||
let verified = if let Some(s) = contact::VerifiedStatus::from_i32(verified) {
|
||||
s
|
||||
} else {
|
||||
return 0;
|
||||
};
|
||||
|
||||
chat::create_group_chat(context, verified, as_str(name))
|
||||
.unwrap_or_log_default(context, "Failed to create group chat")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -591,7 +624,7 @@ pub unsafe extern "C" fn dc_is_contact_in_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_is_contact_in_chat(context, chat_id, contact_id)
|
||||
chat::is_contact_in_chat(context, chat_id, contact_id)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -603,7 +636,7 @@ pub unsafe extern "C" fn dc_add_contact_to_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_add_contact_to_chat(context, chat_id, contact_id)
|
||||
chat::add_contact_to_chat(context, chat_id, contact_id)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -615,7 +648,9 @@ pub unsafe extern "C" fn dc_remove_contact_from_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_remove_contact_from_chat(context, chat_id, contact_id)
|
||||
chat::remove_contact_from_chat(context, chat_id, contact_id)
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(context, "Failed to remove contact")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -629,7 +664,9 @@ pub unsafe extern "C" fn dc_set_chat_name(
|
||||
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_set_chat_name(context, chat_id, name)
|
||||
chat::set_chat_name(context, chat_id, as_str(name))
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(context, "Failed to set chat name")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -642,7 +679,9 @@ pub unsafe extern "C" fn dc_set_chat_profile_image(
|
||||
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_set_chat_profile_image(context, chat_id, image)
|
||||
chat::set_chat_profile_image(context, chat_id, as_str(image))
|
||||
.map(|_| 1)
|
||||
.unwrap_or_log_default(context, "Failed to set profile image")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -694,7 +733,7 @@ pub unsafe extern "C" fn dc_forward_msgs(
|
||||
assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32);
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id)
|
||||
chat::forward_msgs(context, msg_ids, msg_cnt, chat_id)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -814,7 +853,7 @@ pub unsafe extern "C" fn dc_get_contacts(
|
||||
};
|
||||
|
||||
match Contact::get_all(context, flags, query) {
|
||||
Ok(contacts) => contacts,
|
||||
Ok(contacts) => dc_array_t::from(contacts).into_raw(),
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
@@ -834,7 +873,7 @@ pub unsafe extern "C" fn dc_get_blocked_contacts(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
Contact::get_all_blocked(context)
|
||||
dc_array_t::from(Contact::get_all_blocked(context)).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -861,7 +900,12 @@ pub unsafe extern "C" fn dc_get_contact_encrinfo(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
Contact::get_encrinfo(context, contact_id).strdup()
|
||||
Contact::get_encrinfo(context, contact_id)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|e| {
|
||||
error!(context, 0, "{}", e);
|
||||
std::ptr::null_mut()
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -949,12 +993,13 @@ pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) {
|
||||
pub unsafe extern "C" fn dc_check_qr(
|
||||
context: *mut dc_context_t,
|
||||
qr: *mut libc::c_char,
|
||||
) -> *mut dc_lot::dc_lot_t {
|
||||
) -> *mut dc_lot_t {
|
||||
assert!(!context.is_null());
|
||||
assert!(!qr.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_qr::dc_check_qr(context, qr)
|
||||
let lot = qr::check_qr(context, as_str(qr));
|
||||
Box::into_raw(Box::new(lot))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -989,7 +1034,7 @@ pub unsafe extern "C" fn dc_send_locations_to_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_location::dc_send_locations_to_chat(context, chat_id, seconds)
|
||||
dc_location::dc_send_locations_to_chat(context, chat_id, seconds as i64)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1027,13 +1072,14 @@ pub unsafe extern "C" fn dc_get_locations(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_location::dc_get_locations(
|
||||
let res = dc_location::dc_get_locations(
|
||||
context,
|
||||
chat_id,
|
||||
contact_id,
|
||||
timestamp_begin as i64,
|
||||
timestamp_end as i64,
|
||||
)
|
||||
);
|
||||
dc_array_t::from(res).into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1056,24 +1102,12 @@ pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) {
|
||||
dc_array::dc_array_unref(a)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_add_uint(array: *mut dc_array_t, item: libc::uintptr_t) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_add_uint(array, item)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_add_id(array: *mut dc_array_t, item: libc::c_uint) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_add_id(array, item)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_add_ptr(array, item)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t {
|
||||
@@ -1082,15 +1116,6 @@ pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::siz
|
||||
dc_array::dc_array_get_cnt(array)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_uint(
|
||||
array: *const dc_array_t,
|
||||
index: libc::size_t,
|
||||
) -> libc::uintptr_t {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_uint(array, index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_id(
|
||||
array: *const dc_array_t,
|
||||
index: libc::size_t,
|
||||
@@ -1100,22 +1125,13 @@ pub unsafe extern "C" fn dc_array_get_id(
|
||||
dc_array::dc_array_get_id(array, index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_ptr(
|
||||
array: *const dc_array_t,
|
||||
index: libc::size_t,
|
||||
) -> *mut libc::c_void {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_ptr(array, index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_latitude(
|
||||
array: *const dc_array_t,
|
||||
index: libc::size_t,
|
||||
) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_latitude(array, index)
|
||||
(*array).get_latitude(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_longitude(
|
||||
@@ -1124,7 +1140,7 @@ pub unsafe extern "C" fn dc_array_get_longitude(
|
||||
) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_longitude(array, index)
|
||||
(*array).get_longitude(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_accuracy(
|
||||
@@ -1133,7 +1149,7 @@ pub unsafe extern "C" fn dc_array_get_accuracy(
|
||||
) -> libc::c_double {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_accuracy(array, index)
|
||||
(*array).get_accuracy(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_timestamp(
|
||||
@@ -1142,7 +1158,7 @@ pub unsafe extern "C" fn dc_array_get_timestamp(
|
||||
) -> i64 {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_timestamp(array, index)
|
||||
(*array).get_timestamp(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_chat_id(
|
||||
@@ -1151,7 +1167,7 @@ pub unsafe extern "C" fn dc_array_get_chat_id(
|
||||
) -> libc::c_uint {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_chat_id(array, index)
|
||||
(*array).get_chat_id(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_contact_id(
|
||||
@@ -1160,7 +1176,7 @@ pub unsafe extern "C" fn dc_array_get_contact_id(
|
||||
) -> libc::c_uint {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_contact_id(array, index)
|
||||
(*array).get_contact_id(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_msg_id(
|
||||
@@ -1169,7 +1185,7 @@ pub unsafe extern "C" fn dc_array_get_msg_id(
|
||||
) -> libc::c_uint {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_msg_id(array, index)
|
||||
(*array).get_msg_id(index)
|
||||
}
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_marker(
|
||||
@@ -1193,7 +1209,7 @@ pub unsafe extern "C" fn dc_array_search_id(
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const libc::size_t {
|
||||
pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const u32 {
|
||||
assert!(!array.is_null());
|
||||
|
||||
dc_array::dc_array_get_raw(array)
|
||||
@@ -1256,11 +1272,14 @@ pub unsafe extern "C" fn dc_chatlist_get_summary<'a>(
|
||||
chatlist: *mut dc_chatlist_t<'a>,
|
||||
index: libc::size_t,
|
||||
chat: *mut dc_chat_t<'a>,
|
||||
) -> *mut dc_lot::dc_lot_t {
|
||||
) -> *mut dc_lot_t {
|
||||
assert!(!chatlist.is_null());
|
||||
|
||||
let chat = if chat.is_null() { None } else { Some(&*chat) };
|
||||
let list = &*chatlist;
|
||||
list.get_summary(index as usize, chat)
|
||||
|
||||
let lot = list.get_summary(index as usize, chat);
|
||||
Box::into_raw(Box::new(lot))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1276,90 +1295,104 @@ pub unsafe extern "C" fn dc_chatlist_get_context(
|
||||
// dc_chat_t
|
||||
|
||||
#[no_mangle]
|
||||
pub type dc_chat_t<'a> = dc_chat::Chat<'a>;
|
||||
pub type dc_chat_t<'a> = chat::Chat<'a>;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
|
||||
assert!(!chat.is_null());
|
||||
|
||||
dc_chat::dc_chat_unref(chat)
|
||||
Box::from_raw(chat);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_id(chat)
|
||||
chat.get_id()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_type(chat)
|
||||
chat.get_type() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_name(chat)
|
||||
chat.get_name().strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_subtitle(chat)
|
||||
chat.get_subtitle().strdup()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_profile_image(chat)
|
||||
match chat.get_profile_image() {
|
||||
Some(i) => i.strdup(),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_color(chat)
|
||||
chat.get_color()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_get_archived(chat)
|
||||
chat.is_archived() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_is_unpromoted(chat)
|
||||
chat.is_unpromoted() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_is_self_talk(chat)
|
||||
chat.is_self_talk() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_is_verified(chat)
|
||||
chat.is_verified() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
assert!(!chat.is_null());
|
||||
let chat = &*chat;
|
||||
|
||||
dc_chat::dc_chat_is_sending_locations(chat)
|
||||
chat.is_sending_locations() as libc::c_int
|
||||
}
|
||||
|
||||
// dc_msg_t
|
||||
@@ -1427,7 +1460,7 @@ pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg::dc_msg_t) -> libc
|
||||
pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
|
||||
assert!(!msg.is_null());
|
||||
|
||||
dc_msg::dc_msg_get_state(msg)
|
||||
dc_msg::dc_msg_get_state(msg) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1518,10 +1551,12 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> l
|
||||
pub unsafe extern "C" fn dc_msg_get_summary<'a>(
|
||||
msg: *mut dc_msg::dc_msg_t<'a>,
|
||||
chat: *mut dc_chat_t<'a>,
|
||||
) -> *mut dc_lot::dc_lot_t {
|
||||
) -> *mut dc_lot_t {
|
||||
assert!(!msg.is_null());
|
||||
let chat = if chat.is_null() { None } else { Some(&*chat) };
|
||||
|
||||
dc_msg::dc_msg_get_summary(msg, chat)
|
||||
let lot = dc_msg::dc_msg_get_summary(msg, chat);
|
||||
Box::into_raw(Box::new(lot))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1531,7 +1566,7 @@ pub unsafe extern "C" fn dc_msg_get_summarytext(
|
||||
) -> *mut libc::c_char {
|
||||
assert!(!msg.is_null());
|
||||
|
||||
dc_msg::dc_msg_get_summarytext(msg, approx_characters)
|
||||
dc_msg::dc_msg_get_summarytext(msg, approx_characters.try_into().unwrap())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1764,67 +1799,61 @@ pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> l
|
||||
// dc_lot_t
|
||||
|
||||
#[no_mangle]
|
||||
pub type dc_lot_t = dc_lot::dc_lot_t;
|
||||
pub type dc_lot_t = lot::Lot;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_new() -> *mut dc_lot::dc_lot_t {
|
||||
dc_lot::dc_lot_new()
|
||||
pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot_t) {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
Box::from_raw(lot);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_empty(lot: *mut dc_lot::dc_lot_t) {
|
||||
pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot_t) -> *mut libc::c_char {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_empty(lot)
|
||||
let lot = &*lot;
|
||||
strdup_opt(lot.get_text1())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot::dc_lot_t) {
|
||||
pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot_t) -> *mut libc::c_char {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_unref(lot)
|
||||
let lot = &*lot;
|
||||
strdup_opt(lot.get_text2())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot_t) -> libc::c_int {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_text1(lot)
|
||||
let lot = &*lot;
|
||||
lot.get_text1_meaning() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot_t) -> libc::c_int {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_text2(lot)
|
||||
let lot = &*lot;
|
||||
lot.get_state().to_i64().expect("impossible") as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot::dc_lot_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot_t) -> u32 {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_text1_meaning(lot)
|
||||
let lot = &*lot;
|
||||
lot.get_id()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot::dc_lot_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot_t) -> i64 {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_state(lot)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot::dc_lot_t) -> u32 {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_id(lot)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64 {
|
||||
assert!(!lot.is_null());
|
||||
|
||||
dc_lot::dc_lot_get_timestamp(lot)
|
||||
let lot = &*lot;
|
||||
lot.get_timestamp()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1839,3 +1868,33 @@ fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
|
||||
|
||||
Some(dc_tools::as_str(s))
|
||||
}
|
||||
|
||||
pub trait ResultExt<T: Default> {
|
||||
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T;
|
||||
fn log_err(&self, context: &context::Context, message: &str);
|
||||
}
|
||||
|
||||
impl<T: Default, E: std::fmt::Display> ResultExt<T> for Result<T, E> {
|
||||
fn unwrap_or_log_default(self, context: &context::Context, message: &str) -> T {
|
||||
match self {
|
||||
Ok(t) => t,
|
||||
Err(err) => {
|
||||
error!(context, 0, "{}: {}", message, err);
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log_err(&self, context: &context::Context, message: &str) {
|
||||
if let Err(err) = self {
|
||||
error!(context, 0, "{}: {}", message, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn strdup_opt(s: Option<impl AsRef<str>>) -> *mut libc::c_char {
|
||||
match s {
|
||||
Some(s) => s.as_ref().strdup(),
|
||||
None => ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
12
deltachat_derive/Cargo.toml
Normal file
12
deltachat_derive/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "deltachat_derive"
|
||||
version = "0.1.0"
|
||||
authors = ["Dmitry Bogatov <KAction@debian.org>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
syn = "0.14.4"
|
||||
quote = "0.6.3"
|
||||
44
deltachat_derive/src/lib.rs
Normal file
44
deltachat_derive/src/lib.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
#![recursion_limit = "128"]
|
||||
extern crate proc_macro;
|
||||
|
||||
use crate::proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn;
|
||||
|
||||
// For now, assume (not check) that these macroses are applied to enum without
|
||||
// data. If this assumption is violated, compiler error will point to
|
||||
// generated code, which is not very user-friendly.
|
||||
|
||||
#[proc_macro_derive(ToSql)]
|
||||
pub fn to_sql_derive(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let name = &ast.ident;
|
||||
|
||||
let gen = quote! {
|
||||
impl rusqlite::types::ToSql for #name {
|
||||
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
|
||||
let num = *self as i64;
|
||||
let value = rusqlite::types::Value::Integer(num);
|
||||
let output = rusqlite::types::ToSqlOutput::Owned(value);
|
||||
std::result::Result::Ok(output)
|
||||
}
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromSql)]
|
||||
pub fn from_sql_derive(input: TokenStream) -> TokenStream {
|
||||
let ast: syn::DeriveInput = syn::parse(input).unwrap();
|
||||
let name = &ast.ident;
|
||||
|
||||
let gen = quote! {
|
||||
impl rusqlite::types::FromSql for #name {
|
||||
fn column_result(col: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
|
||||
let inner = rusqlite::types::FromSql::column_result(col)?;
|
||||
num_traits::FromPrimitive::from_i64(inner).ok_or(rusqlite::types::FromSqlError::InvalidType)
|
||||
}
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
use std::ffi::CString;
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_job::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
use deltachat::dc_msg::*;
|
||||
use deltachat::dc_qr::*;
|
||||
use deltachat::dc_receive_imf::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::lot::LotState;
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::qr::*;
|
||||
use deltachat::sql;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
@@ -125,7 +124,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut current_block: u64;
|
||||
let ok_to_continue;
|
||||
let mut success: libc::c_int = 0;
|
||||
let real_spec: *mut libc::c_char;
|
||||
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
@@ -138,77 +137,71 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
.sql
|
||||
.set_config(context, "import_spec", Some(as_str(real_spec)))
|
||||
.unwrap();
|
||||
current_block = 7149356873433890176;
|
||||
ok_to_continue = true;
|
||||
} else {
|
||||
let rs = context.sql.get_config(context, "import_spec");
|
||||
if rs.is_none() {
|
||||
error!(context, 0, "Import: No file or folder given.");
|
||||
current_block = 8522321847195001863;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
current_block = 7149356873433890176;
|
||||
ok_to_continue = true;
|
||||
}
|
||||
real_spec = rs.unwrap_or_default().strdup();
|
||||
}
|
||||
match current_block {
|
||||
8522321847195001863 => {}
|
||||
_ => {
|
||||
suffix = dc_get_filesuffix_lc(real_spec);
|
||||
if !suffix.is_null()
|
||||
&& strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
|
||||
{
|
||||
if 0 != dc_poke_eml_file(context, real_spec) {
|
||||
read_cnt += 1
|
||||
}
|
||||
current_block = 1622411330066726685;
|
||||
if ok_to_continue {
|
||||
let ok_to_continue2;
|
||||
suffix = dc_get_filesuffix_lc(as_str(real_spec));
|
||||
if !suffix.is_null() && strcmp(suffix, b"eml\x00" as *const u8 as *const libc::c_char) == 0
|
||||
{
|
||||
if 0 != dc_poke_eml_file(context, real_spec) {
|
||||
read_cnt += 1
|
||||
}
|
||||
ok_to_continue2 = true;
|
||||
} else {
|
||||
/* import a directory */
|
||||
let dir_name = std::path::Path::new(as_str(real_spec));
|
||||
let dir = std::fs::read_dir(dir_name);
|
||||
if dir.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(real_spec),
|
||||
);
|
||||
ok_to_continue2 = false;
|
||||
} else {
|
||||
/* import a directory */
|
||||
let dir_name = std::path::Path::new(as_str(real_spec));
|
||||
let dir = std::fs::read_dir(dir_name);
|
||||
if dir.is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import: Cannot open directory \"{}\".",
|
||||
as_str(real_spec),
|
||||
);
|
||||
current_block = 8522321847195001863;
|
||||
} else {
|
||||
let dir = dir.unwrap();
|
||||
for entry in dir {
|
||||
if entry.is_err() {
|
||||
break;
|
||||
}
|
||||
let entry = entry.unwrap();
|
||||
let name_f = entry.file_name();
|
||||
let name = name_f.to_string_lossy();
|
||||
if name.ends_with(".eml") {
|
||||
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
|
||||
info!(context, 0, "Import: {}", path_plus_name);
|
||||
let path_plus_name_c = CString::yolo(path_plus_name);
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
||||
read_cnt += 1
|
||||
}
|
||||
let dir = dir.unwrap();
|
||||
for entry in dir {
|
||||
if entry.is_err() {
|
||||
break;
|
||||
}
|
||||
let entry = entry.unwrap();
|
||||
let name_f = entry.file_name();
|
||||
let name = name_f.to_string_lossy();
|
||||
if name.ends_with(".eml") {
|
||||
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
|
||||
info!(context, 0, "Import: {}", path_plus_name);
|
||||
let path_plus_name_c = CString::yolo(path_plus_name);
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
||||
read_cnt += 1
|
||||
}
|
||||
}
|
||||
current_block = 1622411330066726685;
|
||||
}
|
||||
ok_to_continue2 = true;
|
||||
}
|
||||
match current_block {
|
||||
8522321847195001863 => {}
|
||||
_ => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Import: {} items read from \"{}\".",
|
||||
read_cnt,
|
||||
as_str(real_spec)
|
||||
);
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
}
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
if ok_to_continue2 {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Import: {} items read from \"{}\".",
|
||||
read_cnt,
|
||||
as_str(real_spec)
|
||||
);
|
||||
if read_cnt > 0 {
|
||||
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
}
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,10 +216,10 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
let contact_id = contact.get_id();
|
||||
|
||||
let statestr = match dc_msg_get_state(msg) {
|
||||
DC_STATE_OUT_PENDING => " o",
|
||||
DC_STATE_OUT_DELIVERED => " √",
|
||||
DC_STATE_OUT_MDN_RCVD => " √√",
|
||||
DC_STATE_OUT_FAILED => " !!",
|
||||
MessageState::OutPending => " o",
|
||||
MessageState::OutDelivered => " √",
|
||||
MessageState::OutMdnRcvd => " √√",
|
||||
MessageState::OutFailed => " !!",
|
||||
_ => "",
|
||||
};
|
||||
let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg));
|
||||
@@ -249,9 +242,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
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) == DC_STATE_IN_SEEN {
|
||||
} else if dc_msg_get_state(msg) == MessageState::InSeen {
|
||||
"[SEEN]"
|
||||
} else if dc_msg_get_state(msg) == DC_STATE_IN_NOTICED {
|
||||
} else if dc_msg_get_state(msg) == MessageState::InNoticed {
|
||||
"[NOTICED]"
|
||||
} else {
|
||||
"[FRESH]"
|
||||
@@ -267,11 +260,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: *mut dc_msg_t
|
||||
free(msgtext as *mut libc::c_void);
|
||||
}
|
||||
|
||||
unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
let cnt = dc_array_get_cnt(msglist) as usize;
|
||||
unsafe fn log_msglist(context: &Context, msglist: &Vec<u32>) {
|
||||
let mut lines_out = 0;
|
||||
for i in 0..cnt {
|
||||
let msg_id = dc_array_get_id(msglist, i as size_t);
|
||||
for &msg_id in msglist {
|
||||
if msg_id == 9 as libc::c_uint {
|
||||
info!(
|
||||
context,
|
||||
@@ -280,7 +271,7 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
);
|
||||
|
||||
lines_out += 1
|
||||
} else if msg_id > 0 as libc::c_uint {
|
||||
} else if msg_id > 0 {
|
||||
if lines_out == 0 {
|
||||
info!(
|
||||
context, 0,
|
||||
@@ -301,13 +292,12 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_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);
|
||||
unsafe fn log_contactlist(context: &Context, contacts: &Vec<u32>) {
|
||||
let mut contacts = contacts.clone();
|
||||
if !contacts.contains(&1) {
|
||||
contacts.push(1);
|
||||
}
|
||||
let cnt = dc_array_get_cnt(contacts);
|
||||
for i in 0..cnt {
|
||||
let contact_id = dc_array_get_id(contacts, i as size_t);
|
||||
for contact_id in contacts {
|
||||
let line;
|
||||
let mut line2 = "".to_string();
|
||||
if let Ok(contact) = Contact::get_by_id(context, contact_id) {
|
||||
@@ -356,22 +346,16 @@ pub unsafe fn dc_cmdline_skip_auth() {
|
||||
S_IS_AUTH = 1;
|
||||
}
|
||||
|
||||
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
|
||||
if (*chat).type_0 == 120 {
|
||||
"Group"
|
||||
} else if (*chat).type_0 == 130 {
|
||||
"VerifiedGroup"
|
||||
} else {
|
||||
"Single"
|
||||
}
|
||||
fn chat_prefix(chat: &Chat) -> &'static str {
|
||||
chat.typ.into()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
let chat_id = *context.cmdline_sel_chat_id.read().unwrap();
|
||||
let mut sel_chat = if chat_id > 0 {
|
||||
dc_get_chat(context, chat_id)
|
||||
Chat::load_from_db(context, chat_id).ok()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
None
|
||||
};
|
||||
|
||||
let mut args = line.splitn(3, ' ');
|
||||
@@ -471,7 +455,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
event <event-id to test>\n\
|
||||
fileinfo <file>\n\
|
||||
clear -- clear screen\n\
|
||||
exit\n\
|
||||
exit or quit\n\
|
||||
============================================="
|
||||
),
|
||||
},
|
||||
@@ -597,16 +581,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
println!("{}", to_string(dc_get_info(context)));
|
||||
}
|
||||
"maybenetwork" => {
|
||||
dc_maybe_network(context);
|
||||
maybe_network(context);
|
||||
}
|
||||
"housekeeping" => {
|
||||
sql::housekeeping(context);
|
||||
}
|
||||
"listchats" | "listarchived" | "chats" => {
|
||||
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
|
||||
let chatlist = Chatlist::try_load(context, listflags, Some(arg1), None)?;
|
||||
let chatlist = Chatlist::try_load(
|
||||
context,
|
||||
listflags,
|
||||
if arg1.is_empty() { None } else { Some(arg1) },
|
||||
None,
|
||||
)?;
|
||||
|
||||
let mut i: usize;
|
||||
let cnt = chatlist.len();
|
||||
if cnt > 0 {
|
||||
info!(
|
||||
@@ -614,64 +602,54 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"================================================================================"
|
||||
);
|
||||
|
||||
i = cnt - 1;
|
||||
|
||||
while i > 0 {
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
let temp_subtitle = dc_chat_get_subtitle(chat);
|
||||
let temp_name = dc_chat_get_name(chat);
|
||||
for i in (0..cnt).rev() {
|
||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||
let temp_subtitle = chat.get_subtitle();
|
||||
let temp_name = chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}] [{} fresh]",
|
||||
chat_prefix(chat),
|
||||
dc_chat_get_id(chat) as libc::c_int,
|
||||
as_str(temp_name),
|
||||
as_str(temp_subtitle),
|
||||
dc_get_fresh_msg_cnt(context, dc_chat_get_id(chat)) as libc::c_int,
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
temp_name,
|
||||
temp_subtitle,
|
||||
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||
);
|
||||
free(temp_subtitle as *mut libc::c_void);
|
||||
free(temp_name as *mut libc::c_void);
|
||||
let lot = chatlist.get_summary(i, chat);
|
||||
let statestr = if 0 != dc_chat_get_archived(chat) {
|
||||
let lot = chatlist.get_summary(i, Some(&chat));
|
||||
let statestr = if chat.is_archived() {
|
||||
" [Archived]"
|
||||
} else {
|
||||
match dc_lot_get_state(lot) {
|
||||
20 => " o",
|
||||
26 => " √",
|
||||
28 => " √√",
|
||||
24 => " !!",
|
||||
match lot.get_state() {
|
||||
LotState::MsgOutPending => " o",
|
||||
LotState::MsgOutDelivered => " √",
|
||||
LotState::MsgOutMdnRcvd => " √√",
|
||||
LotState::MsgOutFailed => " !!",
|
||||
_ => "",
|
||||
}
|
||||
};
|
||||
let timestr = dc_timestamp_to_str(dc_lot_get_timestamp(lot));
|
||||
let text1 = dc_lot_get_text1(lot);
|
||||
let text2 = dc_lot_get_text2(lot);
|
||||
let timestr = dc_timestamp_to_str(lot.get_timestamp());
|
||||
let text1 = lot.get_text1();
|
||||
let text2 = lot.get_text2();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}{}{}{} [{}]{}",
|
||||
to_string(text1),
|
||||
if !text1.is_null() { ": " } else { "" },
|
||||
to_string(text2),
|
||||
text1.unwrap_or(""),
|
||||
if text1.is_some() { ": " } else { "" },
|
||||
text2.unwrap_or(""),
|
||||
statestr,
|
||||
×tr,
|
||||
if 0 != dc_chat_is_sending_locations(chat) {
|
||||
if chat.is_sending_locations() {
|
||||
"📍"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
);
|
||||
free(text1 as *mut libc::c_void);
|
||||
free(text2 as *mut libc::c_void);
|
||||
dc_lot_unref(lot);
|
||||
dc_chat_unref(chat);
|
||||
info!(
|
||||
context, 0,
|
||||
"================================================================================"
|
||||
);
|
||||
|
||||
i -= 1
|
||||
}
|
||||
}
|
||||
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
@@ -680,109 +658,83 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
println!("{} chats", cnt);
|
||||
}
|
||||
"chat" => {
|
||||
if sel_chat.is_null() && arg1.is_empty() {
|
||||
if sel_chat.is_none() && arg1.is_empty() {
|
||||
bail!("Argument [chat-id] is missing.");
|
||||
}
|
||||
if !sel_chat.is_null() && !arg1.is_empty() {
|
||||
dc_chat_unref(sel_chat);
|
||||
}
|
||||
if !arg1.is_empty() {
|
||||
let chat_id = arg1.parse()?;
|
||||
println!("Selecting chat #{}", chat_id);
|
||||
sel_chat = dc_get_chat(context, chat_id);
|
||||
sel_chat = Some(Chat::load_from_db(context, chat_id)?);
|
||||
*context.cmdline_sel_chat_id.write().unwrap() = chat_id;
|
||||
}
|
||||
|
||||
ensure!(!sel_chat.is_null(), "Failed to select chat");
|
||||
ensure!(sel_chat.is_some(), "Failed to select chat");
|
||||
let sel_chat = sel_chat.as_ref().unwrap();
|
||||
|
||||
let msglist = dc_get_chat_msgs(context, dc_chat_get_id(sel_chat), 0x1, 0);
|
||||
let temp2 = dc_chat_get_subtitle(sel_chat);
|
||||
let temp_name = dc_chat_get_name(sel_chat);
|
||||
let msglist = chat::get_chat_msgs(context, sel_chat.get_id(), 0x1, 0);
|
||||
let temp2 = sel_chat.get_subtitle();
|
||||
let temp_name = sel_chat.get_name();
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}#{}: {} [{}]{}",
|
||||
chat_prefix(sel_chat),
|
||||
dc_chat_get_id(sel_chat),
|
||||
as_str(temp_name),
|
||||
as_str(temp2),
|
||||
if 0 != dc_chat_is_sending_locations(sel_chat) {
|
||||
sel_chat.get_id(),
|
||||
temp_name,
|
||||
temp2,
|
||||
if sel_chat.is_sending_locations() {
|
||||
"📍"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
);
|
||||
free(temp_name as *mut libc::c_void);
|
||||
free(temp2 as *mut libc::c_void);
|
||||
if !msglist.is_null() {
|
||||
log_msglist(context, msglist);
|
||||
dc_array_unref(msglist);
|
||||
}
|
||||
let draft = dc_get_draft(context, dc_chat_get_id(sel_chat));
|
||||
log_msglist(context, &msglist);
|
||||
let draft = chat::get_draft(context, sel_chat.get_id());
|
||||
if !draft.is_null() {
|
||||
log_msg(context, "Draft", draft);
|
||||
dc_msg_unref(draft);
|
||||
}
|
||||
println!(
|
||||
"{} messages.",
|
||||
dc_get_msg_cnt(context, dc_chat_get_id(sel_chat))
|
||||
chat::get_msg_cnt(context, sel_chat.get_id())
|
||||
);
|
||||
dc_marknoticed_chat(context, dc_chat_get_id(sel_chat));
|
||||
chat::marknoticed_chat(context, sel_chat.get_id())?;
|
||||
}
|
||||
"createchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
let contact_id: libc::c_int = arg1.parse()?;
|
||||
let chat_id: libc::c_int =
|
||||
dc_create_chat_by_contact_id(context, contact_id as uint32_t) as libc::c_int;
|
||||
if chat_id != 0 {
|
||||
println!("Single#{} created successfully.", chat_id,);
|
||||
} else {
|
||||
bail!("Failed to create chat");
|
||||
}
|
||||
let chat_id = chat::create_by_contact_id(context, contact_id as uint32_t)?;
|
||||
|
||||
println!("Single#{} created successfully.", chat_id,);
|
||||
}
|
||||
"createchatbymsg" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing");
|
||||
let msg_id_0: libc::c_int = arg1.parse()?;
|
||||
let chat_id_0: libc::c_int =
|
||||
dc_create_chat_by_msg_id(context, msg_id_0 as uint32_t) as libc::c_int;
|
||||
if chat_id_0 != 0 {
|
||||
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
|
||||
println!(
|
||||
"{}#{} created successfully.",
|
||||
chat_prefix(chat_0),
|
||||
chat_id_0,
|
||||
);
|
||||
dc_chat_unref(chat_0);
|
||||
} else {
|
||||
bail!("");
|
||||
}
|
||||
let msg_id: u32 = arg1.parse()?;
|
||||
let chat_id = chat::create_by_msg_id(context, msg_id)?;
|
||||
let chat = Chat::load_from_db(context, chat_id)?;
|
||||
|
||||
println!("{}#{} created successfully.", chat_prefix(&chat), chat_id,);
|
||||
}
|
||||
"creategroup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id_1: libc::c_int = dc_create_group_chat(context, 0, arg1_c) as libc::c_int;
|
||||
if chat_id_1 != 0 {
|
||||
println!("Group#{} created successfully.", chat_id_1,);
|
||||
} else {
|
||||
bail!("Failed to create group");
|
||||
}
|
||||
let chat_id = chat::create_group_chat(context, VerifiedStatus::Unverified, arg1)?;
|
||||
|
||||
println!("Group#{} created successfully.", chat_id);
|
||||
}
|
||||
"createverified" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id_2: libc::c_int = dc_create_group_chat(context, 1, arg1_c) as libc::c_int;
|
||||
if chat_id_2 != 0 {
|
||||
println!("VerifiedGroup#{} created successfully.", chat_id_2,);
|
||||
} else {
|
||||
bail!("Failed to create verified group");
|
||||
}
|
||||
let chat_id = chat::create_group_chat(context, VerifiedStatus::Verified, arg1)?;
|
||||
|
||||
println!("VerifiedGroup#{} created successfully.", chat_id);
|
||||
}
|
||||
"addmember" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected");
|
||||
ensure!(sel_chat.is_some(), "No chat selected");
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
|
||||
let contact_id_0: libc::c_int = arg1.parse()?;
|
||||
if 0 != dc_add_contact_to_chat(
|
||||
if 0 != chat::add_contact_to_chat(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id_0 as uint32_t,
|
||||
) {
|
||||
println!("Contact added to chat.");
|
||||
@@ -791,101 +743,89 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
"removemember" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <contact-id> missing.");
|
||||
let contact_id_1: libc::c_int = arg1.parse()?;
|
||||
if 0 != dc_remove_contact_from_chat(
|
||||
chat::remove_contact_from_chat(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id_1 as uint32_t,
|
||||
) {
|
||||
println!("Contact added to chat.");
|
||||
} else {
|
||||
bail!("Cannot remove member from chat.");
|
||||
}
|
||||
)?;
|
||||
|
||||
println!("Contact added to chat.");
|
||||
}
|
||||
"groupname" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
if 0 != dc_set_chat_name(context, dc_chat_get_id(sel_chat), arg1_c) {
|
||||
println!("Chat name set");
|
||||
} else {
|
||||
bail!("Failed to set chat name");
|
||||
}
|
||||
chat::set_chat_name(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
|
||||
|
||||
println!("Chat name set");
|
||||
}
|
||||
"groupimage" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "Argument <image> missing.");
|
||||
|
||||
if 0 != dc_set_chat_profile_image(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
if !arg1.is_empty() {
|
||||
arg1_c
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
},
|
||||
) {
|
||||
println!("Chat image set");
|
||||
} else {
|
||||
bail!("Failed to set chat image");
|
||||
}
|
||||
chat::set_chat_profile_image(context, sel_chat.as_ref().unwrap().get_id(), arg1)?;
|
||||
|
||||
println!("Chat image set");
|
||||
}
|
||||
"chatinfo" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contacts = dc_get_chat_contacts(context, dc_chat_get_id(sel_chat));
|
||||
ensure!(!contacts.is_null(), "Failed to retreive contacts");
|
||||
let contacts = chat::get_chat_contacts(context, sel_chat.as_ref().unwrap().get_id());
|
||||
info!(context, 0, "Memberlist:");
|
||||
|
||||
log_contactlist(context, contacts);
|
||||
log_contactlist(context, &contacts);
|
||||
println!(
|
||||
"{} contacts\nLocation streaming: {}",
|
||||
dc_array_get_cnt(contacts),
|
||||
dc_is_sending_locations_to_chat(context, dc_chat_get_id(sel_chat)),
|
||||
contacts.len(),
|
||||
dc_is_sending_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id()),
|
||||
);
|
||||
dc_array_unref(contacts);
|
||||
}
|
||||
"getlocations" => {
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let contact_id = arg1.parse().unwrap_or_default();
|
||||
let loc = dc_get_locations(context, dc_chat_get_id(sel_chat), contact_id, 0, 0);
|
||||
let mut j = 0;
|
||||
while j < dc_array_get_cnt(loc) {
|
||||
let timestr_0 = dc_timestamp_to_str(dc_array_get_timestamp(loc, j as size_t));
|
||||
let marker = dc_array_get_marker(loc, j as size_t);
|
||||
let locations = dc_get_locations(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
contact_id,
|
||||
0,
|
||||
0,
|
||||
);
|
||||
let default_marker = "-".to_string();
|
||||
for location in &locations {
|
||||
let marker = location.marker.as_ref().unwrap_or(&default_marker);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}",
|
||||
dc_array_get_id(loc, j as size_t),
|
||||
×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),
|
||||
dc_array_get_chat_id(loc, j as size_t),
|
||||
dc_array_get_contact_id(loc, j as size_t),
|
||||
dc_array_get_msg_id(loc, j as size_t),
|
||||
if !marker.is_null() {
|
||||
as_str(marker)
|
||||
} else {
|
||||
"-"
|
||||
},
|
||||
location.location_id,
|
||||
dc_timestamp_to_str(location.timestamp),
|
||||
location.latitude,
|
||||
location.longitude,
|
||||
location.accuracy,
|
||||
location.chat_id,
|
||||
location.contact_id,
|
||||
location.msg_id,
|
||||
marker
|
||||
);
|
||||
free(marker as *mut libc::c_void);
|
||||
j += 1
|
||||
}
|
||||
if dc_array_get_cnt(loc) == 0 {
|
||||
if locations.is_empty() {
|
||||
info!(context, 0, "No locations.");
|
||||
}
|
||||
dc_array_unref(loc);
|
||||
}
|
||||
"sendlocations" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "No timeout given.");
|
||||
|
||||
let seconds = arg1.parse()?;
|
||||
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
|
||||
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
|
||||
dc_send_locations_to_chat(context, sel_chat.as_ref().unwrap().get_id(), seconds);
|
||||
println!(
|
||||
"Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.",
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
seconds
|
||||
);
|
||||
}
|
||||
"setlocation" => {
|
||||
ensure!(
|
||||
@@ -906,27 +846,19 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
dc_delete_all_locations(context);
|
||||
}
|
||||
"send" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty(), "No message text given.");
|
||||
|
||||
let msg = format!("{} {}", arg1, arg2);
|
||||
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
|
||||
println!("Message sent.");
|
||||
} else {
|
||||
bail!("Sending failed.");
|
||||
}
|
||||
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), msg)?;
|
||||
}
|
||||
"sendempty" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) {
|
||||
println!("Message sent.");
|
||||
} else {
|
||||
bail!("Sending failed.");
|
||||
}
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
chat::send_text_msg(context, sel_chat.as_ref().unwrap().get_id(), "".into())?;
|
||||
}
|
||||
"sendimage" | "sendfile" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
||||
|
||||
let msg_0 = dc_msg_new(
|
||||
@@ -939,54 +871,53 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
);
|
||||
dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char);
|
||||
dc_msg_set_text(msg_0, arg2_c);
|
||||
dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0);
|
||||
chat::send_msg(context, sel_chat.as_ref().unwrap().get_id(), msg_0)?;
|
||||
dc_msg_unref(msg_0);
|
||||
}
|
||||
"listmsgs" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <query> missing.");
|
||||
|
||||
let chat = if !sel_chat.is_null() {
|
||||
dc_chat_get_id(sel_chat)
|
||||
let chat = if let Some(ref sel_chat) = sel_chat {
|
||||
sel_chat.get_id()
|
||||
} else {
|
||||
0 as libc::c_uint
|
||||
};
|
||||
|
||||
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
|
||||
let msglist = dc_search_msgs(context, chat, arg1_c);
|
||||
|
||||
if !msglist_0.is_null() {
|
||||
log_msglist(context, msglist_0);
|
||||
println!("{} messages.", dc_array_get_cnt(msglist_0));
|
||||
dc_array_unref(msglist_0);
|
||||
}
|
||||
log_msglist(context, &msglist);
|
||||
println!("{} messages.", msglist.len());
|
||||
}
|
||||
"draft" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
if !arg1.is_empty() {
|
||||
let draft_0 = dc_msg_new(context, Viewtype::Text);
|
||||
dc_msg_set_text(draft_0, arg1_c);
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
|
||||
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), draft_0);
|
||||
dc_msg_unref(draft_0);
|
||||
println!("Draft saved.");
|
||||
} else {
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), 0 as *mut dc_msg_t);
|
||||
chat::set_draft(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
0 as *mut dc_msg_t,
|
||||
);
|
||||
println!("Draft deleted.");
|
||||
}
|
||||
}
|
||||
"listmedia" => {
|
||||
ensure!(!sel_chat.is_null(), "No chat selected.");
|
||||
ensure!(sel_chat.is_some(), "No chat selected.");
|
||||
|
||||
let images = dc_get_chat_media(
|
||||
let images = chat::get_chat_media(
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
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 {
|
||||
let data = dc_array_get_id(images, i as size_t);
|
||||
println!("{} images or videos: ", images.len());
|
||||
for (i, data) in images.iter().enumerate() {
|
||||
if 0 == i {
|
||||
print!("Msg#{}", data);
|
||||
} else {
|
||||
@@ -994,17 +925,20 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
}
|
||||
print!("\n");
|
||||
dc_array_unref(images);
|
||||
}
|
||||
"archive" | "unarchive" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
dc_archive_chat(context, chat_id, if arg0 == "archive" { 1 } else { 0 });
|
||||
chat::archive(
|
||||
context,
|
||||
chat_id,
|
||||
if arg0 == "archive" { true } else { false },
|
||||
)?;
|
||||
}
|
||||
"delchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = arg1.parse()?;
|
||||
dc_delete_chat(context, chat_id);
|
||||
chat::delete(context, chat_id)?;
|
||||
}
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
@@ -1014,11 +948,9 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"listfresh" => {
|
||||
let msglist = dc_get_fresh_msgs(context);
|
||||
ensure!(!msglist.is_null(), "Failed to retrieve messages");
|
||||
|
||||
log_msglist(context, msglist);
|
||||
print!("{} fresh messages.", dc_array_get_cnt(msglist));
|
||||
dc_array_unref(msglist);
|
||||
log_msglist(context, &msglist);
|
||||
print!("{} fresh messages.", msglist.len());
|
||||
}
|
||||
"forward" => {
|
||||
ensure!(
|
||||
@@ -1029,7 +961,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let mut msg_ids = [0; 1];
|
||||
let chat_id = arg2.parse()?;
|
||||
msg_ids[0] = arg1.parse()?;
|
||||
dc_forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
||||
chat::forward_msgs(context, msg_ids.as_mut_ptr(), 1, chat_id);
|
||||
}
|
||||
"markseen" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
@@ -1064,13 +996,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
Some(arg1),
|
||||
)?;
|
||||
if !contacts.is_null() {
|
||||
log_contactlist(context, contacts);
|
||||
println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,);
|
||||
dc_array_unref(contacts);
|
||||
} else {
|
||||
bail!("");
|
||||
}
|
||||
log_contactlist(context, &contacts);
|
||||
println!("{} contacts.", contacts.len());
|
||||
}
|
||||
"addcontact" => {
|
||||
ensure!(!arg1.is_empty(), "Arguments [<name>] <addr> expected.");
|
||||
@@ -1091,7 +1018,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
let mut res = format!("Contact info for: {}:\n\n", name_n_addr);
|
||||
|
||||
res += &Contact::get_encrinfo(context, contact_id);
|
||||
res += &Contact::get_encrinfo(context, contact_id)?;
|
||||
|
||||
let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?;
|
||||
let chatlist_cnt = chatlist.len();
|
||||
@@ -1104,9 +1031,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
if 0 != i {
|
||||
res += ", ";
|
||||
}
|
||||
let chat = dc_get_chat(context, chatlist.get_chat_id(i));
|
||||
res += &format!("{}#{}", chat_prefix(chat), dc_chat_get_id(chat));
|
||||
dc_chat_unref(chat);
|
||||
let chat = Chat::load_from_db(context, chatlist.get_chat_id(i))?;
|
||||
res += &format!("{}#{}", chat_prefix(&chat), chat.get_id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1118,15 +1044,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"checkqr" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <qr-content> missing.");
|
||||
let res = dc_check_qr(context, arg1_c);
|
||||
let res = check_qr(context, arg1);
|
||||
println!(
|
||||
"state={}, id={}, text1={}, text2={}",
|
||||
(*res).state as libc::c_int,
|
||||
(*res).id,
|
||||
to_string((*res).text1),
|
||||
to_string((*res).text2)
|
||||
"state={}, id={}, text1={:?}, text2={:?}",
|
||||
res.get_state(),
|
||||
res.get_id(),
|
||||
res.get_text1(),
|
||||
res.get_text2()
|
||||
);
|
||||
dc_lot_unref(res);
|
||||
}
|
||||
"event" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <id> missing.");
|
||||
@@ -1140,20 +1065,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"fileinfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing.");
|
||||
let mut buf = 0 as *mut libc::c_uchar;
|
||||
let mut buf_bytes = 0;
|
||||
let mut w = 0;
|
||||
let mut h = 0;
|
||||
|
||||
if 0 != dc_read_file(
|
||||
context,
|
||||
arg1_c,
|
||||
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
|
||||
&mut buf_bytes,
|
||||
) {
|
||||
dc_get_filemeta(buf as *const libc::c_void, buf_bytes, &mut w, &mut h);
|
||||
println!("width={}, height={}", w, h,);
|
||||
free(buf as *mut libc::c_void);
|
||||
if let Some(buf) = dc_read_file_safe(context, &arg1) {
|
||||
let (width, height) = dc_get_filemeta(&buf)?;
|
||||
println!("width={}, height={}", width, height);
|
||||
} else {
|
||||
bail!("Command failed.");
|
||||
}
|
||||
@@ -1162,10 +1077,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
_ => bail!("Unknown command: \"{}\" type ? for help.", arg0),
|
||||
}
|
||||
|
||||
if !sel_chat.is_null() {
|
||||
dc_chat_unref(sel_chat);
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
free(arg2_c as *mut _);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
//!
|
||||
//! Usage: cargo run --example repl --release -- <databasefile>
|
||||
//! All further options can be set using the set-command (type ? for help).
|
||||
#![feature(ptr_cast)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate deltachat;
|
||||
@@ -22,9 +21,9 @@ use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_job::*;
|
||||
use deltachat::dc_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
@@ -173,13 +172,11 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_imap = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe {
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
dc_perform_imap_fetch(&ctx.read().unwrap());
|
||||
}
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
perform_imap_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
let context = ctx.read().unwrap();
|
||||
dc_perform_imap_idle(&context);
|
||||
perform_imap_idle(&context);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -187,9 +184,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_mvbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_mvbox_fetch(&ctx.read().unwrap()) };
|
||||
perform_mvbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
unsafe { dc_perform_mvbox_idle(&ctx.read().unwrap()) };
|
||||
perform_mvbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -197,9 +194,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c.clone();
|
||||
let handle_sentbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_sentbox_fetch(&ctx.read().unwrap()) };
|
||||
perform_sentbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
unsafe { dc_perform_sentbox_idle(&ctx.read().unwrap()) };
|
||||
perform_sentbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -207,9 +204,9 @@ fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
let ctx = c;
|
||||
let handle_smtp = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
unsafe { dc_perform_smtp_jobs(&ctx.read().unwrap()) };
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
unsafe { dc_perform_smtp_idle(&ctx.read().unwrap()) };
|
||||
perform_smtp_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -227,12 +224,10 @@ fn stop_threads(context: &Context) {
|
||||
println!("Stopping threads");
|
||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||
|
||||
unsafe {
|
||||
dc_interrupt_imap_idle(context);
|
||||
dc_interrupt_mvbox_idle(context);
|
||||
dc_interrupt_sentbox_idle(context);
|
||||
dc_interrupt_smtp_idle(context);
|
||||
}
|
||||
interrupt_imap_idle(context);
|
||||
interrupt_mvbox_idle(context);
|
||||
interrupt_sentbox_idle(context);
|
||||
interrupt_smtp_idle(context);
|
||||
|
||||
handle.handle_imap.take().unwrap().join().unwrap();
|
||||
handle.handle_mvbox.take().unwrap().join().unwrap();
|
||||
@@ -335,8 +330,8 @@ const CONTACT_COMMANDS: [&'static str; 6] = [
|
||||
"delcontact",
|
||||
"cleanupcontacts",
|
||||
];
|
||||
const MISC_COMMANDS: [&'static str; 8] = [
|
||||
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "help",
|
||||
const MISC_COMMANDS: [&'static str; 9] = [
|
||||
"getqr", "getbadqr", "checkqr", "event", "fileinfo", "clear", "exit", "quit", "help",
|
||||
];
|
||||
|
||||
impl Hinter for DcHelper {
|
||||
@@ -456,12 +451,6 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
println!("history saved");
|
||||
{
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
|
||||
unsafe {
|
||||
let mut ctx = ctx.write().unwrap();
|
||||
dc_close(&mut ctx);
|
||||
dc_context_unref(&mut ctx);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -494,14 +483,14 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("smtp-jobs are already running in a thread.",);
|
||||
} else {
|
||||
dc_perform_smtp_jobs(&ctx.read().unwrap());
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"imap-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("imap-jobs are already running in a thread.");
|
||||
} else {
|
||||
dc_perform_imap_jobs(&ctx.read().unwrap());
|
||||
perform_imap_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"configure" => {
|
||||
@@ -556,7 +545,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
|
||||
}
|
||||
}
|
||||
"exit" => return Ok(ExitResult::Exit),
|
||||
"exit" | "quit" => return Ok(ExitResult::Exit),
|
||||
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
|
||||
}
|
||||
|
||||
|
||||
@@ -5,18 +5,16 @@ use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::chat;
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::constants::Event;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_job::{
|
||||
dc_perform_imap_fetch, dc_perform_imap_idle, dc_perform_imap_jobs, dc_perform_smtp_idle,
|
||||
dc_perform_smtp_jobs,
|
||||
use deltachat::job::{
|
||||
perform_imap_fetch, perform_imap_idle, perform_imap_jobs, perform_smtp_idle, perform_smtp_jobs,
|
||||
};
|
||||
use deltachat::dc_lot::*;
|
||||
|
||||
extern "C" fn cb(_ctx: &Context, event: Event, data1: usize, data2: usize) -> usize {
|
||||
println!("[{:?}]", event);
|
||||
@@ -53,12 +51,12 @@ fn main() {
|
||||
let r1 = running.clone();
|
||||
let t1 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
dc_perform_imap_jobs(&ctx1);
|
||||
perform_imap_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
dc_perform_imap_fetch(&ctx1);
|
||||
perform_imap_fetch(&ctx1);
|
||||
|
||||
if *r1.read().unwrap() {
|
||||
dc_perform_imap_idle(&ctx1);
|
||||
perform_imap_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,9 +66,9 @@ fn main() {
|
||||
let r1 = running.clone();
|
||||
let t2 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
dc_perform_smtp_jobs(&ctx1);
|
||||
perform_smtp_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
dc_perform_smtp_idle(&ctx1);
|
||||
perform_smtp_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -96,29 +94,17 @@ fn main() {
|
||||
println!("sending a message");
|
||||
let contact_id =
|
||||
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id);
|
||||
dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into());
|
||||
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
|
||||
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
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);
|
||||
|
||||
let text1_s = if !text1.is_null() {
|
||||
Some(CStr::from_ptr(text1))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let text2_s = if !text2.is_null() {
|
||||
Some(CStr::from_ptr(text2))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
println!("chat: {} - {:?} - {:?}", i, text1_s, text2_s,);
|
||||
dc_lot_unref(summary);
|
||||
let summary = chats.get_summary(0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
}
|
||||
|
||||
thread::sleep(duration);
|
||||
@@ -136,8 +122,8 @@ fn main() {
|
||||
println!("stopping threads");
|
||||
|
||||
*running.clone().write().unwrap() = false;
|
||||
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
|
||||
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);
|
||||
deltachat::job::interrupt_imap_idle(&ctx);
|
||||
deltachat::job::interrupt_smtp_idle(&ctx);
|
||||
|
||||
println!("joining");
|
||||
t1.join().unwrap();
|
||||
|
||||
@@ -15,7 +15,7 @@ without any "build-from-source" steps.
|
||||
1. `Install virtualenv <https://virtualenv.pypa.io/en/stable/installation/>`_,
|
||||
then create a fresh python environment and activate it in your shell::
|
||||
|
||||
virtualenv -p python3 venv
|
||||
virtualenv venv # or: python -m venv
|
||||
source venv/bin/activate
|
||||
|
||||
Afterwards, invoking ``python`` or ``pip install`` will only
|
||||
@@ -39,6 +39,12 @@ and push them to a python package index. To install the latest github ``master``
|
||||
|
||||
pip install -i https://m.devpi.net/dc/master deltachat
|
||||
|
||||
.. note::
|
||||
|
||||
If you can help to automate the building of wheels for Mac or Windows,
|
||||
that'd be much appreciated! please then get
|
||||
`in contact with us <https://delta.chat/en/contribute>`_.
|
||||
|
||||
|
||||
Installing bindings from source
|
||||
===============================
|
||||
@@ -48,34 +54,55 @@ to core deltachat library::
|
||||
|
||||
git clone https://github.com/deltachat/deltachat-core-rust
|
||||
cd deltachat-core-rust
|
||||
cargo build -p deltachat_ffi --release
|
||||
|
||||
This will result in a ``libdeltachat.so`` and ``libdeltachat.a`` files
|
||||
in the ``target/release`` directory. These files are needed for
|
||||
creating the python bindings for deltachat::
|
||||
|
||||
cd python
|
||||
DCC_RS_DEV=`pwd`/.. pip install -e .
|
||||
|
||||
Now test if the bindings find the correct library::
|
||||
If you don't have one active, create and activate a python "virtualenv":
|
||||
|
||||
python -c 'import deltachat ; print(deltachat.__version__)'
|
||||
python virtualenv venv # or python -m venv
|
||||
source venv/bin/activate
|
||||
|
||||
This should print your deltachat bindings version.
|
||||
Afterwards ``which python`` tells you that it comes out of the "venv"
|
||||
directory that contains all python install artifacts. Let's first
|
||||
install test tools::
|
||||
|
||||
pip install pytest pytest-timeout requests
|
||||
|
||||
then cargo-build and install the deltachat bindings::
|
||||
|
||||
python install_python_bindings.py
|
||||
|
||||
The bindings will be installed in release mode but with debug symbols.
|
||||
The release mode is neccessary because some tests generate RSA keys
|
||||
which is prohibitively slow in debug mode.
|
||||
|
||||
After succcessul binding installation you can finally run the tests::
|
||||
|
||||
pytest -v tests
|
||||
|
||||
.. note::
|
||||
|
||||
If you can help to automate the building of wheels for Mac or Windows,
|
||||
that'd be much appreciated! please then get
|
||||
`in contact with us <https://delta.chat/en/contribute>`_.
|
||||
Some tests are sometimes failing/hanging because of
|
||||
https://github.com/deltachat/deltachat-core-rust/issues/331
|
||||
and
|
||||
https://github.com/deltachat/deltachat-core-rust/issues/326
|
||||
|
||||
Using a system-installed deltachat-core-rust
|
||||
--------------------------------------------
|
||||
|
||||
When calling ``pip`` without specifying the ``DCC_RS_DEV`` environment
|
||||
variable cffi will try to use a ``deltachat.h`` from a system location
|
||||
like ``/usr/local/include`` and will try to dynamically link against a
|
||||
``libdeltachat.so`` in a similar location (e.g. ``/usr/local/lib``).
|
||||
running "live" tests (experimental)
|
||||
-----------------------------------
|
||||
|
||||
If you want to run "liveconfig" functional tests you can set
|
||||
``DCC_PY_LIVECONFIG`` to:
|
||||
|
||||
- a particular https-url that you can ask for from the delta
|
||||
chat devs.
|
||||
|
||||
- or the path of a file that contains two lines, each describing
|
||||
via "addr=... mail_pwd=..." a test account login that will
|
||||
be used for the live tests.
|
||||
|
||||
With ``DCC_PY_LIVECONFIG`` set pytest invocations will use real
|
||||
e-mail accounts and run through all functional "liveconfig" tests.
|
||||
|
||||
|
||||
|
||||
Code examples
|
||||
@@ -84,68 +111,34 @@ Code examples
|
||||
You may look at `examples <https://py.delta.chat/examples.html>`_.
|
||||
|
||||
|
||||
Running tests
|
||||
=============
|
||||
|
||||
Get a checkout of the `deltachat-core-rust github repository`_ and type::
|
||||
|
||||
pip install tox
|
||||
./run-integration-tests.sh
|
||||
|
||||
If you want to run functional tests with real
|
||||
e-mail test accounts, generate a "liveconfig" file where each
|
||||
lines contains test account settings, for example::
|
||||
|
||||
# 'liveconfig' file specifying imap/smtp accounts
|
||||
addr=some-email@example.org mail_pw=password
|
||||
addr=other-email@example.org mail_pw=otherpassword
|
||||
|
||||
The "keyword=value" style allows to specify any
|
||||
`deltachat account config setting <https://c.delta.chat/classdc__context__t.html#aff3b894f6cfca46cab5248fdffdf083d>`_ so you can also specify smtp or imap servers, ports, ssl modes etc.
|
||||
Typically DC's automatic configuration allows to not specify these settings.
|
||||
|
||||
The ``run-integration-tests.sh`` script will automatically use
|
||||
``python/liveconfig`` if it exists, to manually run tests with this
|
||||
``liveconfig`` file use::
|
||||
|
||||
tox -- --liveconfig liveconfig
|
||||
|
||||
|
||||
.. _`deltachat-core-rust github repository`: https://github.com/deltachat/deltachat-core-rust
|
||||
.. _`deltachat-core`: https://github.com/deltachat/deltachat-core-rust
|
||||
|
||||
Running test using a debug build
|
||||
--------------------------------
|
||||
|
||||
If you need to examine e.g. a coredump you may want to run the tests
|
||||
using a debug build::
|
||||
|
||||
DCC_RS_TARGET=debug ./run-integration-tests.sh -e py37 -- -x -v -k failing_test
|
||||
|
||||
|
||||
Building manylinux1 wheels
|
||||
==========================
|
||||
|
||||
.. note::
|
||||
|
||||
This section may not fully work.
|
||||
|
||||
Building portable manylinux1 wheels which come with libdeltachat.so
|
||||
and all it's dependencies is easy using the provided docker tooling.
|
||||
|
||||
using docker pull / premade images
|
||||
------------------------------------
|
||||
|
||||
We publish a build environment under the ``deltachat/wheel`` tag so
|
||||
We publish a build environment under the ``deltachat/coredeps`` tag so
|
||||
that you can pull it from the ``hub.docker.com`` site's "deltachat"
|
||||
organization::
|
||||
|
||||
$ docker pull deltachat/wheel
|
||||
$ docker pull deltachat/coredeps
|
||||
|
||||
The ``deltachat/wheel`` image can be used to build both libdeltachat.so
|
||||
and the Python wheels::
|
||||
This docker image can be used to run tests and build Python wheels for all interpreters::
|
||||
|
||||
$ docker run --rm -it -v $(pwd):/io/ deltachat/wheel /io/python/wheelbuilder/build-wheels.sh
|
||||
$ bash ci_scripts/ci_run.sh
|
||||
|
||||
This command runs a script within the image, after mounting ``$(pwd)`` as ``/io`` within
|
||||
the docker image. The script is specified as a path within the docker image's filesystem.
|
||||
The resulting wheel files will be in ``python/wheelhouse``.
|
||||
This command runs tests and build-wheel scripts in a docker container.
|
||||
|
||||
|
||||
Optionally build your own docker image
|
||||
@@ -154,10 +147,10 @@ Optionally build your own docker image
|
||||
If you want to build your own custom docker image you can do this::
|
||||
|
||||
$ cd deltachat-core # cd to deltachat-core checkout directory
|
||||
$ docker build -t deltachat/wheel python/wheelbuilder/
|
||||
$ docker build -t deltachat/coredeps ci_scripts/docker_coredeps
|
||||
|
||||
This will use the ``python/wheelbuilder/Dockerfile`` to build
|
||||
up docker image called ``deltachat/wheel``. You can afterwards
|
||||
This will use the ``ci_scripts/docker_coredeps/Dockerfile`` to build
|
||||
up docker image called ``deltachat/coredeps``. You can afterwards
|
||||
find it with::
|
||||
|
||||
$ docker images
|
||||
|
||||
@@ -37,16 +37,3 @@ Message
|
||||
.. autoclass:: deltachat.message.Message
|
||||
:members:
|
||||
|
||||
MessageType
|
||||
------------
|
||||
|
||||
.. autoclass:: deltachat.message.MessageType
|
||||
:members:
|
||||
|
||||
MessageState
|
||||
------------
|
||||
|
||||
.. autoclass:: deltachat.message.MessageState
|
||||
:members:
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ Playing around on the commandline
|
||||
----------------------------------
|
||||
|
||||
Once you have :doc:`installed deltachat bindings <install>`
|
||||
you can start playing from the python interpreter commandline::
|
||||
|
||||
you can start playing from the python interpreter commandline.
|
||||
For example you can type ``python`` and then::
|
||||
|
||||
# instantiate and configure deltachat account
|
||||
@@ -23,7 +22,7 @@ For example you can type ``python`` and then::
|
||||
# create a contact and send a message
|
||||
contact = ac.create_contact("someother@email.address")
|
||||
chat = ac.create_chat_by_contact(contact)
|
||||
chat.send_text_message("hi from the python interpreter command line")
|
||||
chat.send_text("hi from the python interpreter command line")
|
||||
|
||||
Checkout our :doc:`api` for the various high-level things you can do
|
||||
to send/receive messages, create contacts and chats.
|
||||
|
||||
@@ -6,29 +6,18 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ["DCC_RS_TARGET"] = target = "release"
|
||||
if "DCC_RS_DEV" not in os.environ:
|
||||
dn = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
os.environ["DCC_RS_DEV"] = dn
|
||||
|
||||
toml = os.path.join(os.getcwd(), "..", "Cargo.toml")
|
||||
assert os.path.exists(toml)
|
||||
with open(toml) as f:
|
||||
s = orig = f.read()
|
||||
s += "\n"
|
||||
s += "[profile.release]\n"
|
||||
s += "debug = true\n"
|
||||
with open(toml, "w") as f:
|
||||
f.write(s)
|
||||
print("temporarily modifying Cargo.toml to provide release build with debug symbols ")
|
||||
try:
|
||||
subprocess.check_call([
|
||||
"cargo", "build", "-p", "deltachat_ffi", "--" + target
|
||||
])
|
||||
finally:
|
||||
with open(toml, "w") as f:
|
||||
f.write(orig)
|
||||
print("\nreseted Cargo.toml to previous original state")
|
||||
|
||||
os.environ["RUSTFLAGS"] = "-g"
|
||||
subprocess.check_call([
|
||||
"cargo", "build", "-p", "deltachat_ffi", "--" + target
|
||||
])
|
||||
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
|
||||
|
||||
subprocess.check_call([
|
||||
|
||||
@@ -34,13 +34,14 @@ def py_dc_callback(ctx, evt, data1, data2):
|
||||
if data1 and event_sig_types & 1:
|
||||
data1 = ffi.string(ffi.cast('char*', data1)).decode("utf8")
|
||||
if data2 and event_sig_types & 2:
|
||||
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
|
||||
try:
|
||||
data2 = ffi.string(ffi.cast('char*', data2)).decode("utf8")
|
||||
if isinstance(data2, bytes):
|
||||
data2 = data2.decode("utf8")
|
||||
except UnicodeDecodeError:
|
||||
# XXX ignoring this error is not quite correct but for now
|
||||
# XXX ignoring the decode error is not quite correct but for now
|
||||
# i don't want to hunt down encoding problems in the c lib
|
||||
data2 = ffi.string(ffi.cast('char*', data2))
|
||||
|
||||
pass
|
||||
try:
|
||||
ret = callback(ctx, evt_name, data1, data2)
|
||||
if ret is None:
|
||||
|
||||
@@ -6,10 +6,15 @@ import platform
|
||||
import os
|
||||
import cffi
|
||||
import shutil
|
||||
from os.path import dirname as dn
|
||||
from os.path import abspath
|
||||
|
||||
|
||||
def ffibuilder():
|
||||
projdir = os.environ.get('DCC_RS_DEV')
|
||||
if not projdir:
|
||||
p = dn(dn(dn(dn(abspath(__file__)))))
|
||||
projdir = os.environ["DCC_RS_DEV"] = p
|
||||
target = os.environ.get('DCC_RS_TARGET', 'release')
|
||||
if projdir:
|
||||
if platform.system() == 'Darwin':
|
||||
|
||||
@@ -50,8 +50,9 @@ class Account(object):
|
||||
self._configkeys = self.get_config("sys.config_keys").split()
|
||||
self._imex_completed = threading.Event()
|
||||
|
||||
def __del__(self):
|
||||
self.shutdown()
|
||||
# XXX this can cause "illegal instructions" at test ends so we omit it for now
|
||||
# def __del__(self):
|
||||
# self.shutdown()
|
||||
|
||||
def _check_config_key(self, name):
|
||||
if name not in self._configkeys:
|
||||
|
||||
@@ -16,4 +16,4 @@ def iter_array(dc_array_t, constructor):
|
||||
|
||||
|
||||
def from_dc_charpointer(obj):
|
||||
return ffi.string(obj).decode("utf8")
|
||||
return ffi.string(ffi.gc(obj, lib.dc_str_unref)).decode("utf8")
|
||||
|
||||
@@ -142,7 +142,7 @@ class Message(object):
|
||||
import email.parser
|
||||
mime_headers = lib.dc_get_mime_headers(self._dc_context, self.id)
|
||||
if mime_headers:
|
||||
s = ffi.string(mime_headers)
|
||||
s = ffi.string(ffi.gc(mime_headers, lib.dc_str_unref))
|
||||
if isinstance(s, bytes):
|
||||
s = s.decode("ascii")
|
||||
return email.message_from_string(s)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import pytest
|
||||
import requests
|
||||
import time
|
||||
from deltachat import Account
|
||||
from deltachat import props
|
||||
from deltachat.capi import lib
|
||||
import tempfile
|
||||
|
||||
@@ -36,6 +36,8 @@ def pytest_runtest_call(item):
|
||||
|
||||
|
||||
def pytest_report_header(config, startdir):
|
||||
summary = []
|
||||
|
||||
t = tempfile.mktemp()
|
||||
try:
|
||||
ac = Account(t, eventlogging=False)
|
||||
@@ -43,13 +45,18 @@ def pytest_report_header(config, startdir):
|
||||
ac.shutdown()
|
||||
finally:
|
||||
os.remove(t)
|
||||
summary = ['Deltachat core={} sqlite={}'.format(
|
||||
summary.extend(['Deltachat core={} sqlite={}'.format(
|
||||
info['deltachat_core_version'],
|
||||
info['sqlite_version'],
|
||||
)]
|
||||
cfg = config.getoption('--liveconfig')
|
||||
)])
|
||||
|
||||
cfg = config.option.liveconfig
|
||||
if cfg:
|
||||
summary.append('Liveconfig: {}'.format(os.path.abspath(cfg)))
|
||||
if "#" in cfg:
|
||||
url, token = cfg.split("#", 1)
|
||||
summary.append('Liveconfig provider: {}#<token ommitted>'.format(url))
|
||||
else:
|
||||
summary.append('Liveconfig file: {}'.format(cfg))
|
||||
return summary
|
||||
|
||||
|
||||
@@ -66,9 +73,56 @@ def data():
|
||||
return Data()
|
||||
|
||||
|
||||
class SessionLiveConfigFromFile:
|
||||
def __init__(self, fn):
|
||||
self.fn = fn
|
||||
self.configlist = []
|
||||
for line in open(fn):
|
||||
if line.strip() and not line.strip().startswith('#'):
|
||||
d = {}
|
||||
for part in line.split():
|
||||
name, value = part.split("=")
|
||||
d[name] = value
|
||||
self.configlist.append(d)
|
||||
|
||||
def get(self, index):
|
||||
return self.configlist[index]
|
||||
|
||||
def exists(self):
|
||||
return bool(self.configlist)
|
||||
|
||||
|
||||
class SessionLiveConfigFromURL:
|
||||
def __init__(self, url, create_token):
|
||||
self.configlist = []
|
||||
for i in range(2):
|
||||
res = requests.post(url, json={"token_create_user": int(create_token)})
|
||||
if res.status_code != 200:
|
||||
pytest.skip("creating newtmpuser failed {!r}".format(res))
|
||||
d = res.json()
|
||||
config = dict(addr=d["email"], mail_pw=d["password"])
|
||||
self.configlist.append(config)
|
||||
|
||||
def get(self, index):
|
||||
return self.configlist[index]
|
||||
|
||||
def exists(self):
|
||||
return bool(self.configlist)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def session_liveconfig(request):
|
||||
liveconfig_opt = request.config.option.liveconfig
|
||||
if liveconfig_opt:
|
||||
if liveconfig_opt.startswith("http"):
|
||||
url, create_token = liveconfig_opt.split("#", 1)
|
||||
return SessionLiveConfigFromURL(url, create_token)
|
||||
else:
|
||||
return SessionLiveConfigFromFile(liveconfig_opt)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def acfactory(pytestconfig, tmpdir, request):
|
||||
fn = pytestconfig.getoption("--liveconfig")
|
||||
def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||
|
||||
class AccountMaker:
|
||||
def __init__(self):
|
||||
@@ -82,18 +136,6 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
fin = self._finalizers.pop()
|
||||
fin()
|
||||
|
||||
@props.cached
|
||||
def configlist(self):
|
||||
configlist = []
|
||||
for line in open(fn):
|
||||
if line.strip() and not line.strip().startswith('#'):
|
||||
d = {}
|
||||
for part in line.split():
|
||||
name, value = part.split("=")
|
||||
d[name] = value
|
||||
configlist.append(d)
|
||||
return configlist
|
||||
|
||||
def get_unconfigured_account(self):
|
||||
self.offline_count += 1
|
||||
tmpdb = tmpdir.join("offlinedb%d" % self.offline_count)
|
||||
@@ -116,10 +158,10 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
return ac
|
||||
|
||||
def get_online_configuring_account(self):
|
||||
if not fn:
|
||||
pytest.skip("specify a --liveconfig file to run tests with real accounts")
|
||||
if not session_liveconfig:
|
||||
pytest.skip("specify DCC_PY_LIVECONFIG or --liveconfig")
|
||||
configdict = session_liveconfig.get(self.live_count)
|
||||
self.live_count += 1
|
||||
configdict = self.configlist.pop(0)
|
||||
if "e2ee_enabled" not in configdict:
|
||||
configdict["e2ee_enabled"] = "1"
|
||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
||||
|
||||
@@ -19,6 +19,7 @@ deps =
|
||||
pytest
|
||||
pytest-faulthandler
|
||||
pdbpp
|
||||
requests
|
||||
|
||||
[testenv:auditwheels]
|
||||
skipsdist = True
|
||||
@@ -51,6 +52,7 @@ commands =
|
||||
|
||||
|
||||
[pytest]
|
||||
addopts = -v -rs
|
||||
python_files = tests/test_*.py
|
||||
norecursedirs = .tox
|
||||
xfail_strict=true
|
||||
|
||||
@@ -23,7 +23,7 @@ if [ $? != 0 ]; then
|
||||
fi
|
||||
|
||||
pushd python
|
||||
if [ -e "./liveconfig" ]; then
|
||||
if [ -e "./liveconfig" && -z "$DCC_PY_LIVECONFIG" ]; then
|
||||
export DCC_PY_LIVECONFIG=liveconfig
|
||||
fi
|
||||
tox "$@"
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2019-07-10
|
||||
nightly-2019-08-13
|
||||
|
||||
2045
src/chat.rs
Normal file
2045
src/chat.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,10 @@
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
use crate::lot::Lot;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// An object representing a single chatlist in memory.
|
||||
@@ -248,39 +247,39 @@ impl<'a> Chatlist<'a> {
|
||||
/// - dc_lot_t::timestamp: the timestamp of the message. 0 if not applicable.
|
||||
/// - dc_lot_t::state: The state of the message as one of the DC_STATE_* constants (see #dc_msg_get_state()).
|
||||
// 0 if not applicable.
|
||||
pub unsafe fn get_summary(&self, index: usize, mut chat: *mut Chat<'a>) -> *mut dc_lot_t {
|
||||
pub unsafe fn get_summary(&self, index: usize, chat: Option<&Chat<'a>>) -> Lot {
|
||||
// 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();
|
||||
let mut ret = Lot::new();
|
||||
if index >= self.ids.len() {
|
||||
(*ret).text2 = "ErrBadChatlistIndex".strdup();
|
||||
ret.text2 = Some("ErrBadChatlistIndex".to_string());
|
||||
return ret;
|
||||
}
|
||||
|
||||
let lastmsg_id = self.ids[index].1;
|
||||
let mut lastcontact = None;
|
||||
|
||||
if chat.is_null() {
|
||||
chat = dc_chat_new(self.context);
|
||||
let chat_to_delete = chat;
|
||||
if !dc_chat_load_from_db(chat, self.ids[index].0) {
|
||||
(*ret).text2 = "ErrCannotReadChat".strdup();
|
||||
dc_chat_unref(chat_to_delete);
|
||||
|
||||
let chat_loaded: Chat;
|
||||
let chat = if let Some(chat) = chat {
|
||||
chat
|
||||
} else {
|
||||
if let Ok(chat) = Chat::load_from_db(self.context, self.ids[index].0) {
|
||||
chat_loaded = chat;
|
||||
&chat_loaded
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let lastmsg_id = self.ids[index].1;
|
||||
let mut lastcontact = None;
|
||||
|
||||
let lastmsg = if 0 != lastmsg_id {
|
||||
let lastmsg = dc_msg_new_untyped(self.context);
|
||||
dc_msg_load_from_db(lastmsg, self.context, lastmsg_id);
|
||||
|
||||
if (*lastmsg).from_id != 1 as libc::c_uint
|
||||
&& ((*chat).type_0 == DC_CHAT_TYPE_GROUP
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP)
|
||||
&& (chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup)
|
||||
{
|
||||
lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok();
|
||||
}
|
||||
@@ -289,12 +288,12 @@ impl<'a> Chatlist<'a> {
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
|
||||
if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
|
||||
(*ret).text2 = dc_strdup(0 as *const libc::c_char)
|
||||
if chat.id == DC_CHAT_ID_ARCHIVED_LINK as u32 {
|
||||
ret.text2 = None;
|
||||
} else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 {
|
||||
(*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup();
|
||||
ret.text2 = Some(self.context.stock_str(StockMessage::NoMessages).to_string());
|
||||
} else {
|
||||
dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context);
|
||||
ret.fill(lastmsg, chat, lastcontact.as_ref(), self.context);
|
||||
}
|
||||
|
||||
dc_msg_unref(lastmsg);
|
||||
|
||||
@@ -3,9 +3,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_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::job::*;
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
/// The available configuration keys.
|
||||
@@ -48,11 +48,14 @@ pub enum Config {
|
||||
ConfiguredMailUser,
|
||||
ConfiguredMailPw,
|
||||
ConfiguredMailPort,
|
||||
ConfiguredMailSecurity,
|
||||
ConfiguredSendServer,
|
||||
ConfiguredSendUser,
|
||||
ConfiguredSendPw,
|
||||
ConfiguredSendPort,
|
||||
ConfiguredServerFlags,
|
||||
ConfiguredSendSecurity,
|
||||
ConfiguredE2EEEnabled,
|
||||
Configured,
|
||||
// Deprecated
|
||||
#[strum(serialize = "sys.version")]
|
||||
@@ -71,7 +74,7 @@ impl Context {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
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::SysVersion => Some((&*DC_VERSION_STR).clone()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
Config::SysConfigKeys => Some(get_config_keys_string()),
|
||||
_ => self.sql.get_config(self, key),
|
||||
@@ -99,17 +102,17 @@ impl Context {
|
||||
}
|
||||
Config::InboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_imap_idle(self) };
|
||||
interrupt_imap_idle(self);
|
||||
ret
|
||||
}
|
||||
Config::SentboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_sentbox_idle(self) };
|
||||
interrupt_sentbox_idle(self);
|
||||
ret
|
||||
}
|
||||
Config::MvboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_mvbox_idle(self) };
|
||||
interrupt_mvbox_idle(self);
|
||||
ret
|
||||
}
|
||||
Config::Selfstatus => {
|
||||
@@ -120,8 +123,7 @@ impl Context {
|
||||
value
|
||||
};
|
||||
|
||||
let ret = self.sql.set_config(self, key, val);
|
||||
ret
|
||||
self.sql.set_config(self, key, val)
|
||||
}
|
||||
_ => self.sql.set_config(self, key, value),
|
||||
}
|
||||
|
||||
285
src/constants.rs
285
src/constants.rs
@@ -1,61 +1,77 @@
|
||||
//! Constants
|
||||
#![allow(non_camel_case_types)]
|
||||
use num_traits::{FromPrimitive, ToPrimitive};
|
||||
use rusqlite as sql;
|
||||
use rusqlite::types::*;
|
||||
#![allow(non_camel_case_types, dead_code)]
|
||||
|
||||
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
pub const DC_MOVE_STATE_MOVING: u32 = 3;
|
||||
pub const DC_MOVE_STATE_STAY: u32 = 2;
|
||||
pub const DC_MOVE_STATE_PENDING: u32 = 1;
|
||||
pub const DC_MOVE_STATE_UNDEFINED: u32 = 0;
|
||||
use deltachat_derive::*;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref DC_VERSION_STR: String = env!("CARGO_PKG_VERSION").to_string();
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
pub enum MoveState {
|
||||
Undefined = 0,
|
||||
Pending = 1,
|
||||
Stay = 2,
|
||||
Moving = 3,
|
||||
}
|
||||
|
||||
// some defaults
|
||||
const DC_E2EE_DEFAULT_ENABLED: i32 = 1;
|
||||
pub const DC_MDNS_DEFAULT_ENABLED: i32 = 1;
|
||||
const DC_INBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_SENTBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_MVBOX_WATCH_DEFAULT: i32 = 1;
|
||||
const DC_MVBOX_MOVE_DEFAULT: i32 = 1;
|
||||
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(u8)]
|
||||
pub enum Blocked {
|
||||
Not = 0,
|
||||
Manually = 1,
|
||||
Deaddrop = 2,
|
||||
}
|
||||
|
||||
impl Default for Blocked {
|
||||
fn default() -> Self {
|
||||
Blocked::Not
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_IMAP_SEEN: u32 = 0x1;
|
||||
|
||||
const DC_HANDSHAKE_CONTINUE_NORMAL_PROCESSING: i32 = 0x01;
|
||||
pub const DC_HANDSHAKE_STOP_NORMAL_PROCESSING: i32 = 0x02;
|
||||
pub const DC_HANDSHAKE_ADD_DELETE_JOB: i32 = 0x04;
|
||||
|
||||
pub const DC_GCL_ARCHIVED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_NO_SPECIALS: usize = 0x02;
|
||||
pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
|
||||
|
||||
pub const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
const DC_GCM_ADDDAYMARKER: usize = 0x01;
|
||||
|
||||
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
|
||||
const DC_GCL_VERIFIED_ONLY: usize = 0x01;
|
||||
pub const DC_GCL_ADD_SELF: usize = 0x02;
|
||||
|
||||
/// param1 is a directory where the keys are written to
|
||||
pub const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
|
||||
const DC_IMEX_EXPORT_SELF_KEYS: usize = 1;
|
||||
/// param1 is a directory where the keys are searched in and read from
|
||||
pub const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
|
||||
const DC_IMEX_IMPORT_SELF_KEYS: usize = 2;
|
||||
/// param1 is a directory where the backup is written to
|
||||
pub const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
const DC_IMEX_EXPORT_BACKUP: usize = 11;
|
||||
/// param1 is the file with the backup to import
|
||||
pub const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
|
||||
/// id=contact
|
||||
pub const DC_QR_ASK_VERIFYCONTACT: usize = 200;
|
||||
/// text1=groupname
|
||||
pub const DC_QR_ASK_VERIFYGROUP: usize = 202;
|
||||
/// id=contact
|
||||
pub const DC_QR_FPR_OK: usize = 210;
|
||||
/// id=contact
|
||||
pub const DC_QR_FPR_MISMATCH: usize = 220;
|
||||
/// test1=formatted fingerprint
|
||||
pub const DC_QR_FPR_WITHOUT_ADDR: usize = 230;
|
||||
/// id=contact
|
||||
pub const DC_QR_ADDR: usize = 320;
|
||||
/// text1=text
|
||||
pub const DC_QR_TEXT: usize = 330;
|
||||
/// text1=URL
|
||||
pub const DC_QR_URL: usize = 332;
|
||||
/// text1=error string
|
||||
pub const DC_QR_ERROR: usize = 400;
|
||||
const DC_IMEX_IMPORT_BACKUP: usize = 12;
|
||||
|
||||
/// virtual chat showing all messages belonging to chats flagged with chats.blocked=2
|
||||
pub const DC_CHAT_ID_DEADDROP: usize = 1;
|
||||
pub(crate) const DC_CHAT_ID_DEADDROP: usize = 1;
|
||||
/// messages that should be deleted get this chat_id; the messages are deleted from the working thread later then. This is also needed as rfc724_mid should be preset as long as the message is not deleted on the server (otherwise it is downloaded again)
|
||||
pub const DC_CHAT_ID_TRASH: usize = 3;
|
||||
/// a message is just in creation but not yet assigned to a chat (eg. we may need the message ID to set up blobs; this avoids unready message to be sent and shown)
|
||||
pub const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
|
||||
const DC_CHAT_ID_MSGS_IN_CREATION: usize = 4;
|
||||
/// virtual chat showing all messages flagged with msgs.starred=2
|
||||
pub const DC_CHAT_ID_STARRED: usize = 5;
|
||||
const DC_CHAT_ID_STARRED: usize = 5;
|
||||
/// only an indicator in a chatlist
|
||||
pub const DC_CHAT_ID_ARCHIVED_LINK: usize = 6;
|
||||
/// only an indicator in a chatlist
|
||||
@@ -63,41 +79,47 @@ pub const DC_CHAT_ID_ALLDONE_HINT: usize = 7;
|
||||
/// larger chat IDs are "real" chats, their messages are "real" messages.
|
||||
pub const DC_CHAT_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
pub const DC_CHAT_TYPE_UNDEFINED: i32 = 0;
|
||||
pub const DC_CHAT_TYPE_SINGLE: i32 = 100;
|
||||
pub const DC_CHAT_TYPE_GROUP: i32 = 120;
|
||||
pub const DC_CHAT_TYPE_VERIFIED_GROUP: i32 = 130;
|
||||
#[derive(
|
||||
Debug,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromPrimitive,
|
||||
ToPrimitive,
|
||||
FromSql,
|
||||
ToSql,
|
||||
IntoStaticStr,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
pub enum Chattype {
|
||||
Undefined = 0,
|
||||
Single = 100,
|
||||
Group = 120,
|
||||
VerifiedGroup = 130,
|
||||
}
|
||||
|
||||
impl Default for Chattype {
|
||||
fn default() -> Self {
|
||||
Chattype::Undefined
|
||||
}
|
||||
}
|
||||
|
||||
pub const DC_MSG_ID_MARKER1: usize = 1;
|
||||
pub const DC_MSG_ID_DAYMARKER: usize = 9;
|
||||
const DC_MSG_ID_DAYMARKER: usize = 9;
|
||||
pub const DC_MSG_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
pub const DC_STATE_UNDEFINED: i32 = 0;
|
||||
pub const DC_STATE_IN_FRESH: i32 = 10;
|
||||
pub const DC_STATE_IN_NOTICED: i32 = 13;
|
||||
pub const DC_STATE_IN_SEEN: i32 = 16;
|
||||
pub const DC_STATE_OUT_PREPARING: i32 = 18;
|
||||
pub const DC_STATE_OUT_DRAFT: i32 = 19;
|
||||
pub const DC_STATE_OUT_PENDING: i32 = 20;
|
||||
pub const DC_STATE_OUT_FAILED: i32 = 24;
|
||||
/// to check if a mail was sent, use dc_msg_is_sent()
|
||||
pub const DC_STATE_OUT_DELIVERED: i32 = 26;
|
||||
pub const DC_STATE_OUT_MDN_RCVD: i32 = 28;
|
||||
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
|
||||
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;
|
||||
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;
|
||||
const DC_CONTACT_ID_DEVICE: usize = 2;
|
||||
pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9;
|
||||
|
||||
pub const DC_TEXT1_DRAFT: usize = 1;
|
||||
pub const DC_TEXT1_USERNAME: usize = 2;
|
||||
pub const DC_TEXT1_SELF: usize = 3;
|
||||
|
||||
pub const DC_CREATE_MVBOX: usize = 1;
|
||||
|
||||
// Flags for configuring IMAP and SMTP servers.
|
||||
@@ -112,7 +134,7 @@ pub const DC_LP_AUTH_OAUTH2: usize = 0x2;
|
||||
|
||||
/// Force NORMAL authorization, this is the default.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
const DC_LP_AUTH_NORMAL: usize = 0x4;
|
||||
|
||||
/// Connect to IMAP via STARTTLS.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
@@ -120,7 +142,7 @@ pub const DC_LP_IMAP_SOCKET_STARTTLS: usize = 0x100;
|
||||
|
||||
/// Connect to IMAP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
|
||||
const DC_LP_IMAP_SOCKET_SSL: usize = 0x200;
|
||||
|
||||
/// Connect to IMAP unencrypted, this should not be used.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
@@ -132,22 +154,22 @@ pub const DC_LP_SMTP_SOCKET_STARTTLS: usize = 0x10000;
|
||||
|
||||
/// Connect to SMTP via SSL.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
|
||||
/// Connect to SMTP unencrypted, this should not be used.
|
||||
/// If this flag is set, automatic configuration is skipped.
|
||||
pub const DC_LP_SMTP_SOCKET_PLAIN: usize = 0x40000;
|
||||
|
||||
/// if none of these flags are set, the default is chosen
|
||||
pub const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
|
||||
const DC_LP_AUTH_FLAGS: usize = (DC_LP_AUTH_OAUTH2 | DC_LP_AUTH_NORMAL);
|
||||
/// if none of these flags are set, the default is chosen
|
||||
pub const DC_LP_IMAP_SOCKET_FLAGS: usize =
|
||||
const DC_LP_IMAP_SOCKET_FLAGS: usize =
|
||||
(DC_LP_IMAP_SOCKET_STARTTLS | DC_LP_IMAP_SOCKET_SSL | DC_LP_IMAP_SOCKET_PLAIN);
|
||||
/// if none of these flags are set, the default is chosen
|
||||
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
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)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, FromSql, ToSql)]
|
||||
#[repr(i32)]
|
||||
pub enum Viewtype {
|
||||
Unknown = 0,
|
||||
@@ -202,23 +224,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -460,65 +465,65 @@ pub enum Event {
|
||||
GET_STRING = 2091,
|
||||
}
|
||||
|
||||
pub const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
|
||||
pub const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
|
||||
pub const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
|
||||
pub const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
|
||||
pub const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
|
||||
const DC_EVENT_FILE_COPIED: usize = 2055; // deprecated;
|
||||
const DC_EVENT_IS_OFFLINE: usize = 2081; // deprecated;
|
||||
const DC_ERROR_SEE_STRING: usize = 0; // deprecated;
|
||||
const DC_ERROR_SELF_NOT_IN_GROUP: usize = 1; // deprecated;
|
||||
const DC_STR_SELFNOTINGRP: usize = 21; // deprecated;
|
||||
|
||||
/// Values for dc_get|set_config("show_emails")
|
||||
pub const DC_SHOW_EMAILS_OFF: usize = 0;
|
||||
pub const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
|
||||
pub const DC_SHOW_EMAILS_ALL: usize = 2;
|
||||
const DC_SHOW_EMAILS_OFF: usize = 0;
|
||||
const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
|
||||
const DC_SHOW_EMAILS_ALL: usize = 2;
|
||||
|
||||
// TODO: Strings need some doumentation about used placeholders.
|
||||
// These constants are used to request strings using #DC_EVENT_GET_STRING.
|
||||
|
||||
pub const DC_STR_NOMESSAGES: usize = 1;
|
||||
pub const DC_STR_SELF: usize = 2;
|
||||
pub const DC_STR_DRAFT: usize = 3;
|
||||
pub const DC_STR_MEMBER: usize = 4;
|
||||
pub const DC_STR_CONTACT: usize = 6;
|
||||
pub const DC_STR_VOICEMESSAGE: usize = 7;
|
||||
pub const DC_STR_DEADDROP: usize = 8;
|
||||
pub const DC_STR_IMAGE: usize = 9;
|
||||
pub const DC_STR_VIDEO: usize = 10;
|
||||
pub const DC_STR_AUDIO: usize = 11;
|
||||
pub const DC_STR_FILE: usize = 12;
|
||||
pub const DC_STR_STATUSLINE: usize = 13;
|
||||
pub const DC_STR_NEWGROUPDRAFT: usize = 14;
|
||||
pub const DC_STR_MSGGRPNAME: usize = 15;
|
||||
pub const DC_STR_MSGGRPIMGCHANGED: usize = 16;
|
||||
pub const DC_STR_MSGADDMEMBER: usize = 17;
|
||||
pub const DC_STR_MSGDELMEMBER: usize = 18;
|
||||
pub const DC_STR_MSGGROUPLEFT: usize = 19;
|
||||
pub const DC_STR_GIF: usize = 23;
|
||||
pub const DC_STR_ENCRYPTEDMSG: usize = 24;
|
||||
pub const DC_STR_E2E_AVAILABLE: usize = 25;
|
||||
pub const DC_STR_ENCR_TRANSP: usize = 27;
|
||||
pub const DC_STR_ENCR_NONE: usize = 28;
|
||||
pub const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
|
||||
pub const DC_STR_FINGERPRINTS: usize = 30;
|
||||
pub const DC_STR_READRCPT: usize = 31;
|
||||
pub const DC_STR_READRCPT_MAILBODY: usize = 32;
|
||||
pub const DC_STR_MSGGRPIMGDELETED: usize = 33;
|
||||
pub const DC_STR_E2E_PREFERRED: usize = 34;
|
||||
pub const DC_STR_CONTACT_VERIFIED: usize = 35;
|
||||
pub const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
|
||||
pub const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
|
||||
pub const DC_STR_ARCHIVEDCHATS: usize = 40;
|
||||
pub const DC_STR_STARREDMSGS: usize = 41;
|
||||
pub const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
|
||||
pub const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
|
||||
pub const DC_STR_SELFTALK_SUBTITLE: usize = 50;
|
||||
pub const DC_STR_CANNOT_LOGIN: usize = 60;
|
||||
pub const DC_STR_SERVER_RESPONSE: usize = 61;
|
||||
pub const DC_STR_MSGACTIONBYUSER: usize = 62;
|
||||
pub const DC_STR_MSGACTIONBYME: usize = 63;
|
||||
pub const DC_STR_MSGLOCATIONENABLED: usize = 64;
|
||||
pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
|
||||
pub const DC_STR_LOCATION: usize = 66;
|
||||
pub const DC_STR_COUNT: usize = 66;
|
||||
const DC_STR_NOMESSAGES: usize = 1;
|
||||
const DC_STR_SELF: usize = 2;
|
||||
const DC_STR_DRAFT: usize = 3;
|
||||
const DC_STR_MEMBER: usize = 4;
|
||||
const DC_STR_CONTACT: usize = 6;
|
||||
const DC_STR_VOICEMESSAGE: usize = 7;
|
||||
const DC_STR_DEADDROP: usize = 8;
|
||||
const DC_STR_IMAGE: usize = 9;
|
||||
const DC_STR_VIDEO: usize = 10;
|
||||
const DC_STR_AUDIO: usize = 11;
|
||||
const DC_STR_FILE: usize = 12;
|
||||
const DC_STR_STATUSLINE: usize = 13;
|
||||
const DC_STR_NEWGROUPDRAFT: usize = 14;
|
||||
const DC_STR_MSGGRPNAME: usize = 15;
|
||||
const DC_STR_MSGGRPIMGCHANGED: usize = 16;
|
||||
const DC_STR_MSGADDMEMBER: usize = 17;
|
||||
const DC_STR_MSGDELMEMBER: usize = 18;
|
||||
const DC_STR_MSGGROUPLEFT: usize = 19;
|
||||
const DC_STR_GIF: usize = 23;
|
||||
const DC_STR_ENCRYPTEDMSG: usize = 24;
|
||||
const DC_STR_E2E_AVAILABLE: usize = 25;
|
||||
const DC_STR_ENCR_TRANSP: usize = 27;
|
||||
const DC_STR_ENCR_NONE: usize = 28;
|
||||
const DC_STR_CANTDECRYPT_MSG_BODY: usize = 29;
|
||||
const DC_STR_FINGERPRINTS: usize = 30;
|
||||
const DC_STR_READRCPT: usize = 31;
|
||||
const DC_STR_READRCPT_MAILBODY: usize = 32;
|
||||
const DC_STR_MSGGRPIMGDELETED: usize = 33;
|
||||
const DC_STR_E2E_PREFERRED: usize = 34;
|
||||
const DC_STR_CONTACT_VERIFIED: usize = 35;
|
||||
const DC_STR_CONTACT_NOT_VERIFIED: usize = 36;
|
||||
const DC_STR_CONTACT_SETUP_CHANGED: usize = 37;
|
||||
const DC_STR_ARCHIVEDCHATS: usize = 40;
|
||||
const DC_STR_STARREDMSGS: usize = 41;
|
||||
const DC_STR_AC_SETUP_MSG_SUBJECT: usize = 42;
|
||||
const DC_STR_AC_SETUP_MSG_BODY: usize = 43;
|
||||
const DC_STR_SELFTALK_SUBTITLE: usize = 50;
|
||||
const DC_STR_CANNOT_LOGIN: usize = 60;
|
||||
const DC_STR_SERVER_RESPONSE: usize = 61;
|
||||
const DC_STR_MSGACTIONBYUSER: usize = 62;
|
||||
const DC_STR_MSGACTIONBYME: usize = 63;
|
||||
const DC_STR_MSGLOCATIONENABLED: usize = 64;
|
||||
const DC_STR_MSGLOCATIONDISABLED: usize = 65;
|
||||
const DC_STR_LOCATION: usize = 66;
|
||||
const DC_STR_COUNT: usize = 66;
|
||||
|
||||
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
|
||||
|
||||
@@ -534,6 +539,6 @@ pub const DC_CMD_GROUPIMAGE_CHANGED: libc::c_int = 3;
|
||||
pub const DC_CMD_MEMBER_ADDED_TO_GROUP: libc::c_int = 4;
|
||||
pub const DC_CMD_MEMBER_REMOVED_FROM_GROUP: libc::c_int = 5;
|
||||
pub const DC_CMD_AUTOCRYPT_SETUP_MESSAGE: libc::c_int = 6;
|
||||
pub const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
const DC_CMD_SECUREJOIN_MESSAGE: libc::c_int = 7;
|
||||
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
|
||||
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
|
||||
@@ -7,9 +7,9 @@ use crate::aheader::EncryptPreference;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_msg::MessageState;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Result;
|
||||
use crate::key::*;
|
||||
@@ -139,7 +139,7 @@ pub enum Modifier {
|
||||
Created,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum VerifiedStatus {
|
||||
/// Contact is not verified.
|
||||
@@ -251,7 +251,7 @@ impl<'a> Contact<'a> {
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
|
||||
params![DC_STATE_IN_NOTICED, id as i32, DC_STATE_IN_FRESH],
|
||||
params![MessageState::InNoticed, id as i32, MessageState::InFresh],
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
@@ -328,7 +328,7 @@ impl<'a> Contact<'a> {
|
||||
"<unset>"
|
||||
},
|
||||
);
|
||||
bail!("Bad address supplied");
|
||||
bail!("Bad address supplied: {:?}", addr);
|
||||
}
|
||||
|
||||
let mut update_addr = false;
|
||||
@@ -472,13 +472,13 @@ impl<'a> Contact<'a> {
|
||||
context: &Context,
|
||||
listflags: u32,
|
||||
query: Option<impl AsRef<str>>,
|
||||
) -> Result<*mut dc_array_t> {
|
||||
) -> Result<Vec<u32>> {
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut add_self = false;
|
||||
let mut ret = dc_array_t::new(100);
|
||||
let mut ret = Vec::new();
|
||||
|
||||
if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || query.is_some() {
|
||||
let s3str_like_cmd = format!(
|
||||
@@ -509,7 +509,7 @@ impl<'a> Contact<'a> {
|
||||
|row| row.get::<_, i32>(0),
|
||||
|ids| {
|
||||
for id in ids {
|
||||
ret.add_id(id? as u32);
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
@@ -537,7 +537,7 @@ impl<'a> Contact<'a> {
|
||||
|row| row.get::<_, i32>(0),
|
||||
|ids| {
|
||||
for id in ids {
|
||||
ret.add_id(id? as u32);
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -545,10 +545,10 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
if 0 != listflags & DC_GCL_ADD_SELF as u32 && add_self {
|
||||
ret.add_id(DC_CONTACT_ID_SELF as u32);
|
||||
ret.push(DC_CONTACT_ID_SELF as u32);
|
||||
}
|
||||
|
||||
Ok(ret.into_raw())
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn get_blocked_cnt(context: &Context) -> usize {
|
||||
@@ -564,27 +564,27 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
|
||||
/// Get blocked contacts.
|
||||
pub fn get_all_blocked(context: &Context) -> *mut dc_array_t {
|
||||
pub fn get_all_blocked(context: &Context) -> Vec<u32> {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;",
|
||||
params![DC_CONTACT_ID_LAST_SPECIAL as i32],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|row| row.get::<_, u32>(0),
|
||||
|ids| {
|
||||
let mut ret = dc_array_t::new(100);
|
||||
|
||||
for id in ids {
|
||||
ret.add_id(id? as u32);
|
||||
}
|
||||
|
||||
Ok(ret.into_raw())
|
||||
ids.collect::<std::result::Result<Vec<_>, _>>()
|
||||
.map_err(Into::into)
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| std::ptr::null_mut())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn get_encrinfo(context: &Context, contact_id: u32) -> String {
|
||||
/// Returns a textual summary of the encryption state for the contact.
|
||||
///
|
||||
/// This function returns a string explaining the encryption state
|
||||
/// of the contact and if the connection is encrypted the
|
||||
/// fingerprints of the keys involved.
|
||||
pub fn get_encrinfo(context: &Context, contact_id: u32) -> Result<String> {
|
||||
let mut ret = String::new();
|
||||
|
||||
if let Ok(contact) = Contact::load_from_db(context, contact_id) {
|
||||
@@ -603,7 +603,7 @@ impl<'a> Contact<'a> {
|
||||
});
|
||||
ret += &p;
|
||||
if self_key.is_none() {
|
||||
unsafe { dc_ensure_secret_key_exists(context) };
|
||||
dc_ensure_secret_key_exists(context)?;
|
||||
self_key = Key::from_self_public(context, &loginparam.addr, &context.sql);
|
||||
}
|
||||
let p = context.stock_str(StockMessage::FingerPrints);
|
||||
@@ -646,7 +646,7 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
/// Delete a contact. The contact is deleted from the local device. It may happen that this is not
|
||||
@@ -792,7 +792,7 @@ impl<'a> Contact<'a> {
|
||||
/// and can be used for an fallback avatar with white initials
|
||||
/// as well as for headlines in bubbles of group chats.
|
||||
pub fn get_color(&self) -> u32 {
|
||||
dc_str_to_color_safe(&self.addr)
|
||||
dc_str_to_color(&self.addr)
|
||||
}
|
||||
|
||||
/// Check if a contact was verified. E.g. by a secure-join QR code scan
|
||||
@@ -904,26 +904,15 @@ impl<'a> Contact<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_first_name<'a>(full_name: &'a str) -> &'a str {
|
||||
fn get_first_name<'a>(full_name: &'a str) -> &'a str {
|
||||
full_name.splitn(2, ' ').next().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Returns false if addr is an invalid address, otherwise true.
|
||||
pub fn may_be_valid_addr(addr: &str) -> bool {
|
||||
if addr.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let at = addr.find('@').unwrap_or_default();
|
||||
if at < 1 {
|
||||
return false;
|
||||
}
|
||||
let dot = addr.find('.').unwrap_or_default();
|
||||
if dot < 1 || dot > addr.len() - 3 || dot < at + 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
let res = addr.parse::<EmailAddress>();
|
||||
println!("{:?}", res);
|
||||
res.is_ok()
|
||||
}
|
||||
|
||||
pub fn addr_normalize(addr: &str) -> &str {
|
||||
|
||||
180
src/context.rs
180
src/context.rs
@@ -1,36 +1,37 @@
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_jobthread::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_lot::dc_lot_t;
|
||||
use crate::dc_move::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_receive_imf::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
use crate::job::*;
|
||||
use crate::job_thread::JobThread;
|
||||
use crate::key::*;
|
||||
use crate::lot::Lot;
|
||||
use crate::param::Params;
|
||||
use crate::smtp::*;
|
||||
use crate::sql::Sql;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
use std::ptr;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Context {
|
||||
pub userdata: *mut libc::c_void,
|
||||
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub dbfile: Arc<RwLock<Option<PathBuf>>>,
|
||||
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub sql: Sql,
|
||||
pub inbox: Arc<RwLock<Imap>>,
|
||||
pub perform_inbox_jobs_needed: Arc<RwLock<bool>>,
|
||||
pub probe_imap_network: Arc<RwLock<bool>>,
|
||||
pub sentbox_thread: Arc<RwLock<dc_jobthread_t>>,
|
||||
pub mvbox_thread: Arc<RwLock<dc_jobthread_t>>,
|
||||
pub sentbox_thread: Arc<RwLock<JobThread>>,
|
||||
pub mvbox_thread: Arc<RwLock<JobThread>>,
|
||||
pub smtp: Arc<Mutex<Smtp>>,
|
||||
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
|
||||
pub oauth2_critical: Arc<Mutex<()>>,
|
||||
@@ -40,6 +41,8 @@ pub struct Context {
|
||||
pub bob: Arc<RwLock<BobStatus>>,
|
||||
pub last_smeared_timestamp: Arc<RwLock<i64>>,
|
||||
pub running_state: Arc<RwLock<RunningState>>,
|
||||
/// Mutex to avoid generating the key for the user more than once.
|
||||
pub generating_key_mutex: Mutex<()>,
|
||||
}
|
||||
|
||||
unsafe impl std::marker::Send for Context {}
|
||||
@@ -53,15 +56,17 @@ pub struct RunningState {
|
||||
|
||||
impl Context {
|
||||
pub fn has_dbfile(&self) -> bool {
|
||||
!self.get_dbfile().is_null()
|
||||
self.get_dbfile().is_some()
|
||||
}
|
||||
|
||||
pub fn has_blobdir(&self) -> bool {
|
||||
!self.get_blobdir().is_null()
|
||||
}
|
||||
|
||||
pub fn get_dbfile(&self) -> *const libc::c_char {
|
||||
*self.dbfile.clone().read().unwrap()
|
||||
pub fn get_dbfile(&self) -> Option<PathBuf> {
|
||||
(*self.dbfile.clone().read().unwrap())
|
||||
.as_ref()
|
||||
.map(|x| x.clone())
|
||||
}
|
||||
|
||||
pub fn get_blobdir(&self) -> *const libc::c_char {
|
||||
@@ -77,6 +82,14 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Context {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
dc_close(&self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RunningState {
|
||||
fn default() -> Self {
|
||||
RunningState {
|
||||
@@ -86,21 +99,11 @@ impl Default for RunningState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Default)]
|
||||
pub struct BobStatus {
|
||||
pub expects: i32,
|
||||
pub status: i32,
|
||||
pub qr_scan: *mut dc_lot_t,
|
||||
}
|
||||
|
||||
impl Default for BobStatus {
|
||||
fn default() -> Self {
|
||||
BobStatus {
|
||||
expects: 0,
|
||||
status: 0,
|
||||
qr_scan: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
pub qr_scan: Option<Lot>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
@@ -120,7 +123,7 @@ pub fn dc_context_new(
|
||||
) -> Context {
|
||||
Context {
|
||||
blobdir: Arc::new(RwLock::new(std::ptr::null_mut())),
|
||||
dbfile: Arc::new(RwLock::new(std::ptr::null_mut())),
|
||||
dbfile: Arc::new(RwLock::new(None)),
|
||||
inbox: Arc::new(RwLock::new({
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
@@ -131,7 +134,7 @@ pub fn dc_context_new(
|
||||
})),
|
||||
userdata,
|
||||
cb,
|
||||
os_name: os_name,
|
||||
os_name,
|
||||
running_state: Arc::new(RwLock::new(Default::default())),
|
||||
sql: Sql::new(),
|
||||
smtp: Arc::new(Mutex::new(Smtp::new())),
|
||||
@@ -140,7 +143,7 @@ pub fn dc_context_new(
|
||||
bob: Arc::new(RwLock::new(Default::default())),
|
||||
last_smeared_timestamp: Arc::new(RwLock::new(0)),
|
||||
cmdline_sel_chat_id: Arc::new(RwLock::new(0)),
|
||||
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
sentbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||
"SENTBOX",
|
||||
"configured_sentbox_folder",
|
||||
Imap::new(
|
||||
@@ -150,7 +153,7 @@ pub fn dc_context_new(
|
||||
cb_receive_imf,
|
||||
),
|
||||
))),
|
||||
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
mvbox_thread: Arc::new(RwLock::new(JobThread::new(
|
||||
"MVBOX",
|
||||
"configured_mvbox_folder",
|
||||
Imap::new(
|
||||
@@ -162,16 +165,7 @@ pub fn dc_context_new(
|
||||
))),
|
||||
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) };
|
||||
generating_key_mutex: Mutex::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +195,7 @@ unsafe fn cb_precheck_imf(
|
||||
) -> libc::c_int {
|
||||
let mut rfc724_mid_exists: libc::c_int = 0i32;
|
||||
let msg_id: uint32_t;
|
||||
let mut old_server_folder: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut old_server_folder: *mut libc::c_char = ptr::null_mut();
|
||||
let mut old_server_uid: uint32_t = 0i32 as uint32_t;
|
||||
let mut mark_seen: libc::c_int = 0i32;
|
||||
msg_id = dc_rfc724_mid_exists(
|
||||
@@ -229,18 +223,24 @@ unsafe fn cb_precheck_imf(
|
||||
"[move] detected moved message {}",
|
||||
as_str(rfc724_mid),
|
||||
);
|
||||
dc_update_msg_move_state(context, rfc724_mid, DC_MOVE_STATE_STAY);
|
||||
dc_update_msg_move_state(context, rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
if as_str(old_server_folder) != server_folder || old_server_uid != server_uid {
|
||||
dc_update_server_uid(context, rfc724_mid, server_folder, server_uid);
|
||||
}
|
||||
dc_do_heuristics_moves(context, server_folder, msg_id);
|
||||
if 0 != mark_seen {
|
||||
dc_job_add(context, 130, msg_id as libc::c_int, Params::new(), 0);
|
||||
job_add(
|
||||
context,
|
||||
Action::MarkseenMsgOnImap,
|
||||
msg_id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
free(old_server_folder as *mut libc::c_void);
|
||||
return rfc724_mid_exists;
|
||||
rfc724_mid_exists
|
||||
}
|
||||
|
||||
fn cb_set_config(context: &Context, key: &str, value: Option<&str>) {
|
||||
@@ -258,12 +258,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_close(context: &Context) {
|
||||
info!(context, 0, "disconnecting INBOX-watch",);
|
||||
context.inbox.read().unwrap().disconnect(context);
|
||||
@@ -287,18 +281,14 @@ pub unsafe fn dc_close(context: &Context) {
|
||||
|
||||
context.sql.close(context);
|
||||
let mut dbfile = context.dbfile.write().unwrap();
|
||||
free(*dbfile as *mut libc::c_void);
|
||||
*dbfile = 0 as *mut libc::c_char;
|
||||
*dbfile = None;
|
||||
let mut blobdir = context.blobdir.write().unwrap();
|
||||
free(*blobdir as *mut libc::c_void);
|
||||
*blobdir = 0 as *mut libc::c_char;
|
||||
*blobdir = ptr::null_mut();
|
||||
}
|
||||
|
||||
pub unsafe fn dc_is_open(context: &Context) -> libc::c_int {
|
||||
match context.sql.is_open() {
|
||||
true => 1,
|
||||
false => 0,
|
||||
}
|
||||
context.sql.is_open() as libc::c_int
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void {
|
||||
@@ -310,14 +300,14 @@ pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) ->
|
||||
if 0 != dc_is_open(context) {
|
||||
return false;
|
||||
}
|
||||
*context.dbfile.write().unwrap() = dbfile.strdup();
|
||||
if blobdir.is_some() && blobdir.unwrap().len() > 0 {
|
||||
*context.dbfile.write().unwrap() = Some(PathBuf::from(dbfile));
|
||||
if blobdir.is_some() && !blobdir.unwrap().is_empty() {
|
||||
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;
|
||||
let dir = dbfile.to_string() + "-blobs";
|
||||
dc_create_folder(context, &dir);
|
||||
*context.blobdir.write().unwrap() = dir.strdup();
|
||||
}
|
||||
// Create/open sqlite database, this may already use the blobdir
|
||||
let dbfile_path = std::path::Path::new(dbfile);
|
||||
@@ -343,7 +333,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
let l = dc_loginparam_read(context, &context.sql, "");
|
||||
let l2 = dc_loginparam_read(context, &context.sql, "configured_");
|
||||
let displayname = context.sql.get_config(context, "displayname");
|
||||
let chats = dc_get_chat_cnt(context) as usize;
|
||||
let chats = get_chat_cnt(context) as usize;
|
||||
let real_msgs = dc_get_real_msg_cnt(context) as usize;
|
||||
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
|
||||
let contacts = Contact::get_real_cnt(context) as usize;
|
||||
@@ -445,7 +435,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
public_key_count={}\n\
|
||||
fingerprint={}\n\
|
||||
level=awesome\n",
|
||||
as_str(DC_VERSION_STR as *const u8 as *const _),
|
||||
&*DC_VERSION_STR,
|
||||
rusqlite::version(),
|
||||
sqlite3_threadsafe(),
|
||||
// arch
|
||||
@@ -454,11 +444,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
real_msgs,
|
||||
deaddrop_msgs,
|
||||
contacts,
|
||||
if context.has_dbfile() {
|
||||
as_str(context.get_dbfile())
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or(unset, |p| p.to_str().unwrap()),
|
||||
dbversion,
|
||||
if context.has_blobdir() {
|
||||
as_str(context.get_blobdir())
|
||||
@@ -487,10 +476,10 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
|
||||
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
|
||||
(&*DC_VERSION_STR).strdup()
|
||||
}
|
||||
|
||||
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
|
||||
pub fn dc_get_fresh_msgs(context: &Context) -> Vec<u32> {
|
||||
let show_deaddrop = 0;
|
||||
|
||||
context
|
||||
@@ -505,13 +494,12 @@ pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
|
||||
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|
||||
|row| row.get(0),
|
||||
|rows| {
|
||||
let mut ret = dc_array_t::new(128);
|
||||
|
||||
let mut ret = Vec::new();
|
||||
for row in rows {
|
||||
let id = row?;
|
||||
ret.add_id(id);
|
||||
let id: u32 = row?;
|
||||
ret.push(id);
|
||||
}
|
||||
Ok(ret.into_raw())
|
||||
Ok(ret)
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
@@ -522,14 +510,14 @@ pub fn dc_search_msgs(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
query: *const libc::c_char,
|
||||
) -> *mut dc_array_t {
|
||||
) -> Vec<u32> {
|
||||
if query.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let real_query = to_string(query).trim().to_string();
|
||||
if real_query.is_empty() {
|
||||
return std::ptr::null_mut();
|
||||
return Vec::new();
|
||||
}
|
||||
let strLikeInText = format!("%{}%", &real_query);
|
||||
let strLikeBeg = format!("{}%", &real_query);
|
||||
@@ -545,28 +533,21 @@ pub fn dc_search_msgs(
|
||||
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
};
|
||||
|
||||
let mut ret = dc_array_t::new(100);
|
||||
|
||||
let success = context
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
query,
|
||||
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|rows| {
|
||||
let mut ret = Vec::new();
|
||||
for id in rows {
|
||||
ret.add_id(id? as u32);
|
||||
ret.push(id? as u32);
|
||||
}
|
||||
Ok(())
|
||||
Ok(ret)
|
||||
},
|
||||
)
|
||||
.is_ok();
|
||||
|
||||
if success {
|
||||
return ret.into_raw();
|
||||
}
|
||||
|
||||
std::ptr::null_mut()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
@@ -591,3 +572,24 @@ pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn no_crashes_on_context_deref() {
|
||||
let ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into()));
|
||||
std::mem::drop(ctx);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_context_double_close() {
|
||||
let ctx = dc_context_new(None, std::ptr::null_mut(), None);
|
||||
unsafe {
|
||||
dc_close(&ctx);
|
||||
dc_close(&ctx);
|
||||
}
|
||||
std::mem::drop(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
228
src/dc_array.rs
228
src/dc_array.rs
@@ -7,7 +7,7 @@ use crate::types::*;
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum dc_array_t {
|
||||
Locations(Vec<dc_location>),
|
||||
Uint(Vec<uintptr_t>),
|
||||
Uint(Vec<u32>),
|
||||
}
|
||||
|
||||
impl dc_array_t {
|
||||
@@ -24,18 +24,14 @@ impl dc_array_t {
|
||||
Box::into_raw(Box::new(self))
|
||||
}
|
||||
|
||||
pub fn add_uint(&mut self, item: uintptr_t) {
|
||||
pub fn add_id(&mut self, item: uint32_t) {
|
||||
if let Self::Uint(array) = self {
|
||||
array.push(item);
|
||||
} else {
|
||||
panic!("Attempt to add uint to array of other type");
|
||||
panic!("Attempt to add id 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)
|
||||
@@ -44,14 +40,6 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -59,14 +47,6 @@ impl dc_array_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]
|
||||
@@ -125,7 +105,7 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_id(&self, needle: uintptr_t) -> Option<usize> {
|
||||
pub fn search_id(&self, needle: u32) -> Option<usize> {
|
||||
if let Self::Uint(array) = self {
|
||||
for (i, &u) in array.iter().enumerate() {
|
||||
if u == needle {
|
||||
@@ -147,6 +127,12 @@ impl dc_array_t {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u32>> for dc_array_t {
|
||||
fn from(array: Vec<u32>) -> Self {
|
||||
dc_array_t::Uint(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<dc_location>> for dc_array_t {
|
||||
fn from(array: Vec<dc_location>) -> Self {
|
||||
dc_array_t::Locations(array)
|
||||
@@ -154,120 +140,27 @@ impl From<Vec<dc_location>> for dc_array_t {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_unref(array: *mut dc_array_t) {
|
||||
if array.is_null() {
|
||||
return;
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_id(array: *mut dc_array_t, item: uint32_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) {
|
||||
dc_array_add_uint(array, item as uintptr_t);
|
||||
assert!(!array.is_null());
|
||||
(*array).add_id(item);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
|
||||
if array.is_null() {
|
||||
0
|
||||
} else {
|
||||
(*array).len()
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_uint(index)
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
(*array).len()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_id(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
(*array).get_ptr(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_latitude(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_longitude(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0.0
|
||||
} else {
|
||||
(*array).get_accuracy(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_timestamp(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_chat_id(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_contact_id(index)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
0
|
||||
} else {
|
||||
(*array).get_msg_id(index)
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
(*array).get_id(index)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Locations(v) = &*array {
|
||||
if let Some(s) = &v[index].marker {
|
||||
@@ -276,7 +169,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
panic!("Not an array of locations");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -291,9 +184,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
|
||||
* 1=Location was reported independently.
|
||||
*/
|
||||
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
|
||||
if array.is_null() || index >= (*array).len() {
|
||||
return 0;
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Locations(v) = &*array {
|
||||
v[index].independent as libc::c_int
|
||||
@@ -307,10 +198,9 @@ pub unsafe fn dc_array_search_id(
|
||||
needle: uint32_t,
|
||||
ret_index: *mut size_t,
|
||||
) -> bool {
|
||||
if array.is_null() {
|
||||
return false;
|
||||
}
|
||||
if let Some(i) = (*array).search_id(needle as uintptr_t) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let Some(i) = (*array).search_id(needle) {
|
||||
if !ret_index.is_null() {
|
||||
*ret_index = i
|
||||
}
|
||||
@@ -320,10 +210,9 @@ pub unsafe fn dc_array_search_id(
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const uintptr_t {
|
||||
if array.is_null() {
|
||||
return 0 as *const uintptr_t;
|
||||
}
|
||||
pub unsafe fn dc_array_get_raw(array: *const dc_array_t) -> *const u32 {
|
||||
assert!(!array.is_null());
|
||||
|
||||
if let dc_array_t::Uint(v) = &*array {
|
||||
v.as_ptr()
|
||||
} else {
|
||||
@@ -335,57 +224,22 @@ 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()
|
||||
}
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_array_empty(array: *mut dc_array_t) {
|
||||
assert!(!array.is_null());
|
||||
|
||||
pub unsafe fn dc_array_empty(array: *mut dc_array_t) {
|
||||
if array.is_null() {
|
||||
return;
|
||||
}
|
||||
(*array).clear()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
|
||||
if array.is_null() {
|
||||
std::ptr::null_mut()
|
||||
} else {
|
||||
(*array).clone().into_raw()
|
||||
}
|
||||
}
|
||||
assert!(!array.is_null());
|
||||
|
||||
pub unsafe fn dc_array_get_string(
|
||||
array: *const dc_array_t,
|
||||
sep: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
if array.is_null() || sep.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
if let dc_array_t::Uint(v) = &*array {
|
||||
let cnt = v.len();
|
||||
let sep = as_str(sep);
|
||||
|
||||
let 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");
|
||||
}
|
||||
(*array).clone().into_raw()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::x::*;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
fn test_dc_array() {
|
||||
@@ -406,10 +260,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!(dc_array_get_id(arr, -1i32 as size_t), 0);
|
||||
assert_eq!(dc_array_get_id(arr, 1000 as size_t), 0);
|
||||
assert_eq!(dc_array_get_id(arr, 1001 as size_t), 0);
|
||||
|
||||
dc_array_empty(arr);
|
||||
|
||||
assert_eq!(dc_array_get_cnt(arr), 0);
|
||||
@@ -427,15 +277,17 @@ mod tests {
|
||||
assert_eq!(dc_array_get_id(arr, 2 as size_t), 13);
|
||||
assert_eq!(dc_array_get_id(arr, 3 as size_t), 666);
|
||||
|
||||
let str = dc_array_get_string(arr, b"-\x00" as *const u8 as *const libc::c_char);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"0-7-13-666-5000"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
|
||||
dc_array_unref(arr);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_dc_array_out_of_bounds() {
|
||||
let arr = dc_array_new(7);
|
||||
for i in 0..1000 {
|
||||
unsafe { dc_array_add_id(arr, (i + 2) as uint32_t) };
|
||||
}
|
||||
unsafe { dc_array_get_id(arr, 1000) };
|
||||
}
|
||||
}
|
||||
|
||||
2275
src/dc_chat.rs
2275
src/dc_chat.rs
File diff suppressed because it is too large
Load Diff
1710
src/dc_configure.rs
1710
src/dc_configure.rs
File diff suppressed because it is too large
Load Diff
120
src/dc_dehtml.rs
120
src/dc_dehtml.rs
@@ -1,6 +1,7 @@
|
||||
use lazy_static::lazy_static;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
@@ -34,56 +35,67 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
|
||||
add_text: AddText::YesRemoveLineEnds,
|
||||
last_href: None,
|
||||
};
|
||||
let mut saxparser = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut dehtml as *mut Dehtml as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(dehtml_starttag_cb),
|
||||
Some(dehtml_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, buf_terminated);
|
||||
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(buf_terminated));
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => {
|
||||
dehtml_starttag_cb(e, &mut dehtml, &reader)
|
||||
}
|
||||
Ok(quick_xml::events::Event::End(ref e)) => dehtml_endtag_cb(e, &mut dehtml),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => dehtml_text_cb(e, &mut dehtml),
|
||||
Ok(quick_xml::events::Event::CData(ref e)) => dehtml_cdata_cb(e, &mut dehtml),
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"Parse html error: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
|
||||
dehtml.strbuilder.strdup()
|
||||
}
|
||||
|
||||
unsafe fn dehtml_text_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
text: *const libc::c_char,
|
||||
_len: libc::c_int,
|
||||
) {
|
||||
let dehtml = &mut *(userdata as *mut Dehtml);
|
||||
|
||||
fn dehtml_text_cb(event: &BytesText, dehtml: &mut Dehtml) {
|
||||
if dehtml.add_text == AddText::YesPreserveLineEnds
|
||||
|| dehtml.add_text == AddText::YesRemoveLineEnds
|
||||
{
|
||||
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);
|
||||
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
|
||||
|
||||
if dehtml.add_text == AddText::YesRemoveLineEnds {
|
||||
dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref();
|
||||
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
|
||||
} else {
|
||||
dehtml.strbuilder += last_added.as_ref();
|
||||
dehtml.strbuilder += &last_added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut dehtml = &mut *(userdata as *mut Dehtml);
|
||||
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
|
||||
fn dehtml_cdata_cb(event: &BytesText, dehtml: &mut Dehtml) {
|
||||
if dehtml.add_text == AddText::YesPreserveLineEnds
|
||||
|| dehtml.add_text == AddText::YesRemoveLineEnds
|
||||
{
|
||||
let last_added = escaper::decode_html_buf_sloppy(event.escaped()).unwrap_or_default();
|
||||
|
||||
match tag.as_ref() {
|
||||
if dehtml.add_text == AddText::YesRemoveLineEnds {
|
||||
dehtml.strbuilder += LINE_RE.replace_all(&last_added, "\r").as_ref();
|
||||
} else {
|
||||
dehtml.strbuilder += &last_added;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
match tag.as_str() {
|
||||
"p" | "div" | "table" | "td" | "style" | "script" | "title" | "pre" => {
|
||||
dehtml.strbuilder += "\n\n";
|
||||
dehtml.add_text = AddText::YesRemoveLineEnds;
|
||||
@@ -105,15 +117,14 @@ unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn dehtml_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
attr: *mut *mut libc::c_char,
|
||||
fn dehtml_starttag_cb<B: std::io::BufRead>(
|
||||
event: &BytesStart,
|
||||
dehtml: &mut Dehtml,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let mut dehtml = &mut *(userdata as *mut Dehtml);
|
||||
let tag = std::ffi::CStr::from_ptr(tag).to_string_lossy();
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
match tag.as_ref() {
|
||||
match tag.as_str() {
|
||||
"p" | "div" | "table" | "td" => {
|
||||
dehtml.strbuilder += "\n\n";
|
||||
dehtml.add_text = AddText::YesRemoveLineEnds;
|
||||
@@ -130,14 +141,21 @@ unsafe fn dehtml_starttag_cb(
|
||||
dehtml.add_text = AddText::YesPreserveLineEnds;
|
||||
}
|
||||
"a" => {
|
||||
let text_c = std::ffi::CStr::from_ptr(dc_attr_find(
|
||||
attr,
|
||||
b"href\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
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 += "[";
|
||||
if let Some(href) = event.html_attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "href")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
let href = href
|
||||
.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default()
|
||||
.to_lowercase();
|
||||
|
||||
if !href.is_empty() {
|
||||
dehtml.last_href = Some(href);
|
||||
dehtml.strbuilder += "[";
|
||||
}
|
||||
}
|
||||
}
|
||||
"b" | "strong" => {
|
||||
|
||||
341
src/dc_e2ee.rs
341
src/dc_e2ee.rs
@@ -1,5 +1,8 @@
|
||||
//! End-to-end encryption support.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use mmime::clist::*;
|
||||
@@ -16,10 +19,12 @@ use mmime::mmapstring::*;
|
||||
use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_securejoin::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::*;
|
||||
use crate::key::*;
|
||||
use crate::keyring::*;
|
||||
use crate::peerstate::*;
|
||||
@@ -76,11 +81,6 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
let mut peerstates: Vec<Peerstate> = Vec::new();
|
||||
*helper = Default::default();
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "dc_e2ee_encrypt guaruanteed={}", e2ee_guaranteed
|
||||
);
|
||||
|
||||
if !(recipients_addr.is_null()
|
||||
|| in_out_message.is_null()
|
||||
|| !(*in_out_message).mm_parent.is_null()
|
||||
@@ -101,9 +101,11 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
let addr = context.sql.get_config(context, "configured_addr");
|
||||
|
||||
if let Some(addr) = addr {
|
||||
if let Some(public_key) =
|
||||
load_or_generate_self_public_key(context, &addr, in_out_message)
|
||||
{
|
||||
let pubkey_ret = load_or_generate_self_public_key(context, &addr).map_err(|err| {
|
||||
error!(context, 0, "Failed to load public key: {}", err);
|
||||
err
|
||||
});
|
||||
if let Ok(public_key) = pubkey_ret {
|
||||
/*only for random-seed*/
|
||||
if prefer_encrypt == EncryptPreference::Mutual || 0 != e2ee_guaranteed {
|
||||
do_encrypt = 1i32;
|
||||
@@ -111,10 +113,6 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
iter1 = (*recipients_addr).first;
|
||||
while !iter1.is_null() {
|
||||
let recipient_addr = to_string((*iter1).data as *const libc::c_char);
|
||||
info!(
|
||||
context,
|
||||
0, "dc_e2ee_encrypt recipient_addr {}", recipient_addr
|
||||
);
|
||||
if recipient_addr != addr {
|
||||
let peerstate =
|
||||
Peerstate::from_addr(context, &context.sql, &recipient_addr);
|
||||
@@ -148,7 +146,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
iter1 = if !iter1.is_null() {
|
||||
(*iter1).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,19 +171,19 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
mailprivacy_prepare_mime(in_out_message);
|
||||
let mut part_to_encrypt: *mut mailmime =
|
||||
(*in_out_message).mm_data.mm_message.mm_msg_mime;
|
||||
(*part_to_encrypt).mm_parent = 0 as *mut mailmime;
|
||||
(*part_to_encrypt).mm_parent = ptr::null_mut();
|
||||
let imffields_encrypted: *mut mailimf_fields = mailimf_fields_new_empty();
|
||||
/* mailmime_new_message_data() calls mailmime_fields_new_with_version() which would add the unwanted MIME-Version:-header */
|
||||
let message_to_encrypt: *mut mailmime = mailmime_new(
|
||||
MAILMIME_MESSAGE as libc::c_int,
|
||||
0 as *const libc::c_char,
|
||||
ptr::null(),
|
||||
0i32 as size_t,
|
||||
mailmime_fields_new_empty(),
|
||||
mailmime_get_content_message(),
|
||||
0 as *mut mailmime_data,
|
||||
0 as *mut mailmime_data,
|
||||
0 as *mut mailmime_data,
|
||||
0 as *mut clist,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
imffields_encrypted,
|
||||
part_to_encrypt,
|
||||
);
|
||||
@@ -217,7 +215,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
let field: *mut mailimf_field = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
})
|
||||
as *mut mailimf_field;
|
||||
if !field.is_null() {
|
||||
@@ -268,28 +266,28 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
imffields_unprotected,
|
||||
mailimf_field_new(
|
||||
MAILIMF_FIELD_SUBJECT as libc::c_int,
|
||||
0 as *mut mailimf_return,
|
||||
0 as *mut mailimf_orig_date,
|
||||
0 as *mut mailimf_from,
|
||||
0 as *mut mailimf_sender,
|
||||
0 as *mut mailimf_to,
|
||||
0 as *mut mailimf_cc,
|
||||
0 as *mut mailimf_bcc,
|
||||
0 as *mut mailimf_message_id,
|
||||
0 as *mut mailimf_orig_date,
|
||||
0 as *mut mailimf_from,
|
||||
0 as *mut mailimf_sender,
|
||||
0 as *mut mailimf_reply_to,
|
||||
0 as *mut mailimf_to,
|
||||
0 as *mut mailimf_cc,
|
||||
0 as *mut mailimf_bcc,
|
||||
0 as *mut mailimf_message_id,
|
||||
0 as *mut mailimf_in_reply_to,
|
||||
0 as *mut mailimf_references,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
subject,
|
||||
0 as *mut mailimf_comments,
|
||||
0 as *mut mailimf_keywords,
|
||||
0 as *mut mailimf_optional_field,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
),
|
||||
);
|
||||
clist_insert_after(
|
||||
@@ -317,7 +315,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
|
||||
/* create MIME-structure that will contain the encrypted text */
|
||||
let mut encrypted_part: *mut mailmime = new_data_part(
|
||||
0 as *mut libc::c_void,
|
||||
ptr::null_mut(),
|
||||
0i32 as size_t,
|
||||
b"multipart/encrypted\x00" as *const u8 as *const libc::c_char
|
||||
as *mut libc::c_char,
|
||||
@@ -403,7 +401,7 @@ unsafe fn new_data_part(
|
||||
let encoding_type: libc::c_int;
|
||||
let content_type_str: *mut libc::c_char;
|
||||
let mut do_encoding: libc::c_int;
|
||||
encoding = 0 as *mut mailmime_mechanism;
|
||||
encoding = ptr::null_mut();
|
||||
if default_content_type.is_null() {
|
||||
content_type_str =
|
||||
b"application/octet-stream\x00" as *const u8 as *const libc::c_char as *mut libc::c_char
|
||||
@@ -438,7 +436,7 @@ unsafe fn new_data_part(
|
||||
} else {
|
||||
encoding_type = default_encoding
|
||||
}
|
||||
encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char);
|
||||
encoding = mailmime_mechanism_new(encoding_type, ptr::null_mut());
|
||||
if encoding.is_null() {
|
||||
ok_to_continue = false;
|
||||
}
|
||||
@@ -446,10 +444,10 @@ unsafe fn new_data_part(
|
||||
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,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
);
|
||||
if mime_fields.is_null() {
|
||||
ok_to_continue = false;
|
||||
@@ -479,68 +477,55 @@ unsafe fn new_data_part(
|
||||
mailmime_content_free(content);
|
||||
}
|
||||
}
|
||||
return 0 as *mut mailmime;
|
||||
ptr::null_mut()
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Generate Keypairs
|
||||
******************************************************************************/
|
||||
unsafe fn load_or_generate_self_public_key(
|
||||
context: &Context,
|
||||
self_addr: impl AsRef<str>,
|
||||
_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;
|
||||
/// Load public key from database or generate a new one.
|
||||
///
|
||||
/// This will load a public key from the database, generating and
|
||||
/// storing a new one when one doesn't exist yet. Care is taken to
|
||||
/// only generate one key per context even when multiple threads call
|
||||
/// this function concurrently.
|
||||
fn load_or_generate_self_public_key(context: &Context, self_addr: impl AsRef<str>) -> Result<Key> {
|
||||
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
|
||||
return Ok(key);
|
||||
}
|
||||
let _guard = context.generating_key_mutex.lock().unwrap();
|
||||
|
||||
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
|
||||
if key.is_some() {
|
||||
return key;
|
||||
// Check again in case the key was generated while we were waiting for the lock.
|
||||
if let Some(key) = Key::from_self_public(context, &self_addr, &context.sql) {
|
||||
return Ok(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 {
|
||||
return None;
|
||||
}
|
||||
let key_creation_here = 1;
|
||||
S_IN_KEY_CREATION = 1;
|
||||
|
||||
let start = clock();
|
||||
let start = std::time::Instant::now();
|
||||
info!(
|
||||
context,
|
||||
0, "Generating keypair with {} bits, e={} ...", 2048, 65537,
|
||||
);
|
||||
|
||||
if let Some((public_key, private_key)) = dc_pgp_create_keypair(&self_addr) {
|
||||
if !dc_key_save_self_keypair(
|
||||
context,
|
||||
&public_key,
|
||||
&private_key,
|
||||
&self_addr,
|
||||
1i32,
|
||||
&context.sql,
|
||||
) {
|
||||
/*set default*/
|
||||
warn!(context, 0, "Cannot save keypair.",);
|
||||
} else {
|
||||
info!(
|
||||
match dc_pgp_create_keypair(&self_addr) {
|
||||
Some((public_key, private_key)) => {
|
||||
match dc_key_save_self_keypair(
|
||||
context,
|
||||
0,
|
||||
"Keypair generated in {:.3}s.",
|
||||
clock().wrapping_sub(start) as libc::c_double / 1000000 as libc::c_double,
|
||||
);
|
||||
&public_key,
|
||||
&private_key,
|
||||
&self_addr,
|
||||
1,
|
||||
&context.sql,
|
||||
) {
|
||||
true => {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Keypair generated in {:.3}s.",
|
||||
start.elapsed().as_secs()
|
||||
);
|
||||
Ok(public_key)
|
||||
}
|
||||
false => Err(format_err!("Failed to save keypair")),
|
||||
}
|
||||
}
|
||||
|
||||
key = Some(public_key);
|
||||
} else {
|
||||
warn!(context, 0, "Cannot create keypair.");
|
||||
None => Err(format_err!("Failed to generate keypair")),
|
||||
}
|
||||
|
||||
if 0 != key_creation_here {
|
||||
S_IN_KEY_CREATION = 0;
|
||||
}
|
||||
|
||||
key
|
||||
}
|
||||
|
||||
/* returns 1 if sth. was decrypted, 0 in other cases */
|
||||
@@ -555,25 +540,23 @@ pub unsafe fn dc_e2ee_decrypt(
|
||||
/*just a pointer into mailmime structure, must not be freed*/
|
||||
let imffields: *mut mailimf_fields = mailmime_find_mailimf_fields(in_out_message);
|
||||
let mut message_time = 0;
|
||||
let mut from: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut from: *mut libc::c_char = ptr::null_mut();
|
||||
let mut private_keyring = Keyring::default();
|
||||
let mut public_keyring_for_validate = Keyring::default();
|
||||
let mut gossip_headers: *mut mailimf_fields = 0 as *mut mailimf_fields;
|
||||
let mut gossip_headers: *mut mailimf_fields = ptr::null_mut();
|
||||
if !(in_out_message.is_null() || imffields.is_null()) {
|
||||
if !imffields.is_null() {
|
||||
let mut field: *mut mailimf_field =
|
||||
mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
|
||||
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
|
||||
}
|
||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
|
||||
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
|
||||
if !orig_date.is_null() {
|
||||
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
let mut field: *mut mailimf_field =
|
||||
mailimf_find_field(imffields, MAILIMF_FIELD_FROM as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_from.is_null() {
|
||||
from = mailimf_find_first_addr((*(*field).fld_data.fld_from).frm_mb_list)
|
||||
}
|
||||
field = mailimf_find_field(imffields, MAILIMF_FIELD_ORIG_DATE as libc::c_int);
|
||||
if !field.is_null() && !(*field).fld_data.fld_orig_date.is_null() {
|
||||
let orig_date: *mut mailimf_orig_date = (*field).fld_data.fld_orig_date;
|
||||
if !orig_date.is_null() {
|
||||
message_time = dc_timestamp_from_date((*orig_date).dt_date_time);
|
||||
if message_time != 0 && message_time > time() {
|
||||
message_time = time()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -594,7 +577,6 @@ pub unsafe fn dc_e2ee_decrypt(
|
||||
}
|
||||
} else if let Some(ref header) = autocryptheader {
|
||||
let p = Peerstate::from_header(context, header, message_time);
|
||||
info!(context, 0, "setting peerstate from header for {:?}", p.addr);
|
||||
assert!(p.save_to_db(&context.sql, true));
|
||||
peerstate = Some(p);
|
||||
}
|
||||
@@ -665,7 +647,7 @@ unsafe fn update_gossip_peerstates(
|
||||
let field: *mut mailimf_field = (if !cur1.is_null() {
|
||||
(*cur1).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailimf_field;
|
||||
if (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
let optional_field: *const mailimf_optional_field =
|
||||
@@ -717,7 +699,7 @@ unsafe fn update_gossip_peerstates(
|
||||
cur1 = if !cur1.is_null() {
|
||||
(*cur1).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,7 +732,7 @@ unsafe fn decrypt_recursive(
|
||||
{
|
||||
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
|
||||
while !cur.is_null() {
|
||||
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
|
||||
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
|
||||
if 0 != decrypt_part(
|
||||
context,
|
||||
(if !cur.is_null() {
|
||||
@@ -765,7 +747,7 @@ unsafe fn decrypt_recursive(
|
||||
) {
|
||||
if (*ret_gossip_headers).is_null() && ret_valid_signatures.len() > 0 {
|
||||
let mut dummy: size_t = 0i32 as size_t;
|
||||
let mut test: *mut mailimf_fields = 0 as *mut mailimf_fields;
|
||||
let mut test: *mut mailimf_fields = ptr::null_mut();
|
||||
if mailimf_envelope_and_optional_fields_parse(
|
||||
(*decrypted_mime).mm_mime_start,
|
||||
(*decrypted_mime).mm_length,
|
||||
@@ -796,7 +778,7 @@ unsafe fn decrypt_recursive(
|
||||
(if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailmime,
|
||||
private_keyring,
|
||||
public_keyring_for_validate,
|
||||
@@ -809,7 +791,7 @@ unsafe fn decrypt_recursive(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -844,12 +826,12 @@ unsafe fn decrypt_part(
|
||||
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 */
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut transfer_decoding_buffer: *mut libc::c_char = ptr::null_mut();
|
||||
/* must not be free()'d */
|
||||
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut decoded_data: *const libc::c_char = ptr::null_mut();
|
||||
let mut decoded_data_bytes: size_t = 0i32 as size_t;
|
||||
let mut sth_decrypted: libc::c_int = 0i32;
|
||||
*ret_decrypted_mime = 0 as *mut mailmime;
|
||||
*ret_decrypted_mime = ptr::null_mut();
|
||||
mime_data = (*mime).mm_data.mm_single;
|
||||
/* MAILMIME_DATA_FILE indicates, the data is in a file; AFAIK this is not used on parsing */
|
||||
if !((*mime_data).dt_type != MAILMIME_DATA_TEXT as libc::c_int
|
||||
@@ -863,7 +845,7 @@ unsafe fn decrypt_part(
|
||||
let field: *mut mailmime_field = (if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailmime_field;
|
||||
if !field.is_null() {
|
||||
if (*field).fld_type == MAILMIME_FIELD_TRANSFER_ENCODING as libc::c_int
|
||||
@@ -875,7 +857,7 @@ unsafe fn decrypt_part(
|
||||
cur = if !cur.is_null() {
|
||||
(*cur).next
|
||||
} else {
|
||||
0 as *mut clistcell
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -931,7 +913,7 @@ unsafe fn decrypt_part(
|
||||
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;
|
||||
let mut decrypted_mime: *mut mailmime = ptr::null_mut();
|
||||
if mailmime_parse(
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
@@ -1029,7 +1011,7 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
|
||||
(if !cur.is_null() {
|
||||
(*cur).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
ptr::null_mut()
|
||||
}) as *mut mailmime,
|
||||
) {
|
||||
return 1i32;
|
||||
@@ -1052,36 +1034,52 @@ unsafe fn contains_report(mime: *mut mailmime) -> libc::c_int {
|
||||
/* frees data referenced by "mailmime" but not freed by mailmime_free(). After calling this function, in_out_message cannot be used any longer! */
|
||||
pub unsafe fn dc_e2ee_thanks(helper: &mut dc_e2ee_helper_t) {
|
||||
free(helper.cdata_to_free);
|
||||
helper.cdata_to_free = 0 as *mut libc::c_void;
|
||||
helper.cdata_to_free = ptr::null_mut();
|
||||
}
|
||||
|
||||
/* makes sure, the private key exists, needed only for exporting keys and the case no message was sent before */
|
||||
// TODO should return bool /rtn
|
||||
pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
|
||||
/* normally, the key is generated as soon as the first mail is send
|
||||
(this is to gain some extra-random-seed by the message content and the timespan between program start and message sending) */
|
||||
let mut success: libc::c_int = 0i32;
|
||||
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
if self_addr.is_none() {
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot ensure secret key if context is not configured.",
|
||||
);
|
||||
} else if load_or_generate_self_public_key(context, self_addr.unwrap(), 0 as *mut mailmime)
|
||||
.is_some()
|
||||
{
|
||||
/*no random text data for seeding available*/
|
||||
success = 1;
|
||||
}
|
||||
|
||||
success
|
||||
/// Ensures a private key exists for the configured user.
|
||||
///
|
||||
/// Normally the private key is generated when the first message is
|
||||
/// sent but in a few locations there are no such guarantees,
|
||||
/// e.g. when exporting keys, and calling this function ensures a
|
||||
/// private key will be present.
|
||||
///
|
||||
/// If this succeeds you are also guaranteed that the
|
||||
/// [Config::ConfiguredAddr] is configured, this address is returned.
|
||||
pub fn dc_ensure_secret_key_exists(context: &Context) -> Result<String> {
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.ok_or(format_err!(concat!(
|
||||
"Failed to get self address, ",
|
||||
"cannot ensure secret key if not configured."
|
||||
)))?;
|
||||
load_or_generate_self_public_key(context, &self_addr)?;
|
||||
Ok(self_addr)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test_utils::*;
|
||||
|
||||
mod ensure_secret_key_exists {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_prexisting() {
|
||||
let t = dummy_context();
|
||||
let test_addr = configure_alice_keypair(&t.ctx);
|
||||
assert_eq!(dc_ensure_secret_key_exists(&t.ctx).unwrap(), test_addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_not_configured() {
|
||||
let t = dummy_context();
|
||||
assert!(dc_ensure_secret_key_exists(&t.ctx).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mailmime_parse() {
|
||||
let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de
|
||||
@@ -1142,4 +1140,47 @@ Sent with my Delta Chat Messenger: https://delta.chat";
|
||||
|
||||
unsafe { free(decrypted_mime as *mut _) };
|
||||
}
|
||||
|
||||
mod load_or_generate_self_public_key {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_existing() {
|
||||
let t = dummy_context();
|
||||
let addr = configure_alice_keypair(&t.ctx);
|
||||
let key = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // generating keys is expensive
|
||||
fn test_generate() {
|
||||
let t = dummy_context();
|
||||
let addr = "alice@example.org";
|
||||
let key0 = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key0.is_ok());
|
||||
let key1 = load_or_generate_self_public_key(&t.ctx, addr);
|
||||
assert!(key1.is_ok());
|
||||
assert_eq!(key0.unwrap(), key1.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_generate_concurrent() {
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
let t = dummy_context();
|
||||
let ctx = Arc::new(t.ctx);
|
||||
let ctx0 = Arc::clone(&ctx);
|
||||
let thr0 =
|
||||
thread::spawn(move || load_or_generate_self_public_key(&ctx0, "alice@example.org"));
|
||||
let ctx1 = Arc::clone(&ctx);
|
||||
let thr1 =
|
||||
thread::spawn(move || load_or_generate_self_public_key(&ctx1, "alice@example.org"));
|
||||
let res0 = thr0.join().unwrap();
|
||||
let res1 = thr1.join().unwrap();
|
||||
assert_eq!(res0.unwrap(), res1.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
440
src/dc_imex.rs
440
src/dc_imex.rs
@@ -1,20 +1,21 @@
|
||||
use std::ffi::CString;
|
||||
use std::ptr;
|
||||
|
||||
use mmime::mailmime_content::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::chat;
|
||||
use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::*;
|
||||
use crate::job::*;
|
||||
use crate::key::*;
|
||||
use crate::param::*;
|
||||
use crate::pgp::*;
|
||||
@@ -43,8 +44,8 @@ pub unsafe fn dc_imex(
|
||||
param.set(Param::Arg2, as_str(param2));
|
||||
}
|
||||
|
||||
dc_job_kill_action(context, 910);
|
||||
dc_job_add(context, 910, 0, param, 0);
|
||||
job_kill_action(context, Action::ImexImap);
|
||||
job_add(context, Action::ImexImap, 0, param, 0);
|
||||
}
|
||||
|
||||
/// Returns the filename of the backup if found, nullptr otherwise.
|
||||
@@ -61,7 +62,7 @@ pub unsafe fn dc_imex_has_backup(
|
||||
"Backup check: Cannot open directory \"{}\".\x00",
|
||||
dir_name.display(),
|
||||
);
|
||||
return 0 as *mut libc::c_char;
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let mut newest_backup_time = 0;
|
||||
let mut newest_backup_path: Option<std::path::PathBuf> = None;
|
||||
@@ -100,8 +101,8 @@ pub unsafe fn dc_imex_has_backup(
|
||||
}
|
||||
|
||||
pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let mut setup_file_name: *mut libc::c_char = ptr::null_mut();
|
||||
let mut msg: *mut dc_msg_t = ptr::null_mut();
|
||||
if dc_alloc_ongoing(context) == 0 {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
@@ -137,8 +138,7 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
setup_file_content_c.as_bytes().len(),
|
||||
))
|
||||
{
|
||||
let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t);
|
||||
if !(chat_id == 0i32 as libc::c_uint) {
|
||||
if let Ok(chat_id) = chat::create_by_contact_id(context, 1) {
|
||||
msg = dc_msg_new_untyped(context);
|
||||
(*msg).type_0 = Viewtype::File;
|
||||
(*msg).param.set(Param::File, as_str(setup_file_name));
|
||||
@@ -156,10 +156,9 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
.unwrap()
|
||||
.shall_stop_ongoing
|
||||
{
|
||||
let msg_id = dc_send_msg(context, chat_id, msg);
|
||||
if msg_id != 0 {
|
||||
if let Ok(msg_id) = chat::send_msg(context, chat_id, msg) {
|
||||
dc_msg_unref(msg);
|
||||
msg = 0 as *mut dc_msg_t;
|
||||
msg = ptr::null_mut();
|
||||
info!(context, 0, "Wait for setup message being sent ...",);
|
||||
loop {
|
||||
if context
|
||||
@@ -194,20 +193,15 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char {
|
||||
setup_code.strdup()
|
||||
}
|
||||
|
||||
/// Renders HTML body of a setup file message.
|
||||
///
|
||||
/// The `passphrase` must be at least 2 characters long.
|
||||
pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result<String> {
|
||||
ensure!(
|
||||
passphrase.len() >= 2,
|
||||
"Passphrase must be at least 2 chars long."
|
||||
);
|
||||
unsafe {
|
||||
ensure!(
|
||||
!(dc_ensure_secret_key_exists(context) == 0),
|
||||
"No secret key available."
|
||||
);
|
||||
}
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.ok_or(format_err!("Failed to get self address."))?;
|
||||
let self_addr = dc_ensure_secret_key_exists(context)?;
|
||||
let private_key = Key::from_self_private(context, self_addr, &context.sql)
|
||||
.ok_or(format_err!("Failed to get private key."))?;
|
||||
let ac_headers = match context
|
||||
@@ -290,12 +284,12 @@ pub unsafe fn dc_continue_key_transfer(
|
||||
setup_code: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let mut filename: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut filecontent: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut msg: *mut dc_msg_t = ptr::null_mut();
|
||||
let mut filename: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filecontent: *mut libc::c_char = ptr::null_mut();
|
||||
let mut filebytes: size_t = 0i32 as size_t;
|
||||
let mut armored_key: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut norm_sc: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut armored_key: *mut libc::c_char = ptr::null_mut();
|
||||
let mut norm_sc: *mut libc::c_char = ptr::null_mut();
|
||||
if !(msg_id <= 9i32 as libc::c_uint || setup_code.is_null()) {
|
||||
msg = dc_get_msg(context, msg_id);
|
||||
if msg.is_null()
|
||||
@@ -428,19 +422,19 @@ pub unsafe fn dc_decrypt_setup_file(
|
||||
filecontent: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let fc_buf: *mut libc::c_char;
|
||||
let mut fc_headerline: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut fc_base64: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut binary: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut fc_headerline: *const libc::c_char = ptr::null();
|
||||
let mut fc_base64: *const libc::c_char = ptr::null();
|
||||
let mut binary: *mut libc::c_char = ptr::null_mut();
|
||||
let mut binary_bytes: size_t = 0i32 as size_t;
|
||||
let mut indx: size_t = 0i32 as size_t;
|
||||
|
||||
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut payload: *mut libc::c_char = ptr::null_mut();
|
||||
fc_buf = dc_strdup(filecontent);
|
||||
if dc_split_armored_data(
|
||||
fc_buf,
|
||||
&mut fc_headerline,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut fc_base64,
|
||||
) && !fc_headerline.is_null()
|
||||
&& strcmp(
|
||||
@@ -484,7 +478,7 @@ pub unsafe fn dc_normalize_setup_code(
|
||||
in_0: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
if in_0.is_null() {
|
||||
return 0 as *mut libc::c_char;
|
||||
return ptr::null_mut();
|
||||
}
|
||||
let mut out = String::new();
|
||||
let mut outlen;
|
||||
@@ -512,17 +506,18 @@ pub unsafe fn dc_normalize_setup_code(
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) {
|
||||
let mut current_block: u64;
|
||||
pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) {
|
||||
let mut ok_to_continue = true;
|
||||
let mut success: libc::c_int = 0;
|
||||
let mut ongoing_allocated_here: libc::c_int = 0;
|
||||
let what: libc::c_int;
|
||||
|
||||
if !(0 == dc_alloc_ongoing(context)) {
|
||||
ongoing_allocated_here = 1;
|
||||
what = (*job).param.get_int(Param::Cmd).unwrap_or_default();
|
||||
let param1 = CString::yolo((*job).param.get(Param::Arg).unwrap_or_default());
|
||||
let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default());
|
||||
what = job.param.get_int(Param::Cmd).unwrap_or_default();
|
||||
let param1_s = job.param.get(Param::Arg).unwrap_or_default();
|
||||
let param1 = CString::yolo(param1_s);
|
||||
let _param2 = CString::yolo(job.param.get(Param::Arg2).unwrap_or_default());
|
||||
|
||||
if strlen(param1.as_ptr()) == 0 {
|
||||
error!(context, 0, "No Import/export dir/file given.",);
|
||||
@@ -534,185 +529,45 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t)
|
||||
} else {
|
||||
if what == 1 || what == 11 {
|
||||
/* before we export anything, make sure the private key exists */
|
||||
if 0 == dc_ensure_secret_key_exists(context) {
|
||||
if dc_ensure_secret_key_exists(context).is_err() {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Import/export: Cannot create private key or private key not available.",
|
||||
);
|
||||
current_block = 3568988166330621280;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
dc_create_folder(context, param1.as_ptr());
|
||||
current_block = 4495394744059808450;
|
||||
dc_create_folder(context, ¶m1_s);
|
||||
}
|
||||
} else {
|
||||
current_block = 4495394744059808450;
|
||||
}
|
||||
match current_block {
|
||||
3568988166330621280 => {}
|
||||
_ => match what {
|
||||
if ok_to_continue {
|
||||
match what {
|
||||
1 => {
|
||||
current_block = 10991094515395304355;
|
||||
match current_block {
|
||||
2973387206439775448 => {
|
||||
if 0 == import_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
11250025114629486028 => {
|
||||
if 0 == import_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
12669919903773909120 => {
|
||||
if 0 == export_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if 0 == export_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
3568988166330621280 => {}
|
||||
_ => {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
if 0 != export_self_keys(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
current_block = 11250025114629486028;
|
||||
match current_block {
|
||||
2973387206439775448 => {
|
||||
if 0 == import_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
11250025114629486028 => {
|
||||
if 0 == import_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
12669919903773909120 => {
|
||||
if 0 == export_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if 0 == export_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
3568988166330621280 => {}
|
||||
_ => {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
if 0 != import_self_keys(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
11 => {
|
||||
current_block = 12669919903773909120;
|
||||
match current_block {
|
||||
2973387206439775448 => {
|
||||
if 0 == import_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
11250025114629486028 => {
|
||||
if 0 == import_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
12669919903773909120 => {
|
||||
if 0 == export_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if 0 == export_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
3568988166330621280 => {}
|
||||
_ => {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
if 0 != export_backup(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
12 => {
|
||||
current_block = 2973387206439775448;
|
||||
match current_block {
|
||||
2973387206439775448 => {
|
||||
if 0 == import_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
11250025114629486028 => {
|
||||
if 0 == import_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
12669919903773909120 => {
|
||||
if 0 == export_backup(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if 0 == export_self_keys(context, param1.as_ptr()) {
|
||||
current_block = 3568988166330621280;
|
||||
} else {
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
3568988166330621280 => {}
|
||||
_ => {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
if 0 != import_backup(context, param1.as_ptr()) {
|
||||
info!(context, 0, "Import/export completed.",);
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -740,7 +595,10 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
0,
|
||||
"Import \"{}\" to \"{}\".",
|
||||
as_str(backup_to_import),
|
||||
as_str(context.get_dbfile()),
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or("<<None>>", |p| p.to_str().unwrap())
|
||||
);
|
||||
|
||||
if 0 != dc_is_configured(context) {
|
||||
@@ -748,8 +606,8 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
return 0;
|
||||
}
|
||||
&context.sql.close(&context);
|
||||
dc_delete_file(context, context.get_dbfile());
|
||||
if 0 != dc_file_exist(context, context.get_dbfile()) {
|
||||
dc_delete_file(context, context.get_dbfile().unwrap());
|
||||
if dc_file_exist(context, context.get_dbfile().unwrap()) {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot import backups: Cannot delete the old file.",
|
||||
@@ -757,12 +615,19 @@ unsafe fn import_backup(context: &Context, backup_to_import: *const libc::c_char
|
||||
return 0;
|
||||
}
|
||||
|
||||
if 0 == dc_copy_file(context, backup_to_import, context.get_dbfile()) {
|
||||
if !dc_copy_file(
|
||||
context,
|
||||
as_path(backup_to_import),
|
||||
context.get_dbfile().unwrap(),
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
/* error already logged */
|
||||
/* re-open copied database file */
|
||||
if !context.sql.open(&context, as_path(context.get_dbfile()), 0) {
|
||||
if !context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -881,11 +746,20 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
context,
|
||||
0,
|
||||
"Backup \"{}\" to \"{}\".",
|
||||
as_str(context.get_dbfile()),
|
||||
context
|
||||
.get_dbfile()
|
||||
.as_ref()
|
||||
.map_or("<<None>>", |p| p.to_str().unwrap()),
|
||||
as_str(dest_pathNfilename),
|
||||
);
|
||||
if !(0 == dc_copy_file(context, context.get_dbfile(), dest_pathNfilename)) {
|
||||
context.sql.open(&context, as_path(context.get_dbfile()), 0);
|
||||
if dc_copy_file(
|
||||
context,
|
||||
context.get_dbfile().unwrap(),
|
||||
as_path(dest_pathNfilename),
|
||||
) {
|
||||
context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0);
|
||||
closed = false;
|
||||
/* add all files as blobs to the database copy (this does not require the source to be locked, neigher the destination as it is used only here) */
|
||||
/*for logging only*/
|
||||
@@ -1041,10 +915,12 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_
|
||||
}
|
||||
}
|
||||
if closed {
|
||||
context.sql.open(&context, as_path(context.get_dbfile()), 0);
|
||||
context
|
||||
.sql
|
||||
.open(&context, &context.get_dbfile().unwrap(), 0);
|
||||
}
|
||||
if 0 != delete_dest_file {
|
||||
dc_delete_file(context, dest_pathNfilename);
|
||||
dc_delete_file(context, as_path(dest_pathNfilename));
|
||||
}
|
||||
free(dest_pathNfilename as *mut libc::c_void);
|
||||
|
||||
@@ -1062,16 +938,16 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
Maybe we should make the "default" key handlong also a little bit smarter
|
||||
(currently, the last imported key is the standard key unless it contains the string "legacy" in its name) */
|
||||
let mut imported_cnt: libc::c_int = 0;
|
||||
let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut suffix: *mut libc::c_char = ptr::null_mut();
|
||||
let mut path_plus_name: *mut libc::c_char = ptr::null_mut();
|
||||
let mut set_default: libc::c_int;
|
||||
let mut buf: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut buf: *mut libc::c_char = ptr::null_mut();
|
||||
let mut buf_bytes: size_t = 0 as size_t;
|
||||
// a pointer inside buf, MUST NOT be free()'d
|
||||
let mut private_key: *const libc::c_char;
|
||||
let mut buf2: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut buf2: *mut libc::c_char = ptr::null_mut();
|
||||
// a pointer inside buf2, MUST NOT be free()'d
|
||||
let mut buf2_headerline: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut buf2_headerline: *const libc::c_char = ptr::null_mut();
|
||||
if !dir_name.is_null() {
|
||||
let dir = std::path::Path::new(as_str(dir_name));
|
||||
let dir_handle = std::fs::read_dir(dir);
|
||||
@@ -1092,7 +968,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
free(suffix as *mut libc::c_void);
|
||||
let name_f = entry.file_name();
|
||||
let name_c = name_f.to_c_string().unwrap();
|
||||
suffix = dc_get_filesuffix_lc(name_c.as_ptr());
|
||||
suffix = dc_get_filesuffix_lc(name_f.to_string_lossy());
|
||||
if suffix.is_null()
|
||||
|| strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0
|
||||
{
|
||||
@@ -1106,7 +982,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) ->
|
||||
);
|
||||
info!(context, 0, "Checking: {}", as_str(path_plus_name));
|
||||
free(buf as *mut libc::c_void);
|
||||
buf = 0 as *mut libc::c_char;
|
||||
buf = ptr::null_mut();
|
||||
if 0 == dc_read_file(
|
||||
context,
|
||||
path_plus_name,
|
||||
@@ -1264,7 +1140,7 @@ unsafe fn export_key_to_asc_file(
|
||||
)
|
||||
}
|
||||
info!(context, 0, "Exporting key {}", as_str(file_name),);
|
||||
dc_delete_file(context, file_name);
|
||||
dc_delete_file(context, as_path(file_name));
|
||||
if !key.write_asc_to_file(file_name, context) {
|
||||
error!(context, 0, "Cannot write key to {}", as_str(file_name),);
|
||||
} else {
|
||||
@@ -1284,147 +1160,15 @@ unsafe fn export_key_to_asc_file(
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::CStr;
|
||||
|
||||
use num_traits::ToPrimitive;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::key;
|
||||
use crate::test_utils::*;
|
||||
|
||||
unsafe extern "C" fn logging_cb(
|
||||
_ctx: &Context,
|
||||
evt: Event,
|
||||
_d1: uintptr_t,
|
||||
d2: uintptr_t,
|
||||
) -> uintptr_t {
|
||||
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
|
||||
match evt {
|
||||
Event::INFO => println!("I: {}", to_str(d2)),
|
||||
Event::WARNING => println!("W: {}", to_str(d2)),
|
||||
Event::ERROR => println!("E: {}", to_str(d2)),
|
||||
_ => (),
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Create Alice with a pre-generated keypair.
|
||||
fn create_alice_keypair(ctx: &Context) {
|
||||
ctx.set_config(Config::ConfiguredAddr, Some("alice@example.org"))
|
||||
.unwrap();
|
||||
|
||||
// The keypair was created using:
|
||||
// let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com")
|
||||
// .unwrap();
|
||||
// println!("{}", public.to_base64(64));
|
||||
// println!("{}", private.to_base64(64));
|
||||
let public = key::Key::from_base64(
|
||||
concat!(
|
||||
"xsBNBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
|
||||
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
|
||||
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
|
||||
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
|
||||
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
|
||||
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAHNEzxhbGljZUBleGFtcGxl",
|
||||
"LmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iai",
|
||||
"x4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9",
|
||||
"OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkK",
|
||||
"A8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea",
|
||||
"6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6",
|
||||
"GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUK",
|
||||
"u5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxD",
|
||||
"Fc7ATQRdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG",
|
||||
"9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av",
|
||||
"62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/R",
|
||||
"noW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q",
|
||||
"4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAm",
|
||||
"jxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABwsB2BBgBCAAgBQJdPOn4",
|
||||
"AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoW",
|
||||
"qEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwX",
|
||||
"FNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9m",
|
||||
"MjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6RDXIeYJf",
|
||||
"qrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMlammDliPw",
|
||||
"sK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKObzPqgJCGw",
|
||||
"jTglkixw+aSTXw=="
|
||||
),
|
||||
KeyType::Public,
|
||||
)
|
||||
.unwrap();
|
||||
let private = key::Key::from_base64(
|
||||
concat!(
|
||||
"xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
|
||||
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
|
||||
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
|
||||
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
|
||||
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
|
||||
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtq",
|
||||
"m1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353",
|
||||
"r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68",
|
||||
"JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6F",
|
||||
"FrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHb",
|
||||
"Iu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3V",
|
||||
"WushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0S",
|
||||
"ut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQ",
|
||||
"sWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEm",
|
||||
"dr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8k",
|
||||
"QrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJW",
|
||||
"yyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj",
|
||||
"5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3",
|
||||
"jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeG",
|
||||
"Kyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl08",
|
||||
"6fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQ",
|
||||
"k6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Ee",
|
||||
"h+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BM",
|
||||
"zcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwb",
|
||||
"YklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP",
|
||||
"12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmh",
|
||||
"o0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugz",
|
||||
"OmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnF",
|
||||
"n7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6",
|
||||
"uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEe",
|
||||
"LrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCIC",
|
||||
"N1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86K",
|
||||
"C2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIdd",
|
||||
"KsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T",
|
||||
"/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZL",
|
||||
"j3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtp",
|
||||
"Do5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9u",
|
||||
"RMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe",
|
||||
"/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH",
|
||||
"95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9",
|
||||
"QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ",
|
||||
"8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//",
|
||||
"wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg",
|
||||
"9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwK",
|
||||
"Gjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPB",
|
||||
"f4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAg",
|
||||
"BQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/",
|
||||
"dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJ",
|
||||
"ywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJ",
|
||||
"uiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6",
|
||||
"RDXIeYJfqrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMl",
|
||||
"ammDliPwsK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKOb",
|
||||
"zPqgJCGwjTglkixw+aSTXw=="
|
||||
),
|
||||
KeyType::Private,
|
||||
)
|
||||
.unwrap();
|
||||
let saved = key::dc_key_save_self_keypair(
|
||||
&ctx,
|
||||
&public,
|
||||
&private,
|
||||
"alice@example.org",
|
||||
1,
|
||||
&ctx.sql,
|
||||
);
|
||||
assert_eq!(saved, true, "Failed to save Alice's key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_render_setup_file() {
|
||||
let t = test_context(Some(logging_cb));
|
||||
|
||||
create_alice_keypair(&t.ctx); // Trick things to think we're configured.
|
||||
configure_alice_keypair(&t.ctx);
|
||||
let msg = dc_render_setup_file(&t.ctx, "hello").unwrap();
|
||||
println!("{}", &msg);
|
||||
// Check some substrings, indicating things got substituted.
|
||||
@@ -1456,7 +1200,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_render_setup_file_newline_replace() {
|
||||
let t = test_context(Some(ac_setup_msg_cb));
|
||||
create_alice_keypair(&t.ctx);
|
||||
configure_alice_keypair(&t.ctx);
|
||||
let msg = dc_render_setup_file(&t.ctx, "pw").unwrap();
|
||||
println!("{}", &msg);
|
||||
assert!(msg.contains("<p>hello<br>there</p>"));
|
||||
|
||||
1285
src/dc_job.rs
1285
src/dc_job.rs
File diff suppressed because it is too large
Load Diff
@@ -1,209 +0,0 @@
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::imap::Imap;
|
||||
use crate::x::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct dc_jobthread_t {
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
pub imap: Imap,
|
||||
pub state: Arc<(Mutex<JobState>, Condvar)>,
|
||||
}
|
||||
|
||||
pub fn dc_jobthread_init(
|
||||
name: &'static str,
|
||||
folder_config_name: &'static str,
|
||||
imap: Imap,
|
||||
) -> dc_jobthread_t {
|
||||
dc_jobthread_t {
|
||||
name,
|
||||
folder_config_name,
|
||||
imap,
|
||||
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct JobState {
|
||||
idle: bool,
|
||||
jobs_needed: i32,
|
||||
suspended: i32,
|
||||
using_handle: i32,
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_suspend(
|
||||
context: &Context,
|
||||
jobthread: &dc_jobthread_t,
|
||||
suspend: libc::c_int,
|
||||
) {
|
||||
if 0 != suspend {
|
||||
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
|
||||
{
|
||||
jobthread.state.0.lock().unwrap().suspended = 1;
|
||||
}
|
||||
dc_jobthread_interrupt_idle(context, jobthread);
|
||||
loop {
|
||||
let using_handle = jobthread.state.0.lock().unwrap().using_handle;
|
||||
if using_handle == 0 {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
|
||||
}
|
||||
} else {
|
||||
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.suspended = 0;
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobthread_t) {
|
||||
{
|
||||
jobthread.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
|
||||
|
||||
jobthread.imap.interrupt_idle();
|
||||
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_fetch(
|
||||
context: &Context,
|
||||
jobthread: &mut dc_jobthread_t,
|
||||
use_network: libc::c_int,
|
||||
) {
|
||||
let start;
|
||||
|
||||
{
|
||||
let &(ref lock, _) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.suspended {
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = 1;
|
||||
}
|
||||
|
||||
if 0 != use_network {
|
||||
start = clock();
|
||||
if !(0 == connect_to_imap(context, jobthread)) {
|
||||
info!(context, 0, "{}-fetch started...", jobthread.name);
|
||||
jobthread.imap.fetch(context);
|
||||
|
||||
if jobthread.imap.should_reconnect() {
|
||||
info!(
|
||||
context,
|
||||
0, "{}-fetch aborted, starting over...", jobthread.name,
|
||||
);
|
||||
jobthread.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
jobthread.name,
|
||||
clock().wrapping_sub(start) as f64 / 1000.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
jobthread.state.0.lock().unwrap().using_handle = 0;
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* the typical fetch, idle, interrupt-idle
|
||||
******************************************************************************/
|
||||
|
||||
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
|
||||
if jobthread.imap.is_connected() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
|
||||
|
||||
if !(0 == ret_connected) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
jobthread.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
|
||||
if let Some(mvbox_name) = context
|
||||
.sql
|
||||
.get_config(context, jobthread.folder_config_name)
|
||||
{
|
||||
jobthread.imap.set_watch_folder(mvbox_name);
|
||||
} else {
|
||||
jobthread.imap.disconnect(context);
|
||||
ret_connected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret_connected
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_idle(
|
||||
context: &Context,
|
||||
jobthread: &dc_jobthread_t,
|
||||
use_network: libc::c_int,
|
||||
) {
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
jobthread.name,
|
||||
);
|
||||
state.jobs_needed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if 0 != state.suspended {
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = 1;
|
||||
|
||||
if 0 == use_network {
|
||||
state.using_handle = 0;
|
||||
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connect_to_imap(context, jobthread);
|
||||
info!(context, 0, "{}-IDLE started...", jobthread.name,);
|
||||
jobthread.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", jobthread.name);
|
||||
|
||||
jobthread.state.0.lock().unwrap().using_handle = 0;
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::ffi::CString;
|
||||
use quick_xml;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_saxparser::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::job::*;
|
||||
use crate::param::*;
|
||||
use crate::sql;
|
||||
use crate::stock::StockMessage;
|
||||
@@ -69,15 +68,11 @@ impl dc_kml_t {
|
||||
}
|
||||
|
||||
// location streaming
|
||||
pub unsafe fn dc_send_locations_to_chat(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
seconds: libc::c_int,
|
||||
) {
|
||||
pub unsafe fn dc_send_locations_to_chat(context: &Context, chat_id: uint32_t, seconds: i64) {
|
||||
let now = time();
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let is_sending_locations_before: bool;
|
||||
if !(seconds < 0i32 || chat_id <= 9i32 as libc::c_uint) {
|
||||
if !(seconds < 0 || chat_id <= 9i32 as libc::c_uint) {
|
||||
is_sending_locations_before = dc_is_sending_locations_to_chat(context, chat_id);
|
||||
if sql::execute(
|
||||
context,
|
||||
@@ -88,11 +83,7 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
WHERE id=?",
|
||||
params![
|
||||
if 0 != seconds { now } else { 0 },
|
||||
if 0 != seconds {
|
||||
now + seconds as i64
|
||||
} else {
|
||||
0
|
||||
},
|
||||
if 0 != seconds { now + seconds } else { 0 },
|
||||
chat_id as i32,
|
||||
],
|
||||
)
|
||||
@@ -103,16 +94,11 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
(*msg).text =
|
||||
Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0));
|
||||
(*msg).param.set_int(Param::Cmd, 8);
|
||||
dc_send_msg(context, chat_id, msg);
|
||||
chat::send_msg(context, chat_id, msg).unwrap();
|
||||
} else if 0 == seconds && is_sending_locations_before {
|
||||
let stock_str = CString::new(context.stock_system_msg(
|
||||
StockMessage::MsgLocationDisabled,
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
))
|
||||
.unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
let stock_str =
|
||||
context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
}
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
@@ -121,12 +107,12 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
);
|
||||
if 0 != seconds {
|
||||
schedule_MAYBE_SEND_LOCATIONS(context, 0i32);
|
||||
dc_job_add(
|
||||
job_add(
|
||||
context,
|
||||
5007i32,
|
||||
Action::MaybeSendLocationsEnded,
|
||||
chat_id as libc::c_int,
|
||||
Params::new(),
|
||||
seconds + 1i32,
|
||||
seconds + 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -139,8 +125,8 @@ pub unsafe fn dc_send_locations_to_chat(
|
||||
******************************************************************************/
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn schedule_MAYBE_SEND_LOCATIONS(context: &Context, flags: libc::c_int) {
|
||||
if 0 != flags & 0x1 || !dc_job_action_exists(context, 5005) {
|
||||
dc_job_add(context, 5005, 0, Params::new(), 60);
|
||||
if 0 != flags & 0x1 || !job_action_exists(context, Action::MaybeSendLocations) {
|
||||
job_add(context, Action::MaybeSendLocations, 0, Params::new(), 60);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -201,7 +187,7 @@ pub fn dc_get_locations(
|
||||
contact_id: uint32_t,
|
||||
timestamp_from: i64,
|
||||
mut timestamp_to: i64,
|
||||
) -> *mut dc_array_t {
|
||||
) -> Vec<dc_location> {
|
||||
if timestamp_to == 0 {
|
||||
timestamp_to = time() + 10;
|
||||
}
|
||||
@@ -239,10 +225,10 @@ pub fn dc_get_locations(
|
||||
accuracy: row.get(3)?,
|
||||
timestamp: row.get(4)?,
|
||||
independent: row.get(5)?,
|
||||
msg_id: msg_id,
|
||||
msg_id,
|
||||
contact_id: row.get(7)?,
|
||||
chat_id: row.get(8)?,
|
||||
marker: marker,
|
||||
marker,
|
||||
};
|
||||
Ok(loc)
|
||||
},
|
||||
@@ -252,10 +238,10 @@ pub fn dc_get_locations(
|
||||
for location in locations {
|
||||
ret.push(location?);
|
||||
}
|
||||
Ok(dc_array_t::from(ret).into_raw())
|
||||
Ok(ret)
|
||||
},
|
||||
)
|
||||
.unwrap_or_else(|_| std::ptr::null_mut())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn is_marker(txt: &str) -> bool {
|
||||
@@ -479,165 +465,159 @@ pub unsafe fn dc_kml_parse(
|
||||
content_bytes: size_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,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
|
||||
if content_bytes > (1 * 1024 * 1024) {
|
||||
warn!(
|
||||
context,
|
||||
0, "A kml-files with {} bytes is larger than reasonably expected.", content_bytes,
|
||||
);
|
||||
} else {
|
||||
content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int);
|
||||
if !content_nullterminated.is_null() {
|
||||
kml.locations = Some(Vec::with_capacity(100));
|
||||
dc_saxparser_init(
|
||||
&mut saxparser,
|
||||
&mut kml as *mut dc_kml_t as *mut libc::c_void,
|
||||
);
|
||||
dc_saxparser_set_tag_handler(
|
||||
&mut saxparser,
|
||||
Some(kml_starttag_cb),
|
||||
Some(kml_endtag_cb),
|
||||
);
|
||||
dc_saxparser_set_text_handler(&mut saxparser, Some(kml_text_cb));
|
||||
dc_saxparser_parse(&mut saxparser, content_nullterminated);
|
||||
return kml;
|
||||
}
|
||||
|
||||
let content_null = dc_null_terminate(content, content_bytes as libc::c_int);
|
||||
if !content_null.is_null() {
|
||||
let mut reader = quick_xml::Reader::from_str(as_str(content_null));
|
||||
reader.trim_text(true);
|
||||
|
||||
kml.locations = Some(Vec::with_capacity(100));
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match reader.read_event(&mut buf) {
|
||||
Ok(quick_xml::events::Event::Start(ref e)) => kml_starttag_cb(e, &mut kml, &reader),
|
||||
Ok(quick_xml::events::Event::End(ref e)) => kml_endtag_cb(e, &mut kml),
|
||||
Ok(quick_xml::events::Event::Text(ref e)) => kml_text_cb(e, &mut kml, &reader),
|
||||
Err(e) => {
|
||||
error!(
|
||||
context,
|
||||
0,
|
||||
"Location parsing: Error at position {}: {:?}",
|
||||
reader.buffer_position(),
|
||||
e
|
||||
);
|
||||
}
|
||||
Ok(quick_xml::events::Event::Eof) => break,
|
||||
_ => (),
|
||||
}
|
||||
buf.clear();
|
||||
}
|
||||
}
|
||||
|
||||
free(content_nullterminated as *mut libc::c_void);
|
||||
free(content_null.cast());
|
||||
|
||||
kml
|
||||
}
|
||||
|
||||
unsafe fn kml_text_cb(userdata: *mut libc::c_void, text: *const libc::c_char, _len: libc::c_int) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if 0 != (*kml).tag & (0x4 | 0x10) {
|
||||
let mut val: *mut libc::c_char = dc_strdup(text);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\n\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\r\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b"\t\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_str_replace(
|
||||
&mut val,
|
||||
b" \x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if 0 != (*kml).tag & 0x4 && strlen(val) >= 19 {
|
||||
fn kml_text_cb<B: std::io::BufRead>(
|
||||
event: &BytesText,
|
||||
kml: &mut dc_kml_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
if 0 != kml.tag & (0x4 | 0x10) {
|
||||
let val = event.unescape_and_decode(reader).unwrap_or_default();
|
||||
|
||||
let val = val
|
||||
.replace("\n", "")
|
||||
.replace("\r", "")
|
||||
.replace("\t", "")
|
||||
.replace(" ", "");
|
||||
|
||||
if 0 != kml.tag & 0x4 && val.len() >= 19 {
|
||||
// YYYY-MM-DDTHH:MM:SSZ
|
||||
// 0 4 7 10 13 16 19
|
||||
let val_r = as_str(val);
|
||||
match chrono::NaiveDateTime::parse_from_str(val_r, "%Y-%m-%dT%H:%M:%SZ") {
|
||||
match chrono::NaiveDateTime::parse_from_str(&val, "%Y-%m-%dT%H:%M:%SZ") {
|
||||
Ok(res) => {
|
||||
(*kml).curr.timestamp = res.timestamp();
|
||||
if (*kml).curr.timestamp > time() {
|
||||
(*kml).curr.timestamp = time();
|
||||
kml.curr.timestamp = res.timestamp();
|
||||
if kml.curr.timestamp > time() {
|
||||
kml.curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
Err(_err) => {
|
||||
(*kml).curr.timestamp = time();
|
||||
kml.curr.timestamp = time();
|
||||
}
|
||||
}
|
||||
} else if 0 != (*kml).tag & 0x10 {
|
||||
let mut comma: *mut libc::c_char = strchr(val, ',' as i32);
|
||||
if !comma.is_null() {
|
||||
let longitude: *mut libc::c_char = val;
|
||||
let latitude: *mut libc::c_char = comma.offset(1isize);
|
||||
*comma = 0 as libc::c_char;
|
||||
comma = strchr(latitude, ',' as i32);
|
||||
if !comma.is_null() {
|
||||
*comma = 0 as libc::c_char
|
||||
}
|
||||
(*kml).curr.latitude = dc_atof(latitude);
|
||||
(*kml).curr.longitude = dc_atof(longitude)
|
||||
} else if 0 != kml.tag & 0x10 {
|
||||
let parts = val.splitn(2, ',').collect::<Vec<_>>();
|
||||
if parts.len() == 2 {
|
||||
kml.curr.longitude = parts[0].parse().unwrap_or_default();
|
||||
kml.curr.latitude = parts[1].parse().unwrap_or_default();
|
||||
}
|
||||
}
|
||||
free(val as *mut libc::c_void);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
if 0 != (*kml).tag & 0x1
|
||||
&& 0 != (*kml).curr.timestamp
|
||||
&& 0. != (*kml).curr.latitude
|
||||
&& 0. != (*kml).curr.longitude
|
||||
fn kml_endtag_cb(event: &BytesEnd, kml: &mut dc_kml_t) {
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
|
||||
if tag == "placemark" {
|
||||
if 0 != kml.tag & 0x1
|
||||
&& 0 != kml.curr.timestamp
|
||||
&& 0. != kml.curr.latitude
|
||||
&& 0. != kml.curr.longitude
|
||||
{
|
||||
let location = (*kml).curr.clone();
|
||||
((*kml).locations.as_mut().unwrap()).push(location);
|
||||
if let Some(ref mut locations) = kml.locations {
|
||||
locations.push(std::mem::replace(&mut kml.curr, dc_location::new()));
|
||||
}
|
||||
}
|
||||
(*kml).tag = 0
|
||||
kml.tag = 0
|
||||
};
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* parse kml-files
|
||||
******************************************************************************/
|
||||
unsafe fn kml_starttag_cb(
|
||||
userdata: *mut libc::c_void,
|
||||
tag: *const libc::c_char,
|
||||
attr: *mut *mut libc::c_char,
|
||||
fn kml_starttag_cb<B: std::io::BufRead>(
|
||||
event: &BytesStart,
|
||||
kml: &mut dc_kml_t,
|
||||
reader: &quick_xml::Reader<B>,
|
||||
) {
|
||||
let mut kml: *mut dc_kml_t = userdata as *mut dc_kml_t;
|
||||
if strcmp(tag, b"document\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
let addr: *const libc::c_char =
|
||||
dc_attr_find(attr, b"addr\x00" as *const u8 as *const libc::c_char);
|
||||
if !addr.is_null() {
|
||||
(*kml).addr = dc_strdup(addr)
|
||||
let tag = String::from_utf8_lossy(event.name()).trim().to_lowercase();
|
||||
if tag == "document" {
|
||||
if let Some(addr) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "addr")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
kml.addr = unsafe {
|
||||
addr.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default()
|
||||
.strdup()
|
||||
};
|
||||
}
|
||||
} else if strcmp(tag, b"placemark\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
(*kml).tag = 0x1;
|
||||
(*kml).curr.timestamp = 0;
|
||||
(*kml).curr.latitude = 0 as libc::c_double;
|
||||
(*kml).curr.longitude = 0.0f64;
|
||||
(*kml).curr.accuracy = 0.0f64
|
||||
} else if strcmp(tag, b"timestamp\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x1
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x2
|
||||
} else if strcmp(tag, b"when\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x2
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x2 | 0x4
|
||||
} else if strcmp(tag, b"point\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x1
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x8
|
||||
} else if strcmp(tag, b"coordinates\x00" as *const u8 as *const libc::c_char) == 0
|
||||
&& 0 != (*kml).tag & 0x8
|
||||
{
|
||||
(*kml).tag = 0x1 | 0x8 | 0x10;
|
||||
let accuracy: *const libc::c_char =
|
||||
dc_attr_find(attr, b"accuracy\x00" as *const u8 as *const libc::c_char);
|
||||
if !accuracy.is_null() {
|
||||
(*kml).curr.accuracy = dc_atof(accuracy)
|
||||
} else if tag == "placemark" {
|
||||
kml.tag = 0x1;
|
||||
kml.curr.timestamp = 0;
|
||||
kml.curr.latitude = 0 as libc::c_double;
|
||||
kml.curr.longitude = 0.0f64;
|
||||
kml.curr.accuracy = 0.0f64
|
||||
} else if tag == "timestamp" && 0 != kml.tag & 0x1 {
|
||||
kml.tag = 0x1 | 0x2
|
||||
} else if tag == "when" && 0 != kml.tag & 0x2 {
|
||||
kml.tag = 0x1 | 0x2 | 0x4
|
||||
} else if tag == "point" && 0 != kml.tag & 0x1 {
|
||||
kml.tag = 0x1 | 0x8
|
||||
} else if tag == "coordinates" && 0 != kml.tag & 0x8 {
|
||||
kml.tag = 0x1 | 0x8 | 0x10;
|
||||
if let Some(acc) = event.attributes().find(|attr| {
|
||||
attr.as_ref()
|
||||
.map(|a| String::from_utf8_lossy(a.key).trim().to_lowercase() == "accuracy")
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
let v = acc
|
||||
.unwrap()
|
||||
.unescape_and_decode_value(reader)
|
||||
.unwrap_or_default();
|
||||
|
||||
kml.curr.accuracy = v.trim().parse().unwrap_or_default();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) {
|
||||
free((*kml).addr as *mut libc::c_void);
|
||||
free(kml.addr 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) {
|
||||
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: &Job) {
|
||||
let now = time();
|
||||
let mut continue_streaming: libc::c_int = 1;
|
||||
info!(
|
||||
@@ -702,7 +682,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu
|
||||
let mut msg = dc_msg_new(context, Viewtype::Text);
|
||||
(*msg).hidden = 1;
|
||||
(*msg).param.set_int(Param::Cmd, 9);
|
||||
dc_send_msg(context, chat_id as u32, msg);
|
||||
// TODO: handle cleanup on error
|
||||
chat::send_msg(context, chat_id as u32, msg).unwrap();
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
Ok(())
|
||||
@@ -718,7 +699,7 @@ 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) {
|
||||
pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut Job) {
|
||||
// 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.
|
||||
@@ -740,8 +721,8 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
|
||||
"UPDATE chats SET locations_send_begin=0, locations_send_until=0 WHERE id=?",
|
||||
params![chat_id as i32],
|
||||
).is_ok() {
|
||||
let stock_str = CString::new(context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0)).unwrap();
|
||||
dc_add_device_msg(context, chat_id, stock_str.as_ptr());
|
||||
let stock_str = context.stock_system_msg(StockMessage::MsgLocationDisabled, "", "", 0);
|
||||
chat::add_device_msg(context, chat_id, stock_str);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
chat_id as usize,
|
||||
@@ -752,3 +733,46 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOC_ENDED(context: &Context, job: &mut
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::dummy_context;
|
||||
|
||||
#[test]
|
||||
fn test_dc_kml_parse() {
|
||||
unsafe {
|
||||
let context = dummy_context();
|
||||
|
||||
let xml =
|
||||
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</WHEN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></PlaceMARK>\n</Document>\n</kml>\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
|
||||
|
||||
assert!(!kml.addr.is_null());
|
||||
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
|
||||
|
||||
let locations_ref = &kml.locations.as_ref().unwrap();
|
||||
assert_eq!(locations_ref.len(), 2);
|
||||
|
||||
assert!(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!(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);
|
||||
|
||||
dc_kml_unref(&mut kml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
185
src/dc_lot.rs
185
src/dc_lot.rs
@@ -1,185 +0,0 @@
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_msg::*;
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct dc_lot_t {
|
||||
pub magic: uint32_t,
|
||||
pub text1_meaning: libc::c_int,
|
||||
pub text1: *mut libc::c_char,
|
||||
pub text2: *mut libc::c_char,
|
||||
pub timestamp: i64,
|
||||
pub state: libc::c_int,
|
||||
pub id: uint32_t,
|
||||
pub fingerprint: *mut libc::c_char,
|
||||
pub invitenumber: *mut libc::c_char,
|
||||
pub auth: *mut libc::c_char,
|
||||
}
|
||||
|
||||
/* *
|
||||
* @class dc_lot_t
|
||||
*
|
||||
* An object containing a set of values.
|
||||
* The meaning of the values is defined by the function returning the object.
|
||||
* Lot objects are created
|
||||
* eg. by chatlist.get_summary() or dc_msg_get_summary().
|
||||
*
|
||||
* NB: _Lot_ is used in the meaning _heap_ here.
|
||||
*/
|
||||
pub unsafe fn dc_lot_new() -> *mut dc_lot_t {
|
||||
let mut lot: *mut dc_lot_t;
|
||||
lot = calloc(1, ::std::mem::size_of::<dc_lot_t>()) as *mut dc_lot_t;
|
||||
assert!(!lot.is_null());
|
||||
|
||||
(*lot).magic = 0x107107i32 as uint32_t;
|
||||
(*lot).text1_meaning = 0i32;
|
||||
|
||||
lot
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_empty(mut lot: *mut dc_lot_t) {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return;
|
||||
}
|
||||
free((*lot).text1 as *mut libc::c_void);
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32;
|
||||
free((*lot).text2 as *mut libc::c_void);
|
||||
(*lot).text2 = 0 as *mut libc::c_char;
|
||||
free((*lot).fingerprint as *mut libc::c_void);
|
||||
(*lot).fingerprint = 0 as *mut libc::c_char;
|
||||
free((*lot).invitenumber as *mut libc::c_void);
|
||||
(*lot).invitenumber = 0 as *mut libc::c_char;
|
||||
free((*lot).auth as *mut libc::c_void);
|
||||
(*lot).auth = 0 as *mut libc::c_char;
|
||||
(*lot).timestamp = 0;
|
||||
(*lot).state = 0i32;
|
||||
(*lot).id = 0i32 as uint32_t;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_unref(mut set: *mut dc_lot_t) {
|
||||
if set.is_null() || (*set).magic != 0x107107i32 as libc::c_uint {
|
||||
return;
|
||||
}
|
||||
dc_lot_empty(set);
|
||||
(*set).magic = 0i32 as uint32_t;
|
||||
free(set as *mut libc::c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text1(lot: *const dc_lot_t) -> *mut libc::c_char {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
dc_strdup_keep_null((*lot).text1)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text2(lot: *const dc_lot_t) -> *mut libc::c_char {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
dc_strdup_keep_null((*lot).text2)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_text1_meaning(lot: *const dc_lot_t) -> libc::c_int {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0i32;
|
||||
}
|
||||
|
||||
(*lot).text1_meaning
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_state(lot: *const dc_lot_t) -> libc::c_int {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0i32;
|
||||
}
|
||||
|
||||
(*lot).state
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_id(lot: *const dc_lot_t) -> uint32_t {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0i32 as uint32_t;
|
||||
}
|
||||
|
||||
(*lot).id
|
||||
}
|
||||
|
||||
pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
|
||||
if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint {
|
||||
return 0;
|
||||
}
|
||||
|
||||
(*lot).timestamp
|
||||
}
|
||||
|
||||
/* library-internal */
|
||||
/* in practice, the user additionally cuts the string himself pixel-accurate */
|
||||
pub unsafe fn dc_lot_fill(
|
||||
mut lot: *mut dc_lot_t,
|
||||
msg: *mut dc_msg_t,
|
||||
chat: *const Chat,
|
||||
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 = 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 = 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_none() {
|
||||
(*lot).text1 = 0 as *mut libc::c_char;
|
||||
(*lot).text1_meaning = 0i32
|
||||
} else {
|
||||
if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint {
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_display_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
} else {
|
||||
if let Some(contact) = contact {
|
||||
(*lot).text1 = contact.get_first_name().strdup();
|
||||
} else {
|
||||
(*lot).text1 = std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
(*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, msgtext_ptr, &mut (*msg).param, 160, context);
|
||||
|
||||
(*lot).timestamp = dc_msg_get_timestamp(msg);
|
||||
(*lot).state = (*msg).state;
|
||||
}
|
||||
@@ -9,12 +9,11 @@ use mmime::mailmime_types_helper::*;
|
||||
use mmime::mailmime_write_mem::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
use std::ptr;
|
||||
|
||||
use crate::chat::{self, Chat};
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::context::{dc_get_version_str, Context};
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_msg::*;
|
||||
@@ -25,8 +24,7 @@ use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct dc_mimefactory_t<'a> {
|
||||
pub from_addr: *mut libc::c_char,
|
||||
@@ -38,7 +36,7 @@ pub struct dc_mimefactory_t<'a> {
|
||||
pub rfc724_mid: *mut libc::c_char,
|
||||
pub loaded: dc_mimefactory_loaded_t,
|
||||
pub msg: *mut dc_msg_t<'a>,
|
||||
pub chat: *mut Chat<'a>,
|
||||
pub chat: Option<Chat<'a>>,
|
||||
pub increation: libc::c_int,
|
||||
pub in_reply_to: *mut libc::c_char,
|
||||
pub references: *mut libc::c_char,
|
||||
@@ -52,23 +50,11 @@ pub struct dc_mimefactory_t<'a> {
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type dc_mimefactory_loaded_t = libc::c_uint;
|
||||
pub const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2;
|
||||
type dc_mimefactory_loaded_t = libc::c_uint;
|
||||
const DC_MF_MDN_LOADED: dc_mimefactory_loaded_t = 2;
|
||||
pub const DC_MF_MSG_LOADED: dc_mimefactory_loaded_t = 1;
|
||||
pub const DC_MF_NOTHING_LOADED: dc_mimefactory_loaded_t = 0;
|
||||
|
||||
pub unsafe fn dc_mimefactory_init<'a>(factory: *mut dc_mimefactory_t<'a>, context: &'a Context) {
|
||||
if factory.is_null() {
|
||||
return;
|
||||
}
|
||||
memset(
|
||||
factory as *mut libc::c_void,
|
||||
0,
|
||||
::std::mem::size_of::<dc_mimefactory_t>(),
|
||||
);
|
||||
(*factory).context = context;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
|
||||
if factory.is_null() {
|
||||
return;
|
||||
@@ -93,8 +79,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
|
||||
}
|
||||
dc_msg_unref((*factory).msg);
|
||||
(*factory).msg = 0 as *mut dc_msg_t;
|
||||
dc_chat_unref((*factory).chat);
|
||||
(*factory).chat = 0 as *mut Chat;
|
||||
(*factory).chat = None;
|
||||
free((*factory).in_reply_to as *mut libc::c_void);
|
||||
(*factory).in_reply_to = 0 as *mut libc::c_char;
|
||||
free((*factory).references as *mut libc::c_void);
|
||||
@@ -125,131 +110,139 @@ pub unsafe fn dc_mimefactory_load_msg(
|
||||
(*factory).recipients_names = clist_new();
|
||||
(*factory).recipients_addr = clist_new();
|
||||
(*factory).msg = dc_msg_new_untyped(context);
|
||||
(*factory).chat = dc_chat_new(context);
|
||||
if dc_msg_load_from_db((*factory).msg, context, msg_id)
|
||||
&& dc_chat_load_from_db((*factory).chat, (*(*factory).msg).chat_id)
|
||||
{
|
||||
load_from(factory);
|
||||
(*factory).req_mdn = 0;
|
||||
if 0 != dc_chat_is_self_talk((*factory).chat) {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup((*factory).from_addr) as *mut libc::c_void,
|
||||
);
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT c.authname, c.addr \
|
||||
FROM chats_contacts cc \
|
||||
LEFT JOIN contacts c ON cc.contact_id=c.id \
|
||||
WHERE cc.chat_id=? AND cc.contact_id>9;",
|
||||
params![(*(*factory).msg).chat_id as i32],
|
||||
|row| {
|
||||
let authname: String = row.get(0)?;
|
||||
let addr: String = row.get(1)?;
|
||||
Ok((authname, addr))
|
||||
},
|
||||
|rows| {
|
||||
for row in rows {
|
||||
let (authname, addr) = row?;
|
||||
let addr_c = addr.strdup();
|
||||
if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
if !authname.is_empty() {
|
||||
authname.strdup()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
} as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
addr_c as *mut libc::c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let command = (*(*factory).msg)
|
||||
.param
|
||||
.get_int(Param::Cmd)
|
||||
.unwrap_or_default();
|
||||
if dc_msg_load_from_db((*factory).msg, context, msg_id) {
|
||||
if let Ok(chat) = Chat::load_from_db(context, (*(*factory).msg).chat_id) {
|
||||
(*factory).chat = Some(chat);
|
||||
|
||||
if command == 5 {
|
||||
let email_to_remove = (*(*factory).msg).param.get(Param::Arg).unwrap_or_default();
|
||||
let email_to_remove_c = email_to_remove.strdup();
|
||||
let chat = (*factory).chat.as_ref().unwrap();
|
||||
|
||||
let self_addr = context
|
||||
load_from(factory);
|
||||
(*factory).req_mdn = 0;
|
||||
if chat.is_self_talk() {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
dc_strdup_keep_null((*factory).from_displayname) as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup((*factory).from_addr) as *mut libc::c_void,
|
||||
);
|
||||
} else {
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.query_map(
|
||||
"SELECT c.authname, c.addr \
|
||||
FROM chats_contacts cc \
|
||||
LEFT JOIN contacts c ON cc.contact_id=c.id \
|
||||
WHERE cc.chat_id=? AND cc.contact_id>9;",
|
||||
params![(*(*factory).msg).chat_id as i32],
|
||||
|row| {
|
||||
let authname: String = row.get(0)?;
|
||||
let addr: String = row.get(1)?;
|
||||
Ok((authname, addr))
|
||||
},
|
||||
|rows| {
|
||||
for row in rows {
|
||||
let (authname, addr) = row?;
|
||||
let addr_c = addr.strdup();
|
||||
if clist_search_string_nocase((*factory).recipients_addr, addr_c)
|
||||
== 0
|
||||
{
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
if !authname.is_empty() {
|
||||
authname.strdup()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
addr_c as *mut libc::c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let command = (*(*factory).msg)
|
||||
.param
|
||||
.get_int(Param::Cmd)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !email_to_remove.is_empty() && email_to_remove != self_addr {
|
||||
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c)
|
||||
== 0
|
||||
{
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
0 as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
email_to_remove_c as *mut libc::c_void,
|
||||
);
|
||||
if command == 5 {
|
||||
let email_to_remove =
|
||||
(*(*factory).msg).param.get(Param::Arg).unwrap_or_default();
|
||||
let email_to_remove_c = email_to_remove.strdup();
|
||||
|
||||
let self_addr = context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default();
|
||||
|
||||
if !email_to_remove.is_empty() && email_to_remove != self_addr {
|
||||
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove_c)
|
||||
== 0
|
||||
{
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
0 as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
email_to_remove_c as *mut libc::c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if command != 6
|
||||
&& command != 7
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
(*factory).req_mdn = 1
|
||||
}
|
||||
}
|
||||
if command != 6
|
||||
&& command != 7
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
(*factory).req_mdn = 1
|
||||
}
|
||||
}
|
||||
let row = context.sql.query_row(
|
||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||
params![(*(*factory).msg).id as i32],
|
||||
|row| {
|
||||
let in_reply_to: String = row.get(0)?;
|
||||
let references: String = row.get(1)?;
|
||||
let row = context.sql.query_row(
|
||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||
params![(*(*factory).msg).id as i32],
|
||||
|row| {
|
||||
let in_reply_to: String = row.get(0)?;
|
||||
let references: String = row.get(1)?;
|
||||
|
||||
Ok((in_reply_to, references))
|
||||
},
|
||||
);
|
||||
match row {
|
||||
Ok((in_reply_to, references)) => {
|
||||
(*factory).in_reply_to = in_reply_to.strdup();
|
||||
(*factory).references = references.strdup();
|
||||
Ok((in_reply_to, references))
|
||||
},
|
||||
);
|
||||
match row {
|
||||
Ok((in_reply_to, references)) => {
|
||||
(*factory).in_reply_to = in_reply_to.strdup();
|
||||
(*factory).references = references.strdup();
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
error!(
|
||||
context,
|
||||
0, "mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
success = 1;
|
||||
(*factory).loaded = DC_MF_MSG_LOADED;
|
||||
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
|
||||
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
|
||||
success = 1;
|
||||
(*factory).loaded = DC_MF_MSG_LOADED;
|
||||
(*factory).timestamp = (*(*factory).msg).timestamp_sort;
|
||||
(*factory).rfc724_mid = dc_strdup((*(*factory).msg).rfc724_mid)
|
||||
}
|
||||
}
|
||||
if 0 != success {
|
||||
(*factory).increation = dc_msg_is_increation((*factory).msg)
|
||||
@@ -478,18 +471,19 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
.map(|s| format!("/{}", s))
|
||||
.unwrap_or_default();
|
||||
let os_part = CString::new(os_part).expect("String -> CString conversion failed");
|
||||
|
||||
let version = dc_get_version_str();
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
strdup(b"X-Mailer\x00" as *const u8 as *const libc::c_char),
|
||||
dc_mprintf(
|
||||
b"Delta Chat Core %s%s\x00" as *const u8 as *const libc::c_char,
|
||||
DC_VERSION_STR as *const u8 as *const libc::c_char,
|
||||
version,
|
||||
os_part.as_ptr(),
|
||||
),
|
||||
),
|
||||
);
|
||||
free(version.cast());
|
||||
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -514,11 +508,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
if (*factory).loaded as libc::c_uint == DC_MF_MSG_LOADED as libc::c_int as libc::c_uint {
|
||||
/* Render a normal message
|
||||
*********************************************************************/
|
||||
let chat: *mut Chat = (*factory).chat;
|
||||
let msg: *mut dc_msg_t = (*factory).msg;
|
||||
let chat = (*factory).chat.as_ref().unwrap();
|
||||
let msg = (*factory).msg;
|
||||
let mut meta_part: *mut mailmime = 0 as *mut mailmime;
|
||||
let mut placeholdertext: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int {
|
||||
if chat.typ == Chattype::VerifiedGroup {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -541,29 +535,28 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
if (*chat).gossiped_timestamp == 0
|
||||
|| ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
|
||||
if chat.gossiped_timestamp == 0
|
||||
|| (chat.gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
|
||||
{
|
||||
do_gossip = 1
|
||||
}
|
||||
|
||||
/* build header etc. */
|
||||
let command = (*msg).param.get_int(Param::Cmd).unwrap_or_default();
|
||||
if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
|
||||
{
|
||||
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
strdup(b"Chat-Group-ID\x00" as *const u8 as *const libc::c_char),
|
||||
dc_strdup((*chat).grpid),
|
||||
chat.grpid.strdup(),
|
||||
),
|
||||
);
|
||||
let name = CString::yolo(chat.name.as_bytes());
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
strdup(b"Chat-Group-Name\x00" as *const u8 as *const libc::c_char),
|
||||
dc_encode_header_words((*chat).name),
|
||||
dc_encode_header_words(name.as_ptr()),
|
||||
),
|
||||
);
|
||||
if command == 5 {
|
||||
@@ -594,7 +587,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
email_to_add,
|
||||
),
|
||||
);
|
||||
grpimage = (*chat).param.get(Param::ProfileImage);
|
||||
grpimage = chat.param.get(Param::ProfileImage);
|
||||
}
|
||||
if 0 != (*msg).param.get_int(Param::Arg2).unwrap_or_default() & 0x1 {
|
||||
info!(
|
||||
@@ -838,7 +831,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
free(placeholdertext as *mut libc::c_void);
|
||||
|
||||
/* add attachment part */
|
||||
if msgtype_has_file((*msg).type_0) {
|
||||
if chat::msgtype_has_file((*msg).type_0) {
|
||||
if !is_file_size_okay(msg) {
|
||||
let error: *mut libc::c_char = dc_mprintf(
|
||||
b"Message exceeds the recommended %i MB.\x00" as *const u8
|
||||
@@ -960,12 +953,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
message_text = format!("{}\r\n", p2).strdup();
|
||||
let human_mime_part: *mut mailmime = build_body_text(message_text);
|
||||
mailmime_add_part(multipart, human_mime_part);
|
||||
let version = dc_get_version_str();
|
||||
message_text2 =
|
||||
dc_mprintf(b"Reporting-UA: Delta Chat %s\r\nOriginal-Recipient: rfc822;%s\r\nFinal-Recipient: rfc822;%s\r\nOriginal-Message-ID: <%s>\r\nDisposition: manual-action/MDN-sent-automatically; displayed\r\n\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
DC_VERSION_STR as *const u8 as *const libc::c_char,
|
||||
version,
|
||||
(*factory).from_addr, (*factory).from_addr,
|
||||
(*(*factory).msg).rfc724_mid);
|
||||
free(version.cast());
|
||||
let content_type_0: *mut mailmime_content = mailmime_content_new_with_str(
|
||||
b"message/disposition-notification\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
@@ -998,7 +993,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
e.as_ptr(),
|
||||
);
|
||||
} else {
|
||||
subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email)
|
||||
subject_str = get_subject((*factory).chat.as_ref(), (*factory).msg, afwd_email)
|
||||
}
|
||||
subject = mailimf_subject_new(dc_encode_header_words(subject_str));
|
||||
mailimf_fields_add(
|
||||
@@ -1041,11 +1036,6 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
&mut e2ee_helper,
|
||||
);
|
||||
}
|
||||
info!(
|
||||
(*factory).context,
|
||||
0, "encryption successful {}", e2ee_helper.encryption_successfull
|
||||
);
|
||||
|
||||
if 0 != e2ee_helper.encryption_successfull {
|
||||
(*factory).out_encrypted = 1;
|
||||
if 0 != do_gossip {
|
||||
@@ -1057,6 +1047,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
|
||||
if !message.is_null() {
|
||||
mailmime_free(message);
|
||||
}
|
||||
@@ -1069,21 +1060,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
|
||||
unsafe fn get_subject(
|
||||
chat: *const Chat,
|
||||
chat: Option<&Chat>,
|
||||
msg: *mut dc_msg_t,
|
||||
afwd_email: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
let context = (*chat).context;
|
||||
if chat.is_none() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
let chat = chat.unwrap();
|
||||
let context = chat.context;
|
||||
let ret: *mut libc::c_char;
|
||||
|
||||
let raw_subject = {
|
||||
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());
|
||||
|
||||
dc_msg_get_summarytext_by_raw((*msg).type_0, msgtext_ptr, &mut (*msg).param, 32, context)
|
||||
dc_msg_get_summarytext_by_raw(
|
||||
(*msg).type_0,
|
||||
(*msg).text.as_ref(),
|
||||
&mut (*msg).param,
|
||||
32,
|
||||
context,
|
||||
)
|
||||
.strdup()
|
||||
};
|
||||
|
||||
let fwd = if 0 != afwd_email {
|
||||
@@ -1093,15 +1090,14 @@ unsafe fn get_subject(
|
||||
};
|
||||
if (*msg).param.get_int(Param::Cmd).unwrap_or_default() == 6 {
|
||||
ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup()
|
||||
} else if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
|
||||
{
|
||||
ret = dc_mprintf(
|
||||
b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char,
|
||||
(*chat).name,
|
||||
fwd,
|
||||
raw_subject,
|
||||
} else if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
ret = format!(
|
||||
"Chat: {}: {}{}",
|
||||
chat.name,
|
||||
to_string(fwd),
|
||||
to_string(raw_subject),
|
||||
)
|
||||
.strdup()
|
||||
} else {
|
||||
ret = dc_mprintf(
|
||||
b"Chat: %s%s\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1152,22 +1148,20 @@ unsafe fn build_body_file(
|
||||
let mime_fields: *mut mailmime_fields;
|
||||
let mut mime_sub: *mut mailmime = 0 as *mut mailmime;
|
||||
let content: *mut mailmime_content;
|
||||
let pathNfilename = (*msg)
|
||||
.param
|
||||
.get(Param::File)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
let path_filename = (*msg).param.get(Param::File);
|
||||
|
||||
let mut mimetype = (*msg)
|
||||
.param
|
||||
.get(Param::MimeType)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
|
||||
let suffix = dc_get_filesuffix_lc(pathNfilename);
|
||||
let mut filename_to_send = 0 as *mut libc::c_char;
|
||||
let mut filename_encoded = 0 as *mut libc::c_char;
|
||||
|
||||
if !pathNfilename.is_null() {
|
||||
if let Some(ref path_filename) = path_filename {
|
||||
let suffix = dc_get_filesuffix_lc(path_filename);
|
||||
|
||||
if (*msg).type_0 == Viewtype::Voice {
|
||||
let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0);
|
||||
|
||||
@@ -1181,7 +1175,7 @@ unsafe fn build_body_file(
|
||||
.to_string();
|
||||
filename_to_send = res.strdup();
|
||||
} else if (*msg).type_0 == Viewtype::Audio {
|
||||
filename_to_send = dc_get_filename(pathNfilename)
|
||||
filename_to_send = dc_get_filename(path_filename)
|
||||
} else if (*msg).type_0 == Viewtype::Image || (*msg).type_0 == Viewtype::Gif {
|
||||
if base_name.is_null() {
|
||||
base_name = b"image\x00" as *const u8 as *const libc::c_char
|
||||
@@ -1205,7 +1199,7 @@ unsafe fn build_body_file(
|
||||
},
|
||||
)
|
||||
} else {
|
||||
filename_to_send = dc_get_filename(pathNfilename)
|
||||
filename_to_send = dc_get_filename(path_filename)
|
||||
}
|
||||
if mimetype.is_null() {
|
||||
if suffix.is_null() {
|
||||
@@ -1298,17 +1292,16 @@ unsafe fn build_body_file(
|
||||
) as *mut libc::c_void,
|
||||
);
|
||||
mime_sub = mailmime_new_empty(content, mime_fields);
|
||||
mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, pathNfilename));
|
||||
mailmime_set_body_file(mime_sub, dc_get_abs_path((*msg).context, path_filename));
|
||||
if !ret_file_name_as_sent.is_null() {
|
||||
*ret_file_name_as_sent = dc_strdup(filename_to_send)
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pathNfilename as *mut libc::c_void);
|
||||
|
||||
free(mimetype as *mut libc::c_void);
|
||||
free(filename_to_send as *mut libc::c_void);
|
||||
free(filename_encoded as *mut libc::c_void);
|
||||
free(suffix as *mut libc::c_void);
|
||||
|
||||
mime_sub
|
||||
}
|
||||
@@ -1316,16 +1309,14 @@ unsafe fn build_body_file(
|
||||
/*******************************************************************************
|
||||
* Render
|
||||
******************************************************************************/
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> bool {
|
||||
let mut file_size_okay = true;
|
||||
let pathNfilename = (*msg).param.get(Param::File).unwrap_or_default().strdup();
|
||||
let bytes = dc_get_filebytes((*msg).context, pathNfilename);
|
||||
let path = (*msg).param.get(Param::File).unwrap_or_default();
|
||||
let bytes = dc_get_filebytes((*msg).context, &path);
|
||||
|
||||
if bytes > (49 * 1024 * 1024 / 4 * 3) {
|
||||
file_size_okay = false;
|
||||
}
|
||||
free(pathNfilename as *mut _);
|
||||
|
||||
file_size_okay
|
||||
}
|
||||
|
||||
@@ -61,11 +61,6 @@ pub struct dc_mimeparser_t<'a> {
|
||||
pub message_kml: Option<dc_kml_t>,
|
||||
}
|
||||
|
||||
// deprecated
|
||||
pub unsafe fn dc_no_compound_msgs() {
|
||||
S_GENERATE_COMPOUND_MSGS = 0i32;
|
||||
}
|
||||
|
||||
// deprecated: flag to switch generation of compound messages on and off.
|
||||
static mut S_GENERATE_COMPOUND_MSGS: libc::c_int = 1i32;
|
||||
|
||||
@@ -93,7 +88,7 @@ pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) {
|
||||
dc_mimeparser_empty(mimeparser);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
|
||||
unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) {
|
||||
for part in mimeparser.parts.drain(..) {
|
||||
dc_mimepart_unref(part);
|
||||
}
|
||||
@@ -161,12 +156,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_SUBJECT as libc::c_int {
|
||||
mimeparser.subject = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value)
|
||||
}
|
||||
if !dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
if !dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Version").is_null() {
|
||||
mimeparser.is_send_by_messenger = 1i32
|
||||
}
|
||||
if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() {
|
||||
@@ -201,10 +191,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
optional_field = dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Chat-Content\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
optional_field = dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Content");
|
||||
if !optional_field.is_null() && !(*optional_field).fld_value.is_null() {
|
||||
if strcmp(
|
||||
(*optional_field).fld_value,
|
||||
@@ -312,11 +299,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
}
|
||||
if mimeparser.parts.len() == 1 {
|
||||
if mimeparser.parts[0].type_0 == 40i32 {
|
||||
if !dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Chat-Voice-Message\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null()
|
||||
if !dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Voice-Message").is_null()
|
||||
{
|
||||
let part_mut = &mut mimeparser.parts[0];
|
||||
part_mut.type_0 = 41i32
|
||||
@@ -324,10 +307,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
}
|
||||
let part = &mimeparser.parts[0];
|
||||
if part.type_0 == 40i32 || part.type_0 == 41i32 || part.type_0 == 50i32 {
|
||||
let field_0 = dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Chat-Duration\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let field_0 = dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Duration");
|
||||
if !field_0.is_null() {
|
||||
let duration_ms: libc::c_int = dc_atoi_null_is_0((*field_0).fld_value);
|
||||
if duration_ms > 0i32 && duration_ms < 24i32 * 60i32 * 60i32 * 1000i32 {
|
||||
@@ -338,10 +318,8 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
}
|
||||
}
|
||||
if 0 == mimeparser.decrypting_failed {
|
||||
let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Chat-Disposition-Notification-To\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let dn_field: *const mailimf_optional_field =
|
||||
dc_mimeparser_lookup_optional_field(mimeparser, "Chat-Disposition-Notification-To");
|
||||
if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(mimeparser).is_some() {
|
||||
let mut mb_list: *mut mailimf_mailbox_list = 0 as *mut mailimf_mailbox_list;
|
||||
let mut index_0: size_t = 0i32 as size_t;
|
||||
@@ -459,11 +437,11 @@ pub fn dc_mimeparser_lookup_field(
|
||||
|
||||
pub unsafe fn dc_mimeparser_lookup_optional_field(
|
||||
mimeparser: &dc_mimeparser_t,
|
||||
field_name: *const libc::c_char,
|
||||
field_name: &str,
|
||||
) -> *mut mailimf_optional_field {
|
||||
let field = mimeparser
|
||||
.header
|
||||
.get(as_str(field_name))
|
||||
.get(field_name)
|
||||
.map(|v| *v)
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int {
|
||||
@@ -1099,7 +1077,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
mimeparser: &mut dc_mimeparser_t,
|
||||
mime: *mut mailmime,
|
||||
) -> libc::c_int {
|
||||
let mut current_block: u64;
|
||||
let mut ok_to_continue = true;
|
||||
let old_part_count = mimeparser.parts.len();
|
||||
let mime_type: libc::c_int;
|
||||
let mime_data: *mut mailmime_data;
|
||||
@@ -1112,7 +1090,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
/* must not be free()'d */
|
||||
let mut decoded_data: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let mut decoded_data_bytes = 0;
|
||||
let mut simplifier: Option<dc_simplify_t> = None;
|
||||
let mut simplifier: Option<Simplify> = None;
|
||||
if !(mime.is_null() || (*mime).mm_data.mm_single.is_null()) {
|
||||
mime_type = mailmime_get_mime_type(mime, &mut msg_type, &mut raw_mime);
|
||||
mime_data = (*mime).mm_data.mm_single;
|
||||
@@ -1134,7 +1112,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
match mime_type {
|
||||
60 | 70 => {
|
||||
if simplifier.is_none() {
|
||||
simplifier = Some(dc_simplify_t::new());
|
||||
simplifier = Some(Simplify::new());
|
||||
}
|
||||
/* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */
|
||||
let charset = mailmime_content_charset_get((*mime).mm_content_type);
|
||||
@@ -1156,14 +1134,12 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
info!(mimeparser.context, 0, "decoded message: '{}'", res);
|
||||
if res.is_empty() {
|
||||
/* no error - but nothing to add */
|
||||
current_block = 8795901732489102124;
|
||||
ok_to_continue = false;
|
||||
} else {
|
||||
let b = res.as_bytes();
|
||||
decoded_data = b.as_ptr() as *const libc::c_char;
|
||||
decoded_data_bytes = b.len();
|
||||
std::mem::forget(res);
|
||||
|
||||
current_block = 17788412896529399552;
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
@@ -1173,45 +1149,35 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
decoded_data_bytes as libc::c_int,
|
||||
as_str(charset),
|
||||
);
|
||||
current_block = 17788412896529399552;
|
||||
}
|
||||
} else {
|
||||
current_block = 17788412896529399552;
|
||||
}
|
||||
match current_block {
|
||||
8795901732489102124 => {}
|
||||
_ => {
|
||||
/* check header directly as is_send_by_messenger is not yet set up */
|
||||
let is_msgrmsg = (!dc_mimeparser_lookup_optional_field(
|
||||
&mimeparser,
|
||||
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null())
|
||||
as libc::c_int;
|
||||
if ok_to_continue {
|
||||
/* check header directly as is_send_by_messenger is not yet set up */
|
||||
let is_msgrmsg =
|
||||
(!dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version")
|
||||
.is_null()) as libc::c_int;
|
||||
|
||||
let simplified_txt = simplifier.unwrap().simplify(
|
||||
decoded_data,
|
||||
decoded_data_bytes as libc::c_int,
|
||||
if mime_type == 70i32 { 1i32 } else { 0i32 },
|
||||
is_msgrmsg,
|
||||
);
|
||||
if !simplified_txt.is_null()
|
||||
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
|
||||
{
|
||||
let mut part = dc_mimepart_new();
|
||||
part.type_0 = 10i32;
|
||||
part.int_mimetype = mime_type;
|
||||
part.msg = simplified_txt;
|
||||
part.msg_raw =
|
||||
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
|
||||
do_add_single_part(mimeparser, part);
|
||||
} else {
|
||||
free(simplified_txt as *mut libc::c_void);
|
||||
}
|
||||
if 0 != simplifier.unwrap().is_forwarded {
|
||||
mimeparser.is_forwarded = 1i32
|
||||
}
|
||||
current_block = 10261677128829721533;
|
||||
let simplified_txt = simplifier.unwrap().simplify(
|
||||
decoded_data,
|
||||
decoded_data_bytes as libc::c_int,
|
||||
mime_type == 70i32,
|
||||
is_msgrmsg,
|
||||
);
|
||||
if !simplified_txt.is_null()
|
||||
&& 0 != *simplified_txt.offset(0isize) as libc::c_int
|
||||
{
|
||||
let mut part = dc_mimepart_new();
|
||||
part.type_0 = 10i32;
|
||||
part.int_mimetype = mime_type;
|
||||
part.msg = simplified_txt;
|
||||
part.msg_raw =
|
||||
strndup(decoded_data, decoded_data_bytes as libc::c_ulong);
|
||||
do_add_single_part(mimeparser, part);
|
||||
} else {
|
||||
free(simplified_txt as *mut libc::c_void);
|
||||
}
|
||||
if simplifier.unwrap().is_forwarded {
|
||||
mimeparser.is_forwarded = 1i32
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1314,76 +1280,61 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
b"file.%s\x00" as *const u8 as *const libc::c_char,
|
||||
(*(*mime).mm_content_type).ct_subtype,
|
||||
);
|
||||
current_block = 17019156190352891614;
|
||||
} else {
|
||||
current_block = 8795901732489102124;
|
||||
ok_to_continue = false;
|
||||
}
|
||||
} else {
|
||||
current_block = 17019156190352891614;
|
||||
}
|
||||
match current_block {
|
||||
8795901732489102124 => {}
|
||||
_ => {
|
||||
if strncmp(
|
||||
desired_filename,
|
||||
b"location\x00" as *const u8 as *const libc::c_char,
|
||||
8,
|
||||
if ok_to_continue {
|
||||
if strncmp(
|
||||
desired_filename,
|
||||
b"location\x00" as *const u8 as *const libc::c_char,
|
||||
8,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
desired_filename
|
||||
.offset(strlen(desired_filename) as isize)
|
||||
.offset(-4isize),
|
||||
b".kml\x00" as *const u8 as *const libc::c_char,
|
||||
4,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
desired_filename
|
||||
.offset(strlen(desired_filename) as isize)
|
||||
.offset(-4isize),
|
||||
b".kml\x00" as *const u8 as *const libc::c_char,
|
||||
4,
|
||||
) == 0i32
|
||||
{
|
||||
mimeparser.location_kml = Some(dc_kml_parse(
|
||||
mimeparser.context,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
));
|
||||
current_block = 8795901732489102124;
|
||||
} else if strncmp(
|
||||
desired_filename,
|
||||
b"message\x00" as *const u8 as *const libc::c_char,
|
||||
7,
|
||||
{
|
||||
mimeparser.location_kml = Some(dc_kml_parse(
|
||||
mimeparser.context,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
));
|
||||
} else if strncmp(
|
||||
desired_filename,
|
||||
b"message\x00" as *const u8 as *const libc::c_char,
|
||||
7,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
desired_filename
|
||||
.offset(strlen(desired_filename) as isize)
|
||||
.offset(-4isize),
|
||||
b".kml\x00" as *const u8 as *const libc::c_char,
|
||||
4,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
desired_filename
|
||||
.offset(strlen(desired_filename) as isize)
|
||||
.offset(-4isize),
|
||||
b".kml\x00" as *const u8 as *const libc::c_char,
|
||||
4,
|
||||
) == 0i32
|
||||
{
|
||||
mimeparser.message_kml = Some(dc_kml_parse(
|
||||
mimeparser.context,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
));
|
||||
current_block = 8795901732489102124;
|
||||
} else {
|
||||
dc_replace_bad_utf8_chars(desired_filename);
|
||||
do_add_single_file_part(
|
||||
mimeparser,
|
||||
msg_type,
|
||||
mime_type,
|
||||
raw_mime,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
desired_filename,
|
||||
);
|
||||
current_block = 10261677128829721533;
|
||||
}
|
||||
{
|
||||
mimeparser.message_kml = Some(dc_kml_parse(
|
||||
mimeparser.context,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
));
|
||||
} else {
|
||||
dc_replace_bad_utf8_chars(desired_filename);
|
||||
do_add_single_file_part(
|
||||
mimeparser,
|
||||
msg_type,
|
||||
mime_type,
|
||||
raw_mime,
|
||||
decoded_data,
|
||||
decoded_data_bytes,
|
||||
desired_filename,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
current_block = 10261677128829721533;
|
||||
}
|
||||
}
|
||||
match current_block {
|
||||
8795901732489102124 => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -1396,11 +1347,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
free(file_suffix as *mut libc::c_void);
|
||||
free(desired_filename as *mut libc::c_void);
|
||||
free(raw_mime as *mut libc::c_void);
|
||||
return if mimeparser.parts.len() > old_part_count {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
(mimeparser.parts.len() > old_part_count) as libc::c_int
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@@ -1436,16 +1383,15 @@ unsafe fn do_add_single_file_part(
|
||||
part.param.set(Param::File, as_str(pathNfilename));
|
||||
part.param.set(Param::MimeType, as_str(raw_mime));
|
||||
if mime_type == 80 {
|
||||
let mut w = 0;
|
||||
let mut h = 0;
|
||||
if 0 != dc_get_filemeta(
|
||||
decoded_data as *const libc::c_void,
|
||||
decoded_data_bytes,
|
||||
&mut w,
|
||||
&mut h,
|
||||
) {
|
||||
part.param.set_int(Param::Width, w as i32);
|
||||
part.param.set_int(Param::Height, h as i32);
|
||||
assert!(!decoded_data.is_null(), "invalid image data");
|
||||
let data = std::slice::from_raw_parts(
|
||||
decoded_data as *const u8,
|
||||
decoded_data_bytes as usize,
|
||||
);
|
||||
|
||||
if let Ok((width, height)) = dc_get_filemeta(data) {
|
||||
part.param.set_int(Param::Width, width as i32);
|
||||
part.param.set_int(Param::Height, height as i32);
|
||||
}
|
||||
}
|
||||
do_add_single_part(parser, part);
|
||||
@@ -1552,10 +1498,8 @@ pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t)
|
||||
if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() {
|
||||
return 1i32;
|
||||
}
|
||||
let precedence: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(
|
||||
mimeparser,
|
||||
b"Precedence\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let precedence: *mut mailimf_optional_field =
|
||||
dc_mimeparser_lookup_optional_field(mimeparser, "Precedence");
|
||||
if !precedence.is_null() {
|
||||
if strcasecmp(
|
||||
(*precedence).fld_value,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::job::*;
|
||||
use crate::param::Params;
|
||||
|
||||
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
@@ -27,13 +27,19 @@ pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u3
|
||||
}
|
||||
|
||||
if dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Stay);
|
||||
}
|
||||
|
||||
// 1 = dc message, 2 = reply to dc message
|
||||
if 0 != (*msg).is_dc_message {
|
||||
dc_job_add(context, 200, (*msg).id as libc::c_int, Params::new(), 0);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
|
||||
job_add(
|
||||
context,
|
||||
Action::MoveMsg,
|
||||
(*msg).id as libc::c_int,
|
||||
Params::new(),
|
||||
0,
|
||||
);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, MoveState::Moving);
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
|
||||
800
src/dc_msg.rs
800
src/dc_msg.rs
File diff suppressed because it is too large
Load Diff
346
src/dc_qr.rs
346
src/dc_qr.rs
@@ -1,346 +0,0 @@
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::key::*;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
// out-of-band verification
|
||||
// id=contact
|
||||
// text1=groupname
|
||||
// id=contact
|
||||
// id=contact
|
||||
// test1=formatted fingerprint
|
||||
// id=contact
|
||||
// text1=text
|
||||
// text1=URL
|
||||
// text1=error string
|
||||
pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc_lot_t {
|
||||
let mut current_block: u64;
|
||||
let mut payload: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
// must be normalized, if set
|
||||
let mut addr: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
// must be normalized, if set
|
||||
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut invitenumber: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut qr_parsed: *mut dc_lot_t = dc_lot_new();
|
||||
let mut chat_id: uint32_t = 0i32 as uint32_t;
|
||||
let mut device_msg: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
(*qr_parsed).state = 0i32;
|
||||
if !qr.is_null() {
|
||||
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
|
||||
/* split parameters from the qr code
|
||||
------------------------------------ */
|
||||
if strncasecmp(
|
||||
qr,
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload =
|
||||
dc_strdup(&*qr.offset(strlen(
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
) as isize));
|
||||
let mut fragment: *mut libc::c_char = strchr(payload, '#' as i32);
|
||||
if !fragment.is_null() {
|
||||
*fragment = 0i32 as libc::c_char;
|
||||
fragment = fragment.offset(1isize);
|
||||
let param: Params = as_str(fragment).parse().expect("invalid params");
|
||||
addr = param
|
||||
.get(Param::Forwarded)
|
||||
.map(|s| s.strdup())
|
||||
.unwrap_or_else(|| std::ptr::null_mut());
|
||||
if !addr.is_null() {
|
||||
if let Some(ref name_enc) = param.get(Param::SetLongitude) {
|
||||
let name_r = percent_decode_str(name_enc)
|
||||
.decode_utf8()
|
||||
.expect("invalid name");
|
||||
name = normalize_name(name_r).strdup();
|
||||
}
|
||||
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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fingerprint = dc_normalize_fingerprint_c(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"mailto:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"mailto:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload = dc_strdup(
|
||||
&*qr.offset(strlen(b"mailto:\x00" as *const u8 as *const libc::c_char) as isize),
|
||||
);
|
||||
let query: *mut libc::c_char = strchr(payload, '?' as i32);
|
||||
if !query.is_null() {
|
||||
*query = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"SMTP:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
payload = dc_strdup(
|
||||
&*qr.offset(strlen(b"SMTP:\x00" as *const u8 as *const libc::c_char) as isize),
|
||||
);
|
||||
let colon: *mut libc::c_char = strchr(payload, ':' as i32);
|
||||
if !colon.is_null() {
|
||||
*colon = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"MATMSG:\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"MATMSG:\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
/* scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` - there may or may not be linebreaks after the fields */
|
||||
/* does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. we ignore this case. */
|
||||
let to: *mut libc::c_char = strstr(qr, b"TO:\x00" as *const u8 as *const libc::c_char);
|
||||
if !to.is_null() {
|
||||
addr = dc_strdup(&mut *to.offset(3isize));
|
||||
let semicolon: *mut libc::c_char = strchr(addr, ';' as i32);
|
||||
if !semicolon.is_null() {
|
||||
*semicolon = 0i32 as libc::c_char
|
||||
}
|
||||
current_block = 5023038348526654800;
|
||||
} else {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 =
|
||||
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
|
||||
current_block = 16562876845594826114;
|
||||
}
|
||||
} else {
|
||||
if strncasecmp(
|
||||
qr,
|
||||
b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char,
|
||||
strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char),
|
||||
) == 0i32
|
||||
{
|
||||
let lines = dc_split_into_lines(qr);
|
||||
for &key in &lines {
|
||||
dc_trim(key);
|
||||
let mut value: *mut libc::c_char = strchr(key, ':' as i32);
|
||||
if !value.is_null() {
|
||||
*value = 0i32 as libc::c_char;
|
||||
value = value.offset(1isize);
|
||||
let mut semicolon_0: *mut libc::c_char = strchr(key, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
if strcasecmp(key, b"EMAIL\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
semicolon_0 = strchr(value, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
addr = dc_strdup(value)
|
||||
} else if strcasecmp(key, b"N\x00" as *const u8 as *const libc::c_char)
|
||||
== 0i32
|
||||
{
|
||||
semicolon_0 = strchr(value, ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
semicolon_0 = strchr(semicolon_0.offset(1isize), ';' as i32);
|
||||
if !semicolon_0.is_null() {
|
||||
*semicolon_0 = 0i32 as libc::c_char
|
||||
}
|
||||
}
|
||||
name = dc_strdup(value);
|
||||
dc_str_replace(
|
||||
&mut name,
|
||||
b";\x00" as *const u8 as *const libc::c_char,
|
||||
b",\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
name = normalize_name(as_str(name)).strdup();
|
||||
}
|
||||
}
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
}
|
||||
current_block = 5023038348526654800;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
/* check the parameters
|
||||
---------------------- */
|
||||
if !addr.is_null() {
|
||||
/* urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases */
|
||||
let mut temp: *mut libc::c_char = dc_urldecode(addr);
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
temp = addr_normalize(as_str(addr)).strdup();
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
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,
|
||||
);
|
||||
current_block = 16562876845594826114;
|
||||
} else {
|
||||
current_block = 14116432890150942211;
|
||||
}
|
||||
} else {
|
||||
current_block = 14116432890150942211;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
if !fingerprint.is_null() {
|
||||
if strlen(fingerprint) != 40 {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 = dc_strdup(
|
||||
b"Bad fingerprint length in QR code.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
current_block = 16562876845594826114;
|
||||
} else {
|
||||
current_block = 5409161009579131794;
|
||||
}
|
||||
} else {
|
||||
current_block = 5409161009579131794;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
if !fingerprint.is_null() {
|
||||
let peerstate = Peerstate::from_fingerprint(
|
||||
context,
|
||||
&context.sql,
|
||||
as_str(fingerprint),
|
||||
);
|
||||
if addr.is_null() || invitenumber.is_null() || auth.is_null() {
|
||||
if let Some(peerstate) = peerstate {
|
||||
(*qr_parsed).state = 210i32;
|
||||
let addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
"",
|
||||
addr,
|
||||
Origin::UnhandledQrScan,
|
||||
)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
(*qr_parsed).id,
|
||||
2i32,
|
||||
&mut chat_id,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
device_msg = dc_mprintf(
|
||||
b"%s verified.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
peerstate.addr,
|
||||
)
|
||||
} else {
|
||||
(*qr_parsed).text1 =
|
||||
dc_format_fingerprint_c(fingerprint);
|
||||
(*qr_parsed).state = 230i32
|
||||
}
|
||||
} else {
|
||||
if !grpid.is_null() && !grpname.is_null() {
|
||||
(*qr_parsed).state = 202i32;
|
||||
(*qr_parsed).text1 = dc_strdup(grpname);
|
||||
(*qr_parsed).text2 = dc_strdup(grpid)
|
||||
} else {
|
||||
(*qr_parsed).state = 200i32
|
||||
}
|
||||
(*qr_parsed).id = Contact::add_or_lookup(
|
||||
context,
|
||||
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 = Contact::add_or_lookup(
|
||||
context,
|
||||
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,
|
||||
) == qr as *mut libc::c_char
|
||||
|| strstr(
|
||||
qr,
|
||||
b"https://\x00" as *const u8 as *const libc::c_char,
|
||||
) == qr as *mut libc::c_char
|
||||
{
|
||||
(*qr_parsed).state = 332i32;
|
||||
(*qr_parsed).text1 = dc_strdup(qr)
|
||||
} else {
|
||||
(*qr_parsed).state = 330i32;
|
||||
(*qr_parsed).text1 = dc_strdup(qr)
|
||||
}
|
||||
if !device_msg.is_null() {
|
||||
dc_add_device_msg(context, chat_id, device_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
free(addr as *mut libc::c_void);
|
||||
free(fingerprint as *mut libc::c_void);
|
||||
free(payload as *mut libc::c_void);
|
||||
free(name as *mut libc::c_void);
|
||||
free(invitenumber as *mut libc::c_void);
|
||||
free(auth as *mut libc::c_void);
|
||||
free(device_msg as *mut libc::c_void);
|
||||
free(grpname as *mut libc::c_void);
|
||||
free(grpid as *mut libc::c_void);
|
||||
|
||||
qr_parsed
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
1089
src/dc_saxparser.rs
1089
src/dc_saxparser.rs
File diff suppressed because it is too large
Load Diff
@@ -1,26 +1,22 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use mmime::mailimf_types::*;
|
||||
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::chat::{self, Chat};
|
||||
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_e2ee::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_qr::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_token::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::key::*;
|
||||
use crate::lot::LotState;
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
use crate::qr::check_qr;
|
||||
use crate::stock::StockMessage;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
@@ -37,12 +33,9 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
let mut fingerprint = 0 as *mut libc::c_char;
|
||||
let mut invitenumber: *mut libc::c_char;
|
||||
let mut auth: *mut libc::c_char;
|
||||
let mut chat = 0 as *mut Chat;
|
||||
let mut group_name = 0 as *mut libc::c_char;
|
||||
let mut group_name_urlencoded = 0 as *mut libc::c_char;
|
||||
let mut qr: Option<String> = None;
|
||||
|
||||
dc_ensure_secret_key_exists(context);
|
||||
dc_ensure_secret_key_exists(context).ok();
|
||||
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
|
||||
if invitenumber.is_null() {
|
||||
invitenumber = dc_create_id().strdup();
|
||||
@@ -55,13 +48,10 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
}
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
|
||||
let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| {
|
||||
let cleanup = |fingerprint| {
|
||||
free(fingerprint as *mut libc::c_void);
|
||||
free(invitenumber as *mut libc::c_void);
|
||||
free(auth as *mut libc::c_void);
|
||||
dc_chat_unref(chat);
|
||||
free(group_name as *mut libc::c_void);
|
||||
free(group_name_urlencoded as *mut libc::c_void);
|
||||
|
||||
if let Some(qr) = qr {
|
||||
qr.strdup()
|
||||
@@ -72,7 +62,7 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
|
||||
if self_addr.is_none() {
|
||||
error!(context, 0, "Not configured, cannot generate QR code.",);
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
return cleanup(fingerprint);
|
||||
}
|
||||
|
||||
let self_addr = self_addr.unwrap();
|
||||
@@ -84,34 +74,34 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
fingerprint = get_self_fingerprint(context);
|
||||
|
||||
if fingerprint.is_null() {
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
return cleanup(fingerprint);
|
||||
}
|
||||
|
||||
let self_addr_urlencoded = utf8_percent_encode(&self_addr, NON_ALPHANUMERIC).to_string();
|
||||
let self_name_urlencoded = utf8_percent_encode(&self_name, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
qr = if 0 != group_chat_id {
|
||||
chat = dc_get_chat(context, group_chat_id);
|
||||
if chat.is_null() {
|
||||
if let Ok(chat) = Chat::load_from_db(context, group_chat_id) {
|
||||
let group_name = chat.get_name();
|
||||
let group_name_urlencoded =
|
||||
utf8_percent_encode(&group_name, NON_ALPHANUMERIC).to_string();
|
||||
|
||||
Some(format!(
|
||||
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
||||
as_str(fingerprint),
|
||||
self_addr_urlencoded,
|
||||
&group_name_urlencoded,
|
||||
&chat.grpid,
|
||||
as_str(invitenumber),
|
||||
as_str(auth),
|
||||
))
|
||||
} else {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot get QR-code for chat-id {}", group_chat_id,
|
||||
);
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
return cleanup(fingerprint);
|
||||
}
|
||||
|
||||
group_name = dc_chat_get_name(chat);
|
||||
group_name_urlencoded = dc_urlencode(group_name);
|
||||
|
||||
Some(format!(
|
||||
"OPENPGP4FPR:{}#a={}&g={}&x={}&i={}&s={}",
|
||||
as_str(fingerprint),
|
||||
self_addr_urlencoded,
|
||||
as_str(group_name_urlencoded),
|
||||
as_str((*chat).grpid),
|
||||
as_str(invitenumber),
|
||||
as_str(auth),
|
||||
))
|
||||
} else {
|
||||
Some(format!(
|
||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||
@@ -125,7 +115,7 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
|
||||
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
|
||||
|
||||
cleanup(fingerprint, chat, group_name, group_name_urlencoded)
|
||||
cleanup(fingerprint)
|
||||
}
|
||||
|
||||
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
|
||||
@@ -147,16 +137,19 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
let ongoing_allocated: libc::c_int;
|
||||
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
|
||||
let mut join_vg: libc::c_int = 0i32;
|
||||
let mut qr_scan: *mut dc_lot_t = 0 as *mut dc_lot_t;
|
||||
|
||||
info!(context, 0, "Requesting secure-join ...",);
|
||||
dc_ensure_secret_key_exists(context);
|
||||
dc_ensure_secret_key_exists(context).ok();
|
||||
ongoing_allocated = dc_alloc_ongoing(context);
|
||||
|
||||
if !(ongoing_allocated == 0i32) {
|
||||
qr_scan = dc_check_qr(context, qr);
|
||||
if qr_scan.is_null() || (*qr_scan).state != 200i32 && (*qr_scan).state != 202i32 {
|
||||
let qr_scan = check_qr(context, as_str(qr));
|
||||
if qr_scan.state != LotState::QrAskVerifyContact
|
||||
&& qr_scan.state != LotState::QrAskVerifyGroup
|
||||
{
|
||||
error!(context, 0, "Unknown QR code.",);
|
||||
} else {
|
||||
contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id);
|
||||
contact_chat_id = chat::create_by_contact_id(context, qr_scan.id).unwrap_or_default();
|
||||
if contact_chat_id == 0i32 as libc::c_uint {
|
||||
error!(context, 0, "Unknown contact.",);
|
||||
} else if !(context
|
||||
@@ -166,17 +159,28 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
.unwrap()
|
||||
.shall_stop_ongoing)
|
||||
{
|
||||
join_vg = ((*qr_scan).state == 202i32) as libc::c_int;
|
||||
join_vg = (qr_scan.get_state() == LotState::QrAskVerifyGroup) as libc::c_int;
|
||||
{
|
||||
let bob_a = context.bob.clone();
|
||||
let mut bob = bob_a.write().unwrap();
|
||||
let mut bob = context.bob.write().unwrap();
|
||||
bob.status = 0;
|
||||
bob.qr_scan = qr_scan;
|
||||
bob.qr_scan = Some(qr_scan);
|
||||
}
|
||||
if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id)
|
||||
{
|
||||
if 0 != fingerprint_equals_sender(
|
||||
context,
|
||||
context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.fingerprint
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
contact_chat_id,
|
||||
) {
|
||||
info!(context, 0, "Taking protocol shortcut.");
|
||||
context.bob.clone().write().unwrap().expects = 6;
|
||||
context.bob.write().unwrap().expects = 6;
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_JOINER_PROGRESS,
|
||||
chat_id_2_contact_id(context, contact_chat_id) as uintptr_t,
|
||||
@@ -191,17 +195,37 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
} else {
|
||||
b"vc-request-with-auth\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
(*qr_scan).auth,
|
||||
context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.auth
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
own_fingerprint,
|
||||
if 0 != join_vg {
|
||||
(*qr_scan).text2
|
||||
context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.text2
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
} else {
|
||||
0 as *mut libc::c_char
|
||||
"".to_string()
|
||||
},
|
||||
);
|
||||
free(own_fingerprint as *mut libc::c_void);
|
||||
} else {
|
||||
context.bob.clone().write().unwrap().expects = 2;
|
||||
context.bob.write().unwrap().expects = 2;
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -210,9 +234,18 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
} else {
|
||||
b"vc-request\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
(*qr_scan).invitenumber,
|
||||
0 as *const libc::c_char,
|
||||
context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.invitenumber
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
0 as *const libc::c_char,
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -229,25 +262,23 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
let bob_a = context.bob.clone();
|
||||
let mut bob = bob_a.write().unwrap();
|
||||
|
||||
let mut bob = context.bob.write().unwrap();
|
||||
bob.expects = 0;
|
||||
if bob.status == 1 {
|
||||
if 0 != join_vg {
|
||||
ret_chat_id = dc_get_chat_id_by_grpid(
|
||||
ret_chat_id = chat::get_chat_id_by_grpid(
|
||||
context,
|
||||
(*qr_scan).text2,
|
||||
0 as *mut libc::c_int,
|
||||
bob.qr_scan.as_ref().unwrap().text2.as_ref().unwrap(),
|
||||
None,
|
||||
0 as *mut libc::c_int,
|
||||
) as libc::c_int
|
||||
} else {
|
||||
ret_chat_id = contact_chat_id as libc::c_int
|
||||
}
|
||||
}
|
||||
bob.qr_scan = std::ptr::null_mut();
|
||||
|
||||
dc_lot_unref(qr_scan);
|
||||
bob.qr_scan = None;
|
||||
|
||||
if 0 != ongoing_allocated {
|
||||
dc_free_ongoing(context);
|
||||
}
|
||||
@@ -258,9 +289,9 @@ unsafe fn send_handshake_msg(
|
||||
context: &Context,
|
||||
contact_chat_id: uint32_t,
|
||||
step: *const libc::c_char,
|
||||
param2: *const libc::c_char,
|
||||
param2: impl AsRef<str>,
|
||||
fingerprint: *const libc::c_char,
|
||||
grpid: *const libc::c_char,
|
||||
grpid: impl AsRef<str>,
|
||||
) {
|
||||
let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context);
|
||||
(*msg).type_0 = Viewtype::Text;
|
||||
@@ -272,14 +303,14 @@ unsafe fn send_handshake_msg(
|
||||
} else {
|
||||
(*msg).param.set(Param::Arg, as_str(step));
|
||||
}
|
||||
if !param2.is_null() {
|
||||
(*msg).param.set(Param::Arg2, as_str(param2));
|
||||
if !param2.as_ref().is_empty() {
|
||||
(*msg).param.set(Param::Arg2, param2);
|
||||
}
|
||||
if !fingerprint.is_null() {
|
||||
(*msg).param.set(Param::Arg3, as_str(fingerprint));
|
||||
}
|
||||
if !grpid.is_null() {
|
||||
(*msg).param.set(Param::Arg4, as_str(grpid));
|
||||
if !grpid.as_ref().is_empty() {
|
||||
(*msg).param.set(Param::Arg4, grpid.as_ref());
|
||||
}
|
||||
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
@@ -291,37 +322,33 @@ unsafe fn send_handshake_msg(
|
||||
} else {
|
||||
(*msg).param.set_int(Param::GuranteeE2ee, 1);
|
||||
}
|
||||
dc_send_msg(context, contact_chat_id, msg);
|
||||
// TODO. handle cleanup on error
|
||||
chat::send_msg(context, contact_chat_id, msg).unwrap();
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
|
||||
unsafe fn chat_id_2_contact_id(context: &Context, contact_chat_id: uint32_t) -> uint32_t {
|
||||
let mut contact_id: uint32_t = 0i32 as uint32_t;
|
||||
let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id);
|
||||
if !(dc_array_get_cnt(contacts) != 1) {
|
||||
contact_id = dc_array_get_id(contacts, 0i32 as size_t)
|
||||
let contacts = chat::get_chat_contacts(context, contact_chat_id);
|
||||
if contacts.len() == 1 {
|
||||
contacts[0]
|
||||
} else {
|
||||
0
|
||||
}
|
||||
dc_array_unref(contacts);
|
||||
|
||||
contact_id
|
||||
}
|
||||
|
||||
unsafe fn fingerprint_equals_sender(
|
||||
context: &Context,
|
||||
fingerprint: *const libc::c_char,
|
||||
contact_chat_id: uint32_t,
|
||||
fingerprint: impl AsRef<str>,
|
||||
contact_chat_id: u32,
|
||||
) -> libc::c_int {
|
||||
if fingerprint.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let mut fingerprint_equal: libc::c_int = 0i32;
|
||||
let contacts = dc_get_chat_contacts(context, contact_chat_id);
|
||||
let mut fingerprint_equal = 0;
|
||||
let contacts = chat::get_chat_contacts(context, contact_chat_id);
|
||||
|
||||
if !(dc_array_get_cnt(contacts) != 1) {
|
||||
if let Ok(contact) = Contact::load_from_db(context, dc_array_get_id(contacts, 0)) {
|
||||
if contacts.len() == 1 {
|
||||
if let Ok(contact) = Contact::load_from_db(context, contacts[0]) {
|
||||
if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr())
|
||||
{
|
||||
let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint));
|
||||
let fingerprint_normalized = dc_normalize_fingerprint(fingerprint.as_ref());
|
||||
if peerstate.public_key_fingerprint.is_some()
|
||||
&& &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap()
|
||||
{
|
||||
@@ -332,7 +359,6 @@ unsafe fn fingerprint_equals_sender(
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
dc_array_unref(contacts);
|
||||
|
||||
fingerprint_equal
|
||||
}
|
||||
@@ -346,12 +372,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
let mut current_block: u64;
|
||||
let step: *const libc::c_char;
|
||||
let join_vg: libc::c_int;
|
||||
let mut scanned_fingerprint_of_alice: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut auth: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut own_fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
|
||||
let mut contact_chat_id_blocked: libc::c_int = 0i32;
|
||||
let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let contact_chat_id: u32;
|
||||
let contact_chat_id_blocked: Blocked;
|
||||
let mut grpid = "".to_string();
|
||||
let mut ret: libc::c_int = 0i32;
|
||||
|
||||
if !(contact_id <= 9i32 as libc::c_uint) {
|
||||
@@ -365,15 +389,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
|
||||
as libc::c_int;
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
contact_id,
|
||||
0i32,
|
||||
&mut contact_chat_id,
|
||||
&mut contact_chat_id_blocked,
|
||||
);
|
||||
if 0 != contact_chat_id_blocked {
|
||||
dc_unblock_chat(context, contact_chat_id);
|
||||
let (id, bl) = chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not)
|
||||
.unwrap_or_default();
|
||||
contact_chat_id = id;
|
||||
contact_chat_id_blocked = bl;
|
||||
|
||||
if Blocked::Not != contact_chat_id_blocked {
|
||||
chat::unblock(context, contact_chat_id);
|
||||
}
|
||||
ret = 0x2i32;
|
||||
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
@@ -411,9 +433,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
} else {
|
||||
b"vc-auth-required\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
"",
|
||||
);
|
||||
current_block = 10256747982273457880;
|
||||
}
|
||||
@@ -427,10 +449,11 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
) == 0i32
|
||||
{
|
||||
let cond = {
|
||||
let bob_a = context.bob.clone();
|
||||
let bob = bob_a.read().unwrap();
|
||||
let scan = bob.qr_scan;
|
||||
scan.is_null() || bob.expects != 2 || 0 != join_vg && (*scan).state != 202
|
||||
let bob = context.bob.read().unwrap();
|
||||
let scan = bob.qr_scan.as_ref();
|
||||
scan.is_none()
|
||||
|| bob.expects != 2
|
||||
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
||||
};
|
||||
|
||||
if cond {
|
||||
@@ -438,15 +461,44 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
// no error, just aborted somehow or a mail from another handshake
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
{
|
||||
let scan = context.bob.clone().read().unwrap().qr_scan;
|
||||
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
|
||||
auth = dc_strdup((*scan).auth);
|
||||
if 0 != join_vg {
|
||||
grpid = dc_strdup((*scan).text2)
|
||||
}
|
||||
let scanned_fingerprint_of_alice = context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.fingerprint
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
let auth = context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.auth
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
if 0 != join_vg {
|
||||
grpid = context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.text2
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
}
|
||||
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
|
||||
|
||||
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -461,7 +513,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
} else if 0
|
||||
== fingerprint_equals_sender(
|
||||
context,
|
||||
scanned_fingerprint_of_alice,
|
||||
&scanned_fingerprint_of_alice,
|
||||
contact_chat_id,
|
||||
)
|
||||
{
|
||||
@@ -481,7 +533,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
contact_id as uintptr_t,
|
||||
400i32 as uintptr_t,
|
||||
);
|
||||
context.bob.clone().write().unwrap().expects = 6;
|
||||
context.bob.write().unwrap().expects = 6;
|
||||
|
||||
send_handshake_msg(
|
||||
context,
|
||||
@@ -513,8 +565,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
==== Step 6 in "Out-of-band verified groups" protocol ====
|
||||
============================================================ */
|
||||
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
|
||||
let fingerprint: *const libc::c_char;
|
||||
fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
|
||||
let fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
|
||||
if fingerprint.is_null() {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
@@ -522,14 +573,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"Fingerprint not provided.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else if 0 == encrypted_and_signed(mimeparser, fingerprint) {
|
||||
} else if !encrypted_and_signed(mimeparser, as_str(fingerprint)) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"Auth not encrypted.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else if 0 == fingerprint_equals_sender(context, fingerprint, contact_chat_id) {
|
||||
} else if 0
|
||||
== fingerprint_equals_sender(context, as_str(fingerprint), contact_chat_id)
|
||||
{
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -556,7 +609,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"Auth invalid.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else if 0 == mark_peer_as_verified(context, fingerprint) {
|
||||
} else if 0 == mark_peer_as_verified(context, as_str(fingerprint)) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -583,18 +636,18 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
600i32 as uintptr_t,
|
||||
);
|
||||
if 0 != join_vg {
|
||||
grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group"));
|
||||
let group_chat_id: uint32_t = dc_get_chat_id_by_grpid(
|
||||
grpid = to_string(lookup_field(mimeparser, "Secure-Join-Group"));
|
||||
let group_chat_id: uint32_t = chat::get_chat_id_by_grpid(
|
||||
context,
|
||||
grpid,
|
||||
0 as *mut libc::c_int,
|
||||
&grpid,
|
||||
None,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
if group_chat_id == 0i32 as libc::c_uint {
|
||||
error!(context, 0, "Chat {} not found.", as_str(grpid),);
|
||||
error!(context, 0, "Chat {} not found.", &grpid);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_add_contact_to_chat_ex(
|
||||
chat::add_contact_to_chat_ex(
|
||||
context,
|
||||
group_chat_id,
|
||||
contact_id,
|
||||
@@ -607,9 +660,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"vc-contact-confirm\x00" as *const u8 as *const libc::c_char,
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
"",
|
||||
);
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
@@ -632,13 +685,15 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
if 0 != join_vg {
|
||||
ret = 0x1i32
|
||||
}
|
||||
if context.bob.clone().read().unwrap().expects != 6 {
|
||||
if context.bob.read().unwrap().expects != 6 {
|
||||
info!(context, 0, "Message belongs to a different handshake.",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
let cond = {
|
||||
let scan = context.bob.clone().read().unwrap().qr_scan;
|
||||
scan.is_null() || 0 != join_vg && (*scan).state != 202
|
||||
let bob = context.bob.read().unwrap();
|
||||
let scan = bob.qr_scan.as_ref();
|
||||
scan.is_none()
|
||||
|| 0 != join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
||||
};
|
||||
if cond {
|
||||
warn!(
|
||||
@@ -647,20 +702,39 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
{
|
||||
let scan = context.bob.clone().read().unwrap().qr_scan;
|
||||
scanned_fingerprint_of_alice = dc_strdup((*scan).fingerprint);
|
||||
if 0 != join_vg {
|
||||
grpid = dc_strdup((*scan).text2)
|
||||
}
|
||||
let scanned_fingerprint_of_alice = context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.fingerprint
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
|
||||
if 0 != join_vg {
|
||||
grpid = context
|
||||
.bob
|
||||
.read()
|
||||
.unwrap()
|
||||
.qr_scan
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.text2
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.to_string();
|
||||
}
|
||||
|
||||
let mut vg_expect_encrypted: libc::c_int = 1i32;
|
||||
if 0 != join_vg {
|
||||
let mut is_verified_group: libc::c_int = 0i32;
|
||||
dc_get_chat_id_by_grpid(
|
||||
chat::get_chat_id_by_grpid(
|
||||
context,
|
||||
grpid,
|
||||
0 as *mut libc::c_int,
|
||||
None,
|
||||
&mut is_verified_group,
|
||||
);
|
||||
if 0 == is_verified_group {
|
||||
@@ -668,7 +742,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
}
|
||||
}
|
||||
if 0 != vg_expect_encrypted {
|
||||
if 0 == encrypted_and_signed(mimeparser, scanned_fingerprint_of_alice) {
|
||||
if !encrypted_and_signed(mimeparser, &scanned_fingerprint_of_alice) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -686,8 +760,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
match current_block {
|
||||
4378276786830486580 => {}
|
||||
_ => {
|
||||
if 0 == mark_peer_as_verified(context, scanned_fingerprint_of_alice)
|
||||
{
|
||||
if 0 == mark_peer_as_verified(
|
||||
context,
|
||||
&scanned_fingerprint_of_alice,
|
||||
) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -730,16 +806,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
4378276786830486580 => {}
|
||||
_ => {
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.bob.clone().write().unwrap().expects = 0;
|
||||
context.bob.write().unwrap().expects = 0;
|
||||
if 0 != join_vg {
|
||||
send_handshake_msg(
|
||||
context,
|
||||
contact_chat_id,
|
||||
b"vg-member-added-received\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
"",
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
"",
|
||||
);
|
||||
}
|
||||
end_bobs_joining(context, 1i32);
|
||||
@@ -795,16 +871,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
}
|
||||
}
|
||||
|
||||
free(scanned_fingerprint_of_alice as *mut libc::c_void);
|
||||
free(auth as *mut libc::c_void);
|
||||
free(own_fingerprint as *mut libc::c_void);
|
||||
free(grpid as *mut libc::c_void);
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) {
|
||||
context.bob.clone().write().unwrap().status = status;
|
||||
context.bob.write().unwrap().status = status;
|
||||
dc_stop_ongoing_process(context);
|
||||
}
|
||||
|
||||
@@ -816,9 +889,8 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
|
||||
} else {
|
||||
"?"
|
||||
};
|
||||
let msg =
|
||||
CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap();
|
||||
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
|
||||
let msg = context.stock_string_repl_str(StockMessage::ContactVerified, addr);
|
||||
chat::add_device_msg(context, contact_chat_id, msg);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
contact_chat_id as uintptr_t,
|
||||
@@ -858,21 +930,18 @@ unsafe fn could_not_establish_secure_connection(
|
||||
"?"
|
||||
},
|
||||
);
|
||||
let msg_c = CString::new(msg.as_str()).unwrap();
|
||||
dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr());
|
||||
error!(context, 0, "{} ({})", msg, as_str(details));
|
||||
|
||||
chat::add_device_msg(context, contact_chat_id, &msg);
|
||||
error!(context, 0, "{} ({})", &msg, as_str(details));
|
||||
}
|
||||
|
||||
unsafe fn mark_peer_as_verified(
|
||||
context: &Context,
|
||||
fingerprint: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
unsafe fn mark_peer_as_verified(context: &Context, fingerprint: impl AsRef<str>) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
|
||||
if let Some(ref mut peerstate) =
|
||||
Peerstate::from_fingerprint(context, &context.sql, as_str(fingerprint))
|
||||
Peerstate::from_fingerprint(context, &context.sql, fingerprint.as_ref())
|
||||
{
|
||||
if peerstate.set_verified(1, as_str(fingerprint), 2) {
|
||||
if peerstate.set_verified(1, fingerprint.as_ref(), 2) {
|
||||
peerstate.prefer_encrypt = EncryptPreference::Mutual;
|
||||
peerstate.to_save = Some(ToSave::All);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
@@ -887,43 +956,40 @@ unsafe fn mark_peer_as_verified(
|
||||
* Tools: Misc.
|
||||
******************************************************************************/
|
||||
|
||||
// TODO should return bool
|
||||
unsafe fn encrypted_and_signed(
|
||||
mimeparser: &dc_mimeparser_t,
|
||||
expected_fingerprint: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
expected_fingerprint: impl AsRef<str>,
|
||||
) -> bool {
|
||||
if 0 == mimeparser.e2ee_helper.encrypted {
|
||||
warn!(mimeparser.context, 0, "Message not encrypted.",);
|
||||
return 0i32;
|
||||
return false;
|
||||
}
|
||||
if mimeparser.e2ee_helper.signatures.len() <= 0 {
|
||||
warn!(mimeparser.context, 0, "Message not signed.",);
|
||||
return 0i32;
|
||||
return false;
|
||||
}
|
||||
if expected_fingerprint.is_null() {
|
||||
if expected_fingerprint.as_ref().is_empty() {
|
||||
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
|
||||
return 0i32;
|
||||
return false;
|
||||
}
|
||||
if !mimeparser
|
||||
.e2ee_helper
|
||||
.signatures
|
||||
.contains(as_str(expected_fingerprint))
|
||||
.contains(expected_fingerprint.as_ref())
|
||||
{
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0,
|
||||
"Message does not match expected fingerprint {}.",
|
||||
as_str(expected_fingerprint),
|
||||
expected_fingerprint.as_ref(),
|
||||
);
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
1
|
||||
true
|
||||
}
|
||||
|
||||
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
|
||||
let mut contact_chat_id = 0;
|
||||
|
||||
// - we do not issue an warning for DC_DE_ENCRYPTION_PAUSED as this is quite normal
|
||||
// - currently, we do not issue an extra warning for DC_DE_VERIFICATION_LOST - this always comes
|
||||
// together with DC_DE_FINGERPRINT_CHANGED which is logged, the idea is not to bother
|
||||
@@ -940,22 +1006,17 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
|
||||
)
|
||||
.unwrap_or_default();
|
||||
if contact_id > 0 {
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
contact_id as u32,
|
||||
2,
|
||||
&mut contact_chat_id,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
let (contact_chat_id, _) =
|
||||
chat::create_or_lookup_by_contact_id(context, contact_id as u32, Blocked::Deaddrop)
|
||||
.unwrap_or_default();
|
||||
|
||||
let peeraddr: &str = match peerstate.addr {
|
||||
Some(ref addr) => &addr,
|
||||
None => "",
|
||||
};
|
||||
let msg = CString::new(
|
||||
context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr),
|
||||
)
|
||||
.unwrap();
|
||||
dc_add_device_msg(context, contact_chat_id, msg.as_ptr());
|
||||
let msg = context.stock_string_repl_str(StockMessage::ContactSetupChanged, peeraddr);
|
||||
|
||||
chat::add_device_msg(context, contact_chat_id, msg);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
contact_chat_id as uintptr_t,
|
||||
|
||||
@@ -3,19 +3,18 @@ use crate::dc_tools::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct dc_simplify_t {
|
||||
pub is_forwarded: libc::c_int,
|
||||
pub is_cut_at_begin: libc::c_int,
|
||||
pub is_cut_at_end: libc::c_int,
|
||||
pub struct Simplify {
|
||||
pub is_forwarded: bool,
|
||||
pub is_cut_at_begin: bool,
|
||||
pub is_cut_at_end: bool,
|
||||
}
|
||||
|
||||
impl dc_simplify_t {
|
||||
impl Simplify {
|
||||
pub fn new() -> Self {
|
||||
dc_simplify_t {
|
||||
is_forwarded: 0,
|
||||
is_cut_at_begin: 0,
|
||||
is_cut_at_end: 0,
|
||||
Simplify {
|
||||
is_forwarded: false,
|
||||
is_cut_at_begin: false,
|
||||
is_cut_at_end: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +25,7 @@ impl dc_simplify_t {
|
||||
&mut self,
|
||||
in_unterminated: *const libc::c_char,
|
||||
in_bytes: libc::c_int,
|
||||
is_html: libc::c_int,
|
||||
is_html: bool,
|
||||
is_msgrmsg: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
if in_bytes <= 0 {
|
||||
@@ -36,9 +35,9 @@ impl dc_simplify_t {
|
||||
/* 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;
|
||||
self.is_forwarded = false;
|
||||
self.is_cut_at_begin = false;
|
||||
self.is_cut_at_end = false;
|
||||
out = strndup(
|
||||
in_unterminated as *mut libc::c_char,
|
||||
in_bytes as libc::c_ulong,
|
||||
@@ -46,7 +45,7 @@ impl dc_simplify_t {
|
||||
if out.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
if 0 != is_html {
|
||||
if is_html {
|
||||
temp = dc_dehtml(out);
|
||||
if !temp.is_null() {
|
||||
free(out as *mut libc::c_void);
|
||||
@@ -96,7 +95,7 @@ impl dc_simplify_t {
|
||||
|| strcmp(line, b"----\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
footer_mark = 1i32;
|
||||
self.is_cut_at_end = 1i32
|
||||
self.is_cut_at_end = true
|
||||
}
|
||||
if 0 != footer_mark {
|
||||
l_last = l;
|
||||
@@ -115,7 +114,7 @@ impl dc_simplify_t {
|
||||
&& 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;
|
||||
self.is_forwarded = true;
|
||||
l_first += 3
|
||||
}
|
||||
}
|
||||
@@ -128,7 +127,7 @@ impl dc_simplify_t {
|
||||
|| strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32
|
||||
{
|
||||
l_last = l;
|
||||
self.is_cut_at_end = 1i32;
|
||||
self.is_cut_at_end = true;
|
||||
/* done */
|
||||
break;
|
||||
}
|
||||
@@ -145,7 +144,7 @@ impl dc_simplify_t {
|
||||
}
|
||||
if l_lastQuotedLine.is_some() {
|
||||
l_last = l_lastQuotedLine.unwrap();
|
||||
self.is_cut_at_end = 1i32;
|
||||
self.is_cut_at_end = true;
|
||||
if l_last > 1 {
|
||||
if is_empty_line(lines[l_last - 1]) {
|
||||
l_last -= 1
|
||||
@@ -180,12 +179,12 @@ impl dc_simplify_t {
|
||||
}
|
||||
if l_lastQuotedLine_0.is_some() {
|
||||
l_first = l_lastQuotedLine_0.unwrap() + 1;
|
||||
self.is_cut_at_begin = 1i32
|
||||
self.is_cut_at_begin = true
|
||||
}
|
||||
}
|
||||
/* re-create buffer from the remaining lines */
|
||||
let mut ret = String::new();
|
||||
if 0 != self.is_cut_at_begin {
|
||||
if self.is_cut_at_begin {
|
||||
ret += "[...]";
|
||||
}
|
||||
/* we write empty lines only in case and non-empty line follows */
|
||||
@@ -211,7 +210,7 @@ impl dc_simplify_t {
|
||||
pending_linebreaks = 1i32
|
||||
}
|
||||
}
|
||||
if 0 != self.is_cut_at_end && (0 == self.is_cut_at_begin || 0 != content_lines_added) {
|
||||
if self.is_cut_at_end && (!self.is_cut_at_begin || 0 != content_lines_added) {
|
||||
ret += " [...]";
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
@@ -268,11 +267,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simplify_trim() {
|
||||
unsafe {
|
||||
let mut simplify = dc_simplify_t::new();
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"\r\r\nline1<br>\r\n\r\n\r\rline2\n\r\x00" as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -288,11 +287,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simplify_parse_href() {
|
||||
unsafe {
|
||||
let mut simplify = dc_simplify_t::new();
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<a href=url>text</a\x00" as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -308,12 +307,11 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simplify_bold_text() {
|
||||
unsafe {
|
||||
let mut simplify = dc_simplify_t::new();
|
||||
let mut simplify = Simplify::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<!DOCTYPE name [<!DOCTYPE ...>]><!-- comment -->text <b><?php echo ... ?>bold</b><![CDATA[<>]]>\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
@@ -329,18 +327,17 @@ mod tests {
|
||||
#[test]
|
||||
fn test_simplify_html_encoded() {
|
||||
unsafe {
|
||||
let mut simplify = dc_simplify_t::new();
|
||||
let html: *const libc::c_char =
|
||||
b"<>"'& äÄöÖüÜß fooÆçÇ ♦&noent;‎‏‌‍\x00"
|
||||
let mut simplify = Simplify::new();
|
||||
let html =
|
||||
b"<>"'& äÄöÖüÜß fooÆçÇ ♦‎‏‌&noent;‍\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
let plain: *mut libc::c_char =
|
||||
simplify.simplify(html, strlen(html) as libc::c_int, 1, 0);
|
||||
let plain = simplify.simplify(html, strlen(html) as libc::c_int, true, 0);
|
||||
|
||||
assert_eq!(
|
||||
strcmp(plain,
|
||||
b"<>\"\'& \xc3\xa4\xc3\x84\xc3\xb6\xc3\x96\xc3\xbc\xc3\x9c\xc3\x9f foo\xc3\x86\xc3\xa7\xc3\x87 \xe2\x99\xa6&noent;\x00"
|
||||
as *const u8 as *const libc::c_char),
|
||||
0,
|
||||
CStr::from_ptr(plain as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"<>\"\'& äÄöÖüÜß fooÆçÇ \u{2666}\u{200e}\u{200f}\u{200c}&noent;\u{200d}"
|
||||
);
|
||||
|
||||
free(plain as *mut libc::c_void);
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
#[inline]
|
||||
pub fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
if c < std::u8::MAX as libc::c_int {
|
||||
(c as u8 as char).is_ascii_alphanumeric() as libc::c_int
|
||||
} else {
|
||||
@@ -18,48 +18,6 @@ pub fn isalnum(c: libc::c_int) -> libc::c_int {
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_urlencode(to_encode: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut pstr: *const libc::c_char = to_encode;
|
||||
if to_encode.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let buf: *mut libc::c_char =
|
||||
malloc(strlen(to_encode).wrapping_mul(3).wrapping_add(1)) as *mut libc::c_char;
|
||||
let mut pbuf: *mut libc::c_char = buf;
|
||||
assert!(!buf.is_null());
|
||||
|
||||
while 0 != *pstr {
|
||||
if 0 != isalnum(*pstr as libc::c_int)
|
||||
|| *pstr as libc::c_int == '-' as i32
|
||||
|| *pstr as libc::c_int == '_' as i32
|
||||
|| *pstr as libc::c_int == '.' as i32
|
||||
|| *pstr as libc::c_int == '~' as i32
|
||||
{
|
||||
let fresh0 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh0 = *pstr
|
||||
} else if *pstr as libc::c_int == ' ' as i32 {
|
||||
let fresh1 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh1 = '+' as i32 as libc::c_char
|
||||
} else {
|
||||
let fresh2 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh2 = '%' as i32 as libc::c_char;
|
||||
let fresh3 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh3 = int_2_uppercase_hex((*pstr as libc::c_int >> 4i32) as libc::c_char);
|
||||
let fresh4 = pbuf;
|
||||
pbuf = pbuf.offset(1);
|
||||
*fresh4 = int_2_uppercase_hex((*pstr as libc::c_int & 15i32) as libc::c_char)
|
||||
}
|
||||
pstr = pstr.offset(1isize)
|
||||
}
|
||||
*pbuf = '\u{0}' as i32 as libc::c_char;
|
||||
|
||||
buf
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* URL encoding and decoding, RFC 3986
|
||||
******************************************************************************/
|
||||
@@ -331,7 +289,8 @@ pub unsafe fn dc_decode_header_words(in_0: *const libc::c_char) -> *mut libc::c_
|
||||
out
|
||||
}
|
||||
|
||||
pub unsafe fn dc_encode_modified_utf7(
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_encode_modified_utf7(
|
||||
mut to_encode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
@@ -475,13 +434,15 @@ pub unsafe fn dc_encode_modified_utf7(
|
||||
******************************************************************************/
|
||||
|
||||
// UTF7 modified base64 alphabet
|
||||
#[cfg(test)]
|
||||
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,
|
||||
];
|
||||
|
||||
pub unsafe fn dc_decode_modified_utf7(
|
||||
#[cfg(test)]
|
||||
unsafe fn dc_decode_modified_utf7(
|
||||
to_decode: *const libc::c_char,
|
||||
change_spaces: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
@@ -691,11 +652,11 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
|
||||
}
|
||||
}
|
||||
free(charset as *mut libc::c_void);
|
||||
return if !decoded.is_null() {
|
||||
if !decoded.is_null() {
|
||||
decoded
|
||||
} else {
|
||||
dc_strdup(to_decode)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
@@ -710,6 +671,7 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[test]
|
||||
@@ -889,14 +851,15 @@ mod tests {
|
||||
#[test]
|
||||
fn test_dc_urlencode_urldecode() {
|
||||
unsafe {
|
||||
let buf1 =
|
||||
dc_urlencode(b"Bj\xc3\xb6rn Petersen\x00" as *const u8 as *const libc::c_char);
|
||||
let buf1 = percent_encode(b"Bj\xc3\xb6rn Petersen", NON_ALPHANUMERIC)
|
||||
.to_string()
|
||||
.strdup();
|
||||
|
||||
assert_eq!(
|
||||
CStr::from_ptr(buf1 as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"Bj%C3%B6rn+Petersen"
|
||||
"Bj%C3%B6rn%20Petersen"
|
||||
);
|
||||
|
||||
let buf2 = dc_urldecode(buf1);
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::sql;
|
||||
|
||||
// Token namespaces
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type dc_tokennamespc_t = usize;
|
||||
type dc_tokennamespc_t = usize;
|
||||
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
|
||||
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
|
||||
|
||||
|
||||
937
src/dc_tools.rs
937
src/dc_tools.rs
File diff suppressed because it is too large
Load Diff
16
src/error.rs
16
src/error.rs
@@ -18,6 +18,10 @@ pub enum Error {
|
||||
Io(std::io::Error),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Message(String),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Image(image_meta::ImageError),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Utf8(std::str::Utf8Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -46,6 +50,18 @@ impl From<std::io::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::str::Utf8Error> for Error {
|
||||
fn from(err: std::str::Utf8Error) -> Error {
|
||||
Error::Utf8(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<image_meta::ImageError> for Error {
|
||||
fn from(err: image_meta::ImageError) -> Error {
|
||||
Error::Image(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail {
|
||||
($e:expr) => {
|
||||
|
||||
122
src/imap.rs
122
src/imap.rs
@@ -1,6 +1,9 @@
|
||||
use std::ffi::CString;
|
||||
use std::net;
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Condvar, Mutex, RwLock,
|
||||
};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::constants::*;
|
||||
@@ -10,19 +13,18 @@ use crate::dc_tools::CStringExt;
|
||||
use crate::oauth2::dc_get_oauth2_access_token;
|
||||
use crate::types::*;
|
||||
|
||||
pub const DC_IMAP_SEEN: usize = 0x0001;
|
||||
pub const DC_REGENERATE: usize = 0x01;
|
||||
const DC_IMAP_SEEN: usize = 0x0001;
|
||||
const DC_REGENERATE: usize = 0x01;
|
||||
|
||||
pub const DC_SUCCESS: usize = 3;
|
||||
pub const DC_ALREADY_DONE: usize = 2;
|
||||
pub const DC_RETRY_LATER: usize = 1;
|
||||
pub const DC_FAILED: usize = 0;
|
||||
const DC_SUCCESS: usize = 3;
|
||||
const DC_ALREADY_DONE: usize = 2;
|
||||
const DC_RETRY_LATER: usize = 1;
|
||||
const DC_FAILED: usize = 0;
|
||||
|
||||
const PREFETCH_FLAGS: &'static str = "(UID ENVELOPE)";
|
||||
const BODY_FLAGS: &'static str = "(FLAGS BODY.PEEK[])";
|
||||
const FETCH_FLAGS: &'static str = "(FLAGS)";
|
||||
const PREFETCH_FLAGS: &str = "(UID ENVELOPE)";
|
||||
const BODY_FLAGS: &str = "(FLAGS BODY.PEEK[])";
|
||||
const FETCH_FLAGS: &str = "(FLAGS)";
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Imap {
|
||||
config: Arc<RwLock<ImapConfig>>,
|
||||
watch: Arc<(Mutex<bool>, Condvar)>,
|
||||
@@ -35,6 +37,8 @@ pub struct Imap {
|
||||
session: Arc<Mutex<Option<Session>>>,
|
||||
stream: Arc<RwLock<Option<net::TcpStream>>>,
|
||||
connected: Arc<Mutex<bool>>,
|
||||
|
||||
should_reconnect: AtomicBool,
|
||||
}
|
||||
|
||||
struct OAuth2 {
|
||||
@@ -55,13 +59,13 @@ impl imap::Authenticator for OAuth2 {
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FolderMeaning {
|
||||
enum FolderMeaning {
|
||||
Unknown,
|
||||
SentObjects,
|
||||
Other,
|
||||
}
|
||||
|
||||
pub enum Client {
|
||||
enum Client {
|
||||
Secure(
|
||||
imap::Client<native_tls::TlsStream<net::TcpStream>>,
|
||||
net::TcpStream,
|
||||
@@ -69,12 +73,12 @@ pub enum Client {
|
||||
Insecure(imap::Client<net::TcpStream>, net::TcpStream),
|
||||
}
|
||||
|
||||
pub enum Session {
|
||||
enum Session {
|
||||
Secure(imap::Session<native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::Session<net::TcpStream>),
|
||||
}
|
||||
|
||||
pub enum IdleHandle<'a> {
|
||||
enum IdleHandle<'a> {
|
||||
Secure(imap::extensions::idle::Handle<'a, native_tls::TlsStream<net::TcpStream>>),
|
||||
Insecure(imap::extensions::idle::Handle<'a, net::TcpStream>),
|
||||
}
|
||||
@@ -199,8 +203,8 @@ impl Session {
|
||||
|
||||
pub fn create<S: AsRef<str>>(&mut self, mailbox_name: S) -> imap::error::Result<()> {
|
||||
match self {
|
||||
Session::Secure(i) => i.subscribe(mailbox_name),
|
||||
Session::Insecure(i) => i.subscribe(mailbox_name),
|
||||
Session::Secure(i) => i.create(mailbox_name),
|
||||
Session::Insecure(i) => i.create(mailbox_name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,8 +264,8 @@ impl Session {
|
||||
|
||||
pub fn idle(&mut self) -> imap::error::Result<IdleHandle> {
|
||||
match self {
|
||||
Session::Secure(i) => i.idle().map(|h| IdleHandle::Secure(h)),
|
||||
Session::Insecure(i) => i.idle().map(|h| IdleHandle::Insecure(h)),
|
||||
Session::Secure(i) => i.idle().map(IdleHandle::Secure),
|
||||
Session::Insecure(i) => i.idle().map(IdleHandle::Insecure),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +307,7 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImapConfig {
|
||||
struct ImapConfig {
|
||||
pub addr: String,
|
||||
pub imap_server: String,
|
||||
pub imap_port: u16,
|
||||
@@ -313,7 +317,6 @@ pub struct ImapConfig {
|
||||
pub selected_folder: Option<String>,
|
||||
pub selected_mailbox: Option<imap::types::Mailbox>,
|
||||
pub selected_folder_needs_expunge: bool,
|
||||
pub should_reconnect: bool,
|
||||
pub can_idle: bool,
|
||||
pub has_xlist: bool,
|
||||
pub imap_delimiter: char,
|
||||
@@ -332,7 +335,6 @@ impl Default for ImapConfig {
|
||||
selected_folder: None,
|
||||
selected_mailbox: None,
|
||||
selected_folder_needs_expunge: false,
|
||||
should_reconnect: false,
|
||||
can_idle: false,
|
||||
has_xlist: false,
|
||||
imap_delimiter: '.',
|
||||
@@ -360,6 +362,7 @@ impl Imap {
|
||||
precheck_imf,
|
||||
receive_imf,
|
||||
connected: Arc::new(Mutex::new(false)),
|
||||
should_reconnect: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +371,7 @@ impl Imap {
|
||||
}
|
||||
|
||||
pub fn should_reconnect(&self) -> bool {
|
||||
self.config.read().unwrap().should_reconnect
|
||||
self.should_reconnect.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn setup_handle_if_needed(&self, context: &Context) -> bool {
|
||||
@@ -381,7 +384,7 @@ impl Imap {
|
||||
}
|
||||
|
||||
if self.is_connected() && self.stream.read().unwrap().is_some() {
|
||||
self.config.write().unwrap().should_reconnect = false;
|
||||
self.should_reconnect.store(false, Ordering::Relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -451,7 +454,7 @@ impl Imap {
|
||||
}
|
||||
};
|
||||
|
||||
self.config.write().unwrap().should_reconnect = false;
|
||||
self.should_reconnect.store(false, Ordering::Relaxed);
|
||||
|
||||
match login_res {
|
||||
Ok((session, stream)) => {
|
||||
@@ -539,7 +542,7 @@ impl Imap {
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.addr = addr.to_string();
|
||||
config.imap_server = imap_server.to_string();
|
||||
config.imap_port = imap_port.into();
|
||||
config.imap_port = imap_port;
|
||||
config.imap_user = imap_user.to_string();
|
||||
config.imap_pw = imap_pw.to_string();
|
||||
config.server_flags = server_flags;
|
||||
@@ -550,14 +553,12 @@ impl Imap {
|
||||
return false;
|
||||
}
|
||||
|
||||
let teardown: bool;
|
||||
|
||||
match &mut *self.session.lock().unwrap() {
|
||||
let (teardown, can_idle, has_xlist) = match &mut *self.session.lock().unwrap() {
|
||||
Some(ref mut session) => {
|
||||
if let Ok(caps) = session.capabilities() {
|
||||
if !context.sql.is_open() {
|
||||
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
teardown = true;
|
||||
(true, false, false)
|
||||
} else {
|
||||
let can_idle = caps.has("IDLE");
|
||||
let has_xlist = caps.has("XLIST");
|
||||
@@ -574,24 +575,23 @@ impl Imap {
|
||||
lp.mail_user,
|
||||
caps_list,
|
||||
);
|
||||
self.config.write().unwrap().can_idle = can_idle;
|
||||
self.config.write().unwrap().has_xlist = has_xlist;
|
||||
*self.connected.lock().unwrap() = true;
|
||||
teardown = false;
|
||||
(false, can_idle, has_xlist)
|
||||
}
|
||||
} else {
|
||||
teardown = true;
|
||||
(true, false, false)
|
||||
}
|
||||
}
|
||||
None => {
|
||||
teardown = true;
|
||||
}
|
||||
}
|
||||
None => (true, false, false),
|
||||
};
|
||||
|
||||
if teardown {
|
||||
self.unsetup_handle(context);
|
||||
self.free_connect_params();
|
||||
false
|
||||
} else {
|
||||
self.config.write().unwrap().can_idle = can_idle;
|
||||
self.config.write().unwrap().has_xlist = has_xlist;
|
||||
*self.connected.lock().unwrap() = true;
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -689,9 +689,8 @@ impl Imap {
|
||||
err
|
||||
);
|
||||
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.selected_folder = None;
|
||||
config.should_reconnect = true;
|
||||
self.config.write().unwrap().selected_folder = None;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -777,7 +776,7 @@ impl Imap {
|
||||
match session.fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(_err) => {
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
@@ -822,7 +821,7 @@ impl Imap {
|
||||
match session.uid_fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(list) => list,
|
||||
Err(err) => {
|
||||
eprintln!("fetch err: {:?}", err);
|
||||
warn!(context, 0, "failed to fetch uids: {}", err);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -928,15 +927,13 @@ impl Imap {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut retry_later = false;
|
||||
|
||||
let set = format!("{}", server_uid);
|
||||
|
||||
let msgs = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_fetch(set, BODY_FLAGS) {
|
||||
Ok(msgs) => msgs,
|
||||
Err(err) => {
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
@@ -946,17 +943,11 @@ impl Imap {
|
||||
self.should_reconnect(),
|
||||
err
|
||||
);
|
||||
|
||||
if self.should_reconnect() {
|
||||
// maybe we should also retry on other errors, however, we should check this carefully, as this may result in a dead lock!
|
||||
retry_later = true;
|
||||
}
|
||||
|
||||
return if retry_later { 0 } else { 1 };
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return if retry_later { 0 } else { 1 };
|
||||
return 1;
|
||||
};
|
||||
|
||||
if msgs.is_empty() {
|
||||
@@ -1004,11 +995,7 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
if retry_later {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
1
|
||||
}
|
||||
|
||||
pub fn idle(&self, context: &Context) {
|
||||
@@ -1073,7 +1060,7 @@ impl Imap {
|
||||
context,
|
||||
0, "IMAP-IDLE wait cancelled, we will reconnect soon."
|
||||
);
|
||||
self.config.write().unwrap().should_reconnect = true;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
}
|
||||
_ => {
|
||||
warn!(context, 0, "IMAP-IDLE returns unknown value: {}", err);
|
||||
@@ -1576,20 +1563,23 @@ impl Imap {
|
||||
info!(context, 0, "MVBOX-folder created.",);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("create error: {:?}", err);
|
||||
warn!(
|
||||
context,
|
||||
0, "Cannot create MVBOX-folder, using trying INBOX subfolder."
|
||||
0,
|
||||
"Cannot create MVBOX-folder, using trying INBOX subfolder. ({})",
|
||||
err
|
||||
);
|
||||
|
||||
match session.create(&fallback_folder) {
|
||||
Ok(_) => {
|
||||
mvbox_folder = Some(fallback_folder);
|
||||
info!(context, 0, "MVBOX-folder created as INBOX subfolder.",);
|
||||
info!(
|
||||
context,
|
||||
0, "MVBOX-folder created as INBOX subfolder. ({})", err
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("create error: {:?}", err);
|
||||
warn!(context, 0, "Cannot create MVBOX-folder.",);
|
||||
warn!(context, 0, "Cannot create MVBOX-folder. ({})", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1181
src/job.rs
Normal file
1181
src/job.rs
Normal file
File diff suppressed because it is too large
Load Diff
181
src/job_thread.rs
Normal file
181
src/job_thread.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::imap::Imap;
|
||||
|
||||
pub struct JobThread {
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
pub imap: Imap,
|
||||
pub state: Arc<(Mutex<JobState>, Condvar)>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct JobState {
|
||||
idle: bool,
|
||||
jobs_needed: i32,
|
||||
suspended: bool,
|
||||
using_handle: bool,
|
||||
}
|
||||
|
||||
impl JobThread {
|
||||
pub fn new(name: &'static str, folder_config_name: &'static str, imap: Imap) -> Self {
|
||||
JobThread {
|
||||
name,
|
||||
folder_config_name,
|
||||
imap,
|
||||
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn suspend(&self, context: &Context) {
|
||||
info!(context, 0, "Suspending {}-thread.", self.name,);
|
||||
{
|
||||
self.state.0.lock().unwrap().suspended = true;
|
||||
}
|
||||
self.interrupt_idle(context);
|
||||
loop {
|
||||
let using_handle = self.state.0.lock().unwrap().using_handle;
|
||||
if !using_handle {
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unsuspend(&self, context: &Context) {
|
||||
info!(context, 0, "Unsuspending {}-thread.", self.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.suspended = false;
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub fn interrupt_idle(&self, context: &Context) {
|
||||
{
|
||||
self.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
info!(context, 0, "Interrupting {}-IDLE...", self.name);
|
||||
|
||||
self.imap.interrupt_idle();
|
||||
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
state.idle = true;
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
pub fn fetch(&mut self, context: &Context, use_network: bool) {
|
||||
{
|
||||
let &(ref lock, _) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if state.suspended {
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = true;
|
||||
}
|
||||
|
||||
if use_network {
|
||||
let start = std::time::Instant::now();
|
||||
if self.connect_to_imap(context) {
|
||||
info!(context, 0, "{}-fetch started...", self.name);
|
||||
self.imap.fetch(context);
|
||||
|
||||
if self.imap.should_reconnect() {
|
||||
info!(context, 0, "{}-fetch aborted, starting over...", self.name,);
|
||||
self.imap.fetch(context);
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
self.name,
|
||||
start.elapsed().as_millis(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
|
||||
fn connect_to_imap(&self, context: &Context) -> bool {
|
||||
if self.imap.is_connected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut ret_connected = dc_connect_to_configured_imap(context, &self.imap) != 0;
|
||||
|
||||
if ret_connected {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default()
|
||||
< 3
|
||||
{
|
||||
self.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
|
||||
if let Some(mvbox_name) = context.sql.get_config(context, self.folder_config_name) {
|
||||
self.imap.set_watch_folder(mvbox_name);
|
||||
} else {
|
||||
self.imap.disconnect(context);
|
||||
ret_connected = false;
|
||||
}
|
||||
}
|
||||
|
||||
ret_connected
|
||||
}
|
||||
|
||||
pub fn idle(&self, context: &Context, use_network: bool) {
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*self.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.jobs_needed {
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
self.name,
|
||||
);
|
||||
state.jobs_needed = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if state.suspended {
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
|
||||
state.using_handle = true;
|
||||
|
||||
if !use_network {
|
||||
state.using_handle = false;
|
||||
|
||||
while !state.idle {
|
||||
state = cvar.wait(state).unwrap();
|
||||
}
|
||||
state.idle = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.connect_to_imap(context);
|
||||
info!(context, 0, "{}-IDLE started...", self.name,);
|
||||
self.imap.idle(context);
|
||||
info!(context, 0, "{}-IDLE ended.", self.name);
|
||||
|
||||
self.state.0.lock().unwrap().using_handle = false;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ impl Key {
|
||||
}
|
||||
|
||||
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
|
||||
if 0 == bytes.len() {
|
||||
if bytes.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let res: Result<Key, _> = match key_type {
|
||||
@@ -283,7 +283,7 @@ impl Key {
|
||||
Key::Public(_) => None,
|
||||
Key::Secret(k) => {
|
||||
let pub_key = k.public_key();
|
||||
pub_key.sign(k, || "".into()).map(|k| Key::Public(k)).ok()
|
||||
pub_key.sign(k, || "".into()).map(Key::Public).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
src/lib.rs
50
src/lib.rs
@@ -1,4 +1,9 @@
|
||||
#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)]
|
||||
#![deny(clippy::correctness)]
|
||||
// TODO: make all of these errors, such that clippy actually passes.
|
||||
#![warn(clippy::all, clippy::perf, clippy::not_unsafe_ptr_arg_deref)]
|
||||
// This is nice, but for now just annoying.
|
||||
#![allow(clippy::unreadable_literal)]
|
||||
#![feature(ptr_wrapping_offset_from)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate failure_derive;
|
||||
@@ -15,52 +20,49 @@ extern crate strum_macros;
|
||||
#[macro_use]
|
||||
mod log;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
mod error;
|
||||
|
||||
pub mod aheader;
|
||||
mod aheader;
|
||||
pub mod chat;
|
||||
pub mod chatlist;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
pub mod contact;
|
||||
pub mod context;
|
||||
pub mod imap;
|
||||
mod imap;
|
||||
pub mod job;
|
||||
mod job_thread;
|
||||
pub mod key;
|
||||
pub mod keyring;
|
||||
pub mod lot;
|
||||
pub mod oauth2;
|
||||
pub mod param;
|
||||
mod param;
|
||||
pub mod peerstate;
|
||||
pub mod pgp;
|
||||
pub mod smtp;
|
||||
pub mod qr;
|
||||
mod smtp;
|
||||
pub mod sql;
|
||||
pub mod stock;
|
||||
mod stock;
|
||||
pub mod types;
|
||||
pub mod x;
|
||||
|
||||
pub mod dc_array;
|
||||
pub mod dc_chat;
|
||||
pub mod dc_configure;
|
||||
pub mod dc_dehtml;
|
||||
pub mod dc_e2ee;
|
||||
mod dc_dehtml;
|
||||
mod dc_e2ee;
|
||||
pub mod dc_imex;
|
||||
pub mod dc_job;
|
||||
pub mod dc_jobthread;
|
||||
pub mod dc_location;
|
||||
pub mod dc_loginparam;
|
||||
pub mod dc_lot;
|
||||
pub mod dc_mimefactory;
|
||||
mod dc_loginparam;
|
||||
mod dc_mimefactory;
|
||||
pub mod dc_mimeparser;
|
||||
pub mod dc_move;
|
||||
mod dc_move;
|
||||
pub mod dc_msg;
|
||||
pub mod dc_qr;
|
||||
pub mod dc_receive_imf;
|
||||
pub mod dc_saxparser;
|
||||
pub mod dc_securejoin;
|
||||
pub mod dc_simplify;
|
||||
pub mod dc_strencode;
|
||||
pub mod dc_token;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
mod dc_token;
|
||||
pub mod dc_tools;
|
||||
|
||||
pub use self::constants::*;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test_utils;
|
||||
mod test_utils;
|
||||
|
||||
109
src/lot.rs
Normal file
109
src/lot.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
|
||||
/// An object containing a set of values.
|
||||
/// The meaning of the values is defined by the function returning the object.
|
||||
/// Lot objects are created
|
||||
/// eg. by chatlist.get_summary() or dc_msg_get_summary().
|
||||
///
|
||||
/// _Lot_ is used in the meaning _heap_ here.
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Lot {
|
||||
pub(crate) text1_meaning: Meaning,
|
||||
pub(crate) text1: Option<String>,
|
||||
pub(crate) text2: Option<String>,
|
||||
pub(crate) timestamp: i64,
|
||||
pub(crate) state: LotState,
|
||||
pub(crate) id: u32,
|
||||
pub(crate) fingerprint: Option<String>,
|
||||
pub(crate) invitenumber: Option<String>,
|
||||
pub(crate) auth: Option<String>,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
pub enum Meaning {
|
||||
None = 0,
|
||||
Text1Draft = 1,
|
||||
Text1Username = 2,
|
||||
Text1Self = 3,
|
||||
}
|
||||
|
||||
impl Default for Meaning {
|
||||
fn default() -> Self {
|
||||
Meaning::None
|
||||
}
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn get_text1(&self) -> Option<&str> {
|
||||
self.text1.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
pub fn get_text2(&self) -> Option<&str> {
|
||||
self.text2.as_ref().map(|s| s.as_str())
|
||||
}
|
||||
|
||||
pub fn get_text1_meaning(&self) -> Meaning {
|
||||
self.text1_meaning
|
||||
}
|
||||
|
||||
pub fn get_state(&self) -> LotState {
|
||||
self.state
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn get_timestamp(&self) -> i64 {
|
||||
self.timestamp
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql)]
|
||||
pub enum LotState {
|
||||
// Default
|
||||
Undefined = 0,
|
||||
|
||||
// Qr States
|
||||
/// id=contact
|
||||
QrAskVerifyContact = 200,
|
||||
/// text1=groupname
|
||||
QrAskVerifyGroup = 202,
|
||||
/// id=contact
|
||||
QrFprOk = 210,
|
||||
/// id=contact
|
||||
QrFprMissmatch = 220,
|
||||
/// test1=formatted fingerprint
|
||||
QrFprWithoutAddr = 230,
|
||||
/// id=contact
|
||||
QrAddr = 320,
|
||||
/// text1=text
|
||||
QrText = 330,
|
||||
/// text1=URL
|
||||
QrUrl = 332,
|
||||
/// text1=error string
|
||||
QrError = 400,
|
||||
|
||||
// Message States
|
||||
MsgInFresh = 10,
|
||||
MsgInNoticed = 13,
|
||||
MsgInSeen = 16,
|
||||
MsgOutPreparing = 18,
|
||||
MsgOutDraft = 19,
|
||||
MsgOutPending = 20,
|
||||
MsgOutFailed = 24,
|
||||
MsgOutDelivered = 26,
|
||||
MsgOutMdnRcvd = 28,
|
||||
}
|
||||
|
||||
impl Default for LotState {
|
||||
fn default() -> Self {
|
||||
LotState::Undefined
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ const OAUTH2_YANDEX: Oauth2 = Oauth2 {
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Oauth2 {
|
||||
struct Oauth2 {
|
||||
client_id: &'static str,
|
||||
get_code: &'static str,
|
||||
init_token: &'static str,
|
||||
|
||||
10
src/param.rs
10
src/param.rs
@@ -113,7 +113,7 @@ impl str::FromStr for Params {
|
||||
continue;
|
||||
}
|
||||
// TODO: probably nicer using a regex
|
||||
ensure!(pair.len() > 2, "Invalid key pair: '{}'", pair);
|
||||
ensure!(pair.len() > 1, "Invalid key pair: '{}'", pair);
|
||||
let mut split = pair.splitn(2, '=');
|
||||
let key = split.next();
|
||||
let value = split.next();
|
||||
@@ -235,4 +235,12 @@ mod tests {
|
||||
assert!(p1.is_empty());
|
||||
assert_eq!(p1.len(), 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regression() {
|
||||
let p1: Params = "a=cli%40deltachat.de\nn=\ni=TbnwJ6lSvD5\ns=0ejvbdFSQxB"
|
||||
.parse()
|
||||
.unwrap();
|
||||
assert_eq!(p1.get(Param::Forwarded).unwrap(), "cli%40deltachat.de");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::fmt;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::chat::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::key::*;
|
||||
use crate::sql::{self, Sql};
|
||||
|
||||
@@ -85,7 +85,7 @@ pub enum DegradeEvent {
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum VerifiedKey {
|
||||
enum VerifiedKey {
|
||||
Gossip,
|
||||
Public,
|
||||
None,
|
||||
@@ -166,7 +166,6 @@ impl<'a> Peerstate<'a> {
|
||||
|
||||
pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option<Self> {
|
||||
let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;";
|
||||
info!(context, 0, "peerstate.from_addr {}", addr);
|
||||
Self::from_stmt(context, query, &[addr])
|
||||
}
|
||||
|
||||
@@ -188,7 +187,6 @@ impl<'a> Peerstate<'a> {
|
||||
P: IntoIterator,
|
||||
P::Item: rusqlite::ToSql,
|
||||
{
|
||||
info!(context, 0, "from_stmt query {:?}", query);
|
||||
context
|
||||
.sql
|
||||
.query_row(query, params, |row| {
|
||||
@@ -200,28 +198,38 @@ impl<'a> Peerstate<'a> {
|
||||
let mut res = Self::new(context);
|
||||
|
||||
res.addr = Some(row.get(0)?);
|
||||
info!(context, 0, "from_stmt res.addr {:?} starting", res.addr);
|
||||
res.last_seen = row.get(1)?;
|
||||
res.last_seen_autocrypt = row.get(2)?;
|
||||
res.prefer_encrypt = EncryptPreference::from_i32(row.get(3)?).unwrap_or_default();
|
||||
res.gossip_timestamp = row.get(5)?;
|
||||
let pkf: String = row.get(7)?;
|
||||
res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) };
|
||||
|
||||
let t: Result<String, rusqlite::Error> = row.get(8);
|
||||
let gkf: String = match t {
|
||||
Err(_) => String::from(""),
|
||||
Ok(res) => res,
|
||||
};
|
||||
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
|
||||
|
||||
let t: Result<String, rusqlite::Error> = row.get(10);
|
||||
let vkf: String = match t {
|
||||
Err(_) => String::from(""),
|
||||
Ok(res) => res,
|
||||
};
|
||||
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
|
||||
|
||||
res.public_key_fingerprint = row.get(7)?;
|
||||
if res
|
||||
.public_key_fingerprint
|
||||
.as_ref()
|
||||
.map(|s| s.is_empty())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
res.public_key_fingerprint = None;
|
||||
}
|
||||
res.gossip_key_fingerprint = row.get(8)?;
|
||||
if res
|
||||
.gossip_key_fingerprint
|
||||
.as_ref()
|
||||
.map(|s| s.is_empty())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
res.gossip_key_fingerprint = None;
|
||||
}
|
||||
res.verified_key_fingerprint = row.get(10)?;
|
||||
if res
|
||||
.verified_key_fingerprint
|
||||
.as_ref()
|
||||
.map(|s| s.is_empty())
|
||||
.unwrap_or_default()
|
||||
{
|
||||
res.verified_key_fingerprint = None;
|
||||
}
|
||||
res.public_key = row
|
||||
.get(4)
|
||||
.ok()
|
||||
@@ -234,6 +242,7 @@ impl<'a> Peerstate<'a> {
|
||||
.get(9)
|
||||
.ok()
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
|
||||
|
||||
res.verified_key = if vk == res.gossip_key && res.gossip_key.is_some() {
|
||||
VerifiedKey::Gossip
|
||||
} else if vk == res.public_key {
|
||||
@@ -417,10 +426,6 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
|
||||
if self.to_save == Some(ToSave::All) || create {
|
||||
info!(
|
||||
self.context,
|
||||
0, "update acpeerstates with peerstate {:?}", self
|
||||
);
|
||||
success = sql::execute(
|
||||
self.context,
|
||||
sql,
|
||||
@@ -461,7 +466,7 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
|
||||
if self.to_save == Some(ToSave::All) || create {
|
||||
dc_reset_gossiped_timestamp(self.context, 0);
|
||||
reset_gossiped_timestamp(self.context, 0);
|
||||
}
|
||||
|
||||
success
|
||||
|
||||
13
src/pgp.rs
13
src/pgp.rs
@@ -2,6 +2,7 @@ use std::collections::HashSet;
|
||||
use std::convert::TryInto;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Cursor;
|
||||
use std::ptr;
|
||||
|
||||
use pgp::composed::{
|
||||
Deserializable, KeyType as PgpKeyType, Message, SecretKeyParamsBuilder, SignedPublicKey,
|
||||
@@ -29,19 +30,19 @@ pub unsafe fn dc_split_armored_data(
|
||||
let mut line: *mut libc::c_char = buf;
|
||||
let mut p1: *mut libc::c_char = buf;
|
||||
let mut p2: *mut libc::c_char;
|
||||
let mut headerline: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut base64: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut headerline: *mut libc::c_char = ptr::null_mut();
|
||||
let mut base64: *mut libc::c_char = ptr::null_mut();
|
||||
if !ret_headerline.is_null() {
|
||||
*ret_headerline = 0 as *const libc::c_char
|
||||
}
|
||||
if !ret_setupcodebegin.is_null() {
|
||||
*ret_setupcodebegin = 0 as *const libc::c_char
|
||||
*ret_setupcodebegin = ptr::null_mut();
|
||||
}
|
||||
if !ret_preferencrypt.is_null() {
|
||||
*ret_preferencrypt = 0 as *const libc::c_char
|
||||
*ret_preferencrypt = ptr::null();
|
||||
}
|
||||
if !ret_base64.is_null() {
|
||||
*ret_base64 = 0 as *const libc::c_char
|
||||
*ret_base64 = ptr::null();
|
||||
}
|
||||
if !(buf.is_null() || ret_headerline.is_null()) {
|
||||
dc_remove_cr_chars(buf);
|
||||
@@ -143,7 +144,7 @@ pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
|
||||
.key_type(PgpKeyType::Rsa(2048))
|
||||
.can_create_certificates(true)
|
||||
.can_sign(true)
|
||||
.primary_user_id(user_id.into())
|
||||
.primary_user_id(user_id)
|
||||
.passphrase(None)
|
||||
.preferred_symmetric_algorithms(smallvec![
|
||||
SymmetricKeyAlgorithm::AES256,
|
||||
|
||||
469
src/qr.rs
Normal file
469
src/qr.rs
Normal file
@@ -0,0 +1,469 @@
|
||||
use lazy_static::lazy_static;
|
||||
use percent_encoding::percent_decode_str;
|
||||
|
||||
use crate::chat;
|
||||
use crate::constants::Blocked;
|
||||
use crate::contact::*;
|
||||
use crate::context::Context;
|
||||
use crate::error::Error;
|
||||
use crate::key::dc_format_fingerprint;
|
||||
use crate::key::*;
|
||||
use crate::lot::{Lot, LotState};
|
||||
use crate::param::*;
|
||||
use crate::peerstate::*;
|
||||
|
||||
const OPENPGP4FPR_SCHEME: &str = "OPENPGP4FPR:"; // yes: uppercase
|
||||
const MAILTO_SCHEME: &str = "mailto:";
|
||||
const MATMSG_SCHEME: &str = "MATMSG:";
|
||||
const VCARD_SCHEME: &str = "BEGIN:VCARD";
|
||||
const SMTP_SCHEME: &str = "SMTP:";
|
||||
const HTTP_SCHEME: &str = "http://";
|
||||
const HTTPS_SCHEME: &str = "https://";
|
||||
|
||||
// Make it easy to convert errors into the final `Lot`.
|
||||
impl Into<Lot> for Error {
|
||||
fn into(self) -> Lot {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrError;
|
||||
l.text1 = Some(self.to_string());
|
||||
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
/// Check a scanned QR code.
|
||||
/// The function should be called after a QR code is scanned.
|
||||
/// The function takes the raw text scanned and checks what can be done with it.
|
||||
pub fn check_qr(context: &Context, qr: impl AsRef<str>) -> Lot {
|
||||
let qr = qr.as_ref();
|
||||
|
||||
info!(context, 0, "Scanned QR code: {}", qr);
|
||||
|
||||
if qr.starts_with(OPENPGP4FPR_SCHEME) {
|
||||
decode_openpgp(context, qr)
|
||||
} else if qr.starts_with(MAILTO_SCHEME) {
|
||||
decode_mailto(context, qr)
|
||||
} else if qr.starts_with(SMTP_SCHEME) {
|
||||
decode_smtp(context, qr)
|
||||
} else if qr.starts_with(MATMSG_SCHEME) {
|
||||
decode_matmsg(context, qr)
|
||||
} else if qr.starts_with(VCARD_SCHEME) {
|
||||
decode_vcard(context, qr)
|
||||
} else if qr.starts_with(HTTP_SCHEME) || qr.starts_with(HTTPS_SCHEME) {
|
||||
Lot::from_url(qr)
|
||||
} else {
|
||||
Lot::from_text(qr)
|
||||
}
|
||||
}
|
||||
|
||||
/// scheme: `OPENPGP4FPR:FINGERPRINT#a=ADDR&n=NAME&i=INVITENUMBER&s=AUTH`
|
||||
/// or: `OPENPGP4FPR:FINGERPRINT#a=ADDR&g=GROUPNAME&x=GROUPID&i=INVITENUMBER&s=AUTH`
|
||||
fn decode_openpgp(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[OPENPGP4FPR_SCHEME.len()..];
|
||||
|
||||
let (fingerprint, fragment) = match payload.find('#').map(|offset| {
|
||||
let (fp, rest) = payload.split_at(offset);
|
||||
// need to remove the # from the fragment
|
||||
(fp, &rest[1..])
|
||||
}) {
|
||||
Some(pair) => pair,
|
||||
None => return format_err!("Invalid OPENPGP4FPR found").into(),
|
||||
};
|
||||
|
||||
dbg!(fingerprint);
|
||||
dbg!(fragment);
|
||||
|
||||
// replace & with \n to match expected param format
|
||||
let fragment = fragment.replace('&', "\n");
|
||||
dbg!(&fragment);
|
||||
|
||||
// Then parse the parameters
|
||||
let param: Params = match fragment.parse() {
|
||||
Ok(params) => params,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
dbg!(¶m);
|
||||
|
||||
let addr = if let Some(addr) = param.get(Param::Forwarded) {
|
||||
match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
}
|
||||
} else {
|
||||
return format_err!("Missing address").into();
|
||||
};
|
||||
|
||||
// what is up with that param name?
|
||||
let name = if let Some(encoded_name) = param.get(Param::SetLongitude) {
|
||||
match percent_decode_str(encoded_name).decode_utf8() {
|
||||
Ok(name) => name.to_string(),
|
||||
Err(err) => return format_err!("Invalid name: {}", err).into(),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
|
||||
let invitenumber = param.get(Param::ProfileImage).map(|s| s.to_string());
|
||||
let auth = param.get(Param::Auth).map(|s| s.to_string());
|
||||
let grpid = param.get(Param::GroupId).map(|s| s.to_string());
|
||||
|
||||
let grpname = if grpid.is_some() {
|
||||
if let Some(encoded_name) = param.get(Param::GroupName) {
|
||||
match percent_decode_str(encoded_name).decode_utf8() {
|
||||
Ok(name) => Some(name.to_string()),
|
||||
Err(err) => return format_err!("Invalid group name: {}", err).into(),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let fingerprint = dc_normalize_fingerprint(fingerprint);
|
||||
|
||||
// ensure valid fingerprint
|
||||
if fingerprint.len() != 40 {
|
||||
return format_err!("Bad fingerprint length in QR code").into();
|
||||
}
|
||||
|
||||
println!(
|
||||
"{:?} {:?} {:?} {:?} {:?} {:?} {:?}",
|
||||
addr, name, invitenumber, auth, grpid, grpname, fingerprint
|
||||
);
|
||||
|
||||
let mut lot = Lot::new();
|
||||
|
||||
// retrieve known state for this fingerprint
|
||||
let peerstate = Peerstate::from_fingerprint(context, &context.sql, &fingerprint);
|
||||
|
||||
if invitenumber.is_none() || auth.is_none() {
|
||||
if let Some(peerstate) = peerstate {
|
||||
lot.state = LotState::QrFprOk;
|
||||
let addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| "");
|
||||
|
||||
lot.id = Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
let (id, _) = chat::create_or_lookup_by_contact_id(context, lot.id, Blocked::Deaddrop)
|
||||
.unwrap_or_default();
|
||||
|
||||
chat::add_device_msg(
|
||||
context,
|
||||
id,
|
||||
format!("{} verified.", peerstate.addr.unwrap_or_default()),
|
||||
);
|
||||
} else {
|
||||
lot.state = LotState::QrFprWithoutAddr;
|
||||
lot.text1 = Some(dc_format_fingerprint(&fingerprint));
|
||||
}
|
||||
} else {
|
||||
if grpid.is_some() && grpname.is_some() {
|
||||
lot.state = LotState::QrAskVerifyGroup;
|
||||
lot.text1 = grpname;
|
||||
lot.text2 = grpid
|
||||
} else {
|
||||
lot.state = LotState::QrAskVerifyContact;
|
||||
}
|
||||
lot.id = Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan)
|
||||
.map(|(id, _)| id)
|
||||
.unwrap_or_default();
|
||||
|
||||
lot.fingerprint = Some(fingerprint);
|
||||
lot.invitenumber = invitenumber;
|
||||
lot.auth = auth;
|
||||
}
|
||||
|
||||
lot
|
||||
}
|
||||
|
||||
/// Extract address for the mailto scheme.
|
||||
///
|
||||
/// Scheme: `mailto:addr...?subject=...&body=..`
|
||||
fn decode_mailto(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[MAILTO_SCHEME.len()..];
|
||||
|
||||
let addr = if let Some(query_index) = payload.find('?') {
|
||||
&payload[..query_index]
|
||||
} else {
|
||||
return format_err!("Invalid mailto found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
/// Extract address for the smtp scheme.
|
||||
///
|
||||
/// Scheme: `SMTP:addr...:subject...:body...`
|
||||
fn decode_smtp(context: &Context, qr: &str) -> Lot {
|
||||
let payload = &qr[SMTP_SCHEME.len()..];
|
||||
|
||||
let addr = if let Some(query_index) = payload.find(':') {
|
||||
&payload[..query_index]
|
||||
} else {
|
||||
return format_err!("Invalid SMTP found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
///
|
||||
/// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;`
|
||||
///
|
||||
/// There may or may not be linebreaks after the fields.
|
||||
fn decode_matmsg(context: &Context, qr: &str) -> Lot {
|
||||
// Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field.
|
||||
// we ignore this case.
|
||||
let addr = if let Some(to_index) = qr.find("TO:") {
|
||||
let addr = qr[to_index + 3..].trim();
|
||||
if let Some(semi_index) = addr.find(';') {
|
||||
addr[..semi_index].trim()
|
||||
} else {
|
||||
addr
|
||||
}
|
||||
} else {
|
||||
return format_err!("Invalid MATMSG found").into();
|
||||
};
|
||||
|
||||
let addr = match normalize_address(addr) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
let name = "".to_string();
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref VCARD_NAME_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^N:([^;]*);([^;\n]*)").unwrap();
|
||||
static ref VCARD_EMAIL_RE: regex::Regex =
|
||||
regex::Regex::new(r"(?m)^EMAIL([^:\n]*):([^;\n]*)").unwrap();
|
||||
}
|
||||
|
||||
/// Extract address for the matmsg scheme.
|
||||
///
|
||||
/// Scheme: `VCARD:BEGIN\nN:last name;first name;...;\nEMAIL;<type>:addr...;
|
||||
fn decode_vcard(context: &Context, qr: &str) -> Lot {
|
||||
let name = VCARD_NAME_RE
|
||||
.captures(qr)
|
||||
.map(|caps| {
|
||||
let last_name = &caps[1];
|
||||
let first_name = &caps[2];
|
||||
|
||||
format!("{} {}", first_name.trim(), last_name.trim())
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let addr = if let Some(caps) = VCARD_EMAIL_RE.captures(qr) {
|
||||
match normalize_address(caps[2].trim()) {
|
||||
Ok(addr) => addr,
|
||||
Err(err) => return err.into(),
|
||||
}
|
||||
} else {
|
||||
return format_err!("Bad e-mail address").into();
|
||||
};
|
||||
|
||||
Lot::from_address(context, name, addr)
|
||||
}
|
||||
|
||||
impl Lot {
|
||||
pub fn from_text(text: impl AsRef<str>) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrText;
|
||||
l.text1 = Some(text.as_ref().to_string());
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
pub fn from_url(url: impl AsRef<str>) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrUrl;
|
||||
l.text1 = Some(url.as_ref().to_string());
|
||||
|
||||
l
|
||||
}
|
||||
|
||||
pub fn from_address(context: &Context, name: String, addr: String) -> Self {
|
||||
let mut l = Lot::new();
|
||||
l.state = LotState::QrAddr;
|
||||
l.id = match Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan) {
|
||||
Ok((id, _)) => id,
|
||||
Err(err) => return err.into(),
|
||||
};
|
||||
|
||||
l
|
||||
}
|
||||
}
|
||||
|
||||
/// URL decodes a given address, does basic email validation on the result.
|
||||
fn normalize_address(addr: &str) -> Result<String, Error> {
|
||||
// urldecoding is needed at least for OPENPGP4FPR but should not hurt in the other cases
|
||||
let new_addr = percent_decode_str(addr).decode_utf8()?;
|
||||
let new_addr = addr_normalize(&new_addr);
|
||||
|
||||
ensure!(may_be_valid_addr(&new_addr), "Bad e-mail address");
|
||||
|
||||
Ok(new_addr.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::test_utils::dummy_context;
|
||||
|
||||
#[test]
|
||||
fn test_decode_http() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "http://www.hello.com");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrUrl);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "http://www.hello.com");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_https() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "https://www.hello.com");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrUrl);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "https://www.hello.com");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_text() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "I am so cool");
|
||||
|
||||
assert_eq!(res.get_state(), LotState::QrText);
|
||||
assert_eq!(res.get_id(), 0);
|
||||
assert_eq!(res.get_text1().unwrap(), "I am so cool");
|
||||
assert!(res.get_text2().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_vcard() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
assert_eq!(contact.get_name(), "First Last");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_matmsg() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"MATMSG:TO:\n\nstress@test.local ; \n\nSUB:\n\nSubject here\n\nBODY:\n\nhelloworld\n;;",
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_mailto() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"mailto:stress@test.local?subject=hello&body=world",
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_smtp() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(&ctx.ctx, "SMTP:stress@test.local:subjecthello:bodyworld");
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAddr);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "stress@test.local");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_openpgp_group() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&g=testtesttest&x=h-0oKQf2CDK&i=9JEXlxAqGM0&s=0V7LzL9cxRL"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAskVerifyGroup);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "cli@deltachat.de");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decode_openpgp_secure_join() {
|
||||
let ctx = dummy_context();
|
||||
|
||||
let res = check_qr(
|
||||
&ctx.ctx,
|
||||
"OPENPGP4FPR:79252762C34C5096AF57958F4FC3D21A81B0F0A7#a=cli%40deltachat.de&n=&i=TbnwJ6lSvD5&s=0ejvbdFSQxB"
|
||||
);
|
||||
|
||||
println!("{:?}", res);
|
||||
assert_eq!(res.get_state(), LotState::QrAskVerifyContact);
|
||||
assert_ne!(res.get_id(), 0);
|
||||
|
||||
let contact = Contact::get_by_id(&ctx.ctx, res.get_id()).unwrap();
|
||||
assert_eq!(contact.get_addr(), "cli@deltachat.de");
|
||||
}
|
||||
}
|
||||
21
src/sql.rs
21
src/sql.rs
@@ -4,7 +4,6 @@ use std::sync::{Arc, RwLock};
|
||||
use rusqlite::{Connection, OpenFlags, Statement, NO_PARAMS};
|
||||
use thread_local_object::ThreadLocal;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::{Error, Result};
|
||||
@@ -287,7 +286,7 @@ impl Sql {
|
||||
|
||||
fn table_exists(conn: &Connection, name: impl AsRef<str>) -> Result<bool> {
|
||||
let mut exists = false;
|
||||
conn.pragma(None, "table_info", &format!("{}", name.as_ref()), |_row| {
|
||||
conn.pragma(None, "table_info", &name.as_ref().to_string(), |_row| {
|
||||
// will only be executed if the info was found
|
||||
exists = true;
|
||||
Ok(())
|
||||
@@ -689,10 +688,6 @@ fn open(
|
||||
"ALTER TABLE msgs ADD COLUMN move_state INTEGER DEFAULT 1;",
|
||||
params![],
|
||||
)?;
|
||||
assert_eq!(DC_MOVE_STATE_UNDEFINED as libc::c_int, 0);
|
||||
assert_eq!(DC_MOVE_STATE_PENDING as libc::c_int, 1);
|
||||
assert_eq!(DC_MOVE_STATE_STAY as libc::c_int, 2);
|
||||
assert_eq!(DC_MOVE_STATE_MOVING as libc::c_int, 3);
|
||||
|
||||
dbversion = 48;
|
||||
sql.set_config_int(context, "dbversion", 48)?;
|
||||
@@ -835,7 +830,7 @@ where
|
||||
&err,
|
||||
querystr.as_ref()
|
||||
);
|
||||
Err(err.into())
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1008,10 +1003,10 @@ pub fn housekeeping(context: &Context) {
|
||||
let name_f = entry.file_name();
|
||||
let name_s = name_f.to_string_lossy();
|
||||
|
||||
if is_file_in_use(&mut files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s)
|
||||
if is_file_in_use(&files_in_use, None, &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".increation"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some(".waveform"), &name_s)
|
||||
|| is_file_in_use(&files_in_use, Some("-preview.jpg"), &name_s)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1047,8 +1042,8 @@ pub fn housekeeping(context: &Context) {
|
||||
unreferenced_count,
|
||||
entry.file_name()
|
||||
);
|
||||
let path = entry.path().to_c_string().unwrap();
|
||||
dc_delete_file(context, path.as_ptr());
|
||||
let path = entry.path();
|
||||
dc_delete_file(context, path);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
@@ -2,9 +2,15 @@
|
||||
//!
|
||||
//! This module is only compiled for test runs.
|
||||
|
||||
use std::ffi::CStr;
|
||||
|
||||
use libc::uintptr_t;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::constants::{Event, KeyType};
|
||||
use crate::context::{dc_context_new, dc_open, Context};
|
||||
use crate::key;
|
||||
use crate::types::dc_callback_t;
|
||||
|
||||
/// A Context and temporary directory.
|
||||
@@ -44,3 +50,127 @@ pub fn test_context(cb: Option<dc_callback_t>) -> TestContext {
|
||||
pub fn dummy_context() -> TestContext {
|
||||
test_context(None)
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn logging_cb(
|
||||
_ctx: &Context,
|
||||
evt: Event,
|
||||
_d1: uintptr_t,
|
||||
d2: uintptr_t,
|
||||
) -> uintptr_t {
|
||||
let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap();
|
||||
match evt {
|
||||
Event::INFO => println!("I: {}", to_str(d2)),
|
||||
Event::WARNING => println!("W: {}", to_str(d2)),
|
||||
Event::ERROR => println!("E: {}", to_str(d2)),
|
||||
_ => (),
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// Creates Alice with a pre-generated keypair.
|
||||
///
|
||||
/// Returns the address of the keypair created (alice@example.org).
|
||||
pub fn configure_alice_keypair(ctx: &Context) -> String {
|
||||
let addr = String::from("alice@example.org");
|
||||
ctx.set_config(Config::ConfiguredAddr, Some(&addr)).unwrap();
|
||||
|
||||
// The keypair was created using:
|
||||
// let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com")
|
||||
// .unwrap();
|
||||
// println!("{}", public.to_base64(64));
|
||||
// println!("{}", private.to_base64(64));
|
||||
let public = key::Key::from_base64(
|
||||
concat!(
|
||||
"xsBNBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
|
||||
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
|
||||
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
|
||||
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
|
||||
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
|
||||
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAHNEzxhbGljZUBleGFtcGxl",
|
||||
"LmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iai",
|
||||
"x4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9",
|
||||
"OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkK",
|
||||
"A8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea",
|
||||
"6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6",
|
||||
"GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUK",
|
||||
"u5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxD",
|
||||
"Fc7ATQRdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG",
|
||||
"9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av",
|
||||
"62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/R",
|
||||
"noW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q",
|
||||
"4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAm",
|
||||
"jxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABwsB2BBgBCAAgBQJdPOn4",
|
||||
"AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoW",
|
||||
"qEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwX",
|
||||
"FNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9m",
|
||||
"MjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6RDXIeYJf",
|
||||
"qrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMlammDliPw",
|
||||
"sK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKObzPqgJCGw",
|
||||
"jTglkixw+aSTXw=="
|
||||
),
|
||||
KeyType::Public,
|
||||
)
|
||||
.unwrap();
|
||||
let private = key::Key::from_base64(
|
||||
concat!(
|
||||
"xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l",
|
||||
"FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX",
|
||||
"AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4",
|
||||
"Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9",
|
||||
"iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw",
|
||||
"oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtq",
|
||||
"m1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353",
|
||||
"r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68",
|
||||
"JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6F",
|
||||
"FrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHb",
|
||||
"Iu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3V",
|
||||
"WushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0S",
|
||||
"ut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQ",
|
||||
"sWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEm",
|
||||
"dr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8k",
|
||||
"QrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJW",
|
||||
"yyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj",
|
||||
"5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3",
|
||||
"jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeG",
|
||||
"Kyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl08",
|
||||
"6fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQ",
|
||||
"k6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Ee",
|
||||
"h+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BM",
|
||||
"zcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwb",
|
||||
"YklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP",
|
||||
"12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmh",
|
||||
"o0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugz",
|
||||
"OmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnF",
|
||||
"n7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6",
|
||||
"uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEe",
|
||||
"LrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCIC",
|
||||
"N1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86K",
|
||||
"C2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIdd",
|
||||
"KsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T",
|
||||
"/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZL",
|
||||
"j3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtp",
|
||||
"Do5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9u",
|
||||
"RMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe",
|
||||
"/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH",
|
||||
"95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9",
|
||||
"QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ",
|
||||
"8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//",
|
||||
"wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg",
|
||||
"9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwK",
|
||||
"Gjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPB",
|
||||
"f4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAg",
|
||||
"BQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/",
|
||||
"dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJ",
|
||||
"ywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJ",
|
||||
"uiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6",
|
||||
"RDXIeYJfqrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMl",
|
||||
"ammDliPwsK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKOb",
|
||||
"zPqgJCGwjTglkixw+aSTXw=="
|
||||
),
|
||||
KeyType::Private,
|
||||
)
|
||||
.unwrap();
|
||||
let saved = key::dc_key_save_self_keypair(&ctx, &public, &private, &addr, 1, &ctx.sql);
|
||||
assert_eq!(saved, true, "Failed to save Alice's key");
|
||||
addr
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ pub use rusqlite::ffi::*;
|
||||
pub type dc_callback_t =
|
||||
unsafe extern "C" fn(_: &Context, _: Event, _: uintptr_t, _: uintptr_t) -> uintptr_t;
|
||||
|
||||
pub type dc_move_state_t = u32;
|
||||
|
||||
pub type dc_receive_imf_t = unsafe fn(
|
||||
_: &Context,
|
||||
_: *const libc::c_char,
|
||||
@@ -36,14 +34,9 @@ pub type dc_precheck_imf_t =
|
||||
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;
|
||||
|
||||
pub type int32_t = i32;
|
||||
pub type int64_t = i64;
|
||||
pub type uintptr_t = libc::uintptr_t;
|
||||
pub type size_t = libc::size_t;
|
||||
pub type ssize_t = libc::ssize_t;
|
||||
pub type uint32_t = libc::c_uint;
|
||||
pub type uint8_t = libc::c_uchar;
|
||||
pub type uint16_t = libc::c_ushort;
|
||||
|
||||
21
src/x.rs
21
src/x.rs
@@ -54,27 +54,6 @@ pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn strncasecmp(
|
||||
s1: *const libc::c_char,
|
||||
s2: *const libc::c_char,
|
||||
n: libc::size_t,
|
||||
) -> libc::c_int {
|
||||
let s1 = std::ffi::CStr::from_ptr(s1)
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
let s2 = std::ffi::CStr::from_ptr(s2)
|
||||
.to_string_lossy()
|
||||
.to_lowercase();
|
||||
let m1 = std::cmp::min(n, s1.len());
|
||||
let m2 = std::cmp::min(n, s2.len());
|
||||
|
||||
if s1[..m1] == s2[..m2] {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
335
tests/stress.rs
335
tests/stress.rs
@@ -6,20 +6,13 @@ use std::ffi::CString;
|
||||
use mmime::mailimf_types::*;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use deltachat::chat::{self, Chat};
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
use deltachat::dc_mimeparser::*;
|
||||
use deltachat::dc_qr::*;
|
||||
use deltachat::dc_saxparser::*;
|
||||
use deltachat::dc_securejoin::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::key::*;
|
||||
use deltachat::keyring::*;
|
||||
@@ -40,56 +33,16 @@ static mut S_EM_SETUPFILE: *const libc::c_char =
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
unsafe fn stress_functions(context: &Context) {
|
||||
let mut saxparser: dc_saxparser_t = dc_saxparser_t {
|
||||
starttag_cb: None,
|
||||
endtag_cb: None,
|
||||
text_cb: None,
|
||||
userdata: 0 as *mut libc::c_void,
|
||||
};
|
||||
dc_saxparser_init(&mut saxparser, 0 as *mut libc::c_void);
|
||||
dc_saxparser_parse(
|
||||
&mut saxparser,
|
||||
b"<tag attr=val=\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_saxparser_parse(
|
||||
&mut saxparser,
|
||||
b"<tag attr=\"val\"=\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
|
||||
if 0 != dc_is_open(context) {
|
||||
if 0 != dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
) || 0
|
||||
!= dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
|| 0 != dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
|| 0 != dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
if dc_file_exist(context, "$BLOBDIR/foobar")
|
||||
|| dc_file_exist(context, "$BLOBDIR/dada")
|
||||
|| dc_file_exist(context, "$BLOBDIR/foobar.dadada")
|
||||
|| dc_file_exist(context, "$BLOBDIR/foobar-folder")
|
||||
{
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar.dadada\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_delete_file(context, "$BLOBDIR/foobar");
|
||||
dc_delete_file(context, "$BLOBDIR/dada");
|
||||
dc_delete_file(context, "$BLOBDIR/foobar.dadada");
|
||||
dc_delete_file(context, "$BLOBDIR/foobar-folder");
|
||||
}
|
||||
dc_write_file(
|
||||
context,
|
||||
@@ -97,25 +50,10 @@ unsafe fn stress_functions(context: &Context) {
|
||||
b"content\x00" as *const u8 as *const libc::c_char as *const libc::c_void,
|
||||
7i32 as size_t,
|
||||
);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert!(dc_file_exist(context, "$BLOBDIR/foobar",));
|
||||
assert!(!dc_file_exist(context, "$BLOBDIR/foobarx"));
|
||||
assert_eq!(
|
||||
0,
|
||||
dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobarx\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
dc_get_filebytes(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
dc_get_filebytes(context, "$BLOBDIR/foobar",),
|
||||
7i32 as libc::c_ulonglong
|
||||
);
|
||||
|
||||
@@ -124,32 +62,13 @@ unsafe fn stress_functions(context: &Context) {
|
||||
context.get_blobdir(),
|
||||
b"foobar\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
assert!(dc_is_blobdir_path(context, abs_path));
|
||||
assert!(dc_is_blobdir_path(
|
||||
context,
|
||||
b"$BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
assert!(!dc_is_blobdir_path(
|
||||
context,
|
||||
b"/BLOBDIR/fofo\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
assert_ne!(0, dc_file_exist(context, abs_path));
|
||||
assert!(dc_is_blobdir_path(context, as_str(abs_path)));
|
||||
assert!(dc_is_blobdir_path(context, "$BLOBDIR/fofo",));
|
||||
assert!(!dc_is_blobdir_path(context, "/BLOBDIR/fofo",));
|
||||
assert!(dc_file_exist(context, as_path(abs_path)));
|
||||
free(abs_path as *mut libc::c_void);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_copy_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
dc_get_filebytes(
|
||||
context,
|
||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
7
|
||||
);
|
||||
assert!(dc_copy_file(context, "$BLOBDIR/foobar", "$BLOBDIR/dada",));
|
||||
assert_eq!(dc_get_filebytes(context, "$BLOBDIR/dada",), 7);
|
||||
|
||||
let mut buf: *mut libc::c_void = 0 as *mut libc::c_void;
|
||||
let mut buf_bytes: size_t = 0;
|
||||
@@ -170,41 +89,11 @@ unsafe fn stress_functions(context: &Context) {
|
||||
);
|
||||
|
||||
free(buf as *mut _);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/dada\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_create_folder(
|
||||
context,
|
||||
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_file_exist(
|
||||
context,
|
||||
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_delete_file(
|
||||
context,
|
||||
b"$BLOBDIR/foobar-folder\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
);
|
||||
assert!(dc_delete_file(context, "$BLOBDIR/foobar"));
|
||||
assert!(dc_delete_file(context, "$BLOBDIR/dada"));
|
||||
assert!(dc_create_folder(context, "$BLOBDIR/foobar-folder"));
|
||||
assert!(dc_file_exist(context, "$BLOBDIR/foobar-folder",));
|
||||
assert!(dc_delete_file(context, "$BLOBDIR/foobar-folder"));
|
||||
let fn0: *mut libc::c_char = dc_get_fine_pathNfilename(
|
||||
context,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -237,7 +126,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
),
|
||||
0
|
||||
);
|
||||
assert_ne!(0, dc_delete_file(context, fn0));
|
||||
assert!(dc_delete_file(context, as_path(fn0)));
|
||||
free(fn0 as *mut libc::c_void);
|
||||
free(fn1 as *mut libc::c_void);
|
||||
}
|
||||
@@ -520,63 +409,55 @@ unsafe fn stress_functions(context: &Context) {
|
||||
0
|
||||
);
|
||||
free(buf_1 as *mut libc::c_void);
|
||||
if 0 != dc_is_configured(context) {
|
||||
let setupcode = dc_create_setup_code(context);
|
||||
let setupcode_c = CString::yolo(setupcode.clone());
|
||||
let setupfile = dc_render_setup_file(context, &setupcode).unwrap();
|
||||
let setupfile_c = CString::yolo(setupfile);
|
||||
let payload: *mut libc::c_char;
|
||||
let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
|
||||
payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
assert!(payload.is_null());
|
||||
assert!(!dc_split_armored_data(
|
||||
payload,
|
||||
&mut headerline_2,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
0 as *mut *const libc::c_char,
|
||||
));
|
||||
assert!(!headerline_2.is_null());
|
||||
assert_eq!(
|
||||
strcmp(
|
||||
headerline_2,
|
||||
b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
0
|
||||
);
|
||||
free(payload as *mut libc::c_void);
|
||||
}
|
||||
|
||||
if 0 != dc_is_configured(context) {
|
||||
let qr: *mut libc::c_char = dc_get_securejoin_qr(context, 0i32 as uint32_t);
|
||||
assert!(
|
||||
!(strlen(qr) > 55
|
||||
&& strncmp(
|
||||
qr,
|
||||
b"OPENPGP4FPR:\x00" as *const u8 as *const libc::c_char,
|
||||
12,
|
||||
) == 0i32
|
||||
&& strncmp(
|
||||
&mut *qr.offset(52isize),
|
||||
b"#a=\x00" as *const u8 as *const libc::c_char,
|
||||
3,
|
||||
) == 0i32)
|
||||
);
|
||||
let mut res: *mut dc_lot_t = dc_check_qr(context, qr);
|
||||
assert!(res.is_null());
|
||||
assert!(!((*res).state == 200i32 || (*res).state == 220i32 || (*res).state == 230i32));
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "Missing configured context");
|
||||
|
||||
dc_lot_unref(res);
|
||||
free(qr as *mut libc::c_void);
|
||||
res =
|
||||
dc_check_qr(context,
|
||||
b"BEGIN:VCARD\nVERSION:3.0\nN:Last;First\nEMAIL;TYPE=INTERNET:stress@test.local\nEND:VCARD\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
assert!(res.is_null());
|
||||
assert!(!((*res).state == 320i32));
|
||||
assert!(!((*res).id != 0i32 as libc::c_uint));
|
||||
dc_lot_unref(res);
|
||||
};
|
||||
// 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 mut headerline_2: *const libc::c_char = 0 as *const libc::c_char;
|
||||
// let payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr());
|
||||
|
||||
// assert!(payload.is_null());
|
||||
// assert!(!dc_split_armored_data(
|
||||
// payload,
|
||||
// &mut headerline_2,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// 0 as *mut *const libc::c_char,
|
||||
// ));
|
||||
// assert!(!headerline_2.is_null());
|
||||
// assert_eq!(
|
||||
// strcmp(
|
||||
// headerline_2,
|
||||
// b"-----BEGIN PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char,
|
||||
// ),
|
||||
// 0
|
||||
// );
|
||||
// free(payload as *mut libc::c_void);
|
||||
|
||||
// Cant check, no configured context
|
||||
// assert!(dc_is_configured(context) != 0, "missing configured context");
|
||||
|
||||
// let qr = dc_get_securejoin_qr(context, 0);
|
||||
// assert!(!qr.is_null(), "Invalid qr code generated");
|
||||
// let qr_r = as_str(qr);
|
||||
|
||||
// assert!(qr_r.len() > 55);
|
||||
// assert!(qr_r.starts_with("OPENPGP4FPR:"));
|
||||
|
||||
// let res = dc_check_qr(context, qr);
|
||||
// let s = res.get_state();
|
||||
|
||||
// assert!(
|
||||
// s == QrState::AskVerifyContact
|
||||
// || s == QrState::FprMissmatch
|
||||
// || s == QrState::FprWithoutAddr
|
||||
// );
|
||||
|
||||
// free(qr.cast());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -772,43 +653,6 @@ unsafe fn create_test_context() -> TestContext {
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_kml_parse() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
|
||||
let xml: *const libc::c_char =
|
||||
b"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n<Document addr=\"user@example.org\">\n<Placemark><Timestamp><when>2019-03-06T21:09:57Z</when></Timestamp><Point><coordinates accuracy=\"32.000000\">9.423110,53.790302</coordinates></Point></Placemark>\n<PlaceMARK>\n<Timestamp><WHEN > \n\t2018-12-13T22:11:12Z\t</wHeN></Timestamp><Point><coordinates aCCuracy=\"2.500000\"> 19.423110 \t , \n 63.790302\n </coordinates></Point></Placemark>\n</Document>\n</kml>\x00"
|
||||
as *const u8 as *const libc::c_char;
|
||||
|
||||
let mut kml = dc_kml_parse(&context.ctx, xml, strlen(xml));
|
||||
|
||||
assert!(!kml.addr.is_null());
|
||||
assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",);
|
||||
|
||||
let locations_ref = &kml.locations.as_ref().unwrap();
|
||||
assert_eq!(locations_ref.len(), 2);
|
||||
|
||||
assert!(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!(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);
|
||||
|
||||
dc_kml_unref(&mut kml);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_mimeparser_with_context() {
|
||||
unsafe {
|
||||
@@ -825,22 +669,14 @@ fn test_dc_mimeparser_with_context() {
|
||||
"inner-subject",
|
||||
);
|
||||
|
||||
let mut of: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(
|
||||
&mimeparser,
|
||||
b"X-Special-A\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let mut of: *mut mailimf_optional_field =
|
||||
dc_mimeparser_lookup_optional_field(&mimeparser, "X-Special-A");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "special-a",);
|
||||
|
||||
of = dc_mimeparser_lookup_optional_field(
|
||||
&mimeparser,
|
||||
b"Foo\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Foo");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "Bar",);
|
||||
|
||||
of = dc_mimeparser_lookup_optional_field(
|
||||
&mimeparser,
|
||||
b"Chat-Version\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
of = dc_mimeparser_lookup_optional_field(&mimeparser, "Chat-Version");
|
||||
assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",);
|
||||
assert_eq!(mimeparser.parts.len(), 1);
|
||||
|
||||
@@ -891,19 +727,16 @@ fn test_get_contacts() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
assert_eq!(contacts.len(), 0);
|
||||
|
||||
let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||
assert_ne!(id, 0);
|
||||
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 1);
|
||||
dc_array_unref(contacts);
|
||||
assert_eq!(contacts.len(), 1);
|
||||
|
||||
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap();
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
assert_eq!(contacts.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -914,17 +747,15 @@ fn test_chat() {
|
||||
let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap();
|
||||
assert_ne!(contact1, 0);
|
||||
|
||||
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
let chat_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
|
||||
assert!(chat_id > 9, "chat_id too small {}", chat_id);
|
||||
let chat = dc_chat_new(&context.ctx);
|
||||
assert!(dc_chat_load_from_db(chat, chat_id));
|
||||
let chat = Chat::load_from_db(&context.ctx, chat_id).unwrap();
|
||||
|
||||
let chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
let chat2_id = chat::create_by_contact_id(&context.ctx, contact1).unwrap();
|
||||
assert_eq!(chat2_id, chat_id);
|
||||
let chat2 = dc_chat_new(&context.ctx);
|
||||
assert!(dc_chat_load_from_db(chat2, chat2_id));
|
||||
let chat2 = Chat::load_from_db(&context.ctx, chat2_id).unwrap();
|
||||
|
||||
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
|
||||
assert_eq!(chat2.name, chat.name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user