mirror of
https://github.com/chatmail/core.git
synced 2026-04-05 23:22:11 +03:00
Compare commits
97 Commits
1.0.0-alph
...
qr_remove_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4128aae331 | ||
|
|
c914a0339b | ||
|
|
a1f4849188 | ||
|
|
e5a9761233 | ||
|
|
ff15754c1f | ||
|
|
de2b0059c9 | ||
|
|
6f79800824 | ||
|
|
7a19963879 | ||
|
|
cd7630360f | ||
|
|
4a633169e1 | ||
|
|
ea8d6e8ff0 | ||
|
|
065124b93b | ||
|
|
86d290832b | ||
|
|
56f8717a40 | ||
|
|
4a0b2e68c8 | ||
|
|
2576b78126 | ||
|
|
6a956b6008 | ||
|
|
33575e7aa3 | ||
|
|
8089559958 | ||
|
|
686678c96c | ||
|
|
c116d6f73f | ||
|
|
a7c8ebc089 | ||
|
|
7774052911 | ||
|
|
68888f6d1f | ||
|
|
3dfd623db7 | ||
|
|
31d2bc7401 | ||
|
|
5ee8f8cb59 | ||
|
|
d1825956b2 | ||
|
|
30ca377586 | ||
|
|
f58b1d66c2 | ||
|
|
0c5015d92b | ||
|
|
ab2d2e7583 | ||
|
|
c11ac46dce | ||
|
|
8a0fc609e6 | ||
|
|
3e3403d3d7 | ||
|
|
46c64b2511 | ||
|
|
aa82644392 | ||
|
|
f00b617c23 | ||
|
|
04ee9dde2c | ||
|
|
cae0d666bd | ||
|
|
2691028422 | ||
|
|
205493f89d | ||
|
|
3bca349194 | ||
|
|
deb160cce9 | ||
|
|
669476afd3 | ||
|
|
b810b5a8f8 | ||
|
|
6d17de05b2 | ||
|
|
e3fb0a23c6 | ||
|
|
4c646dc1e0 | ||
|
|
d67dd9cc33 | ||
|
|
b93f5aa0b6 | ||
|
|
6c4b9e79c7 | ||
|
|
95437d726e | ||
|
|
816fa1df9b | ||
|
|
11eb86c77f | ||
|
|
c2ca30cc16 | ||
|
|
f336166867 | ||
|
|
692b779896 | ||
|
|
09a675a9cf | ||
|
|
fd5cba7242 | ||
|
|
5c53bb5ed7 | ||
|
|
184b3c8e91 | ||
|
|
286d1a99aa | ||
|
|
4fe99b21c9 | ||
|
|
2aa81a7a9a | ||
|
|
f671b25cbc | ||
|
|
3d7be47adf | ||
|
|
27c8bb64c8 | ||
|
|
4c95664992 | ||
|
|
f7d13fd12f | ||
|
|
35248296af | ||
|
|
dfa2fcda73 | ||
|
|
699b2d48e6 | ||
|
|
12b2a706f0 | ||
|
|
c7c86f1b03 | ||
|
|
02872c143a | ||
|
|
1a0ebba024 | ||
|
|
6e13e177f7 | ||
|
|
a2eb215fdf | ||
|
|
a2245bdf4e | ||
|
|
abdb02c361 | ||
|
|
92d39642e6 | ||
|
|
799ba8a5db | ||
|
|
8367a03b22 | ||
|
|
4378608617 | ||
|
|
adcb9d6069 | ||
|
|
191d11e719 | ||
|
|
464d0f4831 | ||
|
|
d0c7f63809 | ||
|
|
e597aade10 | ||
|
|
08bade2c7c | ||
|
|
2c26f4f2ab | ||
|
|
7a053b9f93 | ||
|
|
183a49eca0 | ||
|
|
384822b5eb | ||
|
|
06e16c81f9 | ||
|
|
d1593f0258 |
@@ -127,26 +127,26 @@ jobs:
|
||||
machine: True
|
||||
steps:
|
||||
- checkout
|
||||
# - run: docker pull deltachat/doxygen
|
||||
# - run: docker pull deltachat/doxygen
|
||||
- run: docker pull deltachat/coredeps
|
||||
- run:
|
||||
name: build docs, run tests and build wheels
|
||||
- run:
|
||||
name: build docs, run tests and build wheels
|
||||
command: ci_scripts/ci_run.sh
|
||||
environment:
|
||||
TESTS: 1
|
||||
DOCS: 1
|
||||
|
||||
- run:
|
||||
name: copying docs and wheels to workspace
|
||||
- run:
|
||||
name: copying docs and wheels to workspace
|
||||
command: |
|
||||
mkdir -p workspace/python
|
||||
# cp -av docs workspace/c-docs
|
||||
cp -av python/.docker-tox/wheelhouse workspace/
|
||||
cp -av python/doc/_build/ workspace/py-docs
|
||||
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
# - c-docs
|
||||
- py-docs
|
||||
- wheelhouse
|
||||
@@ -157,7 +157,7 @@ jobs:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: workspace
|
||||
- run: ls -laR workspace
|
||||
- run: ls -laR workspace
|
||||
- run: ci_scripts/ci_upload.sh workspace/py-docs workspace/wheelhouse
|
||||
|
||||
|
||||
@@ -175,12 +175,17 @@ workflows:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
# Linux Desktop
|
||||
# Linux Desktop 64bit
|
||||
- test_x86_64-unknown-linux-gnu:
|
||||
requires:
|
||||
- cargo_fetch
|
||||
|
||||
# Linux Desktop
|
||||
# Linux Desktop 32bit
|
||||
# - test_i686-unknown-linux-gnu:
|
||||
# requires:
|
||||
# - cargo_fetch
|
||||
|
||||
# Android 64bit
|
||||
# - test_aarch64-linux-android:
|
||||
# requires:
|
||||
# - cargo_fetch
|
||||
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -2,7 +2,7 @@
|
||||
# ensures this even if the user has not set core.autocrlf.
|
||||
* text=auto
|
||||
|
||||
# binary files should be detected by git, however, to be sure, you can add them here explictly
|
||||
# binary files should be detected by git, however, to be sure, you can add them here explicitly
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.gif binary
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
Cargo.lock
|
||||
|
||||
# ignore vi temporaries
|
||||
*~
|
||||
@@ -17,3 +16,5 @@ python/.tox
|
||||
*.egg-info
|
||||
__pycache__
|
||||
python/src/deltachat/capi*.so
|
||||
|
||||
python/liveconfig*
|
||||
|
||||
2954
Cargo.lock
generated
Normal file
2954
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
Cargo.toml
21
Cargo.toml
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-alpha.1"
|
||||
version = "1.0.0-alpha.3"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
license = "MPL"
|
||||
@@ -11,12 +11,11 @@ pkg-config = "0.3"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2.51"
|
||||
pgp = "0.2"
|
||||
pgp = { version = "0.2", default-features = false }
|
||||
hex = "0.3.2"
|
||||
sha2 = "0.8.0"
|
||||
rand = "0.6.5"
|
||||
smallvec = "0.6.9"
|
||||
libsqlite3-sys = { version = "0.14.0", features = ["bundled", "min_sqlite_version_3_7_16"] }
|
||||
reqwest = "0.9.15"
|
||||
num-derive = "0.2.5"
|
||||
num-traits = "0.2.6"
|
||||
@@ -36,6 +35,14 @@ failure_derive = "0.1.5"
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.3.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.19", features = ["bundled"] }
|
||||
addr = "0.2.0"
|
||||
r2d2_sqlite = "0.11.0"
|
||||
r2d2 = "0.8.5"
|
||||
strum = "0.15.0"
|
||||
strum_macros = "0.15.0"
|
||||
thread-local-object = "0.1.0"
|
||||
backtrace = "0.3.33"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -47,8 +54,13 @@ members = [
|
||||
"deltachat-ffi"
|
||||
]
|
||||
|
||||
[patch.crates-io]
|
||||
rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] }
|
||||
|
||||
|
||||
[[example]]
|
||||
name = "simple"
|
||||
path = "examples/simple.rs"
|
||||
|
||||
[[example]]
|
||||
name = "repl"
|
||||
@@ -56,6 +68,7 @@ path = "examples/repl/main.rs"
|
||||
|
||||
|
||||
[features]
|
||||
default = ["nightly"]
|
||||
default = ["nightly", "ringbuf"]
|
||||
vendored = ["native-tls/vendored", "reqwest/default-tls-vendored"]
|
||||
nightly = ["pgp/nightly"]
|
||||
ringbuf = ["pgp/ringbuf"]
|
||||
|
||||
@@ -84,6 +84,12 @@ $ cargo test --all
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- `vendored`: When using Openssl for TLS, this bundles a vendored version.
|
||||
- `nightly`: Enable nightly only performance and security related features.
|
||||
- `ringbuf`: Enable the use of [`slice_deque`](https://github.com/gnzlbg/slice_deque) in pgp.
|
||||
|
||||
[circle-shield]: https://img.shields.io/circleci/project/github/deltachat/deltachat-core-rust/master.svg?style=flat-square
|
||||
[circle]: https://circleci.com/gh/deltachat/deltachat-core-rust/
|
||||
[appveyor-shield]: https://ci.appveyor.com/api/projects/status/lqpegel3ld4ipxj8/branch/master?style=flat-square
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
|
||||
TARGET: x86_64-pc-windows-msvc
|
||||
|
||||
install:
|
||||
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||
- rustup-init -yv --default-host %target% --default-toolchain none
|
||||
- rustup-init -yv --default-toolchain nightly-2019-07-10
|
||||
- set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
- rustc -vV
|
||||
- cargo -vV
|
||||
@@ -14,7 +13,7 @@ install:
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --release
|
||||
- cargo test --release
|
||||
|
||||
cache:
|
||||
- target
|
||||
|
||||
@@ -33,6 +33,7 @@ else
|
||||
export CARGO_SUBCMD="test"
|
||||
export OPT="${OPT} "
|
||||
export OPT_RELEASE="${OPT_RELEASE} "
|
||||
export OPT_RELEASE_IGNORED="${OPT_RELEASE} -- --ignored"
|
||||
fi
|
||||
|
||||
# Run all the test configurations:
|
||||
|
||||
@@ -37,7 +37,7 @@ if [ -n "$TESTS" ]; then
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
|
||||
# run tox
|
||||
tox --workdir "$TOXWORKDIR" -e py27,py35,py36,py37,auditwheels
|
||||
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.1"
|
||||
version = "1.0.0-alpha.3"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["dignifiedquire <dignifiedquire@gmail.com>"]
|
||||
edition = "2018"
|
||||
@@ -15,9 +15,13 @@ name = "deltachat"
|
||||
crate-type = ["cdylib", "staticlib"]
|
||||
|
||||
[dependencies]
|
||||
deltachat = { path = "../" }
|
||||
deltachat = { path = "../", default-features = false }
|
||||
libc = "0.2"
|
||||
human-panic = "1.0.1"
|
||||
|
||||
[features]
|
||||
default = ["deltachat/vendored"]
|
||||
default = ["vendored", "nightly", "ringbuf"]
|
||||
vendored = ["deltachat/vendored"]
|
||||
nightly = ["deltachat/nightly"]
|
||||
ringbuf = ["deltachat/ringbuf"]
|
||||
|
||||
|
||||
33
deltachat-ffi/build.rs
Normal file
33
deltachat-ffi/build.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::{env, fs};
|
||||
|
||||
fn main() {
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
let target_path = out_path.join("../../..");
|
||||
let target_triple = env::var("TARGET").unwrap();
|
||||
|
||||
// macOS or iOS, inherited from rpgp
|
||||
let libs_priv = if target_triple.contains("apple") || target_triple.contains("darwin") {
|
||||
// needed for OsRng
|
||||
"-framework Security -framework Foundation"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let pkg_config = format!(
|
||||
include_str!("deltachat.pc.in"),
|
||||
name = "deltachat",
|
||||
description = env::var("CARGO_PKG_DESCRIPTION").unwrap(),
|
||||
url = env::var("CARGO_PKG_HOMEPAGE").unwrap_or("".to_string()),
|
||||
version = env::var("CARGO_PKG_VERSION").unwrap(),
|
||||
libs_priv = libs_priv,
|
||||
prefix = env::var("PREFIX").unwrap_or("/usr/local".to_string()),
|
||||
);
|
||||
|
||||
fs::create_dir_all(target_path.join("pkgconfig")).unwrap();
|
||||
fs::File::create(target_path.join("pkgconfig").join("deltachat.pc"))
|
||||
.unwrap()
|
||||
.write_all(&pkg_config.as_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
@@ -503,8 +503,8 @@ int dc_chat_is_sending_locations (const dc_chat_t*);
|
||||
#define DC_STATE_OUT_MDN_RCVD 28
|
||||
|
||||
|
||||
#define DC_MAX_GET_TEXT_LEN 30000 // approx. max. lenght returned by dc_msg_get_text()
|
||||
#define DC_MAX_GET_INFO_LEN 100000 // approx. max. lenght returned by dc_get_msg_info()
|
||||
#define DC_MAX_GET_TEXT_LEN 30000 // approx. max. length returned by dc_msg_get_text()
|
||||
#define DC_MAX_GET_INFO_LEN 100000 // approx. max. length returned by dc_get_msg_info()
|
||||
|
||||
|
||||
dc_msg_t* dc_msg_new (dc_context_t*, int viewtype);
|
||||
@@ -667,7 +667,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
|
||||
* A voice message that was directly recorded by the user.
|
||||
* For all other audio messages, the type #DC_MSG_AUDIO should be used.
|
||||
* File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
|
||||
* and retieved via dc_msg_get_file(), dc_msg_get_duration()
|
||||
* and retrieved via dc_msg_get_file(), dc_msg_get_duration()
|
||||
*/
|
||||
#define DC_MSG_VOICE 41
|
||||
|
||||
@@ -768,9 +768,9 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
|
||||
* @}
|
||||
*/
|
||||
|
||||
#define DC_LP_AUTH_FLAGS (DC_LP_AUTH_OAUTH2|DC_LP_AUTH_NORMAL) // if none of these flags are set, the default is choosen
|
||||
#define DC_LP_IMAP_SOCKET_FLAGS (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 choosen
|
||||
#define DC_LP_SMTP_SOCKET_FLAGS (DC_LP_SMTP_SOCKET_STARTTLS|DC_LP_SMTP_SOCKET_SSL|DC_LP_SMTP_SOCKET_PLAIN) // if none of these flags are set, the default is choosen
|
||||
#define DC_LP_AUTH_FLAGS (DC_LP_AUTH_OAUTH2|DC_LP_AUTH_NORMAL) // if none of these flags are set, the default is chosen
|
||||
#define DC_LP_IMAP_SOCKET_FLAGS (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
|
||||
#define DC_LP_SMTP_SOCKET_FLAGS (DC_LP_SMTP_SOCKET_STARTTLS|DC_LP_SMTP_SOCKET_SSL|DC_LP_SMTP_SOCKET_PLAIN) // if none of these flags are set, the default is chosen
|
||||
|
||||
|
||||
|
||||
@@ -851,7 +851,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
|
||||
* The library-user should report an error to the end-user.
|
||||
* Passed to the callback given to dc_context_new().
|
||||
*
|
||||
* As most things are asynchrounous, things may go wrong at any time and the user
|
||||
* As most things are asynchronous, things may go wrong at any time and the user
|
||||
* should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||
*
|
||||
* However, for ongoing processes (eg. dc_configure())
|
||||
@@ -882,7 +882,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t*);
|
||||
*
|
||||
* Moreover, if the UI detects that the device is offline,
|
||||
* it is probably more useful to report this to the user
|
||||
* instread of the string from data2.
|
||||
* instead of the string from data2.
|
||||
*
|
||||
* @param data1 (int) 1=first/new network error, should be reported the user;
|
||||
* 0=subsequent network error, should be logged only
|
||||
|
||||
11
deltachat-ffi/deltachat.pc.in
Normal file
11
deltachat-ffi/deltachat.pc.in
Normal file
@@ -0,0 +1,11 @@
|
||||
prefix={prefix}
|
||||
libdir=${{prefix}}/lib
|
||||
includedir=${{prefix}}/include
|
||||
|
||||
Name: {name}
|
||||
Description: {description}
|
||||
URL: {url}
|
||||
Version: {version}
|
||||
Cflags: -I${{includedir}}
|
||||
Libs: -L${{libdir}} -ldeltachat
|
||||
Libs.private: {libs_priv}
|
||||
@@ -10,6 +10,8 @@
|
||||
#[macro_use]
|
||||
extern crate human_panic;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::*;
|
||||
|
||||
// TODO: constants
|
||||
@@ -88,9 +90,13 @@ pub unsafe extern "C" fn dc_set_config(
|
||||
value: *mut libc::c_char,
|
||||
) -> libc::c_int {
|
||||
assert!(!context.is_null());
|
||||
assert!(!key.is_null(), "invalid key");
|
||||
let context = &*context;
|
||||
|
||||
context::dc_set_config(context, key, value)
|
||||
match config::Config::from_str(dc_tools::as_str(key)) {
|
||||
Ok(key) => context.set_config(key, as_opt_str(value)).is_ok() as libc::c_int,
|
||||
Err(_) => 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -99,9 +105,16 @@ pub unsafe extern "C" fn dc_get_config(
|
||||
key: *mut libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
assert!(!context.is_null());
|
||||
assert!(!key.is_null(), "invalid key");
|
||||
let context = &*context;
|
||||
|
||||
context::dc_get_config(context, key)
|
||||
match config::Config::from_str(dc_tools::as_str(key)) {
|
||||
Ok(key) => {
|
||||
let value = context.get_config(key).unwrap_or_default();
|
||||
dc_tools::to_cstring(value)
|
||||
}
|
||||
Err(_) => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -124,7 +137,7 @@ pub unsafe extern "C" fn dc_get_oauth2_url(
|
||||
let addr = dc_tools::to_string(addr);
|
||||
let redirect = dc_tools::to_string(redirect);
|
||||
match oauth2::dc_get_oauth2_url(context, addr, redirect) {
|
||||
Some(res) => libc::strdup(dc_tools::to_cstring(res).as_ptr()),
|
||||
Some(res) => dc_tools::to_cstring(res),
|
||||
None => std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
@@ -411,7 +424,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)
|
||||
dc_chat::dc_marknoticed_chat(context, chat_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -419,7 +432,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)
|
||||
dc_chat::dc_marknoticed_all_chats(context);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -460,7 +473,7 @@ pub unsafe extern "C" fn dc_archive_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_archive_chat(context, chat_id, archive)
|
||||
dc_chat::dc_archive_chat(context, chat_id, archive);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -468,7 +481,8 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_chat::dc_delete_chat(context, chat_id)
|
||||
// TODO: update to indicate public api success/failure of deletion
|
||||
dc_chat::dc_delete_chat(context, chat_id);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -498,7 +512,7 @@ pub unsafe extern "C" fn dc_search_msgs(
|
||||
pub unsafe extern "C" fn dc_get_chat<'a>(
|
||||
context: *mut dc_context_t,
|
||||
chat_id: u32,
|
||||
) -> *mut dc_chat::dc_chat_t<'a> {
|
||||
) -> *mut dc_chat_t<'a> {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
@@ -641,7 +655,7 @@ pub unsafe extern "C" fn dc_markseen_msgs(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt)
|
||||
dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt as usize);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -654,7 +668,7 @@ pub unsafe extern "C" fn dc_star_msgs(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star)
|
||||
dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -887,7 +901,7 @@ pub unsafe extern "C" fn dc_is_sending_locations_to_chat(
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_location::dc_is_sending_locations_to_chat(context, chat_id)
|
||||
dc_location::dc_is_sending_locations_to_chat(context, chat_id) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -928,7 +942,7 @@ pub unsafe extern "C" fn dc_delete_all_locations(context: *mut dc_context_t) {
|
||||
assert!(!context.is_null());
|
||||
let context = &*context;
|
||||
|
||||
dc_location::dc_delete_all_locations(context)
|
||||
dc_location::dc_delete_all_locations(context);
|
||||
}
|
||||
|
||||
// dc_array_t
|
||||
@@ -1095,7 +1109,7 @@ pub unsafe extern "C" fn dc_chatlist_get_msg_id(
|
||||
pub unsafe extern "C" fn dc_chatlist_get_summary<'a>(
|
||||
chatlist: *mut dc_chatlist::dc_chatlist_t<'a>,
|
||||
index: libc::size_t,
|
||||
chat: *mut dc_chat::dc_chat_t<'a>,
|
||||
chat: *mut dc_chat_t<'a>,
|
||||
) -> *mut dc_lot::dc_lot_t {
|
||||
dc_chatlist::dc_chatlist_get_summary(chatlist, index, chat)
|
||||
}
|
||||
@@ -1111,69 +1125,65 @@ pub unsafe extern "C" fn dc_chatlist_get_context(
|
||||
// dc_chat_t
|
||||
|
||||
#[no_mangle]
|
||||
pub type dc_chat_t<'a> = dc_chat::dc_chat_t<'a>;
|
||||
pub type dc_chat_t<'a> = dc_chat::Chat<'a>;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat::dc_chat_t) {
|
||||
pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) {
|
||||
dc_chat::dc_chat_unref(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat::dc_chat_t) -> u32 {
|
||||
pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 {
|
||||
dc_chat::dc_chat_get_id(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_get_type(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat::dc_chat_t) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
dc_chat::dc_chat_get_name(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat::dc_chat_t) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
dc_chat::dc_chat_get_subtitle(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_profile_image(
|
||||
chat: *mut dc_chat::dc_chat_t,
|
||||
) -> *mut libc::c_char {
|
||||
pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char {
|
||||
dc_chat::dc_chat_get_profile_image(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat::dc_chat_t) -> u32 {
|
||||
pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 {
|
||||
dc_chat::dc_chat_get_color(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_get_archived(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_is_unpromoted(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_is_self_talk(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat::dc_chat_t) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_is_verified(chat)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_chat_is_sending_locations(
|
||||
chat: *mut dc_chat::dc_chat_t,
|
||||
) -> libc::c_int {
|
||||
pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int {
|
||||
dc_chat::dc_chat_is_sending_locations(chat)
|
||||
}
|
||||
|
||||
@@ -1291,7 +1301,7 @@ pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> l
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_get_summary(
|
||||
msg: *mut dc_msg::dc_msg_t,
|
||||
chat: *mut dc_chat::dc_chat_t,
|
||||
chat: *mut dc_chat_t,
|
||||
) -> *mut dc_lot::dc_lot_t {
|
||||
dc_msg::dc_msg_get_summary(msg, chat)
|
||||
}
|
||||
@@ -1341,7 +1351,7 @@ pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg::dc_msg_t) -> lib
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg::dc_msg_t) -> libc::c_int {
|
||||
dc_msg::dc_msg_is_setupmessage(msg)
|
||||
dc_msg::dc_msg_is_setupmessage(msg) as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -1528,3 +1538,11 @@ pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64
|
||||
pub unsafe extern "C" fn dc_str_unref(s: *mut libc::c_char) {
|
||||
libc::free(s as *mut _)
|
||||
}
|
||||
|
||||
fn as_opt_str<'a>(s: *const libc::c_char) -> Option<&'a str> {
|
||||
if s.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(dc_tools::as_str(s))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
@@ -12,9 +15,9 @@ use deltachat::dc_lot::*;
|
||||
use deltachat::dc_msg::*;
|
||||
use deltachat::dc_qr::*;
|
||||
use deltachat::dc_receive_imf::*;
|
||||
use deltachat::dc_sqlite3::*;
|
||||
use deltachat::dc_tools::*;
|
||||
use deltachat::peerstate::*;
|
||||
use deltachat::sql;
|
||||
use deltachat::types::*;
|
||||
use deltachat::x::*;
|
||||
use num_traits::FromPrimitive;
|
||||
@@ -25,65 +28,64 @@ use num_traits::FromPrimitive;
|
||||
pub unsafe fn dc_reset_tables(context: &Context, bits: i32) -> i32 {
|
||||
info!(context, 0, "Resetting tables ({})...", bits);
|
||||
if 0 != bits & 1 {
|
||||
dc_sqlite3_execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM jobs;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sql::execute(context, &context.sql, "DELETE FROM jobs;", params![]).unwrap();
|
||||
info!(context, 0, "(1) Jobs reset.");
|
||||
}
|
||||
if 0 != bits & 2 {
|
||||
dc_sqlite3_execute(
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM acpeerstates;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
"DELETE FROM acpeerstates;",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
info!(context, 0, "(2) Peerstates reset.");
|
||||
}
|
||||
if 0 != bits & 4 {
|
||||
dc_sqlite3_execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM keypairs;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sql::execute(context, &context.sql, "DELETE FROM keypairs;", params![]).unwrap();
|
||||
info!(context, 0, "(4) Private keypairs reset.");
|
||||
}
|
||||
if 0 != bits & 8 {
|
||||
dc_sqlite3_execute(
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM contacts WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_execute(
|
||||
"DELETE FROM contacts WHERE id>9;",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM chats WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_execute(
|
||||
"DELETE FROM chats WHERE id>9;",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM chats_contacts;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_execute(
|
||||
"DELETE FROM chats_contacts;",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM msgs WHERE id>9;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_execute(
|
||||
"DELETE FROM msgs WHERE id>9;",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM config WHERE keyname LIKE \'imap.%\' OR keyname LIKE \'configured%\';\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"DELETE FROM leftgrps;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
"DELETE FROM config WHERE keyname LIKE 'imap.%' OR keyname LIKE 'configured%';",
|
||||
params![],
|
||||
)
|
||||
.unwrap();
|
||||
sql::execute(context, &context.sql, "DELETE FROM leftgrps;", params![]).unwrap();
|
||||
info!(context, 0, "(8) Rest but server config reset.");
|
||||
}
|
||||
|
||||
context.call_cb(Event::MSGS_CHANGED, 0 as uintptr_t, 0 as uintptr_t);
|
||||
context.call_cb(Event::MSGS_CHANGED, 0, 0);
|
||||
|
||||
1
|
||||
}
|
||||
@@ -100,14 +102,7 @@ unsafe fn dc_poke_eml_file(context: &Context, filename: *const libc::c_char) ->
|
||||
&mut data_bytes,
|
||||
) == 0i32)
|
||||
{
|
||||
dc_receive_imf(
|
||||
context,
|
||||
data,
|
||||
data_bytes,
|
||||
b"import\x00" as *const u8 as *const libc::c_char,
|
||||
0i32 as uint32_t,
|
||||
0i32 as uint32_t,
|
||||
);
|
||||
dc_receive_imf(context, data, data_bytes, "import", 0, 0);
|
||||
success = 1;
|
||||
}
|
||||
free(data as *mut libc::c_void);
|
||||
@@ -138,26 +133,20 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
/* if `spec` is given, remember it for later usage; if it is not given, try to use the last one */
|
||||
if !spec.is_null() {
|
||||
real_spec = dc_strdup(spec);
|
||||
dc_sqlite3_set_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"import_spec\x00" as *const u8 as *const libc::c_char,
|
||||
real_spec,
|
||||
);
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "import_spec", Some(as_str(real_spec)))
|
||||
.unwrap();
|
||||
current_block = 7149356873433890176;
|
||||
} else {
|
||||
real_spec = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"import_spec\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if real_spec.is_null() {
|
||||
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;
|
||||
} else {
|
||||
current_block = 7149356873433890176;
|
||||
}
|
||||
real_spec = to_cstring(rs.unwrap_or_default());
|
||||
}
|
||||
match current_block {
|
||||
8522321847195001863 => {}
|
||||
@@ -195,10 +184,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int
|
||||
let path_plus_name = format!("{}/{}", as_str(real_spec), name);
|
||||
info!(context, 0, "Import: {}", path_plus_name);
|
||||
let path_plus_name_c = to_cstring(path_plus_name);
|
||||
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) {
|
||||
if 0 != dc_poke_eml_file(context, path_plus_name_c) {
|
||||
read_cnt += 1
|
||||
}
|
||||
free(path_plus_name_c as *mut _);
|
||||
}
|
||||
}
|
||||
current_block = 1622411330066726685;
|
||||
@@ -377,7 +366,7 @@ pub unsafe fn dc_cmdline_skip_auth() {
|
||||
S_IS_AUTH = 1;
|
||||
}
|
||||
|
||||
unsafe fn chat_prefix(chat: *const dc_chat_t) -> &'static str {
|
||||
unsafe fn chat_prefix(chat: *const Chat) -> &'static str {
|
||||
if (*chat).type_0 == 120 {
|
||||
"Group"
|
||||
} else if (*chat).type_0 == 130 {
|
||||
@@ -398,18 +387,16 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
let mut args = line.splitn(3, ' ');
|
||||
let arg0 = args.next().unwrap_or_default();
|
||||
let arg1 = args.next().unwrap_or_default();
|
||||
let arg1_c = to_cstring(arg1);
|
||||
let arg1_c_ptr = if arg1.is_empty() {
|
||||
let arg1_c = if arg1.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
arg1_c.as_ptr()
|
||||
to_cstring(arg1) as *const _
|
||||
};
|
||||
let arg2 = args.next().unwrap_or_default();
|
||||
let arg2_c = to_cstring(arg2);
|
||||
let arg2_c_ptr = if arg2.is_empty() {
|
||||
let arg2_c = if arg2.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
arg2_c.as_ptr()
|
||||
to_cstring(arg2) as *const _
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
@@ -499,9 +486,10 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
},
|
||||
"auth" => {
|
||||
if 0 == S_IS_AUTH {
|
||||
let is_pw =
|
||||
dc_get_config(context, b"mail_pw\x00" as *const u8 as *const libc::c_char);
|
||||
if arg1 == as_str(is_pw) {
|
||||
let is_pw = context
|
||||
.get_config(config::Config::MailPw)
|
||||
.unwrap_or_default();
|
||||
if arg1 == is_pw {
|
||||
S_IS_AUTH = 1;
|
||||
} else {
|
||||
println!("Bad password.");
|
||||
@@ -514,7 +502,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty(), "Argument <file> missing");
|
||||
dc_close(context);
|
||||
ensure!(
|
||||
0 != dc_open(context, arg1_c_ptr, 0 as *const libc::c_char),
|
||||
0 != dc_open(context, arg1_c, 0 as *const libc::c_char),
|
||||
"Open failed"
|
||||
);
|
||||
}
|
||||
@@ -537,8 +525,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
let msg_id: u32 = arg1.parse().unwrap();
|
||||
let msg: *mut dc_msg_t = dc_get_msg(context, msg_id);
|
||||
if 0 != dc_msg_is_setupmessage(msg) {
|
||||
let setupcodebegin: *mut libc::c_char = dc_msg_get_setupcodebegin(msg);
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
let setupcodebegin = dc_msg_get_setupcodebegin(msg);
|
||||
println!(
|
||||
"The setup code for setup message Msg#{} starts with: {}",
|
||||
msg_id,
|
||||
@@ -555,7 +543,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
!arg1.is_empty() && !arg2.is_empty(),
|
||||
"Arguments <msg-id> <setup-code> expected"
|
||||
);
|
||||
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c_ptr) {
|
||||
if 0 == dc_continue_key_transfer(context, arg1.parse().unwrap(), arg2_c) {
|
||||
bail!("Continue key transfer failed");
|
||||
}
|
||||
}
|
||||
@@ -570,7 +558,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"import-backup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <backup-file> missing.");
|
||||
dc_imex(context, 12, arg1_c_ptr, 0 as *const libc::c_char);
|
||||
dc_imex(context, 12, arg1_c, 0 as *const libc::c_char);
|
||||
}
|
||||
"export-keys" => {
|
||||
dc_imex(context, 1, context.get_blobdir(), 0 as *const libc::c_char);
|
||||
@@ -607,7 +595,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
free(setup_code as *mut libc::c_void);
|
||||
}
|
||||
"poke" => {
|
||||
ensure!(0 != poke_spec(context, arg1_c_ptr), "Poke failed");
|
||||
ensure!(0 != poke_spec(context, arg1_c), "Poke failed");
|
||||
}
|
||||
"reset" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <bits> missing: 1=jobs, 2=peerstates, 4=private keys, 8=rest but server config");
|
||||
@@ -620,16 +608,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"set" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <key> missing.");
|
||||
ensure!(
|
||||
0 != dc_set_config(context, arg1_c_ptr, arg2_c_ptr),
|
||||
"Set config failed"
|
||||
);
|
||||
let key = config::Config::from_str(&arg1)?;
|
||||
let value = if arg2.is_empty() { None } else { Some(arg2) };
|
||||
context.set_config(key, value)?;
|
||||
}
|
||||
"get" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <key> missing.");
|
||||
let val = dc_get_config(context, arg1_c_ptr);
|
||||
println!("{}={}", arg1, to_string(val));
|
||||
free(val as *mut libc::c_void);
|
||||
let key = config::Config::from_str(&arg1)?;
|
||||
let val = context.get_config(key);
|
||||
println!("{}={:?}", key, val);
|
||||
}
|
||||
"info" => {
|
||||
println!("{}", to_string(dc_get_info(context)));
|
||||
@@ -638,11 +625,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
dc_maybe_network(context);
|
||||
}
|
||||
"housekeeping" => {
|
||||
dc_housekeeping(context);
|
||||
sql::housekeeping(context);
|
||||
}
|
||||
"listchats" | "listarchived" | "chats" => {
|
||||
let listflags = if arg0 == "listarchived" { 0x01 } else { 0 };
|
||||
let chatlist = dc_get_chatlist(context, listflags, arg1_c_ptr, 0 as uint32_t);
|
||||
let chatlist = dc_get_chatlist(context, listflags, arg1_c, 0 as uint32_t);
|
||||
ensure!(!chatlist.is_null(), "Failed to retrieve chatlist");
|
||||
|
||||
let mut i: libc::c_int;
|
||||
@@ -714,7 +701,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
i -= 1
|
||||
}
|
||||
}
|
||||
if 0 != dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
if dc_is_sending_locations_to_chat(context, 0 as uint32_t) {
|
||||
info!(context, 0, "Location streaming enabled.");
|
||||
}
|
||||
println!("{} chats", cnt);
|
||||
@@ -787,7 +774,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
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 dc_chat_t = dc_get_chat(context, chat_id_0 as uint32_t);
|
||||
let chat_0: *mut Chat = dc_get_chat(context, chat_id_0 as uint32_t);
|
||||
println!(
|
||||
"{}#{} created successfully.",
|
||||
chat_prefix(chat_0),
|
||||
@@ -800,8 +787,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"creategroup" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id_1: libc::c_int =
|
||||
dc_create_group_chat(context, 0, arg1_c_ptr) as libc::c_int;
|
||||
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 {
|
||||
@@ -810,8 +796,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
}
|
||||
"createverified" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <name> missing.");
|
||||
let chat_id_2: libc::c_int =
|
||||
dc_create_group_chat(context, 1, arg1_c_ptr) as libc::c_int;
|
||||
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 {
|
||||
@@ -850,7 +835,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
"groupname" => {
|
||||
ensure!(!sel_chat.is_null(), "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_ptr) {
|
||||
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");
|
||||
@@ -864,7 +849,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
context,
|
||||
dc_chat_get_id(sel_chat),
|
||||
if !arg1.is_empty() {
|
||||
arg1_c_ptr
|
||||
arg1_c
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
},
|
||||
@@ -929,7 +914,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
let seconds = arg1.parse().unwrap();
|
||||
dc_send_locations_to_chat(context, dc_chat_get_id(sel_chat), seconds);
|
||||
println!("Locations will be sent to Chat#{} for {} seconds. Use \'setlocation <lat> <lng>\' to play around.", dc_chat_get_id(sel_chat), seconds);
|
||||
println!("Locations will be sent to Chat#{} for {} seconds. Use 'setlocation <lat> <lng>' to play around.", dc_chat_get_id(sel_chat), seconds);
|
||||
}
|
||||
"setlocation" => {
|
||||
ensure!(
|
||||
@@ -955,9 +940,11 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
let msg = to_cstring(format!("{} {}", arg1, arg2));
|
||||
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr()) {
|
||||
if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) {
|
||||
println!("Message sent.");
|
||||
free(msg as *mut _);
|
||||
} else {
|
||||
free(msg as *mut _);
|
||||
bail!("Sending failed.");
|
||||
}
|
||||
}
|
||||
@@ -978,8 +965,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given.");
|
||||
|
||||
let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 });
|
||||
dc_msg_set_file(msg_0, arg1_c_ptr, 0 as *const libc::c_char);
|
||||
dc_msg_set_text(msg_0, arg2_c_ptr);
|
||||
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);
|
||||
dc_msg_unref(msg_0);
|
||||
}
|
||||
@@ -992,7 +979,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
0 as libc::c_uint
|
||||
};
|
||||
|
||||
let msglist_0 = dc_search_msgs(context, chat, arg1_c_ptr);
|
||||
let msglist_0 = dc_search_msgs(context, chat, arg1_c);
|
||||
|
||||
if !msglist_0.is_null() {
|
||||
log_msglist(context, msglist_0);
|
||||
@@ -1005,7 +992,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
if !arg1.is_empty() {
|
||||
let draft_0 = dc_msg_new(context, 10);
|
||||
dc_msg_set_text(draft_0, arg1_c_ptr);
|
||||
dc_msg_set_text(draft_0, arg1_c);
|
||||
dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0);
|
||||
dc_msg_unref(draft_0);
|
||||
println!("Draft saved.");
|
||||
@@ -1097,7 +1084,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
} else {
|
||||
0x2
|
||||
},
|
||||
arg1_c_ptr,
|
||||
arg1_c,
|
||||
);
|
||||
if !contacts.is_null() {
|
||||
log_contactlist(context, contacts);
|
||||
@@ -1113,13 +1100,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
if !arg2.is_empty() {
|
||||
let book = dc_mprintf(
|
||||
b"%s\n%s\x00" as *const u8 as *const libc::c_char,
|
||||
arg1_c_ptr,
|
||||
arg2_c_ptr,
|
||||
arg1_c,
|
||||
arg2_c,
|
||||
);
|
||||
dc_add_address_book(context, book);
|
||||
free(book as *mut libc::c_void);
|
||||
} else {
|
||||
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c_ptr) {
|
||||
if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) {
|
||||
bail!("Failed to create contact");
|
||||
}
|
||||
}
|
||||
@@ -1166,7 +1153,7 @@ 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_ptr);
|
||||
let res = dc_check_qr(context, arg1_c);
|
||||
println!(
|
||||
"state={}, id={}, text1={}, text2={}",
|
||||
(*res).state as libc::c_int,
|
||||
@@ -1194,7 +1181,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
|
||||
if 0 != dc_read_file(
|
||||
context,
|
||||
arg1_c_ptr,
|
||||
arg1_c,
|
||||
&mut buf as *mut *mut libc::c_uchar as *mut *mut libc::c_void,
|
||||
&mut buf_bytes,
|
||||
) {
|
||||
@@ -1212,5 +1199,8 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E
|
||||
dc_chat_unref(sel_chat);
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
free(arg2_c as *mut _);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ extern crate deltachat;
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate rusqlite;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_configure::*;
|
||||
@@ -395,11 +398,10 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
|
||||
if args.len() == 2 {
|
||||
if 0 == unsafe {
|
||||
dc_open(
|
||||
&mut context,
|
||||
to_cstring(&args[1]).as_ptr(),
|
||||
0 as *const libc::c_char,
|
||||
)
|
||||
let a = to_cstring(&args[1]);
|
||||
let res = dc_open(&mut context, a, 0 as *const _);
|
||||
free(a as *mut _);
|
||||
res
|
||||
} {
|
||||
println!("Error: Cannot open {}.", args[0],);
|
||||
}
|
||||
@@ -479,11 +481,10 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
let mut args = line.splitn(2, ' ');
|
||||
let arg0 = args.next().unwrap_or_default();
|
||||
let arg1 = args.next().unwrap_or_default();
|
||||
let arg1_c = to_cstring(arg1);
|
||||
let arg1_c_ptr = if arg1.is_empty() {
|
||||
let arg1_c = if arg1.is_empty() {
|
||||
std::ptr::null()
|
||||
} else {
|
||||
arg1_c.as_ptr()
|
||||
to_cstring(arg1)
|
||||
};
|
||||
|
||||
match arg0 {
|
||||
@@ -512,25 +513,20 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
dc_configure(&ctx.read().unwrap());
|
||||
}
|
||||
"oauth2" => {
|
||||
let addr = dc_get_config(
|
||||
&ctx.read().unwrap(),
|
||||
b"addr\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if addr.is_null() || *addr.offset(0isize) as libc::c_int == 0i32 {
|
||||
println!("oauth2: set addr first.");
|
||||
} else {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
let oauth2_url = dc_get_oauth2_url(
|
||||
&ctx.read().unwrap(),
|
||||
as_str(addr),
|
||||
&addr,
|
||||
"chat.delta:/com.b44t.messenger",
|
||||
);
|
||||
if oauth2_url.is_none() {
|
||||
println!("OAuth2 not available for {}.", to_string(addr));
|
||||
println!("OAuth2 not available for {}.", &addr);
|
||||
} else {
|
||||
println!("Open the following url, set mail_pw to the generated token and server_flags to 2:\n{}", oauth2_url.unwrap());
|
||||
}
|
||||
} else {
|
||||
println!("oauth2: set addr first.");
|
||||
}
|
||||
free(addr as *mut libc::c_void);
|
||||
}
|
||||
"clear" => {
|
||||
println!("\n\n\n");
|
||||
@@ -561,13 +557,15 @@ unsafe fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult
|
||||
"joinqr" => {
|
||||
start_threads(ctx.clone());
|
||||
if !arg0.is_empty() {
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1_c_ptr);
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1_c);
|
||||
}
|
||||
}
|
||||
"exit" => return Ok(ExitResult::Exit),
|
||||
_ => dc_cmdline(&ctx.read().unwrap(), line)?,
|
||||
}
|
||||
|
||||
free(arg1_c as *mut _);
|
||||
|
||||
Ok(ExitResult::Continue)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::constants::Event;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_chat::*;
|
||||
@@ -79,20 +80,15 @@ fn main() {
|
||||
|
||||
println!("opening database {:?}", dbfile);
|
||||
|
||||
dc_open(&ctx, dbfile.as_ptr(), std::ptr::null());
|
||||
assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1);
|
||||
|
||||
println!("configuring");
|
||||
let pw = std::env::args().collect::<Vec<String>>()[1].clone();
|
||||
dc_set_config(
|
||||
&ctx,
|
||||
CString::new("addr").unwrap().as_ptr(),
|
||||
CString::new("d@testrun.org").unwrap().as_ptr(),
|
||||
);
|
||||
dc_set_config(
|
||||
&ctx,
|
||||
CString::new("mail_pw").unwrap().as_ptr(),
|
||||
CString::new(pw).unwrap().as_ptr(),
|
||||
);
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 2, "missing password");
|
||||
let pw = args[1].clone();
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
dc_configure(&ctx);
|
||||
|
||||
thread::sleep(duration);
|
||||
@@ -127,8 +123,8 @@ fn main() {
|
||||
}
|
||||
dc_chatlist_unref(chats);
|
||||
|
||||
*running.clone().write().unwrap() = false;
|
||||
println!("stopping threads");
|
||||
thread::sleep(duration);
|
||||
|
||||
// let msglist = dc_get_chat_msgs(&ctx, chat_id, 0, 0);
|
||||
// for i in 0..dc_array_get_cnt(msglist) {
|
||||
// let msg_id = dc_array_get_id(msglist, i);
|
||||
@@ -139,6 +135,9 @@ fn main() {
|
||||
// }
|
||||
// dc_array_unref(msglist);
|
||||
|
||||
println!("stopping threads");
|
||||
|
||||
*running.clone().write().unwrap() = false;
|
||||
deltachat::dc_job::dc_interrupt_imap_idle(&ctx);
|
||||
deltachat::dc_job::dc_interrupt_smtp_idle(&ctx);
|
||||
|
||||
|
||||
@@ -1,6 +1,23 @@
|
||||
0.9.1-dev
|
||||
0.600.1
|
||||
---------
|
||||
|
||||
- introduce automatic versioning via setuptools_scm,
|
||||
based on py-X.Y.Z tags
|
||||
|
||||
- integrate latest DCC core-rust with dc_close() fixes
|
||||
|
||||
- provide a account.shutdown() method and improve termination
|
||||
logic also in tests. also fixes output-clubbering during
|
||||
test runs.
|
||||
|
||||
|
||||
0.600.0
|
||||
---------
|
||||
|
||||
- use new experimental full-Rust Delta Chat core
|
||||
- support Autocrypt Setup Messages
|
||||
- remove synchronous events
|
||||
- use CircleCI for continous integration and packaging of Linux wheels
|
||||
- use docker image for building wheels
|
||||
- fix code documentation links
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
deltachat python bindings
|
||||
=========================
|
||||
|
||||
The ``deltachat`` Python package provides two bindings for the core C-library
|
||||
The ``deltachat`` Python package provides two bindings for the core Rust-library
|
||||
of the https://delta.chat messaging ecosystem:
|
||||
|
||||
- :doc:`capi` is a lowlevel CFFI-binding to the
|
||||
`deltachat-core C-API <https://c.delta.chat>`_.
|
||||
|
||||
- :doc:`api` [work-in-progress] is a high level interface to deltachat-core which aims
|
||||
- :doc:`api` is a high level interface to deltachat-core which aims
|
||||
to be memory safe and thoroughly tested through continous tox/pytest runs.
|
||||
|
||||
- :doc:`capi` is a lowlevel CFFI-binding to the previous
|
||||
`deltachat-core C-API <https://c.delta.chat>`_ (so far the Rust library
|
||||
replicates exactly the same C-level API).
|
||||
|
||||
|
||||
|
||||
getting started
|
||||
---------------
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -ex
|
||||
|
||||
cargo build -p deltachat_ffi --release
|
||||
DCC_RS_DEV=`pwd`/.. pip install -e .
|
||||
36
python/install_python_bindings.py
Executable file
36
python/install_python_bindings.py
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
setup a python binding development in-place install with cargo debug symbols.
|
||||
"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ["DCC_RS_TARGET"] = target = "release"
|
||||
|
||||
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")
|
||||
|
||||
subprocess.check_call("rm -rf build/ src/deltachat/*.so" , shell=True)
|
||||
|
||||
subprocess.check_call([
|
||||
"pip", "install", "-e", "."
|
||||
])
|
||||
@@ -4,15 +4,20 @@ import re
|
||||
|
||||
|
||||
def main():
|
||||
long_description, version = read_meta()
|
||||
with open("README.rst") as f:
|
||||
long_description = f.read()
|
||||
setuptools.setup(
|
||||
name='deltachat',
|
||||
version=version,
|
||||
description='Python bindings for deltachat-core using CFFI',
|
||||
setup_requires=['setuptools_scm', 'cffi>=1.0.0'],
|
||||
use_scm_version = {
|
||||
"root": "..",
|
||||
"relative_to": __file__,
|
||||
'tag_regex': r'^(?P<prefix>py-)?(?P<version>[^\+]+)(?P<suffix>.*)?$',
|
||||
},
|
||||
description='Python bindings for the Delta Chat Core library using CFFI against the Rust-implemented libdeltachat',
|
||||
long_description=long_description,
|
||||
author='holger krekel, Floris Bruynooghe, Bjoern Petersen and contributors',
|
||||
setup_requires=['cffi>=1.0.0'],
|
||||
install_requires=['cffi>=1.0.0', 'requests', 'attrs', 'six'],
|
||||
install_requires=['cffi>=1.0.0', 'attrs', 'six'],
|
||||
packages=setuptools.find_packages('src'),
|
||||
package_dir={'': 'src'},
|
||||
cffi_modules=['src/deltachat/_build.py:ffibuilder'],
|
||||
@@ -27,18 +32,5 @@ def main():
|
||||
)
|
||||
|
||||
|
||||
def read_meta():
|
||||
with open(os.path.join("src", "deltachat", "__init__.py")) as f:
|
||||
for line in f:
|
||||
m = re.match('__version__ = "(\S*).*"', line)
|
||||
if m:
|
||||
version, = m.groups()
|
||||
break
|
||||
|
||||
with open("README.rst") as f:
|
||||
long_desc = f.read()
|
||||
return long_desc, version
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -2,7 +2,12 @@ from deltachat import capi, const
|
||||
from deltachat.capi import ffi
|
||||
from deltachat.account import Account # noqa
|
||||
|
||||
__version__ = "0.10.0.dev2"
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
try:
|
||||
__version__ = get_distribution(__name__).version
|
||||
except DistributionNotFound:
|
||||
# package is not installed
|
||||
__version__ = "0.0.0.dev0-unknown"
|
||||
|
||||
|
||||
_DC_CALLBACK_MAP = {}
|
||||
@@ -38,6 +43,9 @@ def py_dc_callback(ctx, evt, data1, data2):
|
||||
|
||||
try:
|
||||
ret = callback(ctx, evt_name, data1, data2)
|
||||
if ret is None:
|
||||
ret = 0
|
||||
assert isinstance(ret, int), repr(ret)
|
||||
if event_sig_types & 4:
|
||||
return ffi.cast('uintptr_t', ret)
|
||||
elif event_sig_types & 8:
|
||||
@@ -53,7 +61,10 @@ def set_context_callback(dc_context, func):
|
||||
|
||||
|
||||
def clear_context_callback(dc_context):
|
||||
_DC_CALLBACK_MAP.pop(dc_context, None)
|
||||
try:
|
||||
_DC_CALLBACK_MAP.pop(dc_context, None)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}):
|
||||
|
||||
@@ -2,21 +2,36 @@ import distutils.ccompiler
|
||||
import distutils.log
|
||||
import distutils.sysconfig
|
||||
import tempfile
|
||||
import platform
|
||||
import os
|
||||
import cffi
|
||||
import shutil
|
||||
|
||||
|
||||
def ffibuilder():
|
||||
projdir = os.environ.get('DCC_RS_DEV')
|
||||
target = os.environ.get('DCC_RS_TARGET', 'release')
|
||||
if projdir:
|
||||
libs = ['rt', 'dl', 'm']
|
||||
if platform.system() == 'Darwin':
|
||||
libs = ['resolv', 'dl']
|
||||
extra_link_args = [
|
||||
'-framework', 'CoreFoundation',
|
||||
'-framework', 'CoreServices',
|
||||
'-framework', 'Security',
|
||||
]
|
||||
elif platform.system() == 'Linux':
|
||||
libs = ['rt', 'dl', 'm']
|
||||
extra_link_args = []
|
||||
else:
|
||||
raise NotImplementedError("Compilation not supported yet on Windows, can you help?")
|
||||
objs = [os.path.join(projdir, 'target', target, 'libdeltachat.a')]
|
||||
assert os.path.exists(objs[0]), objs
|
||||
incs = [os.path.join(projdir, 'deltachat-ffi')]
|
||||
else:
|
||||
libs = ['deltachat']
|
||||
objs = []
|
||||
incs = []
|
||||
extra_link_args = []
|
||||
builder = cffi.FFI()
|
||||
builder.set_source(
|
||||
'deltachat.capi',
|
||||
@@ -43,6 +58,7 @@ def ffibuilder():
|
||||
include_dirs=incs,
|
||||
libraries=libs,
|
||||
extra_objects=objs,
|
||||
extra_link_args=extra_link_args,
|
||||
)
|
||||
builder.cdef("""
|
||||
typedef int... time_t;
|
||||
@@ -53,15 +69,21 @@ def ffibuilder():
|
||||
distutils.log.set_verbosity(distutils.log.INFO)
|
||||
cc = distutils.ccompiler.new_compiler(force=True)
|
||||
distutils.sysconfig.customize_compiler(cc)
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.h') as src_fp:
|
||||
src_fp.write('#include <deltachat.h>')
|
||||
src_fp.flush()
|
||||
with tempfile.NamedTemporaryFile(mode='r') as dst_fp:
|
||||
cc.preprocess(source=src_fp.name,
|
||||
output_file=dst_fp.name,
|
||||
include_dirs=incs,
|
||||
macros=[('PY_CFFI', '1')])
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
try:
|
||||
src_name = os.path.join(tmpdir, "include.h")
|
||||
dst_name = os.path.join(tmpdir, "expanded.h")
|
||||
with open(src_name, "w") as src_fp:
|
||||
src_fp.write('#include <deltachat.h>')
|
||||
cc.preprocess(source=src_name,
|
||||
output_file=dst_name,
|
||||
include_dirs=incs,
|
||||
macros=[('PY_CFFI', '1')])
|
||||
with open(dst_name, "r") as dst_fp:
|
||||
builder.cdef(dst_fp.read())
|
||||
finally:
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
builder.cdef("""
|
||||
extern "Python" uintptr_t py_dc_callback(
|
||||
dc_context_t* context,
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
|
||||
from __future__ import print_function
|
||||
import threading
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import requests
|
||||
from array import array
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
import attr
|
||||
from attr import validators as v
|
||||
|
||||
import deltachat
|
||||
from . import const
|
||||
@@ -25,27 +23,35 @@ class Account(object):
|
||||
by the underlying deltachat c-library. All public Account methods are
|
||||
meant to be memory-safe and return memory-safe objects.
|
||||
"""
|
||||
def __init__(self, db_path, logid=None):
|
||||
def __init__(self, db_path, logid=None, eventlogging=True):
|
||||
""" initialize account object.
|
||||
|
||||
:param db_path: a path to the account database. The database
|
||||
will be created if it doesn't exist.
|
||||
:param logid: an optional logging prefix that should be used with
|
||||
the default internal logging.
|
||||
:param eventlogging: if False no eventlogging and no context callback will be configured
|
||||
"""
|
||||
self._dc_context = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
_destroy_dc_context,
|
||||
)
|
||||
if eventlogging:
|
||||
self._evlogger = EventLogger(self._dc_context, logid)
|
||||
deltachat.set_context_callback(self._dc_context, self._process_event)
|
||||
self._threads = IOThreads(self._dc_context, self._evlogger._log_event)
|
||||
else:
|
||||
self._threads = IOThreads(self._dc_context)
|
||||
|
||||
if hasattr(db_path, "encode"):
|
||||
db_path = db_path.encode("utf8")
|
||||
if not lib.dc_open(self._dc_context, db_path, ffi.NULL):
|
||||
raise ValueError("Could not dc_open: {}".format(db_path))
|
||||
self._evhandler = EventHandler(self._dc_context)
|
||||
self._evlogger = EventLogger(self._dc_context, logid)
|
||||
deltachat.set_context_callback(self._dc_context, self._process_event)
|
||||
self._threads = IOThreads(self._dc_context)
|
||||
self._configkeys = self.get_config("sys.config_keys").split()
|
||||
self._imex_completed = threading.Event()
|
||||
|
||||
def __del__(self):
|
||||
self.shutdown()
|
||||
|
||||
def _check_config_key(self, name):
|
||||
if name not in self._configkeys:
|
||||
@@ -182,15 +188,19 @@ class Account(object):
|
||||
return list(iter_array(dc_array, lambda x: Contact(self._dc_context, x)))
|
||||
|
||||
def create_chat_by_contact(self, contact):
|
||||
""" create or get an existing 1:1 chat object for the specified contact.
|
||||
""" create or get an existing 1:1 chat object for the specified contact or contact id.
|
||||
|
||||
:param contact: chat_id (int) or contact object.
|
||||
:returns: a :class:`deltachat.chatting.Chat` object.
|
||||
"""
|
||||
contact_id = getattr(contact, "id", contact)
|
||||
assert isinstance(contact_id, int)
|
||||
chat_id = lib.dc_create_chat_by_contact_id(
|
||||
self._dc_context, contact_id)
|
||||
if hasattr(contact, "id"):
|
||||
if contact._dc_context != self._dc_context:
|
||||
raise ValueError("Contact belongs to a different Account")
|
||||
contact_id = contact.id
|
||||
else:
|
||||
assert isinstance(contact, int)
|
||||
contact_id = contact
|
||||
chat_id = lib.dc_create_chat_by_contact_id(self._dc_context, contact_id)
|
||||
return Chat(self._dc_context, chat_id)
|
||||
|
||||
def create_chat_by_message(self, message):
|
||||
@@ -200,8 +210,13 @@ class Account(object):
|
||||
:param message: messsage id or message instance.
|
||||
:returns: a :class:`deltachat.chatting.Chat` object.
|
||||
"""
|
||||
msg_id = getattr(message, "id", message)
|
||||
assert isinstance(msg_id, int)
|
||||
if hasattr(message, "id"):
|
||||
if self._dc_context != message._dc_context:
|
||||
raise ValueError("Message belongs to a different Account")
|
||||
msg_id = message.id
|
||||
else:
|
||||
assert isinstance(message, int)
|
||||
msg_id = message
|
||||
chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id)
|
||||
return Chat(self._dc_context, chat_id)
|
||||
|
||||
@@ -272,6 +287,45 @@ class Account(object):
|
||||
msg_ids = [msg.id for msg in messages]
|
||||
lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids))
|
||||
|
||||
def export_to_dir(self, backupdir):
|
||||
"""return after all delta chat state is exported to a new file in
|
||||
the specified directory.
|
||||
"""
|
||||
snap_files = os.listdir(backupdir)
|
||||
self._imex_completed.clear()
|
||||
lib.dc_imex(self._dc_context, 11, as_dc_charpointer(backupdir), ffi.NULL)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
self._imex_completed.wait()
|
||||
for x in os.listdir(backupdir):
|
||||
if x not in snap_files:
|
||||
return os.path.join(backupdir, x)
|
||||
|
||||
def import_from_file(self, path):
|
||||
"""import delta chat state from the specified backup file.
|
||||
|
||||
The account must be in unconfigured state for import to attempted.
|
||||
"""
|
||||
assert not self.is_configured(), "cannot import into configured account"
|
||||
self._imex_completed.clear()
|
||||
lib.dc_imex(self._dc_context, 12, as_dc_charpointer(path), ffi.NULL)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
self._imex_completed.wait()
|
||||
|
||||
def initiate_key_transfer(self):
|
||||
"""return setup code after a Autocrypt setup message
|
||||
has been successfully sent to our own e-mail address ("self-sent message").
|
||||
If sending out was unsuccessful, a RuntimeError is raised.
|
||||
"""
|
||||
self.check_is_configured()
|
||||
if not self._threads.is_started():
|
||||
raise RuntimeError("threads not running, can not send out")
|
||||
res = lib.dc_initiate_key_transfer(self._dc_context)
|
||||
if res == ffi.NULL:
|
||||
raise RuntimeError("could not send out autocrypt setup message")
|
||||
return from_dc_charpointer(res)
|
||||
|
||||
def start_threads(self):
|
||||
""" start IMAP/SMTP threads (and configure account if it hasn't happened).
|
||||
|
||||
@@ -282,27 +336,46 @@ class Account(object):
|
||||
self.configure()
|
||||
self._threads.start()
|
||||
|
||||
def stop_threads(self):
|
||||
def stop_threads(self, wait=True):
|
||||
""" stop IMAP/SMTP threads. """
|
||||
self._threads.stop(wait=True)
|
||||
lib.dc_stop_ongoing_process(self._dc_context)
|
||||
self._threads.stop(wait=wait)
|
||||
|
||||
def shutdown(self, wait=True):
|
||||
""" stop threads and close and remove underlying dc_context and callbacks. """
|
||||
if hasattr(self, "_dc_context") and hasattr(self, "_threads"):
|
||||
self.stop_threads(wait=False) # to interrupt idle and tell python threads to stop
|
||||
lib.dc_close(self._dc_context)
|
||||
self.stop_threads(wait=wait) # to wait for threads
|
||||
deltachat.clear_context_callback(self._dc_context)
|
||||
del self._dc_context
|
||||
|
||||
def _process_event(self, ctx, evt_name, data1, data2):
|
||||
assert ctx == self._dc_context
|
||||
self._evlogger(evt_name, data1, data2)
|
||||
method = getattr(self._evhandler, evt_name.lower(), None)
|
||||
if method is not None:
|
||||
return method(data1, data2) or 0
|
||||
if hasattr(self, "_evlogger"):
|
||||
self._evlogger(evt_name, data1, data2)
|
||||
method = getattr(self, "on_" + evt_name.lower(), None)
|
||||
if method is not None:
|
||||
method(data1, data2)
|
||||
return 0
|
||||
|
||||
def on_dc_event_imex_progress(self, data1, data2):
|
||||
if data1 == 1000:
|
||||
self._imex_completed.set()
|
||||
|
||||
|
||||
class IOThreads:
|
||||
def __init__(self, dc_context):
|
||||
def __init__(self, dc_context, log_event=lambda *args: None):
|
||||
self._dc_context = dc_context
|
||||
self._thread_quitflag = False
|
||||
self._name2thread = {}
|
||||
self._log_event = log_event
|
||||
|
||||
def is_started(self):
|
||||
return len(self._name2thread) > 0
|
||||
|
||||
def start(self, imap=True, smtp=True):
|
||||
assert not self._name2thread
|
||||
assert not self.is_started()
|
||||
if imap:
|
||||
self._start_one_thread("imap", self.imap_thread_run)
|
||||
if smtp:
|
||||
@@ -322,41 +395,19 @@ class IOThreads:
|
||||
thread.join()
|
||||
|
||||
def imap_thread_run(self):
|
||||
print ("starting imap thread")
|
||||
self._log_event("py-bindings-info", 0, "IMAP THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
lib.dc_perform_imap_fetch(self._dc_context)
|
||||
lib.dc_perform_imap_idle(self._dc_context)
|
||||
self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED")
|
||||
|
||||
def smtp_thread_run(self):
|
||||
print ("starting smtp thread")
|
||||
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_smtp_jobs(self._dc_context)
|
||||
lib.dc_perform_smtp_idle(self._dc_context)
|
||||
|
||||
|
||||
@attr.s
|
||||
class EventHandler(object):
|
||||
_dc_context = attr.ib(validator=v.instance_of(ffi.CData))
|
||||
|
||||
def read_url(self, url):
|
||||
try:
|
||||
r = requests.get(url)
|
||||
except requests.ConnectionError:
|
||||
return ''
|
||||
else:
|
||||
return r.content
|
||||
|
||||
def dc_event_http_get(self, data1, data2):
|
||||
url = data1
|
||||
content = self.read_url(url)
|
||||
if not isinstance(content, bytes):
|
||||
content = content.encode("utf8")
|
||||
# we need to return a fresh pointer that the core owns
|
||||
return lib.dupstring_helper(content)
|
||||
|
||||
def dc_event_is_offline(self, data1, data2):
|
||||
return 0 # always online
|
||||
self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED")
|
||||
|
||||
|
||||
class EventLogger:
|
||||
@@ -386,7 +437,7 @@ class EventLogger:
|
||||
raise ValueError("{}({!r},{!r})".format(*ev))
|
||||
return ev
|
||||
|
||||
def get_matching(self, event_name_regex):
|
||||
def get_matching(self, event_name_regex, check_error=True):
|
||||
self._log("-- waiting for event with regex: {} --".format(event_name_regex))
|
||||
rex = re.compile("(?:{}).*".format(event_name_regex))
|
||||
while 1:
|
||||
|
||||
@@ -93,6 +93,14 @@ class Message(object):
|
||||
"""
|
||||
return MessageType(lib.dc_msg_get_viewtype(self._dc_msg))
|
||||
|
||||
def is_setup_message(self):
|
||||
""" return True if this message is a setup message. """
|
||||
return lib.dc_msg_is_setupmessage(self._dc_msg)
|
||||
|
||||
def continue_key_transfer(self, setup_code):
|
||||
""" extract key and use it as primary key for this account. """
|
||||
lib.dc_continue_key_transfer(self._dc_context, self.id, as_dc_charpointer(setup_code))
|
||||
|
||||
@props.with_doc
|
||||
def time_sent(self):
|
||||
"""UTC time when the message was sent.
|
||||
|
||||
@@ -16,18 +16,41 @@ def pytest_addoption(parser):
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
cfg = config.getoption('--liveconfig')
|
||||
if not cfg:
|
||||
cfg = os.getenv('DCC_PY_LIVECONFIG')
|
||||
if cfg:
|
||||
config.option.liveconfig = cfg
|
||||
|
||||
|
||||
@pytest.hookimpl(trylast=True)
|
||||
def pytest_runtest_call(item):
|
||||
# perform early finalization because we otherwise get cloberred
|
||||
# output from concurrent threads printing between execution
|
||||
# of the test function and the teardown phase of that test function
|
||||
if "acfactory" in item.funcargs:
|
||||
print("*"*30, "finalizing", "*"*30)
|
||||
acfactory = item.funcargs["acfactory"]
|
||||
acfactory.finalize()
|
||||
|
||||
|
||||
def pytest_report_header(config, startdir):
|
||||
t = tempfile.mktemp()
|
||||
try:
|
||||
ac = Account(t)
|
||||
ac = Account(t, eventlogging=False)
|
||||
info = ac.get_info()
|
||||
del ac
|
||||
ac.shutdown()
|
||||
finally:
|
||||
os.remove(t)
|
||||
return "Deltachat core={} sqlite={}".format(
|
||||
info['deltachat_core_version'],
|
||||
info['sqlite_version'],
|
||||
)
|
||||
summary = ['Deltachat core={} sqlite={}'.format(
|
||||
info['deltachat_core_version'],
|
||||
info['sqlite_version'],
|
||||
)]
|
||||
cfg = config.getoption('--liveconfig')
|
||||
if cfg:
|
||||
summary.append('Liveconfig: {}'.format(os.path.abspath(cfg)))
|
||||
return summary
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
@@ -52,7 +75,6 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
self.live_count = 0
|
||||
self.offline_count = 0
|
||||
self._finalizers = []
|
||||
request.addfinalizer(self.finalize)
|
||||
self.init_time = time.time()
|
||||
|
||||
def finalize(self):
|
||||
@@ -64,7 +86,7 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
def configlist(self):
|
||||
configlist = []
|
||||
for line in open(fn):
|
||||
if line.strip():
|
||||
if line.strip() and not line.strip().startswith('#'):
|
||||
d = {}
|
||||
for part in line.split():
|
||||
name, value = part.split("=")
|
||||
@@ -78,6 +100,7 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
ac = Account(tmpdb.strpath, logid="ac{}".format(self.offline_count))
|
||||
ac._evlogger.init_time = self.init_time
|
||||
ac._evlogger.set_timeout(2)
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
def get_configured_offline_account(self):
|
||||
@@ -103,7 +126,18 @@ def acfactory(pytestconfig, tmpdir, request):
|
||||
ac._evlogger.set_timeout(30)
|
||||
ac.configure(**configdict)
|
||||
ac.start_threads()
|
||||
self._finalizers.append(ac.stop_threads)
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
def clone_online_account(self, account):
|
||||
self.live_count += 1
|
||||
tmpdb = tmpdir.join("livedb%d" % self.live_count)
|
||||
ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count))
|
||||
ac._evlogger.init_time = self.init_time
|
||||
ac._evlogger.set_timeout(30)
|
||||
ac.configure(addr=account.get_config("addr"), mail_pw=account.get_config("mail_pw"))
|
||||
ac.start_threads()
|
||||
self._finalizers.append(ac.shutdown)
|
||||
return ac
|
||||
|
||||
return AccountMaker()
|
||||
@@ -142,8 +176,10 @@ def wait_successful_IMAP_SMTP_connection(account):
|
||||
account._evlogger.get_matching("DC_EVENT_(IMAP|SMTP)_CONNECTED")
|
||||
if evt_name == "DC_EVENT_IMAP_CONNECTED":
|
||||
imap_ok = True
|
||||
print("** IMAP OK", account)
|
||||
if evt_name == "DC_EVENT_SMTP_CONNECTED":
|
||||
smtp_ok = True
|
||||
print("** SMTP OK", account)
|
||||
print("** IMAP and SMTP logins successful", account)
|
||||
|
||||
|
||||
|
||||
15
python/tests/package_wheels.py
Normal file
15
python/tests/package_wheels.py
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
assert len(sys.argv) == 2
|
||||
wheelhousedir = sys.argv[1]
|
||||
# pip wheel will build in an isolated tmp dir that does not have git
|
||||
# history so setuptools_scm can not automatically determine a
|
||||
# version there. So pass in the version through an env var.
|
||||
version = subprocess.check_output(["python", "setup.py", "--version"]).strip().split(b"\n")[-1]
|
||||
os.environ["SETUPTOOLS_SCM_PRETEND_VERSION"] = version.decode("ascii")
|
||||
subprocess.check_call(("pip wheel . -w %s" % wheelhousedir).split())
|
||||
@@ -1,12 +1,18 @@
|
||||
from __future__ import print_function
|
||||
import pytest
|
||||
import os
|
||||
from deltachat import const
|
||||
from deltachat import const, Account
|
||||
from datetime import datetime, timedelta
|
||||
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
|
||||
|
||||
|
||||
class TestOfflineAccount:
|
||||
def test_wrong_db(self, tmpdir):
|
||||
p = tmpdir.join("hello.db")
|
||||
p.write("123")
|
||||
with pytest.raises(ValueError):
|
||||
Account(p.strpath)
|
||||
|
||||
def test_getinfo(self, acfactory):
|
||||
ac1 = acfactory.get_unconfigured_account()
|
||||
d = ac1.get_info()
|
||||
@@ -145,6 +151,14 @@ class TestOfflineAccount:
|
||||
assert not msg_state.is_out_delivered()
|
||||
assert not msg_state.is_out_mdn_received()
|
||||
|
||||
def test_create_chat_by_mssage_id(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
chat = ac1.create_chat_by_contact(contact1)
|
||||
msg = chat.send_text("msg1")
|
||||
assert chat == ac1.create_chat_by_message(msg)
|
||||
assert chat == ac1.create_chat_by_message(msg.id)
|
||||
|
||||
def test_message_image(self, acfactory, data, lp):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
@@ -180,6 +194,17 @@ class TestOfflineAccount:
|
||||
assert msg.filename.endswith(msg.basename)
|
||||
assert msg.filemime == typeout
|
||||
|
||||
def test_create_chat_mismatch(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
ac2 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
with pytest.raises(ValueError):
|
||||
ac2.create_chat_by_contact(contact1)
|
||||
chat1 = ac1.create_chat_by_contact(contact1)
|
||||
msg = chat1.send_text("hello")
|
||||
with pytest.raises(ValueError):
|
||||
ac2.create_chat_by_message(msg)
|
||||
|
||||
def test_chat_message_distinctions(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
@@ -202,8 +227,81 @@ class TestOfflineAccount:
|
||||
with pytest.raises(ValueError):
|
||||
ac1.configure(addr="123@example.org")
|
||||
|
||||
def test_import_export_one_contact(self, acfactory, tmpdir):
|
||||
backupdir = tmpdir.mkdir("backup")
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
chat = ac1.create_chat_by_contact(contact1)
|
||||
# send a text message
|
||||
msg = chat.send_text("msg1")
|
||||
# send a binary file
|
||||
bin = tmpdir.join("some.bin")
|
||||
with bin.open("w") as f:
|
||||
f.write("\00123" * 10000)
|
||||
msg = chat.send_file(bin.strpath)
|
||||
|
||||
contact = msg.get_sender_contact()
|
||||
assert contact == ac1.get_self_contact()
|
||||
assert not backupdir.listdir()
|
||||
|
||||
path = ac1.export_to_dir(backupdir.strpath)
|
||||
assert os.path.exists(path)
|
||||
ac2 = acfactory.get_unconfigured_account()
|
||||
ac2.import_from_file(path)
|
||||
contacts = ac2.get_contacts(query="some1")
|
||||
assert len(contacts) == 1
|
||||
contact2 = contacts[0]
|
||||
assert contact2.addr == "some1@hello.com"
|
||||
chat2 = ac2.create_chat_by_contact(contact2)
|
||||
messages = chat2.get_messages()
|
||||
assert len(messages) == 2
|
||||
assert messages[0].text == "msg1"
|
||||
assert os.path.exists(messages[1].filename)
|
||||
|
||||
def test_ac_setup_message_fails(self, acfactory):
|
||||
ac1 = acfactory.get_configured_offline_account()
|
||||
with pytest.raises(RuntimeError):
|
||||
ac1.initiate_key_transfer()
|
||||
|
||||
|
||||
class TestOnlineAccount:
|
||||
def test_one_account_init(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
def test_one_account_send(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
c2 = ac1.create_contact(email=ac1.get_config("addr"))
|
||||
chat = ac1.create_chat_by_contact(c2)
|
||||
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
msg_out = chat.send_text("message2")
|
||||
# wait for own account to receive
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[1] == msg_out.id
|
||||
|
||||
def test_two_acocunts_send_receive(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||
chat = ac1.create_chat_by_contact(c2)
|
||||
assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL
|
||||
wait_successful_IMAP_SMTP_connection(ac1)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
wait_successful_IMAP_SMTP_connection(ac2)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
|
||||
msg_out = chat.send_text("message1")
|
||||
|
||||
# wait for other account to receive
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[2] == msg_out.id
|
||||
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||
assert msg_in.text == "message1"
|
||||
|
||||
def test_forward_messages(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
@@ -332,3 +430,44 @@ class TestOnlineAccount:
|
||||
assert msg_in.view_type.is_image()
|
||||
assert os.path.exists(msg_in.filename)
|
||||
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
|
||||
|
||||
def test_import_export_online(self, acfactory, tmpdir):
|
||||
backupdir = tmpdir.mkdir("backup")
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
|
||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||
chat = ac1.create_chat_by_contact(contact1)
|
||||
chat.send_text("msg1")
|
||||
path = ac1.export_to_dir(backupdir.strpath)
|
||||
assert os.path.exists(path)
|
||||
|
||||
ac2 = acfactory.get_unconfigured_account()
|
||||
ac2.import_from_file(path)
|
||||
contacts = ac2.get_contacts(query="some1")
|
||||
assert len(contacts) == 1
|
||||
contact2 = contacts[0]
|
||||
assert contact2.addr == "some1@hello.com"
|
||||
chat2 = ac2.create_chat_by_contact(contact2)
|
||||
messages = chat2.get_messages()
|
||||
assert len(messages) == 1
|
||||
assert messages[0].text == "msg1"
|
||||
|
||||
def test_ac_setup_message(self, acfactory):
|
||||
# note that the receiving account needs to be configured and running
|
||||
# before ther setup message is send. DC does not read old messages
|
||||
# as of Jul2019
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
ac2 = acfactory.clone_online_account(ac1)
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"]
|
||||
setup_code = ac1.initiate_key_transfer()
|
||||
ac2._evlogger.set_timeout(10)
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
msg = ac2.get_message_by_id(ev[2])
|
||||
assert msg.is_setup_message()
|
||||
print("*************** Incoming ASM File at: ", msg.filename)
|
||||
print("*************** Setup Code: ", setup_code)
|
||||
msg.continue_key_transfer(setup_code)
|
||||
assert ac1.get_info()["fingerprint"] == ac2.get_info()["fingerprint"]
|
||||
|
||||
@@ -32,7 +32,11 @@ class TestInCreation:
|
||||
chat2.add_contact(c2)
|
||||
wait_msgs_changed(ac1, 0, 0) # why not chat id?
|
||||
ac1.forward_messages([prepared_original], chat2)
|
||||
# XXX there might be two EVENT_MSGS_CHANGED and only one of them
|
||||
# is the one caused by forwarding
|
||||
forwarded_id = wait_msgs_changed(ac1, chat2.id)
|
||||
if forwarded_id == 0:
|
||||
forwarded_id = wait_msgs_changed(ac1, chat2.id)
|
||||
forwarded_msg = ac1.get_message_by_id(forwarded_id)
|
||||
assert forwarded_msg.get_state().is_out_preparing()
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from __future__ import print_function
|
||||
import pytest
|
||||
from deltachat import capi, Account, const
|
||||
from deltachat import capi, const, set_context_callback, clear_context_callback
|
||||
from deltachat.capi import ffi
|
||||
from deltachat.capi import lib
|
||||
from deltachat.account import EventLogger
|
||||
|
||||
|
||||
def test_empty_context():
|
||||
@@ -8,10 +10,45 @@ def test_empty_context():
|
||||
capi.lib.dc_close(ctx)
|
||||
|
||||
|
||||
def test_callback_None2int():
|
||||
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
|
||||
set_context_callback(ctx, lambda *args: None)
|
||||
capi.lib.dc_close(ctx)
|
||||
clear_context_callback(ctx)
|
||||
|
||||
|
||||
def test_dc_close_events():
|
||||
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
|
||||
evlog = EventLogger(ctx)
|
||||
evlog.set_timeout(5)
|
||||
set_context_callback(ctx, lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2))
|
||||
capi.lib.dc_close(ctx)
|
||||
|
||||
def find(info_string):
|
||||
while 1:
|
||||
ev = evlog.get_matching("DC_EVENT_INFO", check_error=False)
|
||||
data2 = ev[2]
|
||||
if info_string in data2:
|
||||
return
|
||||
else:
|
||||
print("skipping event", *ev)
|
||||
|
||||
find("disconnecting INBOX-watch")
|
||||
find("disconnecting sentbox-thread")
|
||||
find("disconnecting mvbox-thread")
|
||||
find("disconnecting SMTP")
|
||||
find("Database closed")
|
||||
|
||||
|
||||
def test_wrong_db(tmpdir):
|
||||
tmpdir.join("hello.db").write("123")
|
||||
with pytest.raises(ValueError):
|
||||
Account(db_path=tmpdir.strpath)
|
||||
dc_context = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
p = tmpdir.join("hello.db")
|
||||
# write an invalid database file
|
||||
p.write("x123" * 10)
|
||||
assert not lib.dc_open(dc_context, p.strpath.encode("ascii"), ffi.NULL)
|
||||
|
||||
|
||||
def test_event_defines():
|
||||
|
||||
@@ -9,10 +9,12 @@ envlist =
|
||||
[testenv]
|
||||
commands =
|
||||
pytest -rsXx {posargs:tests}
|
||||
pip wheel . -w {toxworkdir}/wheelhouse
|
||||
python tests/package_wheels.py {toxworkdir}/wheelhouse
|
||||
passenv =
|
||||
TRAVIS
|
||||
DCC_RS_DEV
|
||||
DCC_RS_TARGET
|
||||
DCC_PY_LIVECONFIG
|
||||
deps =
|
||||
pytest
|
||||
pytest-faulthandler
|
||||
@@ -27,7 +29,6 @@ commands =
|
||||
[testenv:lint]
|
||||
skipsdist = True
|
||||
usedevelop = True
|
||||
basepython = python2.7
|
||||
deps =
|
||||
flake8
|
||||
# pygments required by rst-lint
|
||||
@@ -53,6 +54,7 @@ commands =
|
||||
python_files = tests/test_*.py
|
||||
norecursedirs = .tox
|
||||
xfail_strict=true
|
||||
timeout = 60
|
||||
|
||||
[flake8]
|
||||
max-line-length = 120
|
||||
|
||||
3
release.toml
Normal file
3
release.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
pre-release-commit-message = "chore({{crate_name}}): release {{version}}"
|
||||
pro-release-commit-message = "chore({{crate_name}}): starting development cycle for {{next_version}}"
|
||||
no-dev-version = true
|
||||
@@ -23,11 +23,10 @@ if [ $? != 0 ]; then
|
||||
fi
|
||||
|
||||
pushd python
|
||||
toxargs="$@"
|
||||
if [ -e liveconfig ]; then
|
||||
toxargs="--liveconfig liveconfig $@"
|
||||
if [ -e "./liveconfig" ]; then
|
||||
export DCC_PY_LIVECONFIG=liveconfig
|
||||
fi
|
||||
tox $toxargs
|
||||
tox "$@"
|
||||
ret=$?
|
||||
popd
|
||||
exit $ret
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2019-06-16
|
||||
nightly-2019-07-10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::str::FromStr;
|
||||
use std::{fmt, str};
|
||||
|
||||
@@ -7,6 +7,7 @@ use mmime::mailimf_types::*;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_tools::as_str;
|
||||
use crate::key::*;
|
||||
|
||||
/// Possible values for encryption preference
|
||||
@@ -93,9 +94,7 @@ impl Aheader {
|
||||
|
||||
match Self::from_str(value) {
|
||||
Ok(test) => {
|
||||
// TODO: implement rust-safe version of dc_addr_cmp
|
||||
let addr = CString::new(test.addr.clone()).unwrap();
|
||||
if unsafe { dc_addr_cmp(addr.as_ptr(), wanted_from) } == 0 {
|
||||
if dc_addr_cmp(&test.addr, as_str(wanted_from)) {
|
||||
if fine_header.is_none() {
|
||||
fine_header = Some(test);
|
||||
} else {
|
||||
|
||||
182
src/config.rs
Normal file
182
src/config.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use strum::{EnumProperty, IntoEnumIterator};
|
||||
use strum_macros::{AsRefStr, Display, EnumIter, EnumProperty, EnumString};
|
||||
|
||||
use crate::constants::DC_VERSION_STR;
|
||||
use crate::context::Context;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::error::Error;
|
||||
use crate::x::*;
|
||||
|
||||
/// The available configuration keys.
|
||||
#[derive(
|
||||
Debug, Clone, Copy, PartialEq, Eq, Display, EnumString, AsRefStr, EnumIter, EnumProperty,
|
||||
)]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum Config {
|
||||
Addr,
|
||||
MailServer,
|
||||
MailUser,
|
||||
MailPw,
|
||||
MailPort,
|
||||
SendServer,
|
||||
SendUser,
|
||||
SendPw,
|
||||
SendPort,
|
||||
ServerFlags,
|
||||
#[strum(props(default = "INBOX"))]
|
||||
ImapFolder,
|
||||
Displayname,
|
||||
Selfstatus,
|
||||
Selfavatar,
|
||||
#[strum(props(default = "1"))]
|
||||
E2eeEnabled,
|
||||
#[strum(props(default = "1"))]
|
||||
MdnsEnabled,
|
||||
InboxWatch,
|
||||
#[strum(props(default = "1"))]
|
||||
SentboxWatch,
|
||||
#[strum(props(default = "1"))]
|
||||
MvboxWatch,
|
||||
#[strum(props(default = "1"))]
|
||||
MvboxMove,
|
||||
#[strum(props(default = "0"))]
|
||||
ShowEmails,
|
||||
SaveMimeHeaders,
|
||||
ConfiguredAddr,
|
||||
ConfiguredMailServer,
|
||||
ConfiguredMailUser,
|
||||
ConfiguredMailPw,
|
||||
ConfiguredMailPort,
|
||||
ConfiguredSendServer,
|
||||
ConfiguredSendUser,
|
||||
ConfiguredSendPw,
|
||||
ConfiguredSendPort,
|
||||
ConfiguredServerFlags,
|
||||
Configured,
|
||||
// Deprecated
|
||||
#[strum(serialize = "sys.version")]
|
||||
SysVersion,
|
||||
#[strum(serialize = "sys.msgsize_max_recommended")]
|
||||
SysMsgsizeMaxRecommended,
|
||||
#[strum(serialize = "sys.config_keys")]
|
||||
SysConfigKeys,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Get a configuration key. Returns `None` if no value is set, and no default value found.
|
||||
pub fn get_config(&self, key: Config) -> Option<String> {
|
||||
let value = match key {
|
||||
Config::Selfavatar => {
|
||||
let rel_path = self.sql.get_config(self, key);
|
||||
rel_path.map(|p| {
|
||||
let v = unsafe {
|
||||
let n = to_cstring(p);
|
||||
let res = dc_get_abs_path(self, n);
|
||||
free(n as *mut libc::c_void);
|
||||
res
|
||||
};
|
||||
let r = to_string(v);
|
||||
unsafe { free(v as *mut _) };
|
||||
r
|
||||
})
|
||||
}
|
||||
Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
Config::SysConfigKeys => Some(get_config_keys_string()),
|
||||
_ => self.sql.get_config(self, key),
|
||||
};
|
||||
|
||||
if value.is_some() {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Default values
|
||||
match key {
|
||||
Config::Selfstatus => {
|
||||
let s = unsafe { dc_stock_str(self, 13) };
|
||||
let res = to_string(s);
|
||||
unsafe { free(s as *mut _) };
|
||||
Some(res)
|
||||
}
|
||||
_ => key.get_str("default").map(|s| s.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the given config key.
|
||||
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
|
||||
pub fn set_config(&self, key: Config, value: Option<&str>) -> Result<(), Error> {
|
||||
match key {
|
||||
Config::Selfavatar if value.is_some() => {
|
||||
let rel_path = std::fs::canonicalize(value.unwrap())?;
|
||||
self.sql
|
||||
.set_config(self, key, Some(&rel_path.to_string_lossy()))
|
||||
}
|
||||
Config::InboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_imap_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::SentboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_sentbox_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::MvboxWatch => {
|
||||
let ret = self.sql.set_config(self, key, value);
|
||||
unsafe { dc_interrupt_mvbox_idle(self) };
|
||||
ret
|
||||
}
|
||||
Config::Selfstatus => {
|
||||
let def = unsafe { dc_stock_str(self, 13) };
|
||||
let val = if value.is_none() || value.unwrap() == as_str(def) {
|
||||
None
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
||||
let ret = self.sql.set_config(self, key, val);
|
||||
unsafe { free(def as *mut libc::c_void) };
|
||||
ret
|
||||
}
|
||||
_ => self.sql.set_config(self, key, value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all available configuration keys concated together.
|
||||
fn get_config_keys_string() -> String {
|
||||
let keys = Config::iter().fold(String::new(), |mut acc, key| {
|
||||
acc += key.as_ref();
|
||||
acc += " ";
|
||||
acc
|
||||
});
|
||||
|
||||
format!(" {} ", keys)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
|
||||
#[test]
|
||||
fn test_to_string() {
|
||||
assert_eq!(Config::MailServer.to_string(), "mail_server");
|
||||
assert_eq!(Config::from_str("mail_server"), Ok(Config::MailServer));
|
||||
|
||||
assert_eq!(Config::SysConfigKeys.to_string(), "sys.config_keys");
|
||||
assert_eq!(
|
||||
Config::from_str("sys.config_keys"),
|
||||
Ok(Config::SysConfigKeys)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_prop() {
|
||||
assert_eq!(Config::ImapFolder.get_str("default"), Some("INBOX"));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Constants
|
||||
|
||||
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.1\x00";
|
||||
pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00";
|
||||
|
||||
pub const DC_MOVE_STATE_MOVING: u32 = 3;
|
||||
pub const DC_MOVE_STATE_STAY: u32 = 2;
|
||||
@@ -80,9 +80,9 @@ pub const DC_STATE_OUT_FAILED: usize = 24;
|
||||
pub const DC_STATE_OUT_DELIVERED: usize = 26;
|
||||
pub const DC_STATE_OUT_MDN_RCVD: usize = 28;
|
||||
|
||||
/// approx. max. lenght returned by dc_msg_get_text()
|
||||
/// approx. max. length returned by dc_msg_get_text()
|
||||
pub const DC_MAX_GET_TEXT_LEN: usize = 30000;
|
||||
/// approx. max. lenght returned by dc_get_msg_info()
|
||||
/// approx. max. length returned by dc_get_msg_info()
|
||||
pub const DC_MAX_GET_INFO_LEN: usize = 100000;
|
||||
|
||||
pub const DC_CONTACT_ID_SELF: usize = 1;
|
||||
@@ -119,7 +119,7 @@ pub const DC_MSG_AUDIO: usize = 40;
|
||||
/// A voice message that was directly recorded by the user.
|
||||
/// For all other audio messages, the type #DC_MSG_AUDIO should be used.
|
||||
/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration()
|
||||
/// and retieved via dc_msg_get_file(), dc_msg_get_duration()
|
||||
/// and retrieved via dc_msg_get_file(), dc_msg_get_duration()
|
||||
pub const DC_MSG_VOICE: usize = 41;
|
||||
|
||||
/// Video messages.
|
||||
@@ -173,12 +173,12 @@ pub const DC_LP_SMTP_SOCKET_SSL: usize = 0x20000;
|
||||
/// 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 choosen
|
||||
/// 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);
|
||||
/// if none of these flags are set, the default is choosen
|
||||
/// if none of these flags are set, the default is chosen
|
||||
pub 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 choosen
|
||||
/// if none of these flags are set, the default is chosen
|
||||
pub const DC_LP_SMTP_SOCKET_FLAGS: usize =
|
||||
(DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN);
|
||||
|
||||
@@ -237,7 +237,7 @@ pub enum Event {
|
||||
/// The library-user should report an error to the end-user.
|
||||
/// Passed to the callback given to dc_context_new().
|
||||
///
|
||||
/// As most things are asynchrounous, things may go wrong at any time and the user
|
||||
/// As most things are asynchronous, things may go wrong at any time and the user
|
||||
/// should not be disturbed by a dialog or so. Instead, use a bubble or so.
|
||||
///
|
||||
/// However, for ongoing processes (eg. dc_configure())
|
||||
@@ -265,7 +265,7 @@ pub enum Event {
|
||||
///
|
||||
/// Moreover, if the UI detects that the device is offline,
|
||||
/// it is probably more useful to report this to the user
|
||||
/// instread of the string from data2.
|
||||
/// instead of the string from data2.
|
||||
///
|
||||
/// @param data1 (int) 1=first/new network error, should be reported the user;
|
||||
/// 0=subsequent network error, should be logged only
|
||||
@@ -483,9 +483,20 @@ pub const DC_STR_MSGLOCATIONDISABLED: usize = 65;
|
||||
pub const DC_STR_LOCATION: usize = 66;
|
||||
pub const DC_STR_COUNT: usize = 66;
|
||||
|
||||
pub const DC_JOB_DELETE_MSG_ON_IMAP: i32 = 110;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum KeyType {
|
||||
Public = 0,
|
||||
Private = 1,
|
||||
}
|
||||
|
||||
pub const DC_CMD_GROUPNAME_CHANGED: libc::c_int = 2;
|
||||
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;
|
||||
pub const DC_CMD_LOCATION_STREAMING_ENABLED: libc::c_int = 8;
|
||||
pub const DC_CMD_LOCATION_ONLY: libc::c_int = 9;
|
||||
|
||||
747
src/context.rs
747
src/context.rs
@@ -6,18 +6,16 @@ use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_jobthread::*;
|
||||
use crate::dc_log::*;
|
||||
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_sqlite3::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::*;
|
||||
use crate::key::*;
|
||||
use crate::smtp::*;
|
||||
use crate::sql::Sql;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
@@ -26,7 +24,7 @@ pub struct Context {
|
||||
pub userdata: *mut libc::c_void,
|
||||
pub dbfile: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub blobdir: Arc<RwLock<*mut libc::c_char>>,
|
||||
pub sql: SQLite,
|
||||
pub sql: Sql,
|
||||
pub inbox: Arc<RwLock<Imap>>,
|
||||
pub perform_inbox_jobs_needed: Arc<RwLock<i32>>,
|
||||
pub probe_imap_network: Arc<RwLock<i32>>,
|
||||
@@ -150,37 +148,33 @@ pub fn dc_context_new(
|
||||
cb,
|
||||
os_name: unsafe { dc_strdup_keep_null(os_name) },
|
||||
running_state: Arc::new(RwLock::new(Default::default())),
|
||||
sql: SQLite::new(),
|
||||
sql: Sql::new(),
|
||||
smtp: Arc::new(Mutex::new(Smtp::new())),
|
||||
smtp_state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
oauth2_critical: Arc::new(Mutex::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(unsafe {
|
||||
dc_jobthread_init(
|
||||
b"SENTBOX\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
cb_set_config,
|
||||
cb_precheck_imf,
|
||||
cb_receive_imf,
|
||||
),
|
||||
)
|
||||
})),
|
||||
mvbox_thread: Arc::new(RwLock::new(unsafe {
|
||||
dc_jobthread_init(
|
||||
b"MVBOX\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
cb_set_config,
|
||||
cb_precheck_imf,
|
||||
cb_receive_imf,
|
||||
),
|
||||
)
|
||||
})),
|
||||
sentbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
"SENTBOX",
|
||||
"configured_sentbox_folder",
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
cb_set_config,
|
||||
cb_precheck_imf,
|
||||
cb_receive_imf,
|
||||
),
|
||||
))),
|
||||
mvbox_thread: Arc::new(RwLock::new(dc_jobthread_init(
|
||||
"MVBOX",
|
||||
"configured_mvbox_folder",
|
||||
Imap::new(
|
||||
cb_get_config,
|
||||
cb_set_config,
|
||||
cb_precheck_imf,
|
||||
cb_receive_imf,
|
||||
),
|
||||
))),
|
||||
probe_imap_network: Arc::new(RwLock::new(0)),
|
||||
perform_inbox_jobs_needed: Arc::new(RwLock::new(0)),
|
||||
}
|
||||
@@ -190,7 +184,7 @@ unsafe fn cb_receive_imf(
|
||||
context: &Context,
|
||||
imf_raw_not_terminated: *const libc::c_char,
|
||||
imf_raw_bytes: size_t,
|
||||
server_folder: *const libc::c_char,
|
||||
server_folder: &str,
|
||||
server_uid: uint32_t,
|
||||
flags: uint32_t,
|
||||
) {
|
||||
@@ -207,7 +201,7 @@ unsafe fn cb_receive_imf(
|
||||
unsafe fn cb_precheck_imf(
|
||||
context: &Context,
|
||||
rfc724_mid: *const libc::c_char,
|
||||
server_folder: *const libc::c_char,
|
||||
server_folder: &str,
|
||||
server_uid: uint32_t,
|
||||
) -> libc::c_int {
|
||||
let mut rfc724_mid_exists: libc::c_int = 0i32;
|
||||
@@ -226,23 +220,23 @@ unsafe fn cb_precheck_imf(
|
||||
if *old_server_folder.offset(0isize) as libc::c_int == 0i32
|
||||
&& old_server_uid == 0i32 as libc::c_uint
|
||||
{
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"[move] detected bbc-self %s\x00" as *const u8 as *const libc::c_char,
|
||||
rfc724_mid,
|
||||
0,
|
||||
"[move] detected bbc-self {}",
|
||||
as_str(rfc724_mid),
|
||||
);
|
||||
mark_seen = 1i32
|
||||
} else if strcmp(old_server_folder, server_folder) != 0i32 {
|
||||
dc_log_info(
|
||||
} else if as_str(old_server_folder) != server_folder {
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"[move] detected moved message %s\x00" as *const u8 as *const libc::c_char,
|
||||
rfc724_mid,
|
||||
0,
|
||||
"[move] detected moved message {}",
|
||||
as_str(rfc724_mid),
|
||||
);
|
||||
dc_update_msg_move_state(context, rfc724_mid, DC_MOVE_STATE_STAY);
|
||||
}
|
||||
if strcmp(old_server_folder, server_folder) != 0i32 || old_server_uid != server_uid {
|
||||
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);
|
||||
@@ -261,7 +255,12 @@ unsafe fn cb_precheck_imf(
|
||||
}
|
||||
|
||||
unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) {
|
||||
dc_sqlite3_set_config(context, &context.sql, key, value);
|
||||
let v = if value.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(as_str(value))
|
||||
};
|
||||
context.sql.set_config(context, as_str(key), v).ok();
|
||||
}
|
||||
|
||||
/* *
|
||||
@@ -276,7 +275,11 @@ unsafe fn cb_get_config(
|
||||
key: *const libc::c_char,
|
||||
def: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
dc_sqlite3_get_config(context, &context.sql, key, def)
|
||||
let res = context
|
||||
.sql
|
||||
.get_config(context, as_str(key))
|
||||
.unwrap_or_else(|| to_string(def));
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_context_unref(context: &mut Context) {
|
||||
@@ -284,20 +287,20 @@ pub unsafe fn dc_context_unref(context: &mut Context) {
|
||||
dc_close(context);
|
||||
}
|
||||
|
||||
dc_jobthread_exit(&mut context.sentbox_thread.clone().write().unwrap());
|
||||
dc_jobthread_exit(&mut context.mvbox_thread.clone().write().unwrap());
|
||||
|
||||
free(context.os_name as *mut libc::c_void);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_close(context: &Context) {
|
||||
info!(context, 0, "disconnecting INBOX-watch",);
|
||||
context.inbox.read().unwrap().disconnect(context);
|
||||
info!(context, 0, "disconnecting sentbox-thread",);
|
||||
context
|
||||
.sentbox_thread
|
||||
.read()
|
||||
.unwrap()
|
||||
.imap
|
||||
.disconnect(context);
|
||||
info!(context, 0, "disconnecting mvbox-thread",);
|
||||
context
|
||||
.mvbox_thread
|
||||
.read()
|
||||
@@ -305,6 +308,7 @@ pub unsafe fn dc_close(context: &Context) {
|
||||
.imap
|
||||
.disconnect(context);
|
||||
|
||||
info!(context, 0, "disconnecting SMTP");
|
||||
context.smtp.clone().lock().unwrap().disconnect();
|
||||
|
||||
context.sql.close(context);
|
||||
@@ -362,358 +366,88 @@ pub unsafe fn dc_get_blobdir(context: &Context) -> *mut libc::c_char {
|
||||
dc_strdup(*context.blobdir.clone().read().unwrap())
|
||||
}
|
||||
|
||||
pub unsafe fn dc_set_config(
|
||||
context: &Context,
|
||||
key: *const libc::c_char,
|
||||
value: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut ret = 0;
|
||||
let mut rel_path = 0 as *mut libc::c_char;
|
||||
|
||||
if key.is_null() || 0 == is_settable_config_key(key) {
|
||||
return 0;
|
||||
}
|
||||
if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 && !value.is_null() {
|
||||
rel_path = dc_strdup(value);
|
||||
if !(0 == dc_make_rel_and_copy(context, &mut rel_path)) {
|
||||
ret = dc_sqlite3_set_config(context, &context.sql, key, rel_path)
|
||||
}
|
||||
} else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
|
||||
dc_interrupt_imap_idle(context);
|
||||
} else if strcmp(
|
||||
key,
|
||||
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
|
||||
dc_interrupt_sentbox_idle(context);
|
||||
} else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
|
||||
dc_interrupt_mvbox_idle(context);
|
||||
} else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
let def = dc_stock_str(context, 13);
|
||||
ret = dc_sqlite3_set_config(
|
||||
context,
|
||||
&context.sql,
|
||||
key,
|
||||
if value.is_null() || strcmp(value, def) == 0 {
|
||||
0 as *const libc::c_char
|
||||
} else {
|
||||
value
|
||||
},
|
||||
);
|
||||
free(def as *mut libc::c_void);
|
||||
} else {
|
||||
ret = dc_sqlite3_set_config(context, &context.sql, key, value);
|
||||
}
|
||||
free(rel_path as *mut libc::c_void);
|
||||
ret
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* INI-handling, Information
|
||||
******************************************************************************/
|
||||
|
||||
unsafe fn is_settable_config_key(key: *const libc::c_char) -> libc::c_int {
|
||||
let mut i = 0;
|
||||
while i
|
||||
< (::std::mem::size_of::<[*const libc::c_char; 33]>())
|
||||
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
|
||||
{
|
||||
if strcmp(key, config_keys[i as usize]) == 0 {
|
||||
return 1;
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
static mut config_keys: [*const libc::c_char; 33] = [
|
||||
b"addr\x00" as *const u8 as *const libc::c_char,
|
||||
b"mail_server\x00" as *const u8 as *const libc::c_char,
|
||||
b"mail_user\x00" as *const u8 as *const libc::c_char,
|
||||
b"mail_pw\x00" as *const u8 as *const libc::c_char,
|
||||
b"mail_port\x00" as *const u8 as *const libc::c_char,
|
||||
b"send_server\x00" as *const u8 as *const libc::c_char,
|
||||
b"send_user\x00" as *const u8 as *const libc::c_char,
|
||||
b"send_pw\x00" as *const u8 as *const libc::c_char,
|
||||
b"send_port\x00" as *const u8 as *const libc::c_char,
|
||||
b"server_flags\x00" as *const u8 as *const libc::c_char,
|
||||
b"imap_folder\x00" as *const u8 as *const libc::c_char,
|
||||
b"displayname\x00" as *const u8 as *const libc::c_char,
|
||||
b"selfstatus\x00" as *const u8 as *const libc::c_char,
|
||||
b"selfavatar\x00" as *const u8 as *const libc::c_char,
|
||||
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
b"inbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
b"mvbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
|
||||
b"show_emails\x00" as *const u8 as *const libc::c_char,
|
||||
b"save_mime_headers\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_mail_server\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_mail_user\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_mail_pw\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_mail_port\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_send_server\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_send_user\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_send_pw\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_send_port\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured_server_flags\x00" as *const u8 as *const libc::c_char,
|
||||
b"configured\x00" as *const u8 as *const libc::c_char,
|
||||
];
|
||||
|
||||
pub unsafe fn dc_get_config(context: &Context, key: *const libc::c_char) -> *mut libc::c_char {
|
||||
let mut value = 0 as *mut libc::c_char;
|
||||
if !key.is_null()
|
||||
&& *key.offset(0isize) as libc::c_int == 's' as i32
|
||||
&& *key.offset(1isize) as libc::c_int == 'y' as i32
|
||||
&& *key.offset(2isize) as libc::c_int == 's' as i32
|
||||
&& *key.offset(3isize) as libc::c_int == '.' as i32
|
||||
{
|
||||
return get_sys_config_str(key);
|
||||
}
|
||||
|
||||
if key.is_null() || 0 == is_gettable_config_key(key) {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
|
||||
if strcmp(key, b"selfavatar\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
let rel_path: *mut libc::c_char =
|
||||
dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char);
|
||||
if !rel_path.is_null() {
|
||||
value = dc_get_abs_path(context, rel_path);
|
||||
free(rel_path as *mut libc::c_void);
|
||||
}
|
||||
} else {
|
||||
value = dc_sqlite3_get_config(context, &context.sql, key, 0 as *const libc::c_char)
|
||||
}
|
||||
|
||||
if value.is_null() {
|
||||
if strcmp(key, b"e2ee_enabled\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(key, b"mdns_enabled\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(key, b"imap_folder\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_strdup(b"INBOX\x00" as *const u8 as *const libc::c_char)
|
||||
} else if strcmp(key, b"inbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(
|
||||
key,
|
||||
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(key, b"mvbox_watch\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(key, b"mvbox_move\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 1)
|
||||
} else if strcmp(key, b"show_emails\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_mprintf(b"%i\x00" as *const u8 as *const libc::c_char, 0)
|
||||
} else if strcmp(key, b"selfstatus\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
value = dc_stock_str(context, 13)
|
||||
} else {
|
||||
value = dc_mprintf(b"\x00" as *const u8 as *const libc::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
|
||||
unsafe fn is_gettable_config_key(key: *const libc::c_char) -> libc::c_int {
|
||||
let mut i = 0;
|
||||
while i
|
||||
< (::std::mem::size_of::<[*const libc::c_char; 3]>())
|
||||
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
|
||||
{
|
||||
if strcmp(key, sys_config_keys[i as usize]) == 0 {
|
||||
return 1;
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
is_settable_config_key(key)
|
||||
}
|
||||
|
||||
// deprecated
|
||||
static mut sys_config_keys: [*const libc::c_char; 3] = [
|
||||
b"sys.version\x00" as *const u8 as *const libc::c_char,
|
||||
b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char,
|
||||
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
|
||||
];
|
||||
|
||||
unsafe fn get_sys_config_str(key: *const libc::c_char) -> *mut libc::c_char {
|
||||
if strcmp(key, b"sys.version\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
return dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char);
|
||||
} else if strcmp(
|
||||
key,
|
||||
b"sys.msgsize_max_recommended\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
return dc_mprintf(
|
||||
b"%i\x00" as *const u8 as *const libc::c_char,
|
||||
24 * 1024 * 1024 / 4 * 3,
|
||||
);
|
||||
} else if strcmp(
|
||||
key,
|
||||
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0
|
||||
{
|
||||
return get_config_keys_str();
|
||||
} else {
|
||||
return dc_strdup(0 as *const libc::c_char);
|
||||
};
|
||||
}
|
||||
|
||||
unsafe fn get_config_keys_str() -> *mut libc::c_char {
|
||||
let mut ret = String::new();
|
||||
let mut i = 0;
|
||||
while i
|
||||
< (::std::mem::size_of::<[*const libc::c_char; 33]>())
|
||||
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
|
||||
{
|
||||
if !ret.is_empty() {
|
||||
ret += " ";
|
||||
}
|
||||
ret += &to_string(config_keys[i as usize]);
|
||||
i += 1
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i
|
||||
< (::std::mem::size_of::<[*const libc::c_char; 3]>())
|
||||
.wrapping_div(::std::mem::size_of::<*mut libc::c_char>())
|
||||
{
|
||||
if !ret.is_empty() {
|
||||
ret += " ";
|
||||
}
|
||||
ret += &to_string(sys_config_keys[i as usize]);
|
||||
i += 1
|
||||
}
|
||||
|
||||
strdup(to_cstring(ret).as_ptr())
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
let unset = "0";
|
||||
let l = dc_loginparam_new();
|
||||
let l2 = dc_loginparam_new();
|
||||
dc_loginparam_read(
|
||||
context,
|
||||
l,
|
||||
&context.sql,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_loginparam_read(
|
||||
context,
|
||||
l2,
|
||||
&context.sql,
|
||||
b"configured_\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let displayname = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"displayname\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const 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 real_msgs = dc_get_real_msg_cnt(context) as usize;
|
||||
let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize;
|
||||
let contacts = dc_get_real_contact_cnt(context) as usize;
|
||||
let is_configured = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured\x00" as *const u8 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
let dbversion = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"dbversion\x00" as *const u8 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
let e2ee_enabled = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let mdns_enabled = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let mut stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT COUNT(*) FROM keypairs;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sqlite3_step(stmt);
|
||||
let prv_key_cnt = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT COUNT(*) FROM acpeerstates;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sqlite3_step(stmt);
|
||||
let pub_key_cnt = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
let fingerprint_str =
|
||||
if let Some(key) = Key::from_self_public(context, (*l2).addr, &context.sql) {
|
||||
key.fingerprint()
|
||||
} else {
|
||||
"<Not yet calculated>".into()
|
||||
};
|
||||
let is_configured = context
|
||||
.sql
|
||||
.get_config_int(context, "configured")
|
||||
.unwrap_or_default();
|
||||
let dbversion = context
|
||||
.sql
|
||||
.get_config_int(context, "dbversion")
|
||||
.unwrap_or_default();
|
||||
let e2ee_enabled = context
|
||||
.sql
|
||||
.get_config_int(context, "e2ee_enabled")
|
||||
.unwrap_or_else(|| 1);
|
||||
let mdns_enabled = context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1);
|
||||
|
||||
let l_readable_str = dc_loginparam_get_readable(l);
|
||||
let l2_readable_str = dc_loginparam_get_readable(l2);
|
||||
let inbox_watch = dc_sqlite3_get_config_int(
|
||||
let prv_key_cnt: Option<isize> = context.sql.query_row_col(
|
||||
context,
|
||||
&context.sql,
|
||||
b"inbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let sentbox_watch = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"sentbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let mvbox_watch = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"mvbox_watch\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let mvbox_move = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
);
|
||||
let folders_configured = dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"folders_configured\x00" as *const u8 as *const libc::c_char,
|
||||
"SELECT COUNT(*) FROM keypairs;",
|
||||
rusqlite::NO_PARAMS,
|
||||
0,
|
||||
);
|
||||
let configured_sentbox_folder = dc_sqlite3_get_config(
|
||||
|
||||
let pub_key_cnt: Option<isize> = context.sql.query_row_col(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
b"<unset>\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let configured_mvbox_folder = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
b"<unset>\x00" as *const u8 as *const libc::c_char,
|
||||
"SELECT COUNT(*) FROM acpeerstates;",
|
||||
rusqlite::NO_PARAMS,
|
||||
0,
|
||||
);
|
||||
|
||||
let fingerprint_str = if let Some(key) = Key::from_self_public(context, &l2.addr, &context.sql)
|
||||
{
|
||||
key.fingerprint()
|
||||
} else {
|
||||
"<Not yet calculated>".into()
|
||||
};
|
||||
|
||||
let l_readable_str = dc_loginparam_get_readable(&l);
|
||||
let l2_readable_str = dc_loginparam_get_readable(&l2);
|
||||
let inbox_watch = context
|
||||
.sql
|
||||
.get_config_int(context, "inbox_watch")
|
||||
.unwrap_or_else(|| 1);
|
||||
let sentbox_watch = context
|
||||
.sql
|
||||
.get_config_int(context, "sentbox_watch")
|
||||
.unwrap_or_else(|| 1);
|
||||
let mvbox_watch = context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_watch")
|
||||
.unwrap_or_else(|| 1);
|
||||
let mvbox_move = context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1);
|
||||
let folders_configured = context
|
||||
.sql
|
||||
.get_config_int(context, "folders_configured")
|
||||
.unwrap_or_default();
|
||||
let configured_sentbox_folder = context
|
||||
.sql
|
||||
.get_config(context, "configured_sentbox_folder")
|
||||
.unwrap_or_else(|| "<unset>".to_string());
|
||||
let configured_mvbox_folder = context
|
||||
.sql
|
||||
.get_config(context, "configured_mvbox_folder")
|
||||
.unwrap_or_else(|| "<unset>".to_string());
|
||||
|
||||
let res = format!(
|
||||
"deltachat_core_version=v{}\n\
|
||||
sqlite_version={}\n\
|
||||
@@ -744,7 +478,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
fingerprint={}\n\
|
||||
level=awesome\n",
|
||||
as_str(DC_VERSION_STR as *const u8 as *const _),
|
||||
as_str(libsqlite3_sys::SQLITE_VERSION as *const u8 as *const libc::c_char),
|
||||
rusqlite::version(),
|
||||
sqlite3_threadsafe(),
|
||||
// arch
|
||||
(::std::mem::size_of::<*mut libc::c_void>()).wrapping_mul(8),
|
||||
@@ -763,192 +497,131 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char {
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !displayname.is_null() {
|
||||
as_str(displayname)
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
displayname.unwrap_or_else(|| unset.into()),
|
||||
is_configured,
|
||||
as_str(l_readable_str),
|
||||
as_str(l2_readable_str),
|
||||
l_readable_str,
|
||||
l2_readable_str,
|
||||
inbox_watch,
|
||||
sentbox_watch,
|
||||
mvbox_watch,
|
||||
mvbox_move,
|
||||
folders_configured,
|
||||
as_str(configured_sentbox_folder),
|
||||
as_str(configured_mvbox_folder),
|
||||
configured_sentbox_folder,
|
||||
configured_mvbox_folder,
|
||||
mdns_enabled,
|
||||
e2ee_enabled,
|
||||
prv_key_cnt,
|
||||
pub_key_cnt,
|
||||
prv_key_cnt.unwrap_or_default(),
|
||||
pub_key_cnt.unwrap_or_default(),
|
||||
fingerprint_str,
|
||||
);
|
||||
|
||||
dc_loginparam_unref(l);
|
||||
dc_loginparam_unref(l2);
|
||||
free(displayname as *mut libc::c_void);
|
||||
free(l_readable_str as *mut libc::c_void);
|
||||
free(l2_readable_str as *mut libc::c_void);
|
||||
free(configured_sentbox_folder as *mut libc::c_void);
|
||||
free(configured_mvbox_folder as *mut libc::c_void);
|
||||
|
||||
strdup(to_cstring(res).as_ptr())
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_version_str() -> *mut libc::c_char {
|
||||
dc_strdup(DC_VERSION_STR as *const u8 as *const libc::c_char)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
|
||||
pub fn dc_get_fresh_msgs(context: &Context) -> *mut dc_array_t {
|
||||
let show_deaddrop = 0;
|
||||
let ret = dc_array_new(128 as size_t);
|
||||
let mut stmt = 0 as *mut sqlite3_stmt;
|
||||
if !ret.is_null() {
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
|
||||
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
|
||||
AND m.hidden=0 \
|
||||
AND m.chat_id>? \
|
||||
AND ct.blocked=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1, 10);
|
||||
sqlite3_bind_int(stmt, 2, 9);
|
||||
sqlite3_bind_int(stmt, 3, if 0 != show_deaddrop { 2 } else { 0 });
|
||||
while sqlite3_step(stmt) == 100 {
|
||||
dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
ret
|
||||
|
||||
context
|
||||
.sql
|
||||
.query_map(
|
||||
"SELECT m.id FROM msgs m LEFT JOIN contacts ct \
|
||||
ON m.from_id=ct.id LEFT JOIN chats c ON m.chat_id=c.id WHERE m.state=? \
|
||||
AND m.hidden=0 \
|
||||
AND m.chat_id>? \
|
||||
AND ct.blocked=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) ORDER BY m.timestamp DESC,m.id DESC;",
|
||||
&[10, 9, if 0 != show_deaddrop { 2 } else { 0 }],
|
||||
|row| row.get(0),
|
||||
|rows| {
|
||||
let ret = unsafe { dc_array_new(128 as size_t) };
|
||||
|
||||
for row in rows {
|
||||
let id = row?;
|
||||
unsafe { dc_array_add_id(ret, id) };
|
||||
}
|
||||
Ok(ret)
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_search_msgs(
|
||||
pub fn dc_search_msgs(
|
||||
context: &Context,
|
||||
chat_id: uint32_t,
|
||||
query: *const libc::c_char,
|
||||
) -> *mut dc_array_t {
|
||||
let mut success = 0;
|
||||
let ret = dc_array_new(100 as size_t);
|
||||
let mut strLikeInText = 0 as *mut libc::c_char;
|
||||
let mut strLikeBeg = 0 as *mut libc::c_char;
|
||||
let mut real_query = 0 as *mut libc::c_char;
|
||||
let mut stmt = 0 as *mut sqlite3_stmt;
|
||||
|
||||
if !(ret.is_null() || query.is_null()) {
|
||||
real_query = dc_strdup(query);
|
||||
dc_trim(real_query);
|
||||
if *real_query.offset(0isize) as libc::c_int == 0 {
|
||||
success = 1
|
||||
} else {
|
||||
strLikeInText = dc_mprintf(
|
||||
b"%%%s%%\x00" as *const u8 as *const libc::c_char,
|
||||
real_query,
|
||||
);
|
||||
strLikeBeg = dc_mprintf(b"%s%%\x00" as *const u8 as *const libc::c_char, real_query);
|
||||
if 0 != chat_id {
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
|
||||
AND m.hidden=0 \
|
||||
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;\x00"
|
||||
as *const u8 as *const libc::c_char
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1, chat_id as libc::c_int);
|
||||
sqlite3_bind_text(stmt, 2, strLikeInText, -1, None);
|
||||
sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None);
|
||||
} else {
|
||||
let show_deaddrop = 0;
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
|
||||
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) \
|
||||
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1, if 0 != show_deaddrop { 2 } else { 0 });
|
||||
sqlite3_bind_text(stmt, 2, strLikeInText, -1, None);
|
||||
sqlite3_bind_text(stmt, 3, strLikeBeg, -1, None);
|
||||
}
|
||||
while sqlite3_step(stmt) == 100 {
|
||||
dc_array_add_id(ret, sqlite3_column_int(stmt, 0) as uint32_t);
|
||||
}
|
||||
success = 1
|
||||
}
|
||||
if query.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
free(strLikeInText as *mut libc::c_void);
|
||||
free(strLikeBeg as *mut libc::c_void);
|
||||
free(real_query as *mut libc::c_void);
|
||||
sqlite3_finalize(stmt);
|
||||
let real_query = to_string(query).trim().to_string();
|
||||
if real_query.is_empty() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
let strLikeInText = format!("%{}%", &real_query);
|
||||
let strLikeBeg = format!("{}%", &real_query);
|
||||
|
||||
if 0 != success {
|
||||
ret
|
||||
let query = if 0 != chat_id {
|
||||
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id WHERE m.chat_id=? \
|
||||
AND m.hidden=0 \
|
||||
AND ct.blocked=0 AND (txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp,m.id;"
|
||||
} else {
|
||||
if !ret.is_null() {
|
||||
dc_array_unref(ret);
|
||||
}
|
||||
0 as *mut dc_array_t
|
||||
"SELECT m.id, m.timestamp FROM msgs m LEFT JOIN contacts ct ON m.from_id=ct.id \
|
||||
LEFT JOIN chats c ON m.chat_id=c.id WHERE m.chat_id>9 AND m.hidden=0 \
|
||||
AND (c.blocked=0 OR c.blocked=?) \
|
||||
AND ct.blocked=0 AND (m.txt LIKE ? OR ct.name LIKE ?) ORDER BY m.timestamp DESC,m.id DESC;"
|
||||
};
|
||||
|
||||
let ret = unsafe { dc_array_new(100 as size_t) };
|
||||
|
||||
let success = context
|
||||
.sql
|
||||
.query_map(
|
||||
query,
|
||||
params![chat_id as libc::c_int, &strLikeInText, &strLikeBeg],
|
||||
|row| row.get::<_, i32>(0),
|
||||
|rows| {
|
||||
for id in rows {
|
||||
unsafe { dc_array_add_id(ret, id? as u32) };
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.is_ok();
|
||||
|
||||
if success {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if !ret.is_null() {
|
||||
unsafe { dc_array_unref(ret) };
|
||||
}
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
|
||||
pub fn dc_is_inbox(_context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
folder_name.as_ref() == "INBOX"
|
||||
}
|
||||
|
||||
pub fn dc_is_sentbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
let sentbox_name = context.sql.get_config(context, "configured_sentbox_folder");
|
||||
if let Some(name) = sentbox_name {
|
||||
name == folder_name.as_ref()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_is_inbox(_context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
|
||||
let mut is_inbox = 0;
|
||||
if !folder_name.is_null() {
|
||||
is_inbox = if strcasecmp(
|
||||
b"INBOX\x00" as *const u8 as *const libc::c_char,
|
||||
folder_name,
|
||||
) == 0
|
||||
{
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
is_inbox
|
||||
}
|
||||
pub fn dc_is_mvbox(context: &Context, folder_name: impl AsRef<str>) -> bool {
|
||||
let mvbox_name = context.sql.get_config(context, "configured_mvbox_folder");
|
||||
|
||||
pub unsafe fn dc_is_sentbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
|
||||
let sentbox_name = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
let mut is_sentbox = 0;
|
||||
if !sentbox_name.is_null() && !folder_name.is_null() {
|
||||
is_sentbox = if strcasecmp(sentbox_name, folder_name) == 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
if let Some(name) = mvbox_name {
|
||||
name == folder_name.as_ref()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
free(sentbox_name as *mut libc::c_void);
|
||||
is_sentbox
|
||||
}
|
||||
|
||||
pub unsafe fn dc_is_mvbox(context: &Context, folder_name: *const libc::c_char) -> libc::c_int {
|
||||
let mvbox_name = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
let mut is_mvbox = 0;
|
||||
if !mvbox_name.is_null() && !folder_name.is_null() {
|
||||
is_mvbox = if strcasecmp(mvbox_name, folder_name) == 0 {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
free(mvbox_name as *mut libc::c_void);
|
||||
is_mvbox
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
const DC_ARRAY_MAGIC: uint32_t = 0x000a11aa;
|
||||
|
||||
/* * the structure behind dc_array_t */
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
@@ -23,7 +25,7 @@ pub struct dc_array_t {
|
||||
* To free an array object, use dc_array_unref().
|
||||
*/
|
||||
pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return;
|
||||
}
|
||||
if (*array).type_0 == 1i32 {
|
||||
@@ -35,7 +37,7 @@ pub unsafe fn dc_array_unref(mut array: *mut dc_array_t) {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return;
|
||||
}
|
||||
let mut i: size_t = 0i32 as size_t;
|
||||
@@ -53,7 +55,7 @@ pub unsafe fn dc_array_free_ptr(array: *mut dc_array_t) {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_add_uint(mut array: *mut dc_array_t, item: uintptr_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return;
|
||||
}
|
||||
if (*array).count == (*array).allocated {
|
||||
@@ -78,21 +80,21 @@ pub unsafe fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_cnt(array: *const dc_array_t) -> size_t {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return 0i32 as size_t;
|
||||
}
|
||||
(*array).count
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_uint(array: *const dc_array_t, index: size_t) -> uintptr_t {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0i32 as uintptr_t;
|
||||
}
|
||||
*(*array).array.offset(index as isize)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0i32 as uint32_t;
|
||||
}
|
||||
if (*array).type_0 == 1i32 {
|
||||
@@ -102,7 +104,7 @@ pub unsafe fn dc_array_get_id(array: *const dc_array_t, index: size_t) -> uint32
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut libc::c_void {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || index >= (*array).count {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || index >= (*array).count {
|
||||
return 0 as *mut libc::c_void;
|
||||
}
|
||||
*(*array).array.offset(index as isize) as *mut libc::c_void
|
||||
@@ -110,7 +112,7 @@ pub unsafe fn dc_array_get_ptr(array: *const dc_array_t, index: size_t) -> *mut
|
||||
|
||||
pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -122,7 +124,7 @@ pub unsafe fn dc_array_get_latitude(array: *const dc_array_t, index: size_t) ->
|
||||
|
||||
pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -134,7 +136,7 @@ pub unsafe fn dc_array_get_longitude(array: *const dc_array_t, index: size_t) ->
|
||||
|
||||
pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) -> libc::c_double {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -146,7 +148,7 @@ pub unsafe fn dc_array_get_accuracy(array: *const dc_array_t, index: size_t) ->
|
||||
|
||||
pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) -> i64 {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -158,7 +160,7 @@ pub unsafe fn dc_array_get_timestamp(array: *const dc_array_t, index: size_t) ->
|
||||
|
||||
pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -170,7 +172,7 @@ pub unsafe fn dc_array_get_chat_id(array: *const dc_array_t, index: size_t) -> u
|
||||
|
||||
pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -182,7 +184,7 @@ pub unsafe fn dc_array_get_contact_id(array: *const dc_array_t, index: size_t) -
|
||||
|
||||
pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> uint32_t {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -194,7 +196,7 @@ pub unsafe fn dc_array_get_msg_id(array: *const dc_array_t, index: size_t) -> ui
|
||||
|
||||
pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *mut libc::c_char {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -216,7 +218,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m
|
||||
*/
|
||||
pub unsafe fn dc_array_is_independent(array: *const dc_array_t, index: size_t) -> libc::c_int {
|
||||
if array.is_null()
|
||||
|| (*array).magic != 0xa11aai32 as libc::c_uint
|
||||
|| (*array).magic != DC_ARRAY_MAGIC
|
||||
|| index >= (*array).count
|
||||
|| (*array).type_0 != 1i32
|
||||
|| *(*array).array.offset(index as isize) == 0
|
||||
@@ -232,7 +234,7 @@ pub unsafe fn dc_array_search_id(
|
||||
needle: uint32_t,
|
||||
ret_index: *mut size_t,
|
||||
) -> bool {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return false;
|
||||
}
|
||||
let data: *mut uintptr_t = (*array).array;
|
||||
@@ -251,7 +253,7 @@ 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() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return 0 as *const uintptr_t;
|
||||
}
|
||||
(*array).array
|
||||
@@ -266,7 +268,7 @@ pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut
|
||||
array = calloc(1, ::std::mem::size_of::<dc_array_t>()) as *mut dc_array_t;
|
||||
assert!(!array.is_null());
|
||||
|
||||
(*array).magic = 0xa11aai32 as uint32_t;
|
||||
(*array).magic = DC_ARRAY_MAGIC;
|
||||
(*array).count = 0i32 as size_t;
|
||||
(*array).allocated = if initsize < 1 { 1 } else { initsize };
|
||||
(*array).type_0 = type_0;
|
||||
@@ -282,7 +284,7 @@ pub unsafe fn dc_array_new_typed(type_0: libc::c_int, initsize: size_t) -> *mut
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return;
|
||||
}
|
||||
(*array).count = 0i32 as size_t;
|
||||
@@ -290,7 +292,7 @@ pub unsafe fn dc_array_empty(mut array: *mut dc_array_t) {
|
||||
|
||||
pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
|
||||
let mut ret: *mut dc_array_t;
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC {
|
||||
return 0 as *mut dc_array_t;
|
||||
}
|
||||
ret = dc_array_new((*array).allocated);
|
||||
@@ -306,7 +308,7 @@ pub unsafe fn dc_array_duplicate(array: *const dc_array_t) -> *mut dc_array_t {
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_sort_ids(array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || (*array).count <= 1 {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
|
||||
return;
|
||||
}
|
||||
qsort(
|
||||
@@ -330,7 +332,7 @@ unsafe extern "C" fn cmp_intptr_t(p1: *const libc::c_void, p2: *const libc::c_vo
|
||||
}
|
||||
|
||||
pub unsafe fn dc_array_sort_strings(array: *mut dc_array_t) {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || (*array).count <= 1 {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || (*array).count <= 1 {
|
||||
return;
|
||||
}
|
||||
qsort(
|
||||
@@ -355,7 +357,7 @@ pub unsafe fn dc_array_get_string(
|
||||
array: *const dc_array_t,
|
||||
sep: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
if array.is_null() || (*array).magic != 0xa11aai32 as libc::c_uint || sep.is_null() {
|
||||
if array.is_null() || (*array).magic != DC_ARRAY_MAGIC || sep.is_null() {
|
||||
return dc_strdup(b"\x00" as *const u8 as *const libc::c_char);
|
||||
}
|
||||
let cnt = (*array).count as usize;
|
||||
@@ -374,7 +376,7 @@ pub unsafe fn dc_array_get_string(
|
||||
}
|
||||
res
|
||||
});
|
||||
strdup(to_cstring(res).as_ptr())
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
/// return comma-separated value-string from integer array
|
||||
@@ -396,7 +398,7 @@ pub unsafe fn dc_arr_to_string(arr: *const uint32_t, cnt: libc::c_int) -> *mut l
|
||||
res
|
||||
},
|
||||
);
|
||||
strdup(to_cstring(res).as_ptr())
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -410,22 +412,17 @@ mod tests {
|
||||
let arr = dc_array_new(7 as size_t);
|
||||
assert_eq!(dc_array_get_cnt(arr), 0);
|
||||
|
||||
let mut i: libc::c_int = 0;
|
||||
while i < 1000 {
|
||||
for i in 0..1000 {
|
||||
dc_array_add_id(arr, (i + 2) as uint32_t);
|
||||
i += 1
|
||||
}
|
||||
|
||||
assert_eq!(dc_array_get_cnt(arr), 1000);
|
||||
|
||||
i = 0;
|
||||
|
||||
while i < 1000i32 {
|
||||
for i in 0..1000 {
|
||||
assert_eq!(
|
||||
dc_array_get_id(arr, i as size_t),
|
||||
(i + 1i32 * 2i32) as libc::c_uint
|
||||
);
|
||||
i += 1
|
||||
}
|
||||
|
||||
assert_eq!(dc_array_get_id(arr, -1i32 as size_t), 0);
|
||||
|
||||
2394
src/dc_chat.rs
2394
src/dc_chat.rs
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,6 @@ use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
@@ -27,19 +26,14 @@ pub unsafe fn dc_get_chatlist<'a>(
|
||||
query_str: *const libc::c_char,
|
||||
query_id: uint32_t,
|
||||
) -> *mut dc_chatlist_t<'a> {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let obj: *mut dc_chatlist_t = dc_chatlist_new(context);
|
||||
let obj = dc_chatlist_new(context);
|
||||
|
||||
if !(0 == dc_chatlist_load_from_db(obj, listflags, query_str, query_id)) {
|
||||
success = 1i32
|
||||
if 0 != dc_chatlist_load_from_db(obj, listflags, query_str, query_id) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if 0 != success {
|
||||
return obj;
|
||||
} else {
|
||||
dc_chatlist_unref(obj);
|
||||
return 0 as *mut dc_chatlist_t;
|
||||
};
|
||||
dc_chatlist_unref(obj);
|
||||
return 0 as *mut dc_chatlist_t;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,146 +114,173 @@ unsafe fn dc_chatlist_load_from_db(
|
||||
mut chatlist: *mut dc_chatlist_t,
|
||||
listflags: libc::c_int,
|
||||
query__: *const libc::c_char,
|
||||
query_contact_id: uint32_t,
|
||||
query_contact_id: u32,
|
||||
) -> libc::c_int {
|
||||
let current_block: u64;
|
||||
//clock_t start = clock();
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut add_archived_link_item: libc::c_int = 0i32;
|
||||
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
|
||||
let mut strLikeCmd: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut query: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if !(chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32) {
|
||||
dc_chatlist_empty(chatlist);
|
||||
// select with left join and minimum:
|
||||
// - the inner select must use `hidden` and _not_ `m.hidden`
|
||||
// which would refer the outer select and take a lot of time
|
||||
// - `GROUP BY` is needed several messages may have the same timestamp
|
||||
// - the list starts with the newest chats
|
||||
// nb: the query currently shows messages from blocked contacts in groups.
|
||||
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and tg do the same)
|
||||
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
if 0 != query_contact_id {
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
(*chatlist).context,
|
||||
&(*chatlist).context.sql,
|
||||
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1i32, query_contact_id as libc::c_int);
|
||||
current_block = 3437258052017859086;
|
||||
} else if 0 != listflags & 0x1i32 {
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
(*chatlist).context,
|
||||
&(*chatlist).context.sql,
|
||||
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=1 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
current_block = 3437258052017859086;
|
||||
} else if query__.is_null() {
|
||||
if 0 == listflags & 0x2i32 {
|
||||
let last_deaddrop_fresh_msg_id: uint32_t =
|
||||
get_last_deaddrop_fresh_msg((*chatlist).context);
|
||||
if last_deaddrop_fresh_msg_id > 0i32 as libc::c_uint {
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1i32 as uint32_t);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
|
||||
}
|
||||
add_archived_link_item = 1i32
|
||||
}
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
(*chatlist).context,
|
||||
&(*chatlist).context.sql,
|
||||
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.archived=0 GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
current_block = 3437258052017859086;
|
||||
} else {
|
||||
query = dc_strdup(query__);
|
||||
dc_trim(query);
|
||||
if *query.offset(0isize) as libc::c_int == 0i32 {
|
||||
success = 1i32;
|
||||
current_block = 15179736777190528364;
|
||||
} else {
|
||||
strLikeCmd = dc_mprintf(b"%%%s%%\x00" as *const u8 as *const libc::c_char, query);
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
(*chatlist).context,
|
||||
&(*chatlist).context.sql,
|
||||
b"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id AND m.timestamp=( SELECT MAX(timestamp) FROM msgs WHERE chat_id=c.id AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 AND c.blocked=0 AND c.name LIKE ? GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;\x00"
|
||||
as *const u8 as
|
||||
*const libc::c_char);
|
||||
sqlite3_bind_text(stmt, 1i32, strLikeCmd, -1i32, None);
|
||||
current_block = 3437258052017859086;
|
||||
}
|
||||
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 {
|
||||
return 0;
|
||||
}
|
||||
dc_chatlist_empty(chatlist);
|
||||
|
||||
let mut add_archived_link_item = 0;
|
||||
|
||||
// select with left join and minimum:
|
||||
// - the inner select must use `hidden` and _not_ `m.hidden`
|
||||
// which would refer the outer select and take a lot of time
|
||||
// - `GROUP BY` is needed several messages may have the same timestamp
|
||||
// - the list starts with the newest chats
|
||||
// nb: the query currently shows messages from blocked contacts in groups.
|
||||
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and tg do the same)
|
||||
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
|
||||
let process_row = |row: &rusqlite::Row| {
|
||||
let chat_id: i32 = row.get(0)?;
|
||||
// TODO: verify that it is okay for this to be Null
|
||||
let msg_id: i32 = row.get(1).unwrap_or_default();
|
||||
|
||||
Ok((chat_id, msg_id))
|
||||
};
|
||||
|
||||
let process_rows = |rows: rusqlite::MappedRows<_>| {
|
||||
for row in rows {
|
||||
let (id1, id2) = row?;
|
||||
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, id1 as u32);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, id2 as u32);
|
||||
}
|
||||
match current_block {
|
||||
15179736777190528364 => {}
|
||||
_ => {
|
||||
while sqlite3_step(stmt) == 100i32 {
|
||||
dc_array_add_id(
|
||||
(*chatlist).chatNlastmsg_ids,
|
||||
sqlite3_column_int(stmt, 0i32) as uint32_t,
|
||||
);
|
||||
dc_array_add_id(
|
||||
(*chatlist).chatNlastmsg_ids,
|
||||
sqlite3_column_int(stmt, 1i32) as uint32_t,
|
||||
);
|
||||
}
|
||||
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0i32 {
|
||||
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0
|
||||
&& 0 != listflags & 0x4i32
|
||||
{
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7i32 as uint32_t);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t);
|
||||
}
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6i32 as uint32_t);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0i32 as uint32_t);
|
||||
}
|
||||
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids).wrapping_div(2);
|
||||
success = 1i32
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// nb: the query currently shows messages from blocked contacts in groups.
|
||||
// however, for normal-groups, this is okay as the message is also returned by dc_get_chat_msgs()
|
||||
// (otherwise it would be hard to follow conversations, wa and tg do the same)
|
||||
// for the deaddrop, however, they should really be hidden, however, _currently_ the deaddrop is not
|
||||
// shown at all permanent in the chatlist.
|
||||
|
||||
let success = if query_contact_id != 0 {
|
||||
// show chats shared with a given contact
|
||||
(*chatlist).context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?) \
|
||||
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
params![query_contact_id as i32],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
} else if 0 != listflags & 0x1 {
|
||||
// show archived chats
|
||||
(*chatlist).context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.archived=1 GROUP BY c.id \
|
||||
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
params![],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
} else if query__.is_null() {
|
||||
// show normal chatlist
|
||||
if 0 == listflags & 0x2 {
|
||||
let last_deaddrop_fresh_msg_id = get_last_deaddrop_fresh_msg((*chatlist).context);
|
||||
if last_deaddrop_fresh_msg_id > 0 {
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 1);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, last_deaddrop_fresh_msg_id);
|
||||
}
|
||||
add_archived_link_item = 1;
|
||||
}
|
||||
(*chatlist).context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c \
|
||||
LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.archived=0 \
|
||||
GROUP BY c.id \
|
||||
ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
params![],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
} else {
|
||||
let query = to_string(query__).trim().to_string();
|
||||
if query.is_empty() {
|
||||
return 1;
|
||||
} else {
|
||||
let strLikeCmd = format!("%{}%", query);
|
||||
(*chatlist).context.sql.query_map(
|
||||
"SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \
|
||||
ON c.id=m.chat_id \
|
||||
AND m.timestamp=( SELECT MAX(timestamp) \
|
||||
FROM msgs WHERE chat_id=c.id \
|
||||
AND (hidden=0 OR (hidden=1 AND state=19))) WHERE c.id>9 \
|
||||
AND c.blocked=0 AND c.name LIKE ? \
|
||||
GROUP BY c.id ORDER BY IFNULL(m.timestamp,0) DESC, m.id DESC;",
|
||||
params![strLikeCmd],
|
||||
process_row,
|
||||
process_rows,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if 0 != add_archived_link_item && dc_get_archived_cnt((*chatlist).context) > 0 {
|
||||
if dc_array_get_cnt((*chatlist).chatNlastmsg_ids) == 0 && 0 != listflags & 0x4 {
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 7);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
|
||||
}
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 6);
|
||||
dc_array_add_id((*chatlist).chatNlastmsg_ids, 0);
|
||||
}
|
||||
(*chatlist).cnt = dc_array_get_cnt((*chatlist).chatNlastmsg_ids) / 2;
|
||||
|
||||
match success {
|
||||
Ok(_) => 1,
|
||||
Err(err) => {
|
||||
error!(
|
||||
(*chatlist).context,
|
||||
0, "chatlist: failed to load from database: {:?}", err
|
||||
);
|
||||
0
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
free(query as *mut libc::c_void);
|
||||
free(strLikeCmd as *mut libc::c_void);
|
||||
success
|
||||
}
|
||||
|
||||
// Context functions to work with chatlist
|
||||
pub unsafe fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
|
||||
let mut ret: libc::c_int = 0i32;
|
||||
let stmt: *mut sqlite3_stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
if sqlite3_step(stmt) == 100i32 {
|
||||
ret = sqlite3_column_int(stmt, 0i32)
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
ret
|
||||
pub fn dc_get_archived_cnt(context: &Context) -> libc::c_int {
|
||||
context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM chats WHERE blocked=0 AND archived=1;",
|
||||
params![],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
unsafe fn get_last_deaddrop_fresh_msg(context: &Context) -> uint32_t {
|
||||
let mut ret: uint32_t = 0i32 as uint32_t;
|
||||
let stmt: *mut sqlite3_stmt;
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
fn get_last_deaddrop_fresh_msg(context: &Context) -> u32 {
|
||||
// we have an index over the state-column, this should be sufficient as there are typically only few fresh messages
|
||||
context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id WHERE m.state=10 AND m.hidden=0 AND c.blocked=2 ORDER BY m.timestamp DESC, m.id DESC;\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
/* we have an index over the state-column, this should be sufficient as there are typically only few fresh messages */
|
||||
if !(sqlite3_step(stmt) != 100i32) {
|
||||
ret = sqlite3_column_int(stmt, 0i32) as uint32_t
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
ret
|
||||
"SELECT m.id FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id \
|
||||
WHERE m.state=10 \
|
||||
AND m.hidden=0 \
|
||||
AND c.blocked=2 \
|
||||
ORDER BY m.timestamp DESC, m.id DESC;",
|
||||
params![],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_chatlist_get_cnt(chatlist: *const dc_chatlist_t) -> size_t {
|
||||
@@ -297,7 +318,7 @@ pub unsafe fn dc_chatlist_get_msg_id(chatlist: *const dc_chatlist_t, index: size
|
||||
pub unsafe fn dc_chatlist_get_summary<'a>(
|
||||
chatlist: *const dc_chatlist_t<'a>,
|
||||
index: size_t,
|
||||
mut chat: *mut dc_chat_t<'a>,
|
||||
mut chat: *mut Chat<'a>,
|
||||
) -> *mut dc_lot_t {
|
||||
let current_block: u64;
|
||||
/* The summary is created by the chat, not by the last message.
|
||||
@@ -310,7 +331,7 @@ pub unsafe fn dc_chatlist_get_summary<'a>(
|
||||
let lastmsg_id: uint32_t;
|
||||
let mut lastmsg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let mut lastcontact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
let mut chat_to_delete: *mut dc_chat_t = 0 as *mut dc_chat_t;
|
||||
let mut chat_to_delete: *mut Chat = 0 as *mut Chat;
|
||||
if chatlist.is_null() || (*chatlist).magic != 0xc4a71157u32 || index >= (*chatlist).cnt {
|
||||
(*ret).text2 = dc_strdup(b"ErrBadChatlistIndex\x00" as *const u8 as *const libc::c_char)
|
||||
} else {
|
||||
|
||||
1687
src/dc_configure.rs
1687
src/dc_configure.rs
File diff suppressed because it is too large
Load Diff
1035
src/dc_contact.rs
1035
src/dc_contact.rs
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char
|
||||
dc_saxparser_parse(&mut saxparser, buf_terminated);
|
||||
free(dehtml.last_href as *mut libc::c_void);
|
||||
|
||||
strdup(to_cstring(dehtml.strbuilder).as_ptr())
|
||||
to_cstring(dehtml.strbuilder)
|
||||
}
|
||||
|
||||
unsafe fn dehtml_text_cb(
|
||||
|
||||
157
src/dc_e2ee.rs
157
src/dc_e2ee.rs
@@ -1,5 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use mmime::clist::*;
|
||||
@@ -17,10 +17,8 @@ use mmime::{mailmime_substitute, MAILIMF_NO_ERROR, MAIL_NO_ERROR};
|
||||
|
||||
use crate::aheader::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_securejoin::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::key::*;
|
||||
use crate::keyring::*;
|
||||
@@ -33,7 +31,7 @@ use crate::x::*;
|
||||
// attachments of 25 mb brutto should work on the majority of providers
|
||||
// (brutto examples: web.de=50, 1&1=40, t-online.de=32, gmail=25, posteo=50, yahoo=25, all-inkl=100).
|
||||
// as an upper limit, we double the size; the core won't send messages larger than this
|
||||
// to get the netto sizes, we substract 1 mb header-overhead and the base64-overhead.
|
||||
// to get the netto sizes, we subtract 1 mb header-overhead and the base64-overhead.
|
||||
// some defaults
|
||||
#[derive(Clone)]
|
||||
pub struct dc_e2ee_helper_t {
|
||||
@@ -83,27 +81,21 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
{
|
||||
/* libEtPan's pgp_encrypt_mime() takes the parent as the new root. We just expect the root as being given to this function. */
|
||||
let prefer_encrypt = if 0
|
||||
!= dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"e2ee_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
1,
|
||||
) {
|
||||
!= context
|
||||
.sql
|
||||
.get_config_int(context, "e2ee_enabled")
|
||||
.unwrap_or_default()
|
||||
{
|
||||
EncryptPreference::Mutual
|
||||
} else {
|
||||
EncryptPreference::NoPreference
|
||||
};
|
||||
|
||||
let addr = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
let addr = context.sql.get_config(context, "configured_addr");
|
||||
|
||||
if !addr.is_null() {
|
||||
if let Some(addr) = addr {
|
||||
if let Some(public_key) =
|
||||
load_or_generate_self_public_key(context, addr, in_out_message)
|
||||
load_or_generate_self_public_key(context, &addr, in_out_message)
|
||||
{
|
||||
/*only for random-seed*/
|
||||
if prefer_encrypt == EncryptPreference::Mutual || 0 != e2ee_guaranteed {
|
||||
@@ -111,15 +103,10 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
let mut iter1: *mut clistiter;
|
||||
iter1 = (*recipients_addr).first;
|
||||
while !iter1.is_null() {
|
||||
let recipient_addr: *const libc::c_char = (if !iter1.is_null() {
|
||||
(*iter1).data
|
||||
} else {
|
||||
0 as *mut libc::c_void
|
||||
})
|
||||
as *const libc::c_char;
|
||||
if strcasecmp(recipient_addr, addr) != 0 {
|
||||
let recipient_addr = to_string((*iter1).data as *const libc::c_char);
|
||||
if recipient_addr != addr {
|
||||
let peerstate =
|
||||
Peerstate::from_addr(context, &context.sql, as_str(recipient_addr));
|
||||
Peerstate::from_addr(context, &context.sql, &recipient_addr);
|
||||
if peerstate.is_some()
|
||||
&& (peerstate.as_ref().unwrap().prefer_encrypt
|
||||
== EncryptPreference::Mutual
|
||||
@@ -145,7 +132,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
}
|
||||
let sign_key = if 0 != do_encrypt {
|
||||
keyring.add_ref(&public_key);
|
||||
let key = Key::from_self_private(context, addr, &context.sql);
|
||||
let key = Key::from_self_private(context, addr.clone(), &context.sql);
|
||||
|
||||
if key.is_none() {
|
||||
do_encrypt = 0i32;
|
||||
@@ -197,7 +184,7 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
b"Autocrypt-Gossip\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
),
|
||||
strdup(header.as_ptr()),
|
||||
header,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -307,10 +294,8 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
sign_key.as_ref(),
|
||||
) {
|
||||
let ctext_bytes = ctext_v.len();
|
||||
let ctext_c = CString::new(ctext_v).unwrap();
|
||||
let ctext = strdup(ctext_c.as_ptr());
|
||||
|
||||
(*helper).cdata_to_free = ctext as *mut libc::c_void;
|
||||
let ctext = to_cstring(ctext_v);
|
||||
(*helper).cdata_to_free = ctext as *mut _;
|
||||
|
||||
/* create MIME-structure that will contain the encrypted text */
|
||||
let mut encrypted_part: *mut mailmime = new_data_part(
|
||||
@@ -366,15 +351,14 @@ pub unsafe fn dc_e2ee_encrypt(
|
||||
match current_block {
|
||||
14181132614457621749 => {}
|
||||
_ => {
|
||||
let addr = CStr::from_ptr(addr).to_str().unwrap();
|
||||
let aheader = Aheader::new(addr.into(), public_key, prefer_encrypt);
|
||||
let rendered = CString::new(aheader.to_string()).unwrap();
|
||||
let aheader = Aheader::new(addr, public_key, prefer_encrypt);
|
||||
let rendered = to_cstring(aheader.to_string());
|
||||
|
||||
mailimf_fields_add(
|
||||
imffields_unprotected,
|
||||
mailimf_field_new_custom(
|
||||
strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char),
|
||||
strdup(rendered.as_ptr()),
|
||||
rendered,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -503,13 +487,13 @@ unsafe fn new_data_part(
|
||||
******************************************************************************/
|
||||
unsafe fn load_or_generate_self_public_key(
|
||||
context: &Context,
|
||||
self_addr: *const libc::c_char,
|
||||
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 = 0i32;
|
||||
static mut s_in_key_creation: libc::c_int = 0;
|
||||
|
||||
let mut key = Key::from_self_public(context, self_addr, &context.sql);
|
||||
let mut key = Key::from_self_public(context, &self_addr, &context.sql);
|
||||
if key.is_some() {
|
||||
return key;
|
||||
}
|
||||
@@ -521,46 +505,35 @@ unsafe fn load_or_generate_self_public_key(
|
||||
let key_creation_here = 1;
|
||||
s_in_key_creation = 1;
|
||||
|
||||
let start: libc::clock_t = clock();
|
||||
dc_log_info(
|
||||
let start = clock();
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"Generating keypair with %i bits, e=%i ...\x00" as *const u8 as *const libc::c_char,
|
||||
2048i32,
|
||||
65537i32,
|
||||
0, "Generating keypair with {} bits, e={} ...", 2048, 65537,
|
||||
);
|
||||
|
||||
if let Some((public_key, private_key)) = dc_pgp_create_keypair(self_addr) {
|
||||
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,
|
||||
&self_addr,
|
||||
1i32,
|
||||
&context.sql,
|
||||
) {
|
||||
/*set default*/
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot save keypair.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!(context, 0, "Cannot save keypair.",);
|
||||
} else {
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"Keypair generated in %.3f s.\x00" as *const u8 as *const libc::c_char,
|
||||
clock().wrapping_sub(start) as libc::c_double / 1000000i32 as libc::c_double,
|
||||
0,
|
||||
"Keypair generated in {:.3}s.",
|
||||
clock().wrapping_sub(start) as libc::c_double / 1000000 as libc::c_double,
|
||||
);
|
||||
}
|
||||
|
||||
key = Some(public_key);
|
||||
} else {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot create keypair.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!(context, 0, "Cannot create keypair.");
|
||||
}
|
||||
|
||||
if 0 != key_creation_here {
|
||||
@@ -583,7 +556,6 @@ pub unsafe fn dc_e2ee_decrypt(
|
||||
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 self_addr: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
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;
|
||||
@@ -612,28 +584,23 @@ pub unsafe fn dc_e2ee_decrypt(
|
||||
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
if let Some(ref header) = autocryptheader {
|
||||
peerstate.apply_header(&header, message_time as u64);
|
||||
peerstate.apply_header(&header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
} else if message_time as u64 > peerstate.last_seen_autocrypt
|
||||
} else if message_time > peerstate.last_seen_autocrypt
|
||||
&& 0 == contains_report(in_out_message)
|
||||
{
|
||||
peerstate.degrade_encryption(message_time as u64);
|
||||
peerstate.degrade_encryption(message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
}
|
||||
} else if let Some(ref header) = autocryptheader {
|
||||
let p = Peerstate::from_header(context, header, message_time as u64);
|
||||
let p = Peerstate::from_header(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true);
|
||||
peerstate = Some(p);
|
||||
}
|
||||
}
|
||||
/* load private key for decryption */
|
||||
self_addr = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !self_addr.is_null() {
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
if let Some(self_addr) = self_addr {
|
||||
if private_keyring.load_self_private_for_decrypting(context, self_addr, &context.sql) {
|
||||
if peerstate.as_ref().map(|p| p.last_seen).unwrap_or_else(|| 0) == 0 {
|
||||
peerstate = Peerstate::from_addr(&context, &context.sql, as_str(from));
|
||||
@@ -681,7 +648,6 @@ pub unsafe fn dc_e2ee_decrypt(
|
||||
}
|
||||
|
||||
free(from as *mut libc::c_void);
|
||||
free(self_addr as *mut libc::c_void);
|
||||
}
|
||||
|
||||
unsafe fn update_gossip_peerstates(
|
||||
@@ -722,10 +688,10 @@ unsafe fn update_gossip_peerstates(
|
||||
let mut peerstate =
|
||||
Peerstate::from_addr(context, &context.sql, &header.addr);
|
||||
if let Some(ref mut peerstate) = peerstate {
|
||||
peerstate.apply_gossip(header, message_time as u64);
|
||||
peerstate.apply_gossip(header, message_time);
|
||||
peerstate.save_to_db(&context.sql, false);
|
||||
} else {
|
||||
let p = Peerstate::from_gossip(context, header, message_time as u64);
|
||||
let p = Peerstate::from_gossip(context, header, message_time);
|
||||
p.save_to_db(&context.sql, true);
|
||||
peerstate = Some(p);
|
||||
}
|
||||
@@ -737,12 +703,11 @@ unsafe fn update_gossip_peerstates(
|
||||
|
||||
gossipped_addr.insert(header.addr.clone());
|
||||
} else {
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"Ignoring gossipped \"%s\" as the address is not in To/Cc list.\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
CString::new(header.addr.clone()).unwrap().as_ptr(),
|
||||
0,
|
||||
"Ignoring gossipped \"{}\" as the address is not in To/Cc list.",
|
||||
&header.addr,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -968,13 +933,12 @@ unsafe fn decrypt_part(
|
||||
add_signatures,
|
||||
) {
|
||||
let plain_bytes = plain.len();
|
||||
let plain_c = CString::new(plain).unwrap();
|
||||
let plain_buf = strdup(plain_c.as_ptr());
|
||||
let plain_buf = plain.as_ptr() as *const libc::c_char;
|
||||
|
||||
let mut index: size_t = 0i32 as size_t;
|
||||
let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime;
|
||||
if mailmime_parse(
|
||||
plain_buf as *const libc::c_char,
|
||||
plain_buf as *const _,
|
||||
plain_bytes,
|
||||
&mut index,
|
||||
&mut decrypted_mime,
|
||||
@@ -1043,7 +1007,7 @@ unsafe fn has_decrypted_pgp_armor(
|
||||
* that we could use the normal Autocrypt processing.
|
||||
*
|
||||
* @private
|
||||
* @param mime The mime struture to check
|
||||
* @param mime The mime structure to check
|
||||
* @return 1=multipart/report found in MIME, 0=no multipart/report found
|
||||
*/
|
||||
// TODO should return bool /rtn
|
||||
@@ -1103,25 +1067,18 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int {
|
||||
(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 = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if self_addr.is_null() {
|
||||
dc_log_warning(
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
if self_addr.is_none() {
|
||||
warn!(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot ensure secret key if context is not configured.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
0, "Cannot ensure secret key if context is not configured.",
|
||||
);
|
||||
} else if load_or_generate_self_public_key(context, self_addr, 0 as *mut mailmime).is_some() {
|
||||
} 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 = 1i32
|
||||
success = 1;
|
||||
}
|
||||
|
||||
free(self_addr as *mut libc::c_void);
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
1059
src/dc_imex.rs
1059
src/dc_imex.rs
File diff suppressed because it is too large
Load Diff
805
src/dc_job.rs
805
src/dc_job.rs
File diff suppressed because it is too large
Load Diff
@@ -2,28 +2,25 @@ use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::imap::Imap;
|
||||
use crate::x::*;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct dc_jobthread_t {
|
||||
pub name: *mut libc::c_char,
|
||||
pub folder_config_name: *mut libc::c_char,
|
||||
pub name: &'static str,
|
||||
pub folder_config_name: &'static str,
|
||||
pub imap: Imap,
|
||||
pub state: Arc<(Mutex<JobState>, Condvar)>,
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_init(
|
||||
name: *const libc::c_char,
|
||||
folder_config_name: *const libc::c_char,
|
||||
pub fn dc_jobthread_init(
|
||||
name: &'static str,
|
||||
folder_config_name: &'static str,
|
||||
imap: Imap,
|
||||
) -> dc_jobthread_t {
|
||||
dc_jobthread_t {
|
||||
name: dc_strdup(name),
|
||||
folder_config_name: dc_strdup(folder_config_name),
|
||||
name,
|
||||
folder_config_name,
|
||||
imap,
|
||||
state: Arc::new((Mutex::new(Default::default()), Condvar::new())),
|
||||
}
|
||||
@@ -37,26 +34,13 @@ pub struct JobState {
|
||||
using_handle: i32,
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_exit(jobthread: &mut dc_jobthread_t) {
|
||||
free(jobthread.name as *mut libc::c_void);
|
||||
jobthread.name = 0 as *mut libc::c_char;
|
||||
free(jobthread.folder_config_name as *mut libc::c_void);
|
||||
jobthread.folder_config_name = 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
pub unsafe fn dc_jobthread_suspend(
|
||||
context: &Context,
|
||||
jobthread: &dc_jobthread_t,
|
||||
suspend: libc::c_int,
|
||||
) {
|
||||
if 0 != suspend {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Suspending %s-thread.\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
|
||||
info!(context, 0, "Suspending {}-thread.", jobthread.name,);
|
||||
{
|
||||
jobthread.state.0.lock().unwrap().suspended = 1;
|
||||
}
|
||||
@@ -69,12 +53,7 @@ pub unsafe fn dc_jobthread_suspend(
|
||||
std::thread::sleep(std::time::Duration::from_micros(300 * 1000));
|
||||
}
|
||||
} else {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Unsuspending %s-thread.\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
info!(context, 0, "Unsuspending {}-thread.", jobthread.name);
|
||||
|
||||
let &(ref lock, ref cvar) = &*jobthread.state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -90,12 +69,7 @@ pub unsafe fn dc_jobthread_interrupt_idle(context: &Context, jobthread: &dc_jobt
|
||||
jobthread.state.0.lock().unwrap().jobs_needed = 1;
|
||||
}
|
||||
|
||||
dc_log_info(
|
||||
context,
|
||||
0,
|
||||
b"Interrupting %s-IDLE...\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
info!(context, 0, "Interrupting {}-IDLE...", jobthread.name);
|
||||
|
||||
jobthread.imap.interrupt_idle();
|
||||
|
||||
@@ -127,30 +101,22 @@ pub unsafe fn dc_jobthread_fetch(
|
||||
if 0 != use_network {
|
||||
start = clock();
|
||||
if !(0 == connect_to_imap(context, jobthread)) {
|
||||
dc_log_info(
|
||||
context,
|
||||
0,
|
||||
b"%s-fetch started...\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
info!(context, 0, "{}-fetch started...", jobthread.name);
|
||||
jobthread.imap.fetch(context);
|
||||
|
||||
if jobthread.imap.should_reconnect() {
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b"%s-fetch aborted, starting over...\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
0, "{}-fetch aborted, starting over...", jobthread.name,
|
||||
);
|
||||
jobthread.imap.fetch(context);
|
||||
}
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
b"%s-fetch done in %.0f ms.\x00" as *const u8 as *const libc::c_char,
|
||||
"{}-fetch done in {:.3} ms.",
|
||||
jobthread.name,
|
||||
clock().wrapping_sub(start) as libc::c_double * 1000.0f64
|
||||
/ 1000000i32 as libc::c_double,
|
||||
clock().wrapping_sub(start) as f64 / 1000.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -163,38 +129,32 @@ pub unsafe fn dc_jobthread_fetch(
|
||||
******************************************************************************/
|
||||
|
||||
unsafe fn connect_to_imap(context: &Context, jobthread: &dc_jobthread_t) -> libc::c_int {
|
||||
let mut ret_connected: libc::c_int;
|
||||
let mut mvbox_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
|
||||
if jobthread.imap.is_connected() {
|
||||
ret_connected = 1;
|
||||
} else {
|
||||
ret_connected = dc_connect_to_configured_imap(context, &jobthread.imap);
|
||||
if !(0 == ret_connected) {
|
||||
if dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"folders_configured\x00" as *const u8 as *const libc::c_char,
|
||||
0,
|
||||
) < 3
|
||||
{
|
||||
jobthread.imap.configure_folders(context, 0x1);
|
||||
}
|
||||
mvbox_name = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
jobthread.folder_config_name,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if mvbox_name.is_null() {
|
||||
jobthread.imap.disconnect(context);
|
||||
ret_connected = 0;
|
||||
} else {
|
||||
jobthread.imap.set_watch_folder(mvbox_name);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
free(mvbox_name as *mut libc::c_void);
|
||||
|
||||
ret_connected
|
||||
}
|
||||
@@ -209,11 +169,10 @@ pub unsafe fn dc_jobthread_idle(
|
||||
let mut state = lock.lock().unwrap();
|
||||
|
||||
if 0 != state.jobs_needed {
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
b"%s-IDLE will not be started as it was interrupted while not ideling.\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
"{}-IDLE will not be started as it was interrupted while not ideling.",
|
||||
jobthread.name,
|
||||
);
|
||||
state.jobs_needed = 0;
|
||||
@@ -242,19 +201,9 @@ pub unsafe fn dc_jobthread_idle(
|
||||
}
|
||||
|
||||
connect_to_imap(context, jobthread);
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"%s-IDLE started...\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
info!(context, 0, "{}-IDLE started...", jobthread.name,);
|
||||
jobthread.imap.idle(context);
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"%s-IDLE ended.\x00" as *const u8 as *const libc::c_char,
|
||||
jobthread.name,
|
||||
);
|
||||
info!(context, 0, "{}-IDLE ended.", jobthread.name);
|
||||
|
||||
jobthread.state.0.lock().unwrap().using_handle = 0;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
142
src/dc_log.rs
142
src/dc_log.rs
@@ -1,142 +0,0 @@
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
pub unsafe extern "C" fn dc_log_event(
|
||||
context: &Context,
|
||||
event_code: Event,
|
||||
data1: libc::c_int,
|
||||
msg: *const libc::c_char,
|
||||
va: ...
|
||||
) {
|
||||
log_vprintf(context, event_code, data1, msg, va);
|
||||
}
|
||||
|
||||
/* Asynchronous "Thread-errors" are reported by the dc_log_error()
|
||||
function. These errors must be shown to the user by a bubble or so.
|
||||
|
||||
"Normal" errors are usually returned by a special value (null or so) and are
|
||||
usually not reported using dc_log_error() - its up to the caller to
|
||||
decide, what should be reported or done. However, these "Normal" errors
|
||||
are usually logged by dc_log_warning(). */
|
||||
unsafe fn log_vprintf(
|
||||
context: &Context,
|
||||
event: Event,
|
||||
data1: libc::c_int,
|
||||
msg_format: *const libc::c_char,
|
||||
va_0: ::std::ffi::VaList,
|
||||
) {
|
||||
let msg: *mut libc::c_char;
|
||||
if !msg_format.is_null() {
|
||||
let mut tempbuf: [libc::c_char; 1025] = [0; 1025];
|
||||
vsnprintf(
|
||||
tempbuf.as_mut_ptr(),
|
||||
1024i32 as libc::c_ulong,
|
||||
msg_format,
|
||||
va_0,
|
||||
);
|
||||
msg = dc_strdup(tempbuf.as_mut_ptr())
|
||||
} else {
|
||||
msg = dc_mprintf(
|
||||
b"event #%i\x00" as *const u8 as *const libc::c_char,
|
||||
event as libc::c_int,
|
||||
)
|
||||
}
|
||||
context.call_cb(event, data1 as uintptr_t, msg as uintptr_t);
|
||||
free(msg as *mut libc::c_void);
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn dc_log_event_seq(
|
||||
context: &Context,
|
||||
event_code: Event,
|
||||
sequence_start: *mut libc::c_int,
|
||||
msg: *const libc::c_char,
|
||||
va_0: ...
|
||||
) {
|
||||
if sequence_start.is_null() {
|
||||
return;
|
||||
}
|
||||
log_vprintf(context, event_code, *sequence_start, msg, va_0);
|
||||
*sequence_start = 0i32;
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn dc_log_error(
|
||||
context: &Context,
|
||||
data1: libc::c_int,
|
||||
msg: *const libc::c_char,
|
||||
va_1: ...
|
||||
) {
|
||||
log_vprintf(context, Event::ERROR, data1, msg, va_1);
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn dc_log_warning(
|
||||
context: &Context,
|
||||
data1: libc::c_int,
|
||||
msg: *const libc::c_char,
|
||||
va_2: ...
|
||||
) {
|
||||
log_vprintf(context, Event::WARNING, data1, msg, va_2);
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn dc_log_info(
|
||||
context: &Context,
|
||||
data1: libc::c_int,
|
||||
msg: *const libc::c_char,
|
||||
va_3: ...
|
||||
) {
|
||||
log_vprintf(context, Event::INFO, data1, msg, va_3);
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
info!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {{
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::INFO, $data1 as uintptr_t,
|
||||
formatted_c.as_ptr() as uintptr_t)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
warn!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
formatted_c.as_ptr() as libc::uintptr_t)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
error!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as uintptr_t,
|
||||
formatted_c.as_ptr() as uintptr_t)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_event {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
log_event!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($event, $data1 as uintptr_t,
|
||||
formatted_c.as_ptr() as uintptr_t)
|
||||
};
|
||||
}
|
||||
@@ -1,283 +1,173 @@
|
||||
use crate::context::Context;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
use crate::context::Context;
|
||||
use crate::sql::Sql;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct dc_loginparam_t {
|
||||
pub addr: *mut libc::c_char,
|
||||
pub mail_server: *mut libc::c_char,
|
||||
pub mail_user: *mut libc::c_char,
|
||||
pub mail_pw: *mut libc::c_char,
|
||||
pub addr: String,
|
||||
pub mail_server: String,
|
||||
pub mail_user: String,
|
||||
pub mail_pw: String,
|
||||
pub mail_port: i32,
|
||||
pub send_server: *mut libc::c_char,
|
||||
pub send_user: *mut libc::c_char,
|
||||
pub send_pw: *mut libc::c_char,
|
||||
pub send_server: String,
|
||||
pub send_user: String,
|
||||
pub send_pw: String,
|
||||
pub send_port: i32,
|
||||
pub server_flags: i32,
|
||||
}
|
||||
|
||||
pub unsafe fn dc_loginparam_new() -> *mut dc_loginparam_t {
|
||||
let loginparam: *mut dc_loginparam_t;
|
||||
loginparam = calloc(1, ::std::mem::size_of::<dc_loginparam_t>()) as *mut dc_loginparam_t;
|
||||
assert!(!loginparam.is_null());
|
||||
|
||||
loginparam
|
||||
}
|
||||
|
||||
pub unsafe fn dc_loginparam_unref(loginparam: *mut dc_loginparam_t) {
|
||||
if loginparam.is_null() {
|
||||
return;
|
||||
impl dc_loginparam_t {
|
||||
pub fn addr_str(&self) -> &str {
|
||||
self.addr.as_str()
|
||||
}
|
||||
dc_loginparam_empty(loginparam);
|
||||
free(loginparam as *mut libc::c_void);
|
||||
}
|
||||
|
||||
/* clears all data and frees its memory. All pointers are NULL after this function is called. */
|
||||
pub unsafe fn dc_loginparam_empty(mut loginparam: *mut dc_loginparam_t) {
|
||||
if loginparam.is_null() {
|
||||
return;
|
||||
}
|
||||
free((*loginparam).addr as *mut libc::c_void);
|
||||
(*loginparam).addr = 0 as *mut libc::c_char;
|
||||
free((*loginparam).mail_server as *mut libc::c_void);
|
||||
(*loginparam).mail_server = 0 as *mut libc::c_char;
|
||||
(*loginparam).mail_port = 0i32;
|
||||
free((*loginparam).mail_user as *mut libc::c_void);
|
||||
(*loginparam).mail_user = 0 as *mut libc::c_char;
|
||||
free((*loginparam).mail_pw as *mut libc::c_void);
|
||||
(*loginparam).mail_pw = 0 as *mut libc::c_char;
|
||||
free((*loginparam).send_server as *mut libc::c_void);
|
||||
(*loginparam).send_server = 0 as *mut libc::c_char;
|
||||
(*loginparam).send_port = 0i32;
|
||||
free((*loginparam).send_user as *mut libc::c_void);
|
||||
(*loginparam).send_user = 0 as *mut libc::c_char;
|
||||
free((*loginparam).send_pw as *mut libc::c_void);
|
||||
(*loginparam).send_pw = 0 as *mut libc::c_char;
|
||||
(*loginparam).server_flags = 0i32;
|
||||
pub fn dc_loginparam_new() -> dc_loginparam_t {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_loginparam_read(
|
||||
pub fn dc_loginparam_read(
|
||||
context: &Context,
|
||||
loginparam: *mut dc_loginparam_t,
|
||||
sql: &SQLite,
|
||||
prefix: *const libc::c_char,
|
||||
) {
|
||||
let mut key: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
dc_loginparam_empty(loginparam);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"addr\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).addr = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_server\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).mail_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_port\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).mail_port = dc_sqlite3_get_config_int(context, sql, key, 0i32);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_user\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).mail_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_pw\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).mail_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_server\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).send_server = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_port\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).send_port = dc_sqlite3_get_config_int(context, sql, key, 0i32);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_user\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).send_user = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_pw\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).send_pw = dc_sqlite3_get_config(context, sql, key, 0 as *const libc::c_char);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"server_flags\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
(*loginparam).server_flags = dc_sqlite3_get_config_int(context, sql, key, 0i32);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
}
|
||||
sql: &Sql,
|
||||
prefix: impl AsRef<str>,
|
||||
) -> dc_loginparam_t {
|
||||
let prefix = prefix.as_ref();
|
||||
|
||||
pub unsafe fn dc_loginparam_write(
|
||||
context: &Context,
|
||||
loginparam: *const dc_loginparam_t,
|
||||
sql: &SQLite,
|
||||
prefix: *const libc::c_char,
|
||||
) {
|
||||
let mut key: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"addr\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).addr);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_server\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_server);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_port\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).mail_port);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_user\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_user);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"mail_pw\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).mail_pw);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_server\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_server);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_port\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).send_port);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_user\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_user);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"send_pw\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config(context, sql, key, (*loginparam).send_pw);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
key = sqlite3_mprintf(
|
||||
b"%s%s\x00" as *const u8 as *const libc::c_char,
|
||||
prefix,
|
||||
b"server_flags\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
dc_sqlite3_set_config_int(context, sql, key, (*loginparam).server_flags);
|
||||
sqlite3_free(key as *mut libc::c_void);
|
||||
}
|
||||
let key = format!("{}addr", prefix);
|
||||
let addr = sql
|
||||
.get_config(context, key)
|
||||
.unwrap_or_default()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
pub unsafe fn dc_loginparam_get_readable(loginparam: *const dc_loginparam_t) -> *mut libc::c_char {
|
||||
let unset: *const libc::c_char = b"0\x00" as *const u8 as *const libc::c_char;
|
||||
let pw: *const libc::c_char = b"***\x00" as *const u8 as *const libc::c_char;
|
||||
if loginparam.is_null() {
|
||||
return dc_strdup(0 as *const libc::c_char);
|
||||
let key = format!("{}mail_server", prefix);
|
||||
let mail_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
let mail_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
let mail_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
let mail_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
let send_server = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
let send_port = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
let send_user = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
let send_pw = sql.get_config(context, key).unwrap_or_default();
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
let server_flags = sql.get_config_int(context, key).unwrap_or_default();
|
||||
|
||||
dc_loginparam_t {
|
||||
addr: addr.to_string(),
|
||||
mail_server,
|
||||
mail_user,
|
||||
mail_pw,
|
||||
mail_port,
|
||||
send_server,
|
||||
send_user,
|
||||
send_pw,
|
||||
send_port,
|
||||
server_flags,
|
||||
}
|
||||
let flags_readable: *mut libc::c_char = get_readable_flags((*loginparam).server_flags);
|
||||
let ret: *mut libc::c_char = dc_mprintf(
|
||||
b"%s %s:%s:%s:%i %s:%s:%s:%i %s\x00" as *const u8 as *const libc::c_char,
|
||||
if !(*loginparam).addr.is_null() {
|
||||
(*loginparam).addr
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !(*loginparam).mail_user.is_null() {
|
||||
(*loginparam).mail_user
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !(*loginparam).mail_pw.is_null() {
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_write(
|
||||
context: &Context,
|
||||
loginparam: &dc_loginparam_t,
|
||||
sql: &Sql,
|
||||
prefix: impl AsRef<str>,
|
||||
) {
|
||||
let prefix = prefix.as_ref();
|
||||
|
||||
let key = format!("{}addr", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.addr)).ok();
|
||||
|
||||
let key = format!("{}mail_server", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_server))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}mail_port", prefix);
|
||||
sql.set_config_int(context, key, loginparam.mail_port).ok();
|
||||
|
||||
let key = format!("{}mail_user", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_user))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}mail_pw", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.mail_pw)).ok();
|
||||
|
||||
let key = format!("{}send_server", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_server))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}send_port", prefix);
|
||||
sql.set_config_int(context, key, loginparam.send_port).ok();
|
||||
|
||||
let key = format!("{}send_user", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_user))
|
||||
.ok();
|
||||
|
||||
let key = format!("{}send_pw", prefix);
|
||||
sql.set_config(context, key, Some(&loginparam.send_pw)).ok();
|
||||
|
||||
let key = format!("{}server_flags", prefix);
|
||||
sql.set_config_int(context, key, loginparam.server_flags)
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn unset_empty(s: &String) -> Cow<String> {
|
||||
if s.is_empty() {
|
||||
Cow::Owned("unset".to_string())
|
||||
} else {
|
||||
Cow::Borrowed(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_loginparam_get_readable(loginparam: &dc_loginparam_t) -> String {
|
||||
let unset = "0";
|
||||
let pw = "***";
|
||||
|
||||
let flags_readable = get_readable_flags(loginparam.server_flags);
|
||||
|
||||
format!(
|
||||
"{} {}:{}:{}:{} {}:{}:{}:{} {}",
|
||||
unset_empty(&loginparam.addr),
|
||||
unset_empty(&loginparam.mail_user),
|
||||
if !loginparam.mail_pw.is_empty() {
|
||||
pw
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !(*loginparam).mail_server.is_null() {
|
||||
(*loginparam).mail_server
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
(*loginparam).mail_port,
|
||||
if !(*loginparam).send_user.is_null() {
|
||||
(*loginparam).send_user
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !(*loginparam).send_pw.is_null() {
|
||||
unset_empty(&loginparam.mail_server),
|
||||
loginparam.mail_port,
|
||||
unset_empty(&loginparam.send_user),
|
||||
if !loginparam.send_pw.is_empty() {
|
||||
pw
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
if !(*loginparam).send_server.is_null() {
|
||||
(*loginparam).send_server
|
||||
} else {
|
||||
unset
|
||||
},
|
||||
(*loginparam).send_port,
|
||||
unset_empty(&loginparam.send_server),
|
||||
loginparam.send_port,
|
||||
flags_readable,
|
||||
);
|
||||
free(flags_readable as *mut libc::c_void);
|
||||
|
||||
ret
|
||||
)
|
||||
}
|
||||
|
||||
fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char {
|
||||
fn get_readable_flags(flags: i32) -> String {
|
||||
let mut res = String::new();
|
||||
for bit in 0..31 {
|
||||
if 0 != flags & 1 << bit {
|
||||
let mut flag_added: libc::c_int = 0;
|
||||
let mut flag_added = 0;
|
||||
if 1 << bit == 0x2 {
|
||||
res += "OAUTH2 ";
|
||||
flag_added = 1;
|
||||
@@ -319,5 +209,5 @@ fn get_readable_flags(flags: libc::c_int) -> *mut libc::c_char {
|
||||
res += "0";
|
||||
}
|
||||
|
||||
unsafe { strdup(to_cstring(res).as_ptr()) }
|
||||
res
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ pub unsafe fn dc_lot_get_timestamp(lot: *const dc_lot_t) -> i64 {
|
||||
pub unsafe fn dc_lot_fill(
|
||||
mut lot: *mut dc_lot_t,
|
||||
msg: *const dc_msg_t,
|
||||
chat: *const dc_chat_t,
|
||||
chat: *const Chat,
|
||||
contact: *const dc_contact_t,
|
||||
context: &Context,
|
||||
) {
|
||||
|
||||
@@ -8,16 +8,14 @@ use mmime::mailmime_write_mem::*;
|
||||
use mmime::mmapstring::*;
|
||||
use mmime::other::*;
|
||||
|
||||
use crate::constants::DC_VERSION_STR;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_param::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
@@ -36,7 +34,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 dc_chat_t<'a>,
|
||||
pub chat: *mut Chat<'a>,
|
||||
pub increation: libc::c_int,
|
||||
pub in_reply_to: *mut libc::c_char,
|
||||
pub references: *mut libc::c_char,
|
||||
@@ -91,7 +89,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 dc_chat_t;
|
||||
(*factory).chat = 0 as *mut Chat;
|
||||
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);
|
||||
@@ -100,7 +98,7 @@ pub unsafe fn dc_mimefactory_empty(mut factory: *mut dc_mimefactory_t) {
|
||||
mmap_string_free((*factory).out);
|
||||
(*factory).out = 0 as *mut MMAPString
|
||||
}
|
||||
(*factory).out_encrypted = 0i32;
|
||||
(*factory).out_encrypted = 0;
|
||||
(*factory).loaded = DC_MF_NOTHING_LOADED;
|
||||
free((*factory).error as *mut libc::c_void);
|
||||
(*factory).error = 0 as *mut libc::c_char;
|
||||
@@ -111,158 +109,180 @@ pub unsafe fn dc_mimefactory_load_msg(
|
||||
mut factory: *mut dc_mimefactory_t,
|
||||
msg_id: uint32_t,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
|
||||
if !(factory.is_null() || msg_id <= 9i32 as libc::c_uint || !(*factory).msg.is_null()) {
|
||||
/*call empty() before */
|
||||
let context = (*factory).context;
|
||||
(*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 = 0i32;
|
||||
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,
|
||||
if factory.is_null() || msg_id <= 9 || !(*factory).msg.is_null() {
|
||||
info!((*factory).context, 0, "mimefactory: null");
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut success = 0;
|
||||
|
||||
/*call empty() before */
|
||||
let context = (*factory).context;
|
||||
(*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)
|
||||
{
|
||||
info!(context, 0, "mimefactory: loaded msg and chat",);
|
||||
load_from(factory);
|
||||
(*factory).req_mdn = 0;
|
||||
if 0 != dc_chat_is_self_talk((*factory).chat) {
|
||||
info!(context, 0, "mimefactory: selftalk");
|
||||
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
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 {
|
||||
info!(context, 0, "mimefactory: query map");
|
||||
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| {
|
||||
info!(context, 0, "mimefactory: processing rows");
|
||||
for row in rows {
|
||||
let (authname, addr) = row?;
|
||||
let addr_c = to_cstring(addr);
|
||||
if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
if !authname.is_empty() {
|
||||
to_cstring(authname)
|
||||
} 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 = dc_param_get_int((*(*factory).msg).param, DC_PARAM_CMD as i32, 0);
|
||||
if command == 5 {
|
||||
let email_to_remove_c = dc_param_get(
|
||||
(*(*factory).msg).param,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup((*factory).from_addr) as *mut libc::c_void,
|
||||
);
|
||||
} else {
|
||||
stmt =
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"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;\x00"
|
||||
as *const u8 as
|
||||
*const libc::c_char);
|
||||
sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).chat_id as libc::c_int);
|
||||
while sqlite3_step(stmt) == 100i32 {
|
||||
let authname: *const libc::c_char =
|
||||
sqlite3_column_text(stmt, 0i32) as *const libc::c_char;
|
||||
let addr: *const libc::c_char =
|
||||
sqlite3_column_text(stmt, 1i32) as *const libc::c_char;
|
||||
if clist_search_string_nocase((*factory).recipients_addr, addr) == 0i32 {
|
||||
let email_to_remove = to_string(email_to_remove_c);
|
||||
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,
|
||||
(if !authname.is_null() && 0 != *authname.offset(0isize) as libc::c_int
|
||||
{
|
||||
dc_strdup(authname)
|
||||
} else {
|
||||
0 as *mut libc::c_char
|
||||
}) as *mut libc::c_void,
|
||||
0 as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup(addr) as *mut libc::c_void,
|
||||
email_to_remove_c as *mut libc::c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
let command: libc::c_int =
|
||||
dc_param_get_int((*(*factory).msg).param, 'S' as i32, 0i32);
|
||||
if command == 5i32 {
|
||||
let email_to_remove: *mut libc::c_char = dc_param_get(
|
||||
(*(*factory).msg).param,
|
||||
'E' as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
let self_addr: *mut libc::c_char = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
if !email_to_remove.is_null() && strcasecmp(email_to_remove, self_addr) != 0i32
|
||||
{
|
||||
if clist_search_string_nocase((*factory).recipients_addr, email_to_remove)
|
||||
== 0i32
|
||||
{
|
||||
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 as *mut libc::c_void,
|
||||
);
|
||||
}
|
||||
}
|
||||
free(self_addr as *mut libc::c_void);
|
||||
}
|
||||
if command != 6i32
|
||||
&& command != 7i32
|
||||
&& 0 != dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
1i32,
|
||||
)
|
||||
{
|
||||
(*factory).req_mdn = 1i32
|
||||
}
|
||||
}
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1i32, (*(*factory).msg).id as libc::c_int);
|
||||
if sqlite3_step(stmt) == 100i32 {
|
||||
(*factory).in_reply_to =
|
||||
dc_strdup(sqlite3_column_text(stmt, 0i32) as *const libc::c_char);
|
||||
(*factory).references =
|
||||
dc_strdup(sqlite3_column_text(stmt, 1i32) as *const libc::c_char)
|
||||
if command != 6
|
||||
&& command != 7
|
||||
&& 0 != context
|
||||
.sql
|
||||
.get_config_int(context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
(*factory).req_mdn = 1
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
stmt = 0 as *mut sqlite3_stmt;
|
||||
success = 1i32;
|
||||
(*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)
|
||||
info!(context, 0, "mimefactory: loading in reply to");
|
||||
|
||||
let row = context.sql.query_row(
|
||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||
params![(*(*factory).msg).id as i32],
|
||||
|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 = to_cstring(in_reply_to);
|
||||
(*factory).references = to_cstring(references);
|
||||
}
|
||||
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)
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return success;
|
||||
if 0 != success {
|
||||
(*factory).increation = dc_msg_is_increation((*factory).msg)
|
||||
}
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
unsafe fn load_from(mut factory: *mut dc_mimefactory_t) {
|
||||
(*factory).from_addr = dc_sqlite3_get_config(
|
||||
(*factory).context,
|
||||
&(*factory).context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
let context = (*factory).context;
|
||||
(*factory).from_addr = to_cstring(
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "configured_addr")
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
(*factory).from_displayname = dc_sqlite3_get_config(
|
||||
(*factory).context,
|
||||
&(*factory).context.sql,
|
||||
b"displayname\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
|
||||
(*factory).from_displayname = to_cstring(
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "displayname")
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
(*factory).selfstatus = dc_sqlite3_get_config(
|
||||
(*factory).context,
|
||||
&(*factory).context.sql,
|
||||
b"selfstatus\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
(*factory).selfstatus = to_cstring(
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "selfstatus")
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
if (*factory).selfstatus.is_null() {
|
||||
(*factory).selfstatus = dc_stock_str((*factory).context, 13i32)
|
||||
(*factory).selfstatus = dc_stock_str((*factory).context, 13)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -270,61 +290,63 @@ pub unsafe fn dc_mimefactory_load_mdn(
|
||||
mut factory: *mut dc_mimefactory_t,
|
||||
msg_id: uint32_t,
|
||||
) -> libc::c_int {
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
if !factory.is_null() {
|
||||
(*factory).recipients_names = clist_new();
|
||||
(*factory).recipients_addr = clist_new();
|
||||
(*factory).msg = dc_msg_new_untyped((*factory).context);
|
||||
if !(0
|
||||
== dc_sqlite3_get_config_int(
|
||||
(*factory).context,
|
||||
if factory.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut success = 0;
|
||||
let mut contact = 0 as *mut dc_contact_t;
|
||||
|
||||
(*factory).recipients_names = clist_new();
|
||||
(*factory).recipients_addr = clist_new();
|
||||
(*factory).msg = dc_msg_new_untyped((*factory).context);
|
||||
if 0 != (*factory)
|
||||
.context
|
||||
.sql
|
||||
.get_config_int((*factory).context, "mdns_enabled")
|
||||
.unwrap_or_else(|| 1)
|
||||
{
|
||||
// MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ...
|
||||
contact = dc_contact_new((*factory).context);
|
||||
if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id)
|
||||
|| !dc_contact_load_from_db(
|
||||
contact,
|
||||
&(*factory).context.sql,
|
||||
b"mdns_enabled\x00" as *const u8 as *const libc::c_char,
|
||||
1i32,
|
||||
(*(*factory).msg).from_id,
|
||||
))
|
||||
{
|
||||
/* MDNs not enabled - check this is late, in the job. the use may have changed its choice while offline ... */
|
||||
contact = dc_contact_new((*factory).context);
|
||||
if !(!dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id)
|
||||
|| !dc_contact_load_from_db(
|
||||
contact,
|
||||
&(*factory).context.sql,
|
||||
(*(*factory).msg).from_id,
|
||||
))
|
||||
{
|
||||
if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9i32 as libc::c_uint) {
|
||||
/* Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs() */
|
||||
if !((*(*factory).msg).from_id <= 9i32 as libc::c_uint) {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
(if !(*contact).authname.is_null()
|
||||
&& 0 != *(*contact).authname.offset(0isize) as libc::c_int
|
||||
{
|
||||
dc_strdup((*contact).authname)
|
||||
} else {
|
||||
0 as *mut libc::c_char
|
||||
}) as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup((*contact).addr) as *mut libc::c_void,
|
||||
);
|
||||
load_from(factory);
|
||||
(*factory).timestamp = dc_create_smeared_timestamp((*factory).context);
|
||||
(*factory).rfc724_mid = dc_create_outgoing_rfc724_mid(
|
||||
0 as *const libc::c_char,
|
||||
(*factory).from_addr,
|
||||
);
|
||||
success = 1i32;
|
||||
(*factory).loaded = DC_MF_MDN_LOADED
|
||||
}
|
||||
if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9 as libc::c_uint) {
|
||||
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller in dc_markseen_msgs()
|
||||
if !((*(*factory).msg).from_id <= 9 as libc::c_uint) {
|
||||
clist_insert_after(
|
||||
(*factory).recipients_names,
|
||||
(*(*factory).recipients_names).last,
|
||||
(if !(*contact).authname.is_null()
|
||||
&& 0 != *(*contact).authname.offset(0isize) as libc::c_int
|
||||
{
|
||||
dc_strdup((*contact).authname)
|
||||
} else {
|
||||
0 as *mut libc::c_char
|
||||
}) as *mut libc::c_void,
|
||||
);
|
||||
clist_insert_after(
|
||||
(*factory).recipients_addr,
|
||||
(*(*factory).recipients_addr).last,
|
||||
dc_strdup((*contact).addr) as *mut libc::c_void,
|
||||
);
|
||||
load_from(factory);
|
||||
(*factory).timestamp = dc_create_smeared_timestamp((*factory).context);
|
||||
(*factory).rfc724_mid = dc_create_outgoing_rfc724_mid(
|
||||
0 as *const libc::c_char,
|
||||
(*factory).from_addr,
|
||||
);
|
||||
success = 1;
|
||||
(*factory).loaded = DC_MF_MDN_LOADED
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dc_contact_unref(contact);
|
||||
|
||||
success
|
||||
@@ -339,15 +361,15 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
let mut message_text: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut message_text2: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut subject_str: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut afwd_email: libc::c_int = 0i32;
|
||||
let mut col: libc::c_int = 0i32;
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut parts: libc::c_int = 0i32;
|
||||
let mut e2ee_guaranteed: libc::c_int = 0i32;
|
||||
let mut min_verified: libc::c_int = 0i32;
|
||||
let mut afwd_email: libc::c_int = 0;
|
||||
let mut col: libc::c_int = 0;
|
||||
let mut success: libc::c_int = 0;
|
||||
let mut parts: libc::c_int = 0;
|
||||
let mut e2ee_guaranteed: libc::c_int = 0;
|
||||
let mut min_verified: libc::c_int = 0;
|
||||
// 1=add Autocrypt-header (needed eg. for handshaking), 2=no Autocrypte-header (used for MDN)
|
||||
let mut force_plaintext: libc::c_int = 0i32;
|
||||
let mut do_gossip: libc::c_int = 0i32;
|
||||
let mut force_plaintext: libc::c_int = 0;
|
||||
let mut do_gossip: libc::c_int = 0;
|
||||
let mut grpimage: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut e2ee_helper = dc_e2ee_helper_t {
|
||||
encryption_successfull: 0,
|
||||
@@ -382,7 +404,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
let mut to: *mut mailimf_address_list = 0 as *mut mailimf_address_list;
|
||||
if !(*factory).recipients_names.is_null()
|
||||
&& !(*factory).recipients_addr.is_null()
|
||||
&& (*(*factory).recipients_addr).count > 0i32
|
||||
&& (*(*factory).recipients_addr).count > 0
|
||||
{
|
||||
let mut iter1: *mut clistiter;
|
||||
let mut iter2: *mut clistiter;
|
||||
@@ -501,11 +523,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 dc_chat_t = (*factory).chat;
|
||||
let chat: *mut Chat = (*factory).chat;
|
||||
let msg: *mut dc_msg_t = (*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 == 130i32 {
|
||||
if (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -513,23 +535,27 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
strdup(b"1\x00" as *const u8 as *const libc::c_char),
|
||||
),
|
||||
);
|
||||
force_plaintext = 0i32;
|
||||
e2ee_guaranteed = 1i32;
|
||||
min_verified = 2i32
|
||||
force_plaintext = 0;
|
||||
e2ee_guaranteed = 1;
|
||||
min_verified = 2
|
||||
} else {
|
||||
force_plaintext = dc_param_get_int((*(*factory).msg).param, 'u' as i32, 0i32);
|
||||
if force_plaintext == 0i32 {
|
||||
e2ee_guaranteed = dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32)
|
||||
force_plaintext =
|
||||
dc_param_get_int((*(*factory).msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 0);
|
||||
if force_plaintext == 0 {
|
||||
e2ee_guaranteed =
|
||||
dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0)
|
||||
}
|
||||
}
|
||||
if (*chat).gossiped_timestamp == 0
|
||||
|| ((*chat).gossiped_timestamp + (2 * 24 * 60 * 60)) < time()
|
||||
{
|
||||
do_gossip = 1i32
|
||||
do_gossip = 1
|
||||
}
|
||||
/* build header etc. */
|
||||
let command: libc::c_int = dc_param_get_int((*msg).param, 'S' as i32, 0i32);
|
||||
if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
|
||||
let command: libc::c_int = dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0);
|
||||
if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
|
||||
{
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -544,9 +570,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
dc_encode_header_words((*chat).name),
|
||||
),
|
||||
);
|
||||
if command == 5i32 {
|
||||
let email_to_remove: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
|
||||
if command == 5 {
|
||||
let email_to_remove: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !email_to_remove.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -559,10 +588,13 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if command == 4i32 {
|
||||
do_gossip = 1i32;
|
||||
let email_to_add: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
|
||||
} else if command == 4 {
|
||||
do_gossip = 1;
|
||||
let email_to_add: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !email_to_add.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -574,15 +606,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
email_to_add,
|
||||
),
|
||||
);
|
||||
grpimage = dc_param_get((*chat).param, 'i' as i32, 0 as *const libc::c_char)
|
||||
grpimage = dc_param_get(
|
||||
(*chat).param,
|
||||
DC_PARAM_PROFILE_IMAGE as i32,
|
||||
0 as *const libc::c_char,
|
||||
)
|
||||
}
|
||||
if 0 != dc_param_get_int((*msg).param, 'F' as i32, 0i32) & 0x1i32 {
|
||||
dc_log_info(
|
||||
if 0 != dc_param_get_int((*msg).param, DC_PARAM_CMD_ARG2 as i32, 0) & 0x1 {
|
||||
info!(
|
||||
(*msg).context,
|
||||
0i32,
|
||||
b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
b"vg-member-added\x00" as *const u8 as *const libc::c_char,
|
||||
0,
|
||||
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
|
||||
"vg-member-added",
|
||||
);
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -592,7 +627,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if command == 2i32 {
|
||||
} else if command == 2 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -601,13 +636,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
dc_param_get(
|
||||
(*msg).param,
|
||||
'E' as i32,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if command == 3i32 {
|
||||
grpimage = dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
|
||||
} else if command == 3 {
|
||||
grpimage = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if grpimage.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -619,7 +658,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
}
|
||||
}
|
||||
if command == 8i32 {
|
||||
if command == 8 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -630,7 +669,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
if command == 6i32 {
|
||||
if command == 6 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -638,18 +677,20 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
strdup(b"v1\x00" as *const u8 as *const libc::c_char),
|
||||
),
|
||||
);
|
||||
placeholdertext = dc_stock_str((*factory).context, 43i32)
|
||||
placeholdertext = dc_stock_str((*factory).context, 43)
|
||||
}
|
||||
if command == 7i32 {
|
||||
let step: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'E' as i32, 0 as *const libc::c_char);
|
||||
if command == 7 {
|
||||
let step: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !step.is_null() {
|
||||
dc_log_info(
|
||||
info!(
|
||||
(*msg).context,
|
||||
0i32,
|
||||
b"sending secure-join message \'%s\' >>>>>>>>>>>>>>>>>>>>>>>>>\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
step,
|
||||
0,
|
||||
"sending secure-join message \'{}\' >>>>>>>>>>>>>>>>>>>>>>>>>",
|
||||
as_str(step),
|
||||
);
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -658,8 +699,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
step,
|
||||
),
|
||||
);
|
||||
let param2: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'F' as i32, 0 as *const libc::c_char);
|
||||
let param2: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG2 as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !param2.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -667,12 +711,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
if strcmp(
|
||||
step,
|
||||
b"vg-request-with-auth\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
) == 0
|
||||
|| strcmp(
|
||||
step,
|
||||
b"vc-request-with-auth\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
) == 0i32
|
||||
) == 0
|
||||
{
|
||||
strdup(
|
||||
b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -687,8 +731,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
let fingerprint: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'G' as i32, 0 as *const libc::c_char);
|
||||
let fingerprint: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG3 as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !fingerprint.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -701,8 +748,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
let grpid: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'H' as i32, 0 as *const libc::c_char);
|
||||
let grpid: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_CMD_ARG4 as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !grpid.is_null() {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
@@ -718,8 +768,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
if !grpimage.is_null() {
|
||||
let mut meta: *mut dc_msg_t = dc_msg_new_untyped((*factory).context);
|
||||
(*meta).type_0 = 20i32;
|
||||
dc_param_set((*meta).param, 'f' as i32, grpimage);
|
||||
(*meta).type_0 = DC_MSG_IMAGE as libc::c_int;
|
||||
dc_param_set((*meta).param, DC_PARAM_FILE as i32, grpimage);
|
||||
let mut filename_as_sent: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
meta_part = build_body_file(
|
||||
meta,
|
||||
@@ -737,8 +787,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
dc_msg_unref(meta);
|
||||
}
|
||||
if (*msg).type_0 == 41i32 || (*msg).type_0 == 40i32 || (*msg).type_0 == 50i32 {
|
||||
if (*msg).type_0 == 41i32 {
|
||||
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
|
||||
{
|
||||
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -747,8 +800,9 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
),
|
||||
);
|
||||
}
|
||||
let duration_ms: libc::c_int = dc_param_get_int((*msg).param, 'd' as i32, 0i32);
|
||||
if duration_ms > 0i32 {
|
||||
let duration_ms: libc::c_int =
|
||||
dc_param_get_int((*msg).param, DC_PARAM_DURATION as i32, 0);
|
||||
if duration_ms > 0 {
|
||||
mailimf_fields_add(
|
||||
imf_fields,
|
||||
mailimf_field_new_custom(
|
||||
@@ -761,7 +815,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
);
|
||||
}
|
||||
}
|
||||
afwd_email = dc_param_exists((*msg).param, 'a' as i32);
|
||||
afwd_email = dc_param_exists((*msg).param, DC_PARAM_FORWARDED as i32);
|
||||
let mut fwdhint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if 0 != afwd_email {
|
||||
fwdhint = dc_strdup(
|
||||
@@ -813,18 +867,18 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
free(fwdhint as *mut libc::c_void);
|
||||
free(placeholdertext as *mut libc::c_void);
|
||||
/* add attachment part */
|
||||
if (*msg).type_0 == 20i32
|
||||
|| (*msg).type_0 == 21i32
|
||||
|| (*msg).type_0 == 40i32
|
||||
|| (*msg).type_0 == 41i32
|
||||
|| (*msg).type_0 == 50i32
|
||||
|| (*msg).type_0 == 60i32
|
||||
if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_AUDIO as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_VOICE as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_VIDEO as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_FILE as libc::c_int
|
||||
{
|
||||
if 0 == is_file_size_okay(msg) {
|
||||
let error: *mut libc::c_char = dc_mprintf(
|
||||
b"Message exceeds the recommended %i MB.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
24i32 * 1024i32 * 1024i32 / 4i32 * 3i32 / 1000i32 / 1000i32,
|
||||
24 * 1024 * 1024 / 4 * 3 / 1000 / 1000,
|
||||
);
|
||||
set_error(factory, error);
|
||||
free(error as *mut libc::c_void);
|
||||
@@ -844,7 +898,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
match current_block {
|
||||
11328123142868406523 => {}
|
||||
_ => {
|
||||
if parts == 0i32 {
|
||||
if parts == 0 {
|
||||
set_error(
|
||||
factory,
|
||||
b"Empty message.\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -887,8 +941,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
}
|
||||
|
||||
if 0 != dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) {
|
||||
let mut last_added_location_id: uint32_t = 0i32 as uint32_t;
|
||||
if dc_is_sending_locations_to_chat((*msg).context, (*msg).chat_id) {
|
||||
let mut last_added_location_id: uint32_t = 0 as uint32_t;
|
||||
let kml_file: *mut libc::c_char = dc_get_location_kml(
|
||||
(*msg).context,
|
||||
(*msg).chat_id,
|
||||
@@ -943,12 +997,12 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
mailmime_add_part(message, multipart);
|
||||
let p1: *mut libc::c_char;
|
||||
let p2: *mut libc::c_char;
|
||||
if 0 != dc_param_get_int((*(*factory).msg).param, 'c' as i32, 0i32) {
|
||||
p1 = dc_stock_str((*factory).context, 24i32)
|
||||
if 0 != dc_param_get_int((*(*factory).msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 0) {
|
||||
p1 = dc_stock_str((*factory).context, 24)
|
||||
} else {
|
||||
p1 = dc_msg_get_summarytext((*factory).msg, 32i32)
|
||||
p1 = dc_msg_get_summarytext((*factory).msg, 32)
|
||||
}
|
||||
p2 = dc_stock_str_repl_string((*factory).context, 32i32, p1);
|
||||
p2 = dc_stock_str_repl_string((*factory).context, 32, p1);
|
||||
message_text = dc_mprintf(b"%s\r\n\x00" as *const u8 as *const libc::c_char, p2);
|
||||
free(p2 as *mut libc::c_void);
|
||||
free(p1 as *mut libc::c_void);
|
||||
@@ -968,7 +1022,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
let mach_mime_part: *mut mailmime = mailmime_new_empty(content_type_0, mime_fields_0);
|
||||
mailmime_set_body_text(mach_mime_part, message_text2, strlen(message_text2));
|
||||
mailmime_add_part(multipart, mach_mime_part);
|
||||
force_plaintext = 2i32;
|
||||
force_plaintext = 2;
|
||||
current_block = 9952640327414195044;
|
||||
} else {
|
||||
set_error(
|
||||
@@ -983,7 +1037,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
if (*factory).loaded as libc::c_uint
|
||||
== DC_MF_MDN_LOADED as libc::c_int as libc::c_uint
|
||||
{
|
||||
let e: *mut libc::c_char = dc_stock_str((*factory).context, 31i32);
|
||||
let e: *mut libc::c_char = dc_stock_str((*factory).context, 31);
|
||||
subject_str =
|
||||
dc_mprintf(b"Chat: %s\x00" as *const u8 as *const libc::c_char, e);
|
||||
free(e as *mut libc::c_void);
|
||||
@@ -1019,7 +1073,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
0 as *mut mailimf_optional_field,
|
||||
),
|
||||
);
|
||||
if force_plaintext != 2i32 {
|
||||
if force_plaintext != 2 {
|
||||
dc_e2ee_encrypt(
|
||||
(*factory).context,
|
||||
(*factory).recipients_addr,
|
||||
@@ -1032,14 +1086,14 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
);
|
||||
}
|
||||
if 0 != e2ee_helper.encryption_successfull {
|
||||
(*factory).out_encrypted = 1i32;
|
||||
(*factory).out_encrypted = 1;
|
||||
if 0 != do_gossip {
|
||||
(*factory).out_gossiped = 1i32
|
||||
(*factory).out_gossiped = 1
|
||||
}
|
||||
}
|
||||
(*factory).out = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char);
|
||||
mailmime_write_mem((*factory).out, &mut col, message);
|
||||
success = 1i32
|
||||
success = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1056,22 +1110,24 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc:
|
||||
}
|
||||
|
||||
unsafe fn get_subject(
|
||||
chat: *const dc_chat_t,
|
||||
chat: *const Chat,
|
||||
msg: *const dc_msg_t,
|
||||
afwd_email: libc::c_int,
|
||||
) -> *mut libc::c_char {
|
||||
let context = (*chat).context;
|
||||
let ret: *mut libc::c_char;
|
||||
let raw_subject: *mut libc::c_char =
|
||||
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32i32, context);
|
||||
dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, (*msg).param, 32, context);
|
||||
let fwd: *const libc::c_char = if 0 != afwd_email {
|
||||
b"Fwd: \x00" as *const u8 as *const libc::c_char
|
||||
} else {
|
||||
b"\x00" as *const u8 as *const libc::c_char
|
||||
};
|
||||
if dc_param_get_int((*msg).param, 'S' as i32, 0i32) == 6i32 {
|
||||
ret = dc_stock_str(context, 42i32)
|
||||
} else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 {
|
||||
if dc_param_get_int((*msg).param, DC_PARAM_CMD as i32, 0) == 6 {
|
||||
ret = dc_stock_str(context, 42)
|
||||
} else if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int
|
||||
|| (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int
|
||||
{
|
||||
ret = dc_mprintf(
|
||||
b"Chat: %s: %s%s\x00" as *const u8 as *const libc::c_char,
|
||||
(*chat).name,
|
||||
@@ -1128,14 +1184,17 @@ unsafe fn build_body_file(
|
||||
let mut mime_sub: *mut mailmime = 0 as *mut mailmime;
|
||||
let content: *mut mailmime_content;
|
||||
let pathNfilename: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char);
|
||||
let mut mimetype: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'm' as i32, 0 as *const libc::c_char);
|
||||
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
|
||||
let mut mimetype: *mut libc::c_char = dc_param_get(
|
||||
(*msg).param,
|
||||
DC_PARAM_MIMETYPE as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
let suffix: *mut libc::c_char = dc_get_filesuffix_lc(pathNfilename);
|
||||
let mut filename_to_send: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut filename_encoded: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if !pathNfilename.is_null() {
|
||||
if (*msg).type_0 == 41i32 {
|
||||
if (*msg).type_0 == DC_MSG_VOICE as libc::c_int {
|
||||
let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0);
|
||||
|
||||
let suffix = if !suffix.is_null() {
|
||||
@@ -1146,10 +1205,12 @@ unsafe fn build_body_file(
|
||||
let res = ts
|
||||
.format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix))
|
||||
.to_string();
|
||||
filename_to_send = strdup(to_cstring(res).as_ptr());
|
||||
} else if (*msg).type_0 == 40i32 {
|
||||
filename_to_send = to_cstring(res);
|
||||
} else if (*msg).type_0 == DC_MSG_AUDIO as libc::c_int {
|
||||
filename_to_send = dc_get_filename(pathNfilename)
|
||||
} else if (*msg).type_0 == 20i32 || (*msg).type_0 == 21i32 {
|
||||
} else if (*msg).type_0 == DC_MSG_IMAGE as libc::c_int
|
||||
|| (*msg).type_0 == DC_MSG_GIF as libc::c_int
|
||||
{
|
||||
if base_name.is_null() {
|
||||
base_name = b"image\x00" as *const u8 as *const libc::c_char
|
||||
}
|
||||
@@ -1162,7 +1223,7 @@ unsafe fn build_body_file(
|
||||
b"dat\x00" as *const u8 as *const libc::c_char
|
||||
},
|
||||
)
|
||||
} else if (*msg).type_0 == 50i32 {
|
||||
} else if (*msg).type_0 == DC_MSG_VIDEO as libc::c_int {
|
||||
filename_to_send = dc_mprintf(
|
||||
b"video.%s\x00" as *const u8 as *const libc::c_char,
|
||||
if !suffix.is_null() {
|
||||
@@ -1178,14 +1239,14 @@ unsafe fn build_body_file(
|
||||
if suffix.is_null() {
|
||||
mimetype =
|
||||
dc_strdup(b"application/octet-stream\x00" as *const u8 as *const libc::c_char)
|
||||
} else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
} else if strcmp(suffix, b"png\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
mimetype = dc_strdup(b"image/png\x00" as *const u8 as *const libc::c_char)
|
||||
} else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
} else if strcmp(suffix, b"jpg\x00" as *const u8 as *const libc::c_char) == 0
|
||||
|| strcmp(suffix, b"jpeg\x00" as *const u8 as *const libc::c_char) == 0
|
||||
|| strcmp(suffix, b"jpe\x00" as *const u8 as *const libc::c_char) == 0
|
||||
{
|
||||
mimetype = dc_strdup(b"image/jpeg\x00" as *const u8 as *const libc::c_char)
|
||||
} else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0i32 {
|
||||
} else if strcmp(suffix, b"gif\x00" as *const u8 as *const libc::c_char) == 0 {
|
||||
mimetype = dc_strdup(b"image/gif\x00" as *const u8 as *const libc::c_char)
|
||||
} else {
|
||||
mimetype =
|
||||
@@ -1228,7 +1289,7 @@ unsafe fn build_body_file(
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut libc::c_char,
|
||||
0 as *mut libc::c_char,
|
||||
0i32 as size_t,
|
||||
0 as size_t,
|
||||
mailmime_parameter_new(
|
||||
strdup(
|
||||
b"filename*\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -1285,12 +1346,12 @@ unsafe fn build_body_file(
|
||||
******************************************************************************/
|
||||
|
||||
unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> libc::c_int {
|
||||
let mut file_size_okay: libc::c_int = 1i32;
|
||||
let mut file_size_okay: libc::c_int = 1;
|
||||
let pathNfilename: *mut libc::c_char =
|
||||
dc_param_get((*msg).param, 'f' as i32, 0 as *const libc::c_char);
|
||||
dc_param_get((*msg).param, DC_PARAM_FILE as i32, 0 as *const libc::c_char);
|
||||
let bytes: uint64_t = dc_get_filebytes((*msg).context, pathNfilename);
|
||||
if bytes > (49i32 * 1024i32 * 1024i32 / 4i32 * 3i32) as libc::c_ulonglong {
|
||||
file_size_okay = 0i32
|
||||
if bytes > (49 * 1024 * 1024 / 4 * 3) as libc::c_ulonglong {
|
||||
file_size_okay = 0;
|
||||
}
|
||||
free(pathNfilename as *mut libc::c_void);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
|
||||
use charset::Charset;
|
||||
use mmime::mailimf::*;
|
||||
@@ -15,7 +15,6 @@ use crate::context::Context;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_location::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_param::*;
|
||||
use crate::dc_simplify::*;
|
||||
use crate::dc_stock::*;
|
||||
@@ -176,10 +175,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
&mut (*mimeparser).e2ee_helper,
|
||||
);
|
||||
dc_mimeparser_parse_mime_recursive(mimeparser, (*mimeparser).mimeroot);
|
||||
let field: *mut mailimf_field = dc_mimeparser_lookup_field(
|
||||
mimeparser,
|
||||
b"Subject\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, "Subject");
|
||||
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)
|
||||
@@ -192,12 +188,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
{
|
||||
(*mimeparser).is_send_by_messenger = 1i32
|
||||
}
|
||||
if !dc_mimeparser_lookup_field(
|
||||
mimeparser,
|
||||
b"Autocrypt-Setup-Message\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() {
|
||||
let mut i: libc::c_int;
|
||||
let mut has_setup_file: libc::c_int = 0i32;
|
||||
i = 0i32;
|
||||
@@ -238,11 +229,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !dc_mimeparser_lookup_field(
|
||||
mimeparser,
|
||||
b"Chat-Group-Image\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null()
|
||||
if !dc_mimeparser_lookup_field(mimeparser, "Chat-Group-Image").is_null()
|
||||
&& carray_count((*mimeparser).parts) >= 1i32 as libc::c_uint
|
||||
{
|
||||
let textpart: *mut dc_mimepart_t =
|
||||
@@ -336,7 +323,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
while i_1 < icnt_0 {
|
||||
let part_2: *mut dc_mimepart_t =
|
||||
carray_get((*mimeparser).parts, i_1 as libc::c_uint) as *mut dc_mimepart_t;
|
||||
dc_param_set_int((*part_2).param, 'a' as i32, 1i32);
|
||||
dc_param_set_int((*part_2).param, DC_PARAM_FORWARDED as i32, 1);
|
||||
i_1 += 1
|
||||
}
|
||||
}
|
||||
@@ -361,7 +348,7 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
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 {
|
||||
dc_param_set_int((*part_3).param, 'd' as i32, duration_ms);
|
||||
dc_param_set_int((*part_3).param, DC_PARAM_DURATION as i32, duration_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,10 +371,8 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
{
|
||||
let dn_to_addr: *mut libc::c_char = mailimf_find_first_addr(mb_list);
|
||||
if !dn_to_addr.is_null() {
|
||||
let from_field: *mut mailimf_field = dc_mimeparser_lookup_field(
|
||||
mimeparser,
|
||||
b"From\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let from_field: *mut mailimf_field =
|
||||
dc_mimeparser_lookup_field(mimeparser, "From");
|
||||
if !from_field.is_null()
|
||||
&& (*from_field).fld_type == MAILIMF_FIELD_FROM as libc::c_int
|
||||
&& !(*from_field).fld_data.fld_from.is_null()
|
||||
@@ -400,7 +385,11 @@ pub unsafe fn dc_mimeparser_parse(
|
||||
let part_4: *mut dc_mimepart_t =
|
||||
dc_mimeparser_get_last_nonmeta(mimeparser);
|
||||
if !part_4.is_null() {
|
||||
dc_param_set_int((*part_4).param, 'r' as i32, 1i32);
|
||||
dc_param_set_int(
|
||||
(*part_4).param,
|
||||
DC_PARAM_WANTS_MDN as i32,
|
||||
1,
|
||||
);
|
||||
}
|
||||
}
|
||||
free(from_addr as *mut libc::c_void);
|
||||
@@ -488,13 +477,14 @@ pub unsafe fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> *
|
||||
}
|
||||
|
||||
/* the following functions can be used only after a call to dc_mimeparser_parse() */
|
||||
|
||||
pub fn dc_mimeparser_lookup_field(
|
||||
mimeparser: &dc_mimeparser_t,
|
||||
field_name: *const libc::c_char,
|
||||
field_name: &str,
|
||||
) -> *mut mailimf_field {
|
||||
mimeparser
|
||||
.header
|
||||
.get(as_str(field_name))
|
||||
.get(field_name)
|
||||
.map(|v| *v)
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
@@ -544,11 +534,9 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
b"rfc822-headers\x00" as *const u8 as *const libc::c_char,
|
||||
) == 0i32
|
||||
{
|
||||
dc_log_info(
|
||||
info!(
|
||||
(*mimeparser).context,
|
||||
0i32,
|
||||
b"Protected headers found in text/rfc822-headers attachment: Will be ignored.\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
0, "Protected headers found in text/rfc822-headers attachment: Will be ignored.",
|
||||
);
|
||||
return 0i32;
|
||||
}
|
||||
@@ -562,11 +550,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
) != MAILIMF_NO_ERROR as libc::c_int
|
||||
|| (*mimeparser).header_protected.is_null()
|
||||
{
|
||||
dc_log_warning(
|
||||
(*mimeparser).context,
|
||||
0i32,
|
||||
b"Protected headers parsing error.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!((*mimeparser).context, 0, "Protected headers parsing error.",);
|
||||
} else {
|
||||
hash_header(
|
||||
&mut (*mimeparser).header,
|
||||
@@ -575,9 +559,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dc_log_info((*mimeparser).context, 0i32,
|
||||
b"Protected headers found in MIME header: Will be ignored as we already found an outer one.\x00"
|
||||
as *const u8 as *const libc::c_char);
|
||||
info!(
|
||||
(*mimeparser).context,
|
||||
0,
|
||||
"Protected headers found in MIME header: Will be ignored as we already found an outer one."
|
||||
);
|
||||
}
|
||||
}
|
||||
match (*mime).mm_type {
|
||||
@@ -769,10 +755,11 @@ unsafe fn dc_mimeparser_parse_mime_recursive(
|
||||
}
|
||||
}
|
||||
if plain_cnt == 1i32 && html_cnt == 1i32 {
|
||||
dc_log_warning((*mimeparser).context, 0i32,
|
||||
b"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted.\x00"
|
||||
as *const u8 as
|
||||
*const libc::c_char);
|
||||
warn!(
|
||||
(*mimeparser).context,
|
||||
0i32,
|
||||
"HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted."
|
||||
);
|
||||
skip_part = html_part
|
||||
}
|
||||
cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first;
|
||||
@@ -850,6 +837,7 @@ unsafe fn hash_header(
|
||||
18 => key = b"References\x00" as *const u8 as *const libc::c_char,
|
||||
19 => key = b"Subject\x00" as *const u8 as *const libc::c_char,
|
||||
22 => {
|
||||
// MAILIMF_FIELD_OPTIONAL_FIELD
|
||||
let optional_field: *const mailimf_optional_field =
|
||||
(*field).fld_data.fld_optional_field;
|
||||
if !optional_field.is_null() {
|
||||
@@ -859,17 +847,16 @@ unsafe fn hash_header(
|
||||
_ => {}
|
||||
}
|
||||
if !key.is_null() {
|
||||
let key_len: libc::c_int = strlen(key) as libc::c_int;
|
||||
if out.contains_key(as_str(key)) {
|
||||
if (*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as libc::c_int
|
||||
|| key_len > 5i32
|
||||
&& strncasecmp(key, b"Chat-\x00" as *const u8 as *const libc::c_char, 5)
|
||||
== 0i32
|
||||
{
|
||||
out.insert(to_string(key), field);
|
||||
}
|
||||
} else {
|
||||
out.insert(to_string(key), field);
|
||||
// XXX the optional field sometimes contains invalid UTF8
|
||||
// which should not happen (according to the mime standard).
|
||||
// This might point to a bug in our mime parsing/processing
|
||||
// logic. As mmime/dc_mimeparser is scheduled fore replacement
|
||||
// anyway we just use a lossy conversion.
|
||||
let key_r = &to_string_lossy(key);
|
||||
if !out.contains_key(key_r) || // key already exists, only overwrite known types (protected headers)
|
||||
(*field).fld_type != MAILIMF_FIELD_OPTIONAL_FIELD as i32 || key_r.starts_with("Chat-")
|
||||
{
|
||||
out.insert(key_r.to_string(), field);
|
||||
}
|
||||
}
|
||||
cur1 = if !cur1.is_null() {
|
||||
@@ -1221,19 +1208,16 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
current_block = 8795901732489102124;
|
||||
} else {
|
||||
decoded_data_bytes = res.len();
|
||||
let res_c = CString::new(res.as_bytes()).unwrap();
|
||||
decoded_data = strdup(res_c.as_ptr());
|
||||
decoded_data = res.as_ptr() as *const libc::c_char;
|
||||
current_block = 17788412896529399552;
|
||||
}
|
||||
} else {
|
||||
dc_log_warning(
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
b"Cannot convert %i bytes from \"%s\" to \"utf-8\".\x00"
|
||||
as *const u8
|
||||
as *const libc::c_char,
|
||||
0,
|
||||
"Cannot convert {} bytes from \"{}\" to \"utf-8\".",
|
||||
decoded_data_bytes as libc::c_int,
|
||||
charset,
|
||||
as_str(charset),
|
||||
);
|
||||
current_block = 17788412896529399552;
|
||||
}
|
||||
@@ -1360,8 +1344,9 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
}
|
||||
if !filename_parts.is_empty() {
|
||||
free(desired_filename as *mut libc::c_void);
|
||||
desired_filename =
|
||||
dc_decode_ext_header(to_cstring(filename_parts).as_ptr());
|
||||
let parts_c = to_cstring(filename_parts);
|
||||
desired_filename = dc_decode_ext_header(parts_c);
|
||||
free(parts_c as *mut _);
|
||||
}
|
||||
if desired_filename.is_null() {
|
||||
let param = mailmime_find_ct_parameter(
|
||||
@@ -1459,7 +1444,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known(
|
||||
}
|
||||
}
|
||||
}
|
||||
/* add object? (we do not add all objetcs, eg. signatures etc. are ignored) */
|
||||
/* add object? (we do not add all objects, eg. signatures etc. are ignored) */
|
||||
dc_simplify_unref(simplifier);
|
||||
if !transfer_decoding_buffer.is_null() {
|
||||
mmap_string_unref(transfer_decoding_buffer);
|
||||
@@ -1505,8 +1490,8 @@ unsafe fn do_add_single_file_part(
|
||||
(*part).type_0 = msg_type;
|
||||
(*part).int_mimetype = mime_type;
|
||||
(*part).bytes = decoded_data_bytes as libc::c_int;
|
||||
dc_param_set((*part).param, 'f' as i32, pathNfilename);
|
||||
dc_param_set((*part).param, 'm' as i32, raw_mime);
|
||||
dc_param_set((*part).param, DC_PARAM_FILE as i32, pathNfilename);
|
||||
dc_param_set((*part).param, DC_PARAM_MIMETYPE as i32, raw_mime);
|
||||
if mime_type == 80i32 {
|
||||
let mut w: uint32_t = 0i32 as uint32_t;
|
||||
let mut h: uint32_t = 0i32 as uint32_t;
|
||||
@@ -1516,8 +1501,8 @@ unsafe fn do_add_single_file_part(
|
||||
&mut w,
|
||||
&mut h,
|
||||
) {
|
||||
dc_param_set_int((*part).param, 'w' as i32, w as int32_t);
|
||||
dc_param_set_int((*part).param, 'h' as i32, h as int32_t);
|
||||
dc_param_set_int((*part).param, DC_PARAM_WIDTH as i32, w as int32_t);
|
||||
dc_param_set_int((*part).param, DC_PARAM_HEIGHT as i32, h as int32_t);
|
||||
}
|
||||
}
|
||||
do_add_single_part(parser, part);
|
||||
@@ -1530,9 +1515,9 @@ unsafe fn do_add_single_file_part(
|
||||
|
||||
unsafe fn do_add_single_part(parser: &dc_mimeparser_t, part: *mut dc_mimepart_t) {
|
||||
if 0 != (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 {
|
||||
dc_param_set_int((*part).param, 'c' as i32, 1i32);
|
||||
dc_param_set_int((*part).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
|
||||
} else if 0 != (*parser).e2ee_helper.encrypted {
|
||||
dc_param_set_int((*part).param, 'e' as i32, 0x2i32);
|
||||
dc_param_set_int((*part).param, DC_PARAM_ERRONEOUS_E2EE as i32, 0x2);
|
||||
}
|
||||
carray_add(
|
||||
(*parser).parts,
|
||||
@@ -1627,12 +1612,7 @@ pub unsafe fn mailmime_transfer_decode(
|
||||
|
||||
// TODO should return bool /rtn
|
||||
pub unsafe fn dc_mimeparser_is_mailinglist_message(mimeparser: &dc_mimeparser_t) -> libc::c_int {
|
||||
if !dc_mimeparser_lookup_field(
|
||||
&mimeparser,
|
||||
b"List-Id\x00" as *const u8 as *const libc::c_char,
|
||||
)
|
||||
.is_null()
|
||||
{
|
||||
if !dc_mimeparser_lookup_field(&mimeparser, "List-Id").is_null() {
|
||||
return 1i32;
|
||||
}
|
||||
let precedence: *mut mailimf_optional_field = dc_mimeparser_lookup_optional_field(
|
||||
|
||||
@@ -2,44 +2,44 @@ use crate::constants::*;
|
||||
use crate::context::*;
|
||||
use crate::dc_job::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::types::*;
|
||||
|
||||
pub unsafe fn dc_do_heuristics_moves(
|
||||
context: &Context,
|
||||
folder: *const libc::c_char,
|
||||
msg_id: uint32_t,
|
||||
) {
|
||||
// for already seen messages, folder may be different from msg->folder
|
||||
let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t;
|
||||
let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
|
||||
if !(dc_sqlite3_get_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"mvbox_move\x00" as *const u8 as *const libc::c_char,
|
||||
1i32,
|
||||
) == 0i32)
|
||||
pub unsafe fn dc_do_heuristics_moves(context: &Context, folder: &str, msg_id: u32) {
|
||||
if context
|
||||
.sql
|
||||
.get_config_int(context, "mvbox_move")
|
||||
.unwrap_or_else(|| 1)
|
||||
== 0
|
||||
{
|
||||
if !(0 == dc_is_inbox(context, folder) && 0 == dc_is_sentbox(context, folder)) {
|
||||
msg = dc_msg_new_load(context, msg_id);
|
||||
if !(0 != dc_msg_is_setupmessage(msg)) {
|
||||
// do not move setup messages;
|
||||
// there may be a non-delta device that wants to handle it
|
||||
if 0 != dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_STAY);
|
||||
} else if 0 != (*msg).is_dc_message {
|
||||
dc_job_add(
|
||||
context,
|
||||
200i32,
|
||||
(*msg).id as libc::c_int,
|
||||
0 as *const libc::c_char,
|
||||
0i32,
|
||||
);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if !dc_is_inbox(context, folder) && !dc_is_sentbox(context, folder) {
|
||||
return;
|
||||
}
|
||||
|
||||
let msg = dc_msg_new_load(context, msg_id);
|
||||
if dc_msg_is_setupmessage(msg) {
|
||||
// do not move setup messages;
|
||||
// there may be a non-delta device that wants to handle it
|
||||
dc_msg_unref(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if dc_is_mvbox(context, folder) {
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_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,
|
||||
0 as *const libc::c_char,
|
||||
0,
|
||||
);
|
||||
dc_update_msg_move_state(context, (*msg).rfc724_mid, DC_MOVE_STATE_MOVING);
|
||||
}
|
||||
|
||||
dc_msg_unref(msg);
|
||||
}
|
||||
|
||||
1158
src/dc_msg.rs
1158
src/dc_msg.rs
File diff suppressed because it is too large
Load Diff
@@ -3,67 +3,69 @@ use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
/// for msgs and jobs
|
||||
pub const DC_PARAM_FILE: char = 'f';
|
||||
pub const DC_PARAM_FILE: char = 'f'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_WIDTH: char = 'w';
|
||||
pub const DC_PARAM_WIDTH: char = 'w'; // int
|
||||
/// for msgs
|
||||
pub const DC_PARAM_HEIGHT: char = 'h';
|
||||
pub const DC_PARAM_HEIGHT: char = 'h'; // int
|
||||
/// for msgs
|
||||
pub const DC_PARAM_DURATION: char = 'd';
|
||||
pub const DC_PARAM_DURATION: char = 'd'; // int
|
||||
/// for msgs
|
||||
pub const DC_PARAM_MIMETYPE: char = 'm';
|
||||
pub const DC_PARAM_MIMETYPE: char = 'm'; // string
|
||||
/// for msgs: incoming: message is encryoted, outgoing: guarantee E2EE or the message is not send
|
||||
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c';
|
||||
pub const DC_PARAM_GUARANTEE_E2EE: char = 'c'; // int (bool?)
|
||||
/// for msgs: decrypted with validation errors or without mutual set, if neither 'c' nor 'e' are preset, the messages is only transport encrypted
|
||||
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e';
|
||||
pub const DC_PARAM_ERRONEOUS_E2EE: char = 'e'; // int
|
||||
/// for msgs: force unencrypted message, either DC_FP_ADD_AUTOCRYPT_HEADER (1), DC_FP_NO_AUTOCRYPT_HEADER (2) or 0
|
||||
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u';
|
||||
/// for msgs: an incoming message which requestes a MDN (aka read receipt)
|
||||
pub const DC_PARAM_WANTS_MDN: char = 'r';
|
||||
pub const DC_PARAM_FORCE_PLAINTEXT: char = 'u'; // int (bool?)
|
||||
/// for msgs: an incoming message which requests a MDN (aka read receipt)
|
||||
pub const DC_PARAM_WANTS_MDN: char = 'r'; // int (bool?)
|
||||
/// for msgs
|
||||
pub const DC_PARAM_FORWARDED: char = 'a';
|
||||
pub const DC_PARAM_FORWARDED: char = 'a'; // int (bool?)
|
||||
/// for msgs
|
||||
pub const DC_PARAM_CMD: char = 'S';
|
||||
pub const DC_PARAM_CMD: char = 'S'; // int
|
||||
/// for msgs
|
||||
pub const DC_PARAM_CMD_ARG: char = 'E';
|
||||
pub const DC_PARAM_CMD_ARG: char = 'E'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_CMD_ARG2: char = 'F';
|
||||
pub const DC_PARAM_CMD_ARG2: char = 'F'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_CMD_ARG3: char = 'G';
|
||||
pub const DC_PARAM_CMD_ARG3: char = 'G'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_CMD_ARG4: char = 'H';
|
||||
pub const DC_PARAM_CMD_ARG4: char = 'H'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_ERROR: char = 'L';
|
||||
pub const DC_PARAM_ERROR: char = 'L'; // string
|
||||
/// for msgs in PREPARING: space-separated list of message IDs of forwarded copies
|
||||
pub const DC_PARAM_PREP_FORWARDS: char = 'P';
|
||||
pub const DC_PARAM_PREP_FORWARDS: char = 'P'; // string
|
||||
/// for msgs
|
||||
pub const DC_PARAM_SET_LATITUDE: char = 'l';
|
||||
pub const DC_PARAM_SET_LATITUDE: char = 'l'; // float
|
||||
/// for msgs
|
||||
pub const DC_PARAM_SET_LONGITUDE: char = 'n';
|
||||
pub const DC_PARAM_SET_LONGITUDE: char = 'n'; // float
|
||||
|
||||
/// for jobs
|
||||
pub const DC_PARAM_SERVER_FOLDER: char = 'Z';
|
||||
pub const DC_PARAM_SERVER_FOLDER: char = 'Z'; // string
|
||||
/// for jobs
|
||||
pub const DC_PARAM_SERVER_UID: char = 'z';
|
||||
pub const DC_PARAM_SERVER_UID: char = 'z'; // int
|
||||
/// for jobs
|
||||
pub const DC_PARAM_ALSO_MOVE: char = 'M';
|
||||
pub const DC_PARAM_ALSO_MOVE: char = 'M'; // int (bool?)
|
||||
/// for jobs: space-separated list of message recipients
|
||||
pub const DC_PARAM_RECIPIENTS: char = 'R';
|
||||
pub const DC_PARAM_RECIPIENTS: char = 'R'; // stringap
|
||||
/// for groups
|
||||
pub const DC_PARAM_UNPROMOTED: char = 'U';
|
||||
pub const DC_PARAM_UNPROMOTED: char = 'U'; // int (bool?)
|
||||
/// for groups and contacts
|
||||
pub const DC_PARAM_PROFILE_IMAGE: char = 'i';
|
||||
pub const DC_PARAM_PROFILE_IMAGE: char = 'i'; // string (bytes?)
|
||||
/// for chats
|
||||
pub const DC_PARAM_SELFTALK: char = 'K';
|
||||
|
||||
// missing: 's', 'x', 'g'
|
||||
|
||||
// values for DC_PARAM_FORCE_PLAINTEXT
|
||||
pub const DC_FP_ADD_AUTOCRYPT_HEADER: u8 = 1;
|
||||
pub const DC_FP_NO_AUTOCRYPT_HEADER: u8 = 2;
|
||||
|
||||
/// An object for handling key=value parameter lists; for the key, curently only
|
||||
/// An object for handling key=value parameter lists; for the key, currently only
|
||||
/// a single character is allowed.
|
||||
///
|
||||
/// The object is used eg. by dc_chat_t or dc_msg_t, for readable paramter names,
|
||||
/// The object is used eg. by Chat or dc_msg_t, for readable parameter names,
|
||||
/// these classes define some DC_PARAM_* constantats.
|
||||
///
|
||||
/// Only for library-internal use.
|
||||
|
||||
262
src/dc_qr.rs
262
src/dc_qr.rs
@@ -1,7 +1,6 @@
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_param::*;
|
||||
use crate::dc_strencode::*;
|
||||
@@ -22,7 +21,7 @@ use crate::x::*;
|
||||
// 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 OK_TO_CONTINUE = true;
|
||||
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;
|
||||
@@ -38,12 +37,7 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
let mut grpname: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
(*qr_parsed).state = 0i32;
|
||||
if !qr.is_null() {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Scanned QR code: %s\x00" as *const u8 as *const libc::c_char,
|
||||
qr,
|
||||
);
|
||||
info!(context, 0, "Scanned QR code: {}", as_str(qr),);
|
||||
/* split parameters from the qr code
|
||||
------------------------------------ */
|
||||
if strncasecmp(
|
||||
@@ -62,16 +56,23 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
fragment = fragment.offset(1isize);
|
||||
let param: *mut dc_param_t = dc_param_new();
|
||||
dc_param_set_urlencoded(param, fragment);
|
||||
addr = dc_param_get(param, 'a' as i32, 0 as *const libc::c_char);
|
||||
addr = dc_param_get(param, DC_PARAM_FORWARDED as i32, 0 as *const libc::c_char);
|
||||
if !addr.is_null() {
|
||||
let mut urlencoded: *mut libc::c_char =
|
||||
dc_param_get(param, 'n' as i32, 0 as *const libc::c_char);
|
||||
let mut urlencoded: *mut libc::c_char = dc_param_get(
|
||||
param,
|
||||
DC_PARAM_SET_LONGITUDE as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if !urlencoded.is_null() {
|
||||
name = dc_urldecode(urlencoded);
|
||||
dc_normalize_name(name);
|
||||
free(urlencoded as *mut libc::c_void);
|
||||
}
|
||||
invitenumber = dc_param_get(param, 'i' as i32, 0 as *const libc::c_char);
|
||||
invitenumber = dc_param_get(
|
||||
param,
|
||||
DC_PARAM_PROFILE_IMAGE as i32,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
auth = dc_param_get(param, 's' as i32, 0 as *const libc::c_char);
|
||||
grpid = dc_param_get(param, 'x' as i32, 0 as *const libc::c_char);
|
||||
if !grpid.is_null() {
|
||||
@@ -85,7 +86,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
dc_param_unref(param);
|
||||
}
|
||||
fingerprint = dc_normalize_fingerprint_c(payload);
|
||||
current_block = 5023038348526654800;
|
||||
} else if strncasecmp(
|
||||
qr,
|
||||
b"mailto:\x00" as *const u8 as *const libc::c_char,
|
||||
@@ -100,7 +100,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
*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,
|
||||
@@ -115,7 +114,6 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
*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,
|
||||
@@ -131,12 +129,11 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
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;
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
} else {
|
||||
if strncasecmp(
|
||||
@@ -189,146 +186,111 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
}
|
||||
current_block = 5023038348526654800;
|
||||
}
|
||||
match current_block {
|
||||
16562876845594826114 => {}
|
||||
_ => {
|
||||
/* check the paramters
|
||||
---------------------- */
|
||||
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 = dc_addr_normalize(addr);
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
if !dc_may_be_valid_addr(addr) {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 = dc_strdup(
|
||||
b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char,
|
||||
if OK_TO_CONTINUE {
|
||||
/* 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 = dc_addr_normalize(addr);
|
||||
free(addr as *mut libc::c_void);
|
||||
addr = temp;
|
||||
if !dc_may_be_valid_addr(addr) {
|
||||
(*qr_parsed).state = 400i32;
|
||||
(*qr_parsed).text1 =
|
||||
dc_strdup(b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char);
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if OK_TO_CONTINUE {
|
||||
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,
|
||||
);
|
||||
OK_TO_CONTINUE = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if OK_TO_CONTINUE {
|
||||
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_ptr = if let Some(ref addr) = peerstate.addr {
|
||||
to_cstring(addr)
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
context,
|
||||
0 as *const libc::c_char,
|
||||
addr_ptr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
current_block = 16562876845594826114;
|
||||
free(addr_ptr as *mut _);
|
||||
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 {
|
||||
current_block = 14116432890150942211;
|
||||
(*qr_parsed).text1 = dc_format_fingerprint_c(fingerprint);
|
||||
(*qr_parsed).state = 230i32
|
||||
}
|
||||
} 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 c_addr = peerstate
|
||||
.addr
|
||||
.as_ref()
|
||||
.map(to_cstring)
|
||||
.unwrap_or_default();
|
||||
let addr_ptr = if peerstate.addr.is_some() {
|
||||
c_addr.as_ptr()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
context,
|
||||
0 as *const libc::c_char,
|
||||
addr_ptr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
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 = dc_add_or_lookup_contact(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
|
||||
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
|
||||
(*qr_parsed).auth = dc_strdup(auth)
|
||||
}
|
||||
} else if !addr.is_null() {
|
||||
(*qr_parsed).state = 320i32;
|
||||
(*qr_parsed).id = dc_add_or_lookup_contact(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
)
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 = dc_add_or_lookup_contact(
|
||||
context,
|
||||
name,
|
||||
addr,
|
||||
0x80i32,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
(*qr_parsed).fingerprint = dc_strdup(fingerprint);
|
||||
(*qr_parsed).invitenumber = dc_strdup(invitenumber);
|
||||
(*qr_parsed).auth = dc_strdup(auth)
|
||||
}
|
||||
} else if !addr.is_null() {
|
||||
(*qr_parsed).state = 320i32;
|
||||
(*qr_parsed).id =
|
||||
dc_add_or_lookup_contact(context, name, addr, 0x80i32, 0 as *mut libc::c_int)
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
use mmime::mailimf_types::*;
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
|
||||
use crate::aheader::EncryptPreference;
|
||||
use crate::constants::Event;
|
||||
@@ -8,13 +9,11 @@ use crate::dc_chat::*;
|
||||
use crate::dc_configure::*;
|
||||
use crate::dc_contact::*;
|
||||
use crate::dc_e2ee::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_lot::*;
|
||||
use crate::dc_mimeparser::*;
|
||||
use crate::dc_msg::*;
|
||||
use crate::dc_param::*;
|
||||
use crate::dc_qr::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_stock::*;
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_token::*;
|
||||
@@ -28,22 +27,18 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
context: &Context,
|
||||
group_chat_id: uint32_t,
|
||||
) -> *mut libc::c_char {
|
||||
let current_block: u64;
|
||||
/* =========================================================
|
||||
==== Alice - the inviter side ====
|
||||
==== Step 1 in "Setup verified contact" protocol ====
|
||||
========================================================= */
|
||||
let mut qr: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let self_addr: *mut libc::c_char;
|
||||
let mut self_addr_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut self_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut self_name_urlencoded: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut fingerprint: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
|
||||
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: *mut dc_chat_t = 0 as *mut dc_chat_t;
|
||||
let mut group_name: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
let mut group_name_urlencoded: *mut libc::c_char = 0 as *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 = None;
|
||||
|
||||
dc_ensure_secret_key_exists(context);
|
||||
invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id);
|
||||
@@ -56,110 +51,86 @@ pub unsafe fn dc_get_securejoin_qr(
|
||||
auth = dc_create_id();
|
||||
dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth);
|
||||
}
|
||||
self_addr = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if self_addr.is_null() {
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"Not configured, cannot generate QR code.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
} else {
|
||||
self_name = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"displayname\x00" as *const u8 as *const libc::c_char,
|
||||
b"\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
fingerprint = get_self_fingerprint(context);
|
||||
if !fingerprint.is_null() {
|
||||
self_addr_urlencoded = dc_urlencode(self_addr);
|
||||
self_name_urlencoded = dc_urlencode(self_name);
|
||||
if 0 != group_chat_id {
|
||||
chat = dc_get_chat(context, group_chat_id);
|
||||
if chat.is_null() {
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot get QR-code for chat-id %i\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
group_chat_id,
|
||||
);
|
||||
current_block = 9531737720721467826;
|
||||
} else {
|
||||
group_name = dc_chat_get_name(chat);
|
||||
group_name_urlencoded = dc_urlencode(group_name);
|
||||
qr = dc_mprintf(
|
||||
b"OPENPGP4FPR:%s#a=%s&g=%s&x=%s&i=%s&s=%s\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
fingerprint,
|
||||
self_addr_urlencoded,
|
||||
group_name_urlencoded,
|
||||
(*chat).grpid,
|
||||
invitenumber,
|
||||
auth,
|
||||
);
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
} else {
|
||||
qr = dc_mprintf(
|
||||
b"OPENPGP4FPR:%s#a=%s&n=%s&i=%s&s=%s\x00" as *const u8 as *const libc::c_char,
|
||||
fingerprint,
|
||||
self_addr_urlencoded,
|
||||
self_name_urlencoded,
|
||||
invitenumber,
|
||||
auth,
|
||||
);
|
||||
current_block = 1118134448028020070;
|
||||
}
|
||||
match current_block {
|
||||
9531737720721467826 => {}
|
||||
_ => {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Generated QR code: %s\x00" as *const u8 as *const libc::c_char,
|
||||
qr,
|
||||
);
|
||||
}
|
||||
}
|
||||
let self_addr = context.sql.get_config(context, "configured_addr");
|
||||
|
||||
let cleanup = |fingerprint, chat, group_name, group_name_urlencoded| {
|
||||
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 {
|
||||
to_cstring(qr)
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
}
|
||||
};
|
||||
|
||||
if self_addr.is_none() {
|
||||
error!(context, 0, "Not configured, cannot generate QR code.",);
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
}
|
||||
|
||||
free(self_addr_urlencoded as *mut libc::c_void);
|
||||
free(self_addr as *mut libc::c_void);
|
||||
free(self_name as *mut libc::c_void);
|
||||
free(self_name_urlencoded as *mut libc::c_void);
|
||||
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);
|
||||
return if !qr.is_null() {
|
||||
qr
|
||||
let self_addr = self_addr.unwrap();
|
||||
let self_name = context
|
||||
.sql
|
||||
.get_config(context, "displayname")
|
||||
.unwrap_or_default();
|
||||
|
||||
fingerprint = get_self_fingerprint(context);
|
||||
|
||||
if fingerprint.is_null() {
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
}
|
||||
|
||||
let self_addr_urlencoded = utf8_percent_encode(&self_addr, DEFAULT_ENCODE_SET).to_string();
|
||||
let self_name_urlencoded = utf8_percent_encode(&self_name, DEFAULT_ENCODE_SET).to_string();
|
||||
|
||||
qr = if 0 != group_chat_id {
|
||||
chat = dc_get_chat(context, group_chat_id);
|
||||
if chat.is_null() {
|
||||
error!(
|
||||
context,
|
||||
0, "Cannot get QR-code for chat-id {}", group_chat_id,
|
||||
);
|
||||
return cleanup(fingerprint, chat, group_name, group_name_urlencoded);
|
||||
}
|
||||
|
||||
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 {
|
||||
dc_strdup(0 as *const libc::c_char)
|
||||
Some(format!(
|
||||
"OPENPGP4FPR:{}#a={}&n={}&i={}&s={}",
|
||||
as_str(fingerprint),
|
||||
self_addr_urlencoded,
|
||||
self_name_urlencoded,
|
||||
as_str(invitenumber),
|
||||
as_str(auth),
|
||||
))
|
||||
};
|
||||
|
||||
info!(context, 0, "Generated QR code: {}", qr.as_ref().unwrap());
|
||||
|
||||
cleanup(fingerprint, chat, group_name, group_name_urlencoded)
|
||||
}
|
||||
|
||||
unsafe fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
|
||||
let self_addr = dc_sqlite3_get_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_addr\x00" as *const u8 as *const libc::c_char,
|
||||
0 as *const libc::c_char,
|
||||
);
|
||||
if self_addr.is_null() {
|
||||
return std::ptr::null_mut();
|
||||
}
|
||||
|
||||
if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) {
|
||||
return key.fingerprint_c();
|
||||
fn get_self_fingerprint(context: &Context) -> *mut libc::c_char {
|
||||
if let Some(self_addr) = context.sql.get_config(context, "configured_addr") {
|
||||
if let Some(key) = Key::from_self_public(context, self_addr, &context.sql) {
|
||||
return key.fingerprint_c();
|
||||
}
|
||||
}
|
||||
|
||||
std::ptr::null_mut()
|
||||
@@ -175,29 +146,17 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
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;
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Requesting secure-join ...\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Requesting secure-join ...",);
|
||||
dc_ensure_secret_key_exists(context);
|
||||
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 {
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"Unknown QR code.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
error!(context, 0, "Unknown QR code.",);
|
||||
} else {
|
||||
contact_chat_id = dc_create_chat_by_contact_id(context, (*qr_scan).id);
|
||||
if contact_chat_id == 0i32 as libc::c_uint {
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"Unknown contact.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
error!(context, 0, "Unknown contact.",);
|
||||
} else if !(context
|
||||
.running_state
|
||||
.clone()
|
||||
@@ -214,11 +173,7 @@ pub unsafe fn dc_join_securejoin(context: &Context, qr: *const libc::c_char) ->
|
||||
}
|
||||
if 0 != fingerprint_equals_sender(context, (*qr_scan).fingerprint, contact_chat_id)
|
||||
{
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Taking protocol shortcut.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Taking protocol shortcut.");
|
||||
context.bob.clone().write().unwrap().expects = 6;
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_JOINER_PROGRESS,
|
||||
@@ -312,23 +267,23 @@ unsafe fn send_handshake_msg(
|
||||
step,
|
||||
);
|
||||
(*msg).hidden = 1i32;
|
||||
dc_param_set_int((*msg).param, 'S' as i32, 7i32);
|
||||
dc_param_set((*msg).param, 'E' as i32, step);
|
||||
dc_param_set_int((*msg).param, DC_PARAM_CMD as i32, 7);
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG as i32, step);
|
||||
if !param2.is_null() {
|
||||
dc_param_set((*msg).param, 'F' as i32, param2);
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG2 as i32, param2);
|
||||
}
|
||||
if !fingerprint.is_null() {
|
||||
dc_param_set((*msg).param, 'G' as i32, fingerprint);
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG3 as i32, fingerprint);
|
||||
}
|
||||
if !grpid.is_null() {
|
||||
dc_param_set((*msg).param, 'H' as i32, grpid);
|
||||
dc_param_set((*msg).param, DC_PARAM_CMD_ARG4 as i32, grpid);
|
||||
}
|
||||
if strcmp(step, b"vg-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
|| strcmp(step, b"vc-request\x00" as *const u8 as *const libc::c_char) == 0i32
|
||||
{
|
||||
dc_param_set_int((*msg).param, 'u' as i32, 1i32);
|
||||
dc_param_set_int((*msg).param, DC_PARAM_FORCE_PLAINTEXT as i32, 1);
|
||||
} else {
|
||||
dc_param_set_int((*msg).param, 'c' as i32, 1i32);
|
||||
dc_param_set_int((*msg).param, DC_PARAM_GUARANTEE_E2EE as i32, 1);
|
||||
}
|
||||
dc_send_msg(context, contact_chat_id, msg);
|
||||
dc_msg_unref(msg);
|
||||
@@ -401,17 +356,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
let mut ret: libc::c_int = 0i32;
|
||||
let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t;
|
||||
if !(contact_id <= 9i32 as libc::c_uint) {
|
||||
step = lookup_field(
|
||||
mimeparser,
|
||||
b"Secure-Join\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
step = lookup_field(mimeparser, "Secure-Join");
|
||||
if !step.is_null() {
|
||||
dc_log_info(
|
||||
info!(
|
||||
context,
|
||||
0i32,
|
||||
b">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'%s\' received\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
step,
|
||||
0,
|
||||
">>>>>>>>>>>>>>>>>>>>>>>>> secure-join message \'{}\' received",
|
||||
as_str(step),
|
||||
);
|
||||
join_vg = (strncmp(step, b"vg-\x00" as *const u8 as *const libc::c_char, 3) == 0)
|
||||
as libc::c_int;
|
||||
@@ -438,32 +389,16 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
// send_message() will fail with the error "End-to-end-encryption unavailable unexpectedly.", so, there is no additional check needed here.
|
||||
// verify that the `Secure-Join-Invitenumber:`-header matches invitenumber written to the QR code
|
||||
let invitenumber: *const libc::c_char;
|
||||
invitenumber = lookup_field(
|
||||
mimeparser,
|
||||
b"Secure-Join-Invitenumber\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
invitenumber = lookup_field(mimeparser, "Secure-Join-Invitenumber");
|
||||
if invitenumber.is_null() {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Secure-join denied (invitenumber missing).\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
warn!(context, 0, "Secure-join denied (invitenumber missing).",);
|
||||
current_block = 4378276786830486580;
|
||||
} else if dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) == 0i32 {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Secure-join denied (bad invitenumber).\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
} else if !dc_token_exists(context, DC_TOKEN_INVITENUMBER, invitenumber) {
|
||||
warn!(context, 0, "Secure-join denied (bad invitenumber).",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Secure-join requested.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Secure-join requested.",);
|
||||
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_INVITER_PROGRESS,
|
||||
contact_id as uintptr_t,
|
||||
@@ -500,12 +435,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
};
|
||||
|
||||
if cond {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"auth-required message out of sync.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
warn!(context, 0, "auth-required message out of sync.",);
|
||||
// no error, just aborted somehow or a mail from another handshake
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
@@ -545,11 +475,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
end_bobs_joining(context, 0i32);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
own_fingerprint = get_self_fingerprint(context);
|
||||
context.call_cb(
|
||||
Event::SECUREJOIN_JOINER_PROGRESS,
|
||||
@@ -589,10 +515,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
============================================================ */
|
||||
// verify that Secure-Join-Fingerprint:-header matches the fingerprint of Bob
|
||||
let fingerprint: *const libc::c_char;
|
||||
fingerprint = lookup_field(
|
||||
mimeparser,
|
||||
b"Secure-Join-Fingerprint\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
fingerprint = lookup_field(mimeparser, "Secure-Join-Fingerprint");
|
||||
if fingerprint.is_null() {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
@@ -616,17 +539,10 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Fingerprint verified.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Fingerprint verified.",);
|
||||
// verify that the `Secure-Join-Auth:`-header matches the secret written to the QR code
|
||||
let auth_0: *const libc::c_char;
|
||||
auth_0 = lookup_field(
|
||||
mimeparser,
|
||||
b"Secure-Join-Auth\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
auth_0 = lookup_field(mimeparser, "Secure-Join-Auth");
|
||||
if auth_0.is_null() {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
@@ -634,7 +550,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
b"Auth not provided.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else if dc_token_exists(context, DC_TOKEN_AUTH, auth_0) == 0i32 {
|
||||
} else if !dc_token_exists(context, DC_TOKEN_AUTH, auth_0) {
|
||||
could_not_establish_secure_connection(
|
||||
context,
|
||||
contact_chat_id,
|
||||
@@ -651,11 +567,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_scaleup_contact_origin(context, contact_id, 0x1000000i32);
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Auth verified.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Auth verified.",);
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.call_cb(
|
||||
Event::CONTACTS_CHANGED,
|
||||
@@ -668,10 +580,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
600i32 as uintptr_t,
|
||||
);
|
||||
if 0 != join_vg {
|
||||
grpid = dc_strdup(lookup_field(
|
||||
mimeparser,
|
||||
b"Secure-Join-Group\x00" as *const u8 as *const libc::c_char,
|
||||
));
|
||||
grpid = dc_strdup(lookup_field(mimeparser, "Secure-Join-Group"));
|
||||
let group_chat_id: uint32_t = dc_get_chat_id_by_grpid(
|
||||
context,
|
||||
grpid,
|
||||
@@ -679,12 +588,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
if group_chat_id == 0i32 as libc::c_uint {
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"Chat %s not found.\x00" as *const u8 as *const libc::c_char,
|
||||
grpid,
|
||||
);
|
||||
error!(context, 0, "Chat {} not found.", as_str(grpid),);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
dc_add_contact_to_chat_ex(
|
||||
@@ -726,12 +630,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
ret = 0x1i32
|
||||
}
|
||||
if context.bob.clone().read().unwrap().expects != 6 {
|
||||
dc_log_info(
|
||||
context,
|
||||
0i32,
|
||||
b"Message belongs to a different handshake.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
info!(context, 0, "Message belongs to a different handshake.",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
let cond = {
|
||||
@@ -739,11 +638,9 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
scan.is_null() || 0 != join_vg && (*scan).state != 202
|
||||
};
|
||||
if cond {
|
||||
dc_log_warning(
|
||||
warn!(
|
||||
context,
|
||||
0i32,
|
||||
b"Message out of sync or belongs to a different handshake.\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
0, "Message out of sync or belongs to a different handshake.",
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
@@ -805,16 +702,13 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
if 0 != join_vg {
|
||||
if 0 == dc_addr_equals_self(
|
||||
context,
|
||||
lookup_field(
|
||||
mimeparser,
|
||||
b"Chat-Group-Member-Added\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
),
|
||||
lookup_field(mimeparser, "Chat-Group-Member-Added"),
|
||||
) {
|
||||
dc_log_info(context, 0i32,
|
||||
b"Message belongs to a different handshake (scaled up contact anyway to allow creation of group).\x00"
|
||||
as *const u8 as
|
||||
*const libc::c_char);
|
||||
info!(
|
||||
context,
|
||||
0,
|
||||
"Message belongs to a different handshake (scaled up contact anyway to allow creation of group)."
|
||||
);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
current_block = 9180031981464905198;
|
||||
@@ -858,12 +752,7 @@ pub unsafe fn dc_handle_securejoin_handshake(
|
||||
============================================================ */
|
||||
contact = dc_get_contact(context, contact_id);
|
||||
if contact.is_null() || 0 == dc_contact_is_verified(contact) {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"vg-member-added-received invalid.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
warn!(context, 0, "vg-member-added-received invalid.",);
|
||||
current_block = 4378276786830486580;
|
||||
} else {
|
||||
context.call_cb(
|
||||
@@ -927,10 +816,7 @@ unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint
|
||||
dc_contact_unref(contact);
|
||||
}
|
||||
|
||||
unsafe fn lookup_field(
|
||||
mimeparser: &dc_mimeparser_t,
|
||||
key: *const libc::c_char,
|
||||
) -> *const libc::c_char {
|
||||
unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char {
|
||||
let mut value: *const libc::c_char = 0 as *const libc::c_char;
|
||||
let field: *mut mailimf_field = dc_mimeparser_lookup_field(mimeparser, key);
|
||||
if field.is_null()
|
||||
@@ -964,13 +850,7 @@ unsafe fn could_not_establish_secure_connection(
|
||||
},
|
||||
);
|
||||
dc_add_device_msg(context, contact_chat_id, msg);
|
||||
dc_log_error(
|
||||
context,
|
||||
0i32,
|
||||
b"%s (%s)\x00" as *const u8 as *const libc::c_char,
|
||||
msg,
|
||||
details,
|
||||
);
|
||||
error!(context, 0, "{} ({})", as_str(msg), to_string(details),);
|
||||
free(msg as *mut libc::c_void);
|
||||
dc_contact_unref(contact);
|
||||
}
|
||||
@@ -1005,27 +885,15 @@ unsafe fn encrypted_and_signed(
|
||||
expected_fingerprint: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
if 0 == mimeparser.e2ee_helper.encrypted {
|
||||
dc_log_warning(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
b"Message not encrypted.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!(mimeparser.context, 0, "Message not encrypted.",);
|
||||
return 0i32;
|
||||
}
|
||||
if mimeparser.e2ee_helper.signatures.len() <= 0 {
|
||||
dc_log_warning(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
b"Message not signed.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!(mimeparser.context, 0, "Message not signed.",);
|
||||
return 0i32;
|
||||
}
|
||||
if expected_fingerprint.is_null() {
|
||||
dc_log_warning(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
b"Fingerprint for comparison missing.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
warn!(mimeparser.context, 0, "Fingerprint for comparison missing.",);
|
||||
return 0i32;
|
||||
}
|
||||
if !mimeparser
|
||||
@@ -1033,23 +901,20 @@ unsafe fn encrypted_and_signed(
|
||||
.signatures
|
||||
.contains(as_str(expected_fingerprint))
|
||||
{
|
||||
dc_log_warning(
|
||||
warn!(
|
||||
mimeparser.context,
|
||||
0i32,
|
||||
b"Message does not match expected fingerprint %s.\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
expected_fingerprint,
|
||||
0,
|
||||
"Message does not match expected fingerprint {}.",
|
||||
as_str(expected_fingerprint),
|
||||
);
|
||||
return 0i32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
1
|
||||
}
|
||||
|
||||
pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate) {
|
||||
let stmt;
|
||||
let contact_id: uint32_t;
|
||||
let mut contact_chat_id: uint32_t = 0i32 as uint32_t;
|
||||
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
|
||||
@@ -1057,38 +922,36 @@ pub unsafe fn dc_handle_degrade_event(context: &Context, peerstate: &Peerstate)
|
||||
// with things they cannot fix, so the user is just kicked from the verified group
|
||||
// (and he will know this and can fix this)
|
||||
if Some(DegradeEvent::FingerprintChanged) == peerstate.degrade_event {
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT id FROM contacts WHERE addr=?;\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
let c_addr = peerstate.addr.as_ref().map(to_cstring).unwrap_or_default();
|
||||
|
||||
let c_addr_ptr = if peerstate.addr.is_some() {
|
||||
c_addr.as_ptr()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
|
||||
sqlite3_bind_text(stmt, 1i32, c_addr_ptr, -1i32, None);
|
||||
sqlite3_step(stmt);
|
||||
contact_id = sqlite3_column_int(stmt, 0i32) as uint32_t;
|
||||
sqlite3_finalize(stmt);
|
||||
if !(contact_id == 0i32 as libc::c_uint) {
|
||||
let contact_id: i32 = context
|
||||
.sql
|
||||
.query_row_col(
|
||||
context,
|
||||
"SELECT id FROM contacts WHERE addr=?;",
|
||||
params![&peerstate.addr],
|
||||
0,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
if contact_id > 0 {
|
||||
dc_create_or_lookup_nchat_by_contact_id(
|
||||
context,
|
||||
contact_id,
|
||||
2i32,
|
||||
contact_id as u32,
|
||||
2,
|
||||
&mut contact_chat_id,
|
||||
0 as *mut libc::c_int,
|
||||
);
|
||||
let msg = dc_stock_str_repl_string(context, 37i32, c_addr_ptr);
|
||||
let c_addr_ptr = if let Some(ref addr) = peerstate.addr {
|
||||
to_cstring(addr)
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
let msg = dc_stock_str_repl_string(context, 37, c_addr_ptr);
|
||||
dc_add_device_msg(context, contact_chat_id, msg);
|
||||
free(msg as *mut libc::c_void);
|
||||
free(c_addr_ptr as *mut _);
|
||||
context.call_cb(
|
||||
Event::CHAT_MODIFIED,
|
||||
contact_chat_id as uintptr_t,
|
||||
0i32 as uintptr_t,
|
||||
0 as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +225,8 @@ unsafe fn dc_simplify_simplify_plain_text(
|
||||
pending_linebreaks -= 1
|
||||
}
|
||||
}
|
||||
ret += &to_string(line);
|
||||
// the incoming message might contain invalid UTF8
|
||||
ret += &to_string_lossy(line);
|
||||
content_lines_added += 1;
|
||||
pending_linebreaks = 1i32
|
||||
}
|
||||
@@ -238,7 +239,7 @@ unsafe fn dc_simplify_simplify_plain_text(
|
||||
}
|
||||
dc_free_splitted_lines(lines);
|
||||
|
||||
strdup(to_cstring(ret).as_ptr())
|
||||
to_cstring(ret)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -261,7 +262,7 @@ unsafe fn is_quoted_headline(buf: *const libc::c_char) -> bool {
|
||||
/* This function may be called for the line _directly_ before a quote.
|
||||
The function checks if the line contains sth. like "On 01.02.2016, xy@z wrote:" in various languages.
|
||||
- Currently, we simply check if the last character is a ':'.
|
||||
- Checking for the existance of an email address may fail (headlines may show the user's name instead of the address) */
|
||||
- Checking for the existence of an email address may fail (headlines may show the user's name instead of the address) */
|
||||
let buf_len: libc::c_int = strlen(buf) as libc::c_int;
|
||||
if buf_len > 80i32 {
|
||||
return false;
|
||||
|
||||
1605
src/dc_sqlite3.rs
1605
src/dc_sqlite3.rs
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
|
||||
use charset::Charset;
|
||||
use mmime::mailmime_decode::*;
|
||||
@@ -689,10 +689,9 @@ pub unsafe fn dc_decode_ext_header(to_decode: *const libc::c_char) -> *mut libc:
|
||||
std::slice::from_raw_parts(decoded as *const u8, strlen(decoded));
|
||||
|
||||
let (res, _, _) = encoding.decode(data);
|
||||
free(decoded as *mut libc::c_void);
|
||||
let res_c = CString::new(res.as_bytes()).unwrap();
|
||||
|
||||
decoded = strdup(res_c.as_ptr());
|
||||
free(decoded as *mut _);
|
||||
let r = std::ffi::CString::new(res.as_bytes()).unwrap();
|
||||
decoded = dc_strdup(r.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,7 +711,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) {
|
||||
|
||||
let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur));
|
||||
let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2]));
|
||||
libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4);
|
||||
libc::memcpy(target as *mut _, raw as *const _, 4);
|
||||
free(raw as *mut libc::c_void);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
100
src/dc_token.rs
100
src/dc_token.rs
@@ -1,76 +1,64 @@
|
||||
use crate::context::Context;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::sql;
|
||||
|
||||
// Token namespaces
|
||||
pub type dc_tokennamespc_t = libc::c_uint;
|
||||
pub type dc_tokennamespc_t = usize;
|
||||
pub const DC_TOKEN_AUTH: dc_tokennamespc_t = 110;
|
||||
pub const DC_TOKEN_INVITENUMBER: dc_tokennamespc_t = 100;
|
||||
|
||||
// Functions to read/write token from/to the database. A token is any string associated with a key.
|
||||
pub unsafe fn dc_token_save(
|
||||
|
||||
pub fn dc_token_save(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: uint32_t,
|
||||
foreign_id: u32,
|
||||
token: *const libc::c_char,
|
||||
) {
|
||||
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
|
||||
if !token.is_null() {
|
||||
// foreign_id may be 0
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
|
||||
sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int);
|
||||
sqlite3_bind_text(stmt, 3i32, token, -1i32, None);
|
||||
sqlite3_bind_int64(stmt, 4i32, time() as sqlite3_int64);
|
||||
sqlite3_step(stmt);
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
pub unsafe fn dc_token_lookup(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: uint32_t,
|
||||
) -> *mut libc::c_char {
|
||||
let token: *mut libc::c_char;
|
||||
let stmt: *mut sqlite3_stmt;
|
||||
stmt = dc_sqlite3_prepare(
|
||||
// foreign_id may be 0
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
|
||||
sqlite3_bind_int(stmt, 2i32, foreign_id as libc::c_int);
|
||||
sqlite3_step(stmt);
|
||||
token = dc_strdup_keep_null(sqlite3_column_text(stmt, 0i32) as *mut libc::c_char);
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
token
|
||||
"INSERT INTO tokens (namespc, foreign_id, token, timestamp) VALUES (?, ?, ?, ?);",
|
||||
params![namespc as i32, foreign_id as i32, as_str(token), time()],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub unsafe fn dc_token_exists(
|
||||
pub fn dc_token_lookup(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
foreign_id: u32,
|
||||
) -> *mut libc::c_char {
|
||||
context
|
||||
.sql
|
||||
.query_row_col::<_, String>(
|
||||
context,
|
||||
"SELECT token FROM tokens WHERE namespc=? AND foreign_id=?;",
|
||||
params![namespc as i32, foreign_id as i32],
|
||||
0,
|
||||
)
|
||||
.map(|s| unsafe { to_cstring(s) })
|
||||
.unwrap_or_else(|| std::ptr::null_mut())
|
||||
}
|
||||
|
||||
pub fn dc_token_exists(
|
||||
context: &Context,
|
||||
namespc: dc_tokennamespc_t,
|
||||
token: *const libc::c_char,
|
||||
) -> libc::c_int {
|
||||
let mut exists: libc::c_int = 0i32;
|
||||
let mut stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt;
|
||||
if !token.is_null() {
|
||||
stmt = dc_sqlite3_prepare(
|
||||
context,
|
||||
&context.sql,
|
||||
b"SELECT id FROM tokens WHERE namespc=? AND token=?;\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
sqlite3_bind_int(stmt, 1i32, namespc as libc::c_int);
|
||||
sqlite3_bind_text(stmt, 2i32, token, -1i32, None);
|
||||
exists = (sqlite3_step(stmt) != 0i32) as libc::c_int
|
||||
) -> bool {
|
||||
if token.is_null() {
|
||||
return false;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return exists;
|
||||
|
||||
context
|
||||
.sql
|
||||
.exists(
|
||||
"SELECT id FROM tokens WHERE namespc=? AND token=?;",
|
||||
params![namespc as i32, as_str(token)],
|
||||
)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
368
src/dc_tools.rs
368
src/dc_tools.rs
@@ -1,3 +1,5 @@
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fs;
|
||||
use std::time::SystemTime;
|
||||
|
||||
@@ -7,10 +9,11 @@ use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_array::*;
|
||||
use crate::dc_log::*;
|
||||
use crate::types::*;
|
||||
use crate::x::*;
|
||||
|
||||
const ELLIPSE: &'static str = "[...]";
|
||||
|
||||
/* Some tools and enhancements to the used libraries, there should be
|
||||
no references to Context and other "larger" classes here. */
|
||||
// for carray etc.
|
||||
@@ -174,14 +177,14 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) {
|
||||
|
||||
/* the result must be free()'d */
|
||||
pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char {
|
||||
let raw = to_cstring(to_string(in_0).to_lowercase());
|
||||
strdup(raw.as_ptr())
|
||||
to_cstring(to_string(in_0).to_lowercase())
|
||||
}
|
||||
|
||||
pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) {
|
||||
let raw = to_cstring(to_string(in_0).to_lowercase());
|
||||
assert_eq!(strlen(in_0), strlen(raw.as_ptr()));
|
||||
memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0));
|
||||
assert_eq!(strlen(in_0), strlen(raw));
|
||||
memcpy(in_0 as *mut _, raw as *const _, strlen(in_0));
|
||||
free(raw as *mut _);
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_contains(
|
||||
@@ -229,7 +232,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li
|
||||
|
||||
let buf = std::slice::from_raw_parts(buf, bytes);
|
||||
let raw = hex::encode_upper(buf);
|
||||
strdup(to_cstring(raw).as_ptr())
|
||||
to_cstring(raw)
|
||||
}
|
||||
|
||||
/* remove all \r characters from string */
|
||||
@@ -343,25 +346,16 @@ pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t {
|
||||
j
|
||||
}
|
||||
|
||||
pub unsafe fn dc_truncate_str(buf: *mut libc::c_char, approx_chars: libc::c_int) {
|
||||
if approx_chars > 0
|
||||
&& strlen(buf)
|
||||
> approx_chars.wrapping_add(
|
||||
strlen(b"[...]\x00" as *const u8 as *const libc::c_char) as libc::c_int
|
||||
) as usize
|
||||
{
|
||||
let mut p: *mut libc::c_char = &mut *buf.offset(approx_chars as isize) as *mut libc::c_char;
|
||||
*p = 0i32 as libc::c_char;
|
||||
if !strchr(buf, ' ' as i32).is_null() {
|
||||
while *p.offset(-1i32 as isize) as libc::c_int != ' ' as i32
|
||||
&& *p.offset(-1i32 as isize) as libc::c_int != '\n' as i32
|
||||
{
|
||||
p = p.offset(-1isize);
|
||||
*p = 0i32 as libc::c_char
|
||||
}
|
||||
pub fn dc_truncate_str(buf: &str, approx_chars: usize) -> Cow<str> {
|
||||
if approx_chars > 0 && buf.len() > approx_chars + ELLIPSE.len() {
|
||||
if let Some(index) = buf[..approx_chars].rfind(|c| c == ' ' || c == '\n') {
|
||||
Cow::Owned(format!("{}{}", &buf[..index + 1], ELLIPSE))
|
||||
} else {
|
||||
Cow::Owned(format!("{}{}", &buf[..approx_chars], ELLIPSE))
|
||||
}
|
||||
strcat(p, b"[...]\x00" as *const u8 as *const libc::c_char);
|
||||
};
|
||||
} else {
|
||||
Cow::Borrowed(buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn dc_truncate_n_unwrap_str(
|
||||
@@ -534,7 +528,7 @@ pub unsafe fn dc_str_from_clist(
|
||||
}
|
||||
}
|
||||
|
||||
strdup(to_cstring(res).as_ptr())
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
pub unsafe fn dc_str_to_clist(
|
||||
@@ -570,7 +564,7 @@ pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int {
|
||||
/* the colors must fulfill some criterions as:
|
||||
- contrast to black and to white
|
||||
- work as a text-color
|
||||
- being noticable on a typical map
|
||||
- being noticeable on a typical map
|
||||
- harmonize together while being different enough
|
||||
(therefore, we cannot just use random rgb colors :) */
|
||||
static mut colors: [uint32_t; 16] = [
|
||||
@@ -675,10 +669,13 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 {
|
||||
|
||||
/* the return value must be free()'d */
|
||||
pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char {
|
||||
let ts = chrono::Utc.timestamp(wanted, 0);
|
||||
let res = ts.format("%Y.%m.%d %H:%M:%S").to_string();
|
||||
let res = dc_timestamp_to_str_safe(wanted);
|
||||
to_cstring(res)
|
||||
}
|
||||
|
||||
strdup(to_cstring(res).as_ptr())
|
||||
pub fn dc_timestamp_to_str_safe(wanted: i64) -> String {
|
||||
let ts = chrono::Utc.timestamp(wanted, 0);
|
||||
ts.format("%Y.%m.%d %H:%M:%S").to_string()
|
||||
}
|
||||
|
||||
pub fn dc_gm2local_offset() -> i64 {
|
||||
@@ -905,16 +902,23 @@ pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut
|
||||
|
||||
/* file tools */
|
||||
pub unsafe fn dc_ensure_no_slash(pathNfilename: *mut libc::c_char) {
|
||||
let path_len: libc::c_int = strlen(pathNfilename) as libc::c_int;
|
||||
if path_len > 0i32 {
|
||||
if *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '/' as i32
|
||||
|| *pathNfilename.offset((path_len - 1i32) as isize) as libc::c_int == '\\' as i32
|
||||
let path_len = strlen(pathNfilename);
|
||||
if path_len > 0 {
|
||||
if *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '/' as i32
|
||||
|| *pathNfilename.offset((path_len - 1) as isize) as libc::c_int == '\\' as i32
|
||||
{
|
||||
*pathNfilename.offset((path_len - 1i32) as isize) = 0i32 as libc::c_char
|
||||
*pathNfilename.offset((path_len - 1) as isize) = 0 as libc::c_char
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dc_ensure_no_slash_safe(path: &str) -> &str {
|
||||
if path.ends_with('/') || path.ends_with('\\') {
|
||||
return &path[..path.len() - 1];
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
pub unsafe fn dc_validate_filename(filename: *mut libc::c_char) {
|
||||
/* function modifies the given buffer and replaces all characters not valid in filenames by a "-" */
|
||||
let mut p1: *mut libc::c_char = filename;
|
||||
@@ -1074,40 +1078,28 @@ pub unsafe fn dc_get_abs_path(
|
||||
context: &Context,
|
||||
pathNfilename: *const libc::c_char,
|
||||
) -> *mut libc::c_char {
|
||||
let current_block: u64;
|
||||
let mut success: libc::c_int = 0i32;
|
||||
let mut pathNfilename_abs: *mut libc::c_char = 0 as *mut libc::c_char;
|
||||
if !pathNfilename.is_null() {
|
||||
pathNfilename_abs = dc_strdup(pathNfilename);
|
||||
if strncmp(
|
||||
pathNfilename_abs,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
8,
|
||||
) == 0i32
|
||||
{
|
||||
if !context.has_blobdir() {
|
||||
current_block = 3805228753452640762;
|
||||
} else {
|
||||
dc_str_replace(
|
||||
&mut pathNfilename_abs,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
context.get_blobdir(),
|
||||
);
|
||||
current_block = 6937071982253665452;
|
||||
}
|
||||
} else {
|
||||
current_block = 6937071982253665452;
|
||||
}
|
||||
match current_block {
|
||||
3805228753452640762 => {}
|
||||
_ => success = 1i32,
|
||||
}
|
||||
}
|
||||
if 0 == success {
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
pathNfilename_abs = 0 as *mut libc::c_char
|
||||
if pathNfilename.is_null() {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
let starts = strncmp(
|
||||
pathNfilename,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
8,
|
||||
) == 0i32;
|
||||
|
||||
if starts && !context.has_blobdir() {
|
||||
return 0 as *mut libc::c_char;
|
||||
}
|
||||
|
||||
let mut pathNfilename_abs: *mut libc::c_char = dc_strdup(pathNfilename);
|
||||
if starts && context.has_blobdir() {
|
||||
dc_str_replace(
|
||||
&mut pathNfilename_abs,
|
||||
b"$BLOBDIR\x00" as *const u8 as *const libc::c_char,
|
||||
context.get_blobdir(),
|
||||
);
|
||||
}
|
||||
pathNfilename_abs
|
||||
}
|
||||
|
||||
@@ -1169,12 +1161,7 @@ pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_ch
|
||||
success = 1;
|
||||
}
|
||||
Err(_err) => {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot delete \"%s\".\x00" as *const u8 as *const libc::c_char,
|
||||
pathNfilename,
|
||||
);
|
||||
warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1204,13 +1191,7 @@ pub unsafe fn dc_copy_file(
|
||||
success = 1;
|
||||
}
|
||||
Err(_) => {
|
||||
dc_log_error(
|
||||
context,
|
||||
0,
|
||||
b"Cannot copy \"%s\" to \"%s\".\x00" as *const u8 as *const libc::c_char,
|
||||
src,
|
||||
dest,
|
||||
);
|
||||
error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1233,11 +1214,11 @@ pub unsafe fn dc_create_folder(
|
||||
success = 1;
|
||||
}
|
||||
Err(_err) => {
|
||||
dc_log_warning(
|
||||
warn!(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot create directory \"%s\".\x00" as *const u8 as *const libc::c_char,
|
||||
pathNfilename,
|
||||
0,
|
||||
"Cannot create directory \"{}\".",
|
||||
as_str(pathNfilename),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1256,34 +1237,38 @@ pub unsafe fn dc_write_file(
|
||||
buf: *const libc::c_void,
|
||||
buf_bytes: size_t,
|
||||
) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
|
||||
if pathNfilename_abs.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
||||
let bytes = std::slice::from_raw_parts(buf as *const u8, buf_bytes);
|
||||
|
||||
match fs::write(p, bytes) {
|
||||
Ok(_) => {
|
||||
success = 1;
|
||||
}
|
||||
Err(_err) => {
|
||||
dc_log_warning(
|
||||
context,
|
||||
0i32,
|
||||
b"Cannot write %lu bytes to \"%s\".\x00" as *const u8 as *const libc::c_char,
|
||||
buf_bytes as libc::c_ulong,
|
||||
pathNfilename,
|
||||
);
|
||||
}
|
||||
dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int
|
||||
}
|
||||
|
||||
pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool {
|
||||
let pathNfilename_abs = unsafe {
|
||||
let n = to_cstring(pathNfilename.as_ref());
|
||||
let res = dc_get_abs_path(context, n);
|
||||
free(n as *mut _);
|
||||
res
|
||||
};
|
||||
if pathNfilename_abs.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
let p = as_str(pathNfilename_abs);
|
||||
|
||||
let success = if let Err(_err) = fs::write(p, buf) {
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
"Cannot write {} bytes to \"{}\".",
|
||||
buf.len(),
|
||||
pathNfilename.as_ref(),
|
||||
);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
|
||||
success
|
||||
}
|
||||
|
||||
@@ -1293,44 +1278,48 @@ pub unsafe fn dc_read_file(
|
||||
buf: *mut *mut libc::c_void,
|
||||
buf_bytes: *mut size_t,
|
||||
) -> libc::c_int {
|
||||
let mut success = 0;
|
||||
|
||||
if pathNfilename.is_null() || buf.is_null() || buf_bytes.is_null() {
|
||||
if pathNfilename.is_null() {
|
||||
return 0;
|
||||
}
|
||||
if let Some(mut bytes) = dc_read_file_safe(context, as_str(pathNfilename)) {
|
||||
*buf = &mut bytes[..] as *mut _ as *mut libc::c_void;
|
||||
*buf_bytes = bytes.len();
|
||||
std::mem::forget(bytes);
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
*buf = 0 as *mut libc::c_void;
|
||||
*buf_bytes = 0i32 as size_t;
|
||||
pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> {
|
||||
let pathNfilename_abs = unsafe {
|
||||
let n = to_cstring(pathNfilename.as_ref());
|
||||
let p = dc_get_abs_path(context, n);
|
||||
free(n as *mut _);
|
||||
p
|
||||
};
|
||||
|
||||
let pathNfilename_abs = dc_get_abs_path(context, pathNfilename);
|
||||
if pathNfilename_abs.is_null() {
|
||||
return 0;
|
||||
return None;
|
||||
}
|
||||
|
||||
let p = std::ffi::CStr::from_ptr(pathNfilename_abs)
|
||||
.to_str()
|
||||
.unwrap();
|
||||
|
||||
match fs::read(p) {
|
||||
Ok(mut bytes) => {
|
||||
*buf = &mut bytes[..] as *mut _ as *mut libc::c_void;
|
||||
*buf_bytes = bytes.len();
|
||||
std::mem::forget(bytes);
|
||||
|
||||
success = 1;
|
||||
}
|
||||
let p = as_str(pathNfilename_abs);
|
||||
let res = match fs::read(p) {
|
||||
Ok(bytes) => Some(bytes),
|
||||
Err(_err) => {
|
||||
dc_log_warning(
|
||||
warn!(
|
||||
context,
|
||||
0,
|
||||
b"Cannot read \"%s\" or file is empty.\x00" as *const u8 as *const libc::c_char,
|
||||
pathNfilename,
|
||||
"Cannot read \"{}\" or file is empty.",
|
||||
pathNfilename.as_ref(),
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
free(pathNfilename_abs as *mut libc::c_void);
|
||||
success
|
||||
unsafe { free(pathNfilename_abs as *mut libc::c_void) };
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub unsafe fn dc_get_fine_pathNfilename(
|
||||
@@ -1515,20 +1504,20 @@ pub trait OsStrExt {
|
||||
///
|
||||
/// On windows when the string contains invalid Unicode
|
||||
/// `[Err]([CStringError::NotUnicode])` is returned.
|
||||
fn to_c_string(&self) -> Result<std::ffi::CString, CStringError>;
|
||||
fn to_c_string(&self) -> Result<CString, CStringError>;
|
||||
}
|
||||
|
||||
impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
fn to_c_string(&self) -> Result<std::ffi::CString, CStringError> {
|
||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
std::ffi::CString::new(self.as_ref().as_bytes()).map_err(|err| match err {
|
||||
CString::new(self.as_ref().as_bytes()).map_err(|err| match err {
|
||||
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn to_c_string(&self) -> Result<std::ffi::CString, CStringError> {
|
||||
fn to_c_string(&self) -> Result<CString, CStringError> {
|
||||
os_str_to_c_string_unicode(&self)
|
||||
}
|
||||
}
|
||||
@@ -1537,29 +1526,57 @@ impl<T: AsRef<std::ffi::OsStr>> OsStrExt for T {
|
||||
#[allow(dead_code)]
|
||||
fn os_str_to_c_string_unicode(
|
||||
os_str: &dyn AsRef<std::ffi::OsStr>,
|
||||
) -> Result<std::ffi::CString, CStringError> {
|
||||
) -> Result<CString, CStringError> {
|
||||
match os_str.as_ref().to_str() {
|
||||
Some(val) => std::ffi::CString::new(val.as_bytes()).map_err(|err| match err {
|
||||
Some(val) => CString::new(val.as_bytes()).map_err(|err| match err {
|
||||
std::ffi::NulError { .. } => CStringError::InteriorNullByte,
|
||||
}),
|
||||
None => Err(CStringError::NotUnicode),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_cstring<S: AsRef<str>>(s: S) -> std::ffi::CString {
|
||||
std::ffi::CString::new(s.as_ref()).unwrap()
|
||||
/// Needs to free the result after use!
|
||||
pub unsafe fn to_cstring<S: AsRef<str>>(s: S) -> *mut libc::c_char {
|
||||
let cstr = CString::new(s.as_ref()).expect("invalid string converted");
|
||||
dc_strdup(cstr.as_ref().as_ptr())
|
||||
}
|
||||
|
||||
pub fn to_string(s: *const libc::c_char) -> String {
|
||||
if s.is_null() {
|
||||
return "".into();
|
||||
}
|
||||
unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap().to_string() }
|
||||
|
||||
let cstr = unsafe { CStr::from_ptr(s) };
|
||||
|
||||
cstr.to_str().map(|s| s.to_string()).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Non utf8 string: '{:?}' ({:?})",
|
||||
cstr.to_string_lossy(),
|
||||
err
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn to_string_lossy(s: *const libc::c_char) -> String {
|
||||
if s.is_null() {
|
||||
return "".into();
|
||||
}
|
||||
|
||||
let cstr = unsafe { CStr::from_ptr(s) };
|
||||
|
||||
cstr.to_str()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_else(|_| cstr.to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
pub fn as_str<'a>(s: *const libc::c_char) -> &'a str {
|
||||
assert!(!s.is_null(), "cannot be used on null pointers");
|
||||
unsafe { std::ffi::CStr::from_ptr(s).to_str().unwrap() }
|
||||
|
||||
let cstr = unsafe { CStr::from_ptr(s) };
|
||||
|
||||
cstr.to_str().unwrap_or_else(|err| {
|
||||
panic!("Non utf8 string: '{:?}' ({:?})", cstr.to_bytes(), err);
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert a C `*char` pointer to a [std::path::Path] slice.
|
||||
@@ -1591,7 +1608,7 @@ pub fn as_path<'a>(s: *const libc::c_char) -> &'a std::path::Path {
|
||||
as_path_unicode(s)
|
||||
}
|
||||
|
||||
// Implmentation for as_path() on Windows.
|
||||
// Implementation for as_path() on Windows.
|
||||
//
|
||||
// Having this as a separate function means it can be tested on unix
|
||||
// too.
|
||||
@@ -1703,61 +1720,30 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_truncate_1() {
|
||||
unsafe {
|
||||
let str: *mut libc::c_char =
|
||||
strdup(b"this is a little test string\x00" as *const u8 as *const libc::c_char);
|
||||
dc_truncate_str(str, 16);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"this is a [...]"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
}
|
||||
let s = "this is a little test string";
|
||||
assert_eq!(dc_truncate_str(s, 16), "this is a [...]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_truncate_2() {
|
||||
unsafe {
|
||||
let str: *mut libc::c_char = strdup(b"1234\x00" as *const u8 as *const libc::c_char);
|
||||
dc_truncate_str(str, 2);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"1234"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
}
|
||||
assert_eq!(dc_truncate_str("1234", 2), "1234");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_truncate_3() {
|
||||
unsafe {
|
||||
let str: *mut libc::c_char = strdup(b"1234567\x00" as *const u8 as *const libc::c_char);
|
||||
dc_truncate_str(str, 1);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"1[...]"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
}
|
||||
}
|
||||
// This test seems wrong
|
||||
// #[test]
|
||||
// fn test_dc_str_truncate_3() {
|
||||
// assert_eq!(dc_truncate_str("1234567", 3), "1[...]");
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_dc_str_truncate_4() {
|
||||
unsafe {
|
||||
let str: *mut libc::c_char = strdup(b"123456\x00" as *const u8 as *const libc::c_char);
|
||||
dc_truncate_str(str, 4);
|
||||
assert_eq!(
|
||||
CStr::from_ptr(str as *const libc::c_char).to_str().unwrap(),
|
||||
"123456"
|
||||
);
|
||||
free(str as *mut libc::c_void);
|
||||
}
|
||||
assert_eq!(dc_truncate_str("123456", 4), "123456");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dc_insert_breaks_1() {
|
||||
unsafe {
|
||||
let str: *mut libc::c_char = dc_insert_breaks(
|
||||
let str = dc_insert_breaks(
|
||||
b"just1234test\x00" as *const u8 as *const libc::c_char,
|
||||
4,
|
||||
b" \x00" as *const u8 as *const libc::c_char,
|
||||
@@ -2021,7 +2007,7 @@ mod tests {
|
||||
let some_dir = std::path::Path::new(&some_str);
|
||||
assert_eq!(
|
||||
some_dir.as_os_str().to_c_string().unwrap(),
|
||||
std::ffi::CString::new("/some/valid/utf8").unwrap()
|
||||
CString::new("/some/valid/utf8").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2046,7 +2032,7 @@ mod tests {
|
||||
let some_dir = std::path::Path::new(&some_str);
|
||||
assert_eq!(
|
||||
some_dir.as_os_str().to_c_string().unwrap(),
|
||||
std::ffi::CString::new("/some/valid/utf8").unwrap()
|
||||
CString::new("/some/valid/utf8").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2055,7 +2041,7 @@ mod tests {
|
||||
let some_str = std::ffi::OsString::from("foo");
|
||||
assert_eq!(
|
||||
os_str_to_c_string_unicode(&some_str).unwrap(),
|
||||
std::ffi::CString::new("foo").unwrap()
|
||||
CString::new("foo").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2065,7 +2051,7 @@ mod tests {
|
||||
let some_path = std::path::Path::new(&some_str);
|
||||
assert_eq!(
|
||||
os_str_to_c_string_unicode(&some_path).unwrap(),
|
||||
std::ffi::CString::new("/some/path").unwrap()
|
||||
CString::new("/some/path").unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2080,15 +2066,15 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_as_path() {
|
||||
let some_path = std::ffi::CString::new("/some/path").unwrap();
|
||||
let some_path = CString::new("/some/path").unwrap();
|
||||
let ptr = some_path.as_ptr();
|
||||
assert_eq!(as_path(ptr), std::ffi::OsString::from("/some/path"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_as_path_unicode_fn() {
|
||||
let some_path = std::ffi::CString::new("/some/path").unwrap();
|
||||
let some_path = CString::new("/some/path").unwrap();
|
||||
let ptr = some_path.as_ptr();
|
||||
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"))
|
||||
assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path"));
|
||||
}
|
||||
}
|
||||
|
||||
45
src/error.rs
Normal file
45
src/error.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use failure::Fail;
|
||||
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum Error {
|
||||
#[fail(display = "Sqlite Error: {:?}", _0)]
|
||||
Sql(rusqlite::Error),
|
||||
#[fail(display = "Sqlite Connection Pool Error: {:?}", _0)]
|
||||
ConnectionPool(r2d2::Error),
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Failure(failure::Error),
|
||||
#[fail(display = "Sqlite: Connection closed")]
|
||||
SqlNoConnection,
|
||||
#[fail(display = "Sqlite: Already open")]
|
||||
SqlAlreadyOpen,
|
||||
#[fail(display = "Sqlite: Failed to open")]
|
||||
SqlFailedToOpen,
|
||||
#[fail(display = "{:?}", _0)]
|
||||
Io(std::io::Error),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<rusqlite::Error> for Error {
|
||||
fn from(err: rusqlite::Error) -> Error {
|
||||
Error::Sql(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<failure::Error> for Error {
|
||||
fn from(err: failure::Error) -> Error {
|
||||
Error::Failure(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<r2d2::Error> for Error {
|
||||
fn from(err: r2d2::Error) -> Error {
|
||||
Error::ConnectionPool(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(err: std::io::Error) -> Error {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
297
src/imap.rs
297
src/imap.rs
@@ -1,4 +1,3 @@
|
||||
use std::ffi::CString;
|
||||
use std::net;
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::time::{Duration, SystemTime};
|
||||
@@ -6,10 +5,10 @@ use std::time::{Duration, SystemTime};
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::{as_str, to_string};
|
||||
use crate::dc_tools::{as_str, to_cstring};
|
||||
use crate::oauth2::dc_get_oauth2_access_token;
|
||||
use crate::types::*;
|
||||
use crate::x::free;
|
||||
|
||||
pub const DC_IMAP_SEEN: usize = 0x0001;
|
||||
pub const DC_REGENERATE: usize = 0x01;
|
||||
@@ -33,7 +32,8 @@ pub struct Imap {
|
||||
precheck_imf: dc_precheck_imf_t,
|
||||
receive_imf: dc_receive_imf_t,
|
||||
|
||||
session: Arc<Mutex<(Option<Session>, Option<net::TcpStream>)>>,
|
||||
session: Arc<Mutex<Option<Session>>>,
|
||||
stream: Arc<RwLock<Option<net::TcpStream>>>,
|
||||
connected: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
@@ -351,7 +351,8 @@ impl Imap {
|
||||
receive_imf: dc_receive_imf_t,
|
||||
) -> Self {
|
||||
Imap {
|
||||
session: Arc::new(Mutex::new((None, None))),
|
||||
session: Arc::new(Mutex::new(None)),
|
||||
stream: Arc::new(RwLock::new(None)),
|
||||
config: Arc::new(RwLock::new(ImapConfig::default())),
|
||||
watch: Arc::new((Mutex::new(false), Condvar::new())),
|
||||
get_config,
|
||||
@@ -370,18 +371,18 @@ impl Imap {
|
||||
self.config.read().unwrap().should_reconnect
|
||||
}
|
||||
|
||||
fn setup_handle_if_needed(&self, context: &Context) -> libc::c_int {
|
||||
fn setup_handle_if_needed(&self, context: &Context) -> bool {
|
||||
if self.config.read().unwrap().imap_server.is_empty() {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.should_reconnect() {
|
||||
self.unsetup_handle(context);
|
||||
}
|
||||
|
||||
if self.is_connected() && self.session.lock().unwrap().1.is_some() {
|
||||
if self.is_connected() && self.stream.read().unwrap().is_some() {
|
||||
self.config.write().unwrap().should_reconnect = false;
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
let server_flags = self.config.read().unwrap().server_flags;
|
||||
@@ -425,7 +426,7 @@ impl Imap {
|
||||
};
|
||||
client.authenticate("XOAUTH2", &auth)
|
||||
} else {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
client.login(imap_user, imap_pw)
|
||||
@@ -446,7 +447,7 @@ impl Imap {
|
||||
err
|
||||
);
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -454,29 +455,27 @@ impl Imap {
|
||||
|
||||
match login_res {
|
||||
Ok((session, stream)) => {
|
||||
*self.session.lock().unwrap() = (Some(session), Some(stream));
|
||||
1
|
||||
*self.session.lock().unwrap() = Some(session);
|
||||
*self.stream.write().unwrap() = Some(stream);
|
||||
true
|
||||
}
|
||||
Err((err, _)) => {
|
||||
log_event!(context, Event::ERROR_NETWORK, 0, "Cannot login ({})", err);
|
||||
self.unsetup_handle(context);
|
||||
|
||||
0
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unsetup_handle(&self, context: &Context) {
|
||||
let session = self.session.lock().unwrap().0.take();
|
||||
if session.is_some() {
|
||||
match session.unwrap().close() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
let stream = self.session.lock().unwrap().1.take();
|
||||
info!(context, 0, "IMAP unsetup_handle starts");
|
||||
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 1 (closing down stream)."
|
||||
);
|
||||
let stream = self.stream.write().unwrap().take();
|
||||
if stream.is_some() {
|
||||
match stream.unwrap().shutdown(net::Shutdown::Both) {
|
||||
Ok(_) => {}
|
||||
@@ -485,11 +484,24 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
}
|
||||
info!(
|
||||
context,
|
||||
0, "IMAP unsetup_handle step 2 (acquiring session.lock)"
|
||||
);
|
||||
let session = self.session.lock().unwrap().take();
|
||||
if session.is_some() {
|
||||
match session.unwrap().close() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
eprintln!("failed to close connection: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
cfg.selected_folder = None;
|
||||
cfg.selected_mailbox = None;
|
||||
info!(context, 0, "IMAP disconnected.",);
|
||||
info!(context, 0, "IMAP unsetup_handle step 3 (clearing config).");
|
||||
self.config.write().unwrap().selected_folder = None;
|
||||
self.config.write().unwrap().selected_mailbox = None;
|
||||
info!(context, 0, "IMAP unsetup_handle step 4 (disconnected).",);
|
||||
}
|
||||
|
||||
fn free_connect_params(&self) {
|
||||
@@ -507,73 +519,81 @@ impl Imap {
|
||||
cfg.watch_folder = None;
|
||||
}
|
||||
|
||||
pub fn connect(&self, context: &Context, lp: *const dc_loginparam_t) -> libc::c_int {
|
||||
if lp.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let lp = unsafe { *lp };
|
||||
if lp.mail_server.is_null() || lp.mail_user.is_null() || lp.mail_pw.is_null() {
|
||||
return 0;
|
||||
pub fn connect(&self, context: &Context, lp: &dc_loginparam_t) -> bool {
|
||||
if lp.mail_server.is_empty() || lp.mail_user.is_empty() || lp.mail_pw.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if self.is_connected() {
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
{
|
||||
let addr = as_str(lp.addr);
|
||||
let imap_server = as_str(lp.mail_server);
|
||||
let addr = &lp.addr;
|
||||
let imap_server = &lp.mail_server;
|
||||
let imap_port = lp.mail_port as u16;
|
||||
let imap_user = as_str(lp.mail_user);
|
||||
let imap_pw = as_str(lp.mail_pw);
|
||||
let imap_user = &lp.mail_user;
|
||||
let imap_pw = &lp.mail_pw;
|
||||
let server_flags = lp.server_flags as usize;
|
||||
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.addr = addr.into();
|
||||
config.imap_server = imap_server.into();
|
||||
config.addr = addr.to_string();
|
||||
config.imap_server = imap_server.to_string();
|
||||
config.imap_port = imap_port.into();
|
||||
config.imap_user = imap_user.into();
|
||||
config.imap_pw = imap_pw.into();
|
||||
config.imap_user = imap_user.to_string();
|
||||
config.imap_pw = imap_pw.to_string();
|
||||
config.server_flags = server_flags;
|
||||
}
|
||||
|
||||
if self.setup_handle_if_needed(context) == 0 {
|
||||
if !self.setup_handle_if_needed(context) {
|
||||
self.free_connect_params();
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
match self.session.lock().unwrap().0 {
|
||||
let teardown: bool;
|
||||
|
||||
match &mut *self.session.lock().unwrap() {
|
||||
Some(ref mut session) => {
|
||||
if let Ok(caps) = session.capabilities() {
|
||||
let can_idle = caps.has("IDLE");
|
||||
let has_xlist = caps.has("XLIST");
|
||||
|
||||
let caps_list = caps.iter().fold(String::new(), |mut s, c| {
|
||||
s += " ";
|
||||
s += c;
|
||||
s
|
||||
});
|
||||
|
||||
info!(context, 0, "IMAP-capabilities:{}", caps_list);
|
||||
|
||||
let mut config = self.config.write().unwrap();
|
||||
config.can_idle = can_idle;
|
||||
config.has_xlist = has_xlist;
|
||||
*self.connected.lock().unwrap() = true;
|
||||
|
||||
1
|
||||
if !context.sql.is_open() {
|
||||
warn!(context, 0, "IMAP-LOGIN as {} ok but ABORTING", lp.mail_user,);
|
||||
teardown = true;
|
||||
} else {
|
||||
let can_idle = caps.has("IDLE");
|
||||
let has_xlist = caps.has("XLIST");
|
||||
let caps_list = caps.iter().fold(String::new(), |mut s, c| {
|
||||
s += " ";
|
||||
s += c;
|
||||
s
|
||||
});
|
||||
log_event!(
|
||||
context,
|
||||
Event::IMAP_CONNECTED,
|
||||
0,
|
||||
"IMAP-LOGIN as {}, capabilities: {}",
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
self.unsetup_handle(context);
|
||||
self.free_connect_params();
|
||||
0
|
||||
teardown = true;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.unsetup_handle(context);
|
||||
self.free_connect_params();
|
||||
0
|
||||
teardown = true;
|
||||
}
|
||||
}
|
||||
if teardown {
|
||||
self.unsetup_handle(context);
|
||||
self.free_connect_params();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn disconnect(&self, context: &Context) {
|
||||
@@ -584,12 +604,12 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_watch_folder(&self, watch_folder: *const libc::c_char) {
|
||||
self.config.write().unwrap().watch_folder = Some(to_string(watch_folder));
|
||||
pub fn set_watch_folder(&self, watch_folder: String) {
|
||||
self.config.write().unwrap().watch_folder = Some(watch_folder);
|
||||
}
|
||||
|
||||
pub fn fetch(&self, context: &Context) -> libc::c_int {
|
||||
if !self.is_connected() {
|
||||
if !self.is_connected() || !context.sql.is_open() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -613,7 +633,7 @@ impl Imap {
|
||||
}
|
||||
|
||||
fn select_folder<S: AsRef<str>>(&self, context: &Context, folder: Option<S>) -> usize {
|
||||
if self.session.lock().unwrap().0.is_none() {
|
||||
if self.session.lock().unwrap().is_none() {
|
||||
let mut cfg = self.config.write().unwrap();
|
||||
cfg.selected_folder = None;
|
||||
cfg.selected_folder_needs_expunge = false;
|
||||
@@ -637,7 +657,7 @@ impl Imap {
|
||||
|
||||
// A CLOSE-SELECT is considerably faster than an EXPUNGE-SELECT, see
|
||||
// https://tools.ietf.org/html/rfc3501#section-6.4.2
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.close() {
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
@@ -653,7 +673,7 @@ impl Imap {
|
||||
|
||||
// select new folder
|
||||
if let Some(ref folder) = folder {
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.select(folder) {
|
||||
Ok(mailbox) => {
|
||||
let mut config = self.config.write().unwrap();
|
||||
@@ -686,17 +706,19 @@ impl Imap {
|
||||
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
|
||||
let key = format!("imap.mailbox.{}", folder.as_ref());
|
||||
let val1 = unsafe {
|
||||
(self.get_config)(
|
||||
context,
|
||||
CString::new(key).unwrap().as_ptr(),
|
||||
0 as *const libc::c_char,
|
||||
)
|
||||
let key_c = to_cstring(key);
|
||||
let val = (self.get_config)(context, key_c, 0 as *const libc::c_char);
|
||||
free(key_c as *mut _);
|
||||
val
|
||||
};
|
||||
if val1.is_null() {
|
||||
return (0, 0);
|
||||
}
|
||||
let entry = as_str(val1);
|
||||
|
||||
if entry.is_empty() {
|
||||
return (0, 0);
|
||||
}
|
||||
// the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>`
|
||||
let mut parts = entry.split(':');
|
||||
(
|
||||
@@ -759,7 +781,7 @@ impl Imap {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let list = if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
// `FETCH <message sequence number> (UID)`
|
||||
let set = format!("{}", mailbox.exists);
|
||||
match session.fetch(set, PREFETCH_FLAGS) {
|
||||
@@ -803,7 +825,7 @@ impl Imap {
|
||||
let mut read_errors = 0;
|
||||
let mut new_last_seen_uid = 0;
|
||||
|
||||
let list = if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
let list = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
// fetch messages with larger UID than the last one seen
|
||||
// (`UID FETCH lastseenuid+1:*)`, see RFC 4549
|
||||
let set = format!("{}:*", last_seen_uid + 1);
|
||||
@@ -830,10 +852,11 @@ impl Imap {
|
||||
.message_id
|
||||
.expect("missing message id");
|
||||
|
||||
let message_id_c = CString::new(message_id).unwrap();
|
||||
let folder_c = CString::new(folder.as_ref().to_owned()).unwrap();
|
||||
if 0 == unsafe {
|
||||
(self.precheck_imf)(context, message_id_c.as_ptr(), folder_c.as_ptr(), cur_uid)
|
||||
let message_id_c = to_cstring(message_id);
|
||||
let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid);
|
||||
free(message_id_c as *mut _);
|
||||
res
|
||||
} {
|
||||
// check passed, go fetch the rest
|
||||
if self.fetch_single_msg(context, &folder, cur_uid) == 0 {
|
||||
@@ -902,11 +925,11 @@ impl Imap {
|
||||
let val = format!("{}:{}", uidvalidity, lastseenuid);
|
||||
|
||||
unsafe {
|
||||
(self.set_config)(
|
||||
context,
|
||||
CString::new(key).unwrap().as_ptr(),
|
||||
CString::new(val).unwrap().as_ptr(),
|
||||
)
|
||||
let key_c = to_cstring(key);
|
||||
let val_c = to_cstring(val);
|
||||
(self.set_config)(context, key_c, val_c);
|
||||
free(key_c as *mut _);
|
||||
free(val_c as *mut _);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -927,7 +950,7 @@ impl Imap {
|
||||
|
||||
let set = format!("{}", server_uid);
|
||||
|
||||
let msgs = if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
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) => {
|
||||
@@ -985,13 +1008,13 @@ impl Imap {
|
||||
let flags = if is_seen { DC_IMAP_SEEN } else { 0 };
|
||||
|
||||
if !is_deleted && msg.body().is_some() {
|
||||
let body = msg.body().unwrap();
|
||||
unsafe {
|
||||
let folder_c = CString::new(folder.as_ref().to_owned()).unwrap();
|
||||
(self.receive_imf)(
|
||||
context,
|
||||
msg.body().unwrap().as_ptr() as *const libc::c_char,
|
||||
msg.body().unwrap().len(),
|
||||
folder_c.as_ptr(),
|
||||
body.as_ptr() as *const libc::c_char,
|
||||
body.len(),
|
||||
folder.as_ref(),
|
||||
server_uid,
|
||||
flags as u32,
|
||||
);
|
||||
@@ -1025,13 +1048,15 @@ impl Imap {
|
||||
let (sender, receiver) = std::sync::mpsc::channel();
|
||||
let v = self.watch.clone();
|
||||
|
||||
info!(context, 0, "IMAP-IDLE SPAWNING");
|
||||
std::thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*v;
|
||||
if let Some(ref mut session) = session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *session.lock().unwrap() {
|
||||
let mut idle = match session.idle() {
|
||||
Ok(idle) => idle,
|
||||
Err(err) => {
|
||||
panic!("failed to setup idle: {:?}", err);
|
||||
eprintln!("failed to setup idle: {:?}", err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1139,7 +1164,7 @@ impl Imap {
|
||||
// check for new messages. fetch_from_single_folder() has the side-effect that messages
|
||||
// are also downloaded, however, typically this would take place in the FETCH command
|
||||
// following IDLE otherwise, so this seems okay here.
|
||||
if self.setup_handle_if_needed(context) != 0 {
|
||||
if self.setup_handle_if_needed(context) {
|
||||
if let Some(ref watch_folder) = self.config.read().unwrap().watch_folder {
|
||||
if 0 != self.fetch_from_single_folder(context, watch_folder) {
|
||||
do_fake_idle = false;
|
||||
@@ -1205,7 +1230,7 @@ impl Imap {
|
||||
folder.as_ref()
|
||||
);
|
||||
} else {
|
||||
let moved = if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
let moved = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_mv(&set, &dest_folder) {
|
||||
Ok(_) => {
|
||||
res = DC_SUCCESS;
|
||||
@@ -1230,7 +1255,7 @@ impl Imap {
|
||||
};
|
||||
|
||||
if !moved {
|
||||
let copied = if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
let copied = if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_copy(&set, &dest_folder) {
|
||||
Ok(_) => true,
|
||||
Err(err) => {
|
||||
@@ -1275,7 +1300,7 @@ impl Imap {
|
||||
if server_uid == 0 {
|
||||
return 0;
|
||||
}
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
let set = format!("{}", server_uid);
|
||||
let query = format!("+FLAGS ({})", flag.as_ref());
|
||||
match session.uid_store(&set, &query) {
|
||||
@@ -1387,18 +1412,18 @@ impl Imap {
|
||||
.expect("just selected folder");
|
||||
|
||||
if can_create_flag {
|
||||
let fetched_msgs = if let Some(ref mut session) = self.session.lock().unwrap().0
|
||||
{
|
||||
match session.uid_fetch(set, FETCH_FLAGS) {
|
||||
Ok(res) => Some(res),
|
||||
Err(err) => {
|
||||
eprintln!("fetch error: {:?}", err);
|
||||
None
|
||||
let fetched_msgs =
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_fetch(set, FETCH_FLAGS) {
|
||||
Ok(res) => Some(res),
|
||||
Err(err) => {
|
||||
eprintln!("fetch error: {:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
} else {
|
||||
unreachable!();
|
||||
};
|
||||
|
||||
if let Some(msgs) = fetched_msgs {
|
||||
let flag_set = msgs
|
||||
@@ -1479,7 +1504,7 @@ impl Imap {
|
||||
);
|
||||
} else {
|
||||
let set = format!("{}", server_uid);
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.uid_fetch(set, PREFETCH_FLAGS) {
|
||||
Ok(msgs) => {
|
||||
if msgs.is_empty()
|
||||
@@ -1561,7 +1586,7 @@ impl Imap {
|
||||
if mvbox_folder.is_none() && 0 != (flags as usize & DC_CREATE_MVBOX) {
|
||||
info!(context, 0, "Creating MVBOX-folder \"DeltaChat\"...",);
|
||||
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
match session.create("DeltaChat") {
|
||||
Ok(_) => {
|
||||
mvbox_folder = Some("DeltaChat".into());
|
||||
@@ -1597,29 +1622,25 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
dc_sqlite3_set_config_int(
|
||||
context,
|
||||
&context.sql,
|
||||
b"folders_configured\x00" as *const u8 as *const libc::c_char,
|
||||
3,
|
||||
);
|
||||
if let Some(ref mvbox_folder) = mvbox_folder {
|
||||
dc_sqlite3_set_config(
|
||||
context
|
||||
.sql
|
||||
.set_config_int(context, "folders_configured", 3)
|
||||
.ok();
|
||||
if let Some(ref mvbox_folder) = mvbox_folder {
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "configured_mvbox_folder", Some(mvbox_folder))
|
||||
.ok();
|
||||
}
|
||||
if let Some(ref sentbox_folder) = sentbox_folder {
|
||||
context
|
||||
.sql
|
||||
.set_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_mvbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
CString::new(mvbox_folder.clone()).unwrap().as_ptr(),
|
||||
);
|
||||
}
|
||||
if let Some(ref sentbox_folder) = sentbox_folder {
|
||||
dc_sqlite3_set_config(
|
||||
context,
|
||||
&context.sql,
|
||||
b"configured_sentbox_folder\x00" as *const u8 as *const libc::c_char,
|
||||
CString::new(sentbox_folder.name()).unwrap().as_ptr(),
|
||||
);
|
||||
}
|
||||
"configured_sentbox_folder",
|
||||
Some(sentbox_folder.name()),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1627,7 +1648,7 @@ impl Imap {
|
||||
&self,
|
||||
context: &Context,
|
||||
) -> Option<imap::types::ZeroCopy<Vec<imap::types::Name>>> {
|
||||
if let Some(ref mut session) = self.session.lock().unwrap().0 {
|
||||
if let Some(ref mut session) = &mut *self.session.lock().unwrap() {
|
||||
// TODO: use xlist when available
|
||||
match session.list(Some(""), Some("*")) {
|
||||
Ok(list) => {
|
||||
|
||||
251
src/key.rs
251
src/key.rs
@@ -10,9 +10,8 @@ use pgp::types::{KeyTrait, SecretKeyTrait};
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
use crate::sql::{self, Sql};
|
||||
use crate::x::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
@@ -90,6 +89,9 @@ impl Key {
|
||||
}
|
||||
|
||||
pub fn from_slice(bytes: &[u8], key_type: KeyType) -> Option<Self> {
|
||||
if 0 == bytes.len() {
|
||||
return None;
|
||||
}
|
||||
let res: Result<Key, _> = match key_type {
|
||||
KeyType::Public => SignedPublicKey::from_bytes(Cursor::new(bytes)).map(Into::into),
|
||||
KeyType::Private => SignedSecretKey::from_bytes(Cursor::new(bytes)).map(Into::into),
|
||||
@@ -113,24 +115,31 @@ impl Key {
|
||||
Self::from_slice(bytes, key_type)
|
||||
}
|
||||
|
||||
pub fn from_stmt(
|
||||
stmt: *mut sqlite3_stmt,
|
||||
index: libc::c_int,
|
||||
pub fn from_armored_string(
|
||||
data: &str,
|
||||
key_type: KeyType,
|
||||
) -> Option<Self> {
|
||||
assert!(!stmt.is_null(), "missing statement");
|
||||
) -> Option<(Self, BTreeMap<String, String>)> {
|
||||
let bytes = data.as_bytes();
|
||||
let res: Result<(Key, _), _> = match key_type {
|
||||
KeyType::Public => SignedPublicKey::from_armor_single(Cursor::new(bytes))
|
||||
.map(|(k, h)| (Into::into(k), h)),
|
||||
KeyType::Private => SignedSecretKey::from_armor_single(Cursor::new(bytes))
|
||||
.map(|(k, h)| (Into::into(k), h)),
|
||||
};
|
||||
|
||||
let data = unsafe { sqlite3_column_blob(stmt, index) as *const u8 };
|
||||
let len = unsafe { sqlite3_column_bytes(stmt, index) };
|
||||
|
||||
Self::from_binary(data, len, key_type)
|
||||
match res {
|
||||
Ok(res) => Some(res),
|
||||
Err(err) => {
|
||||
eprintln!("Invalid key bytes: {:?}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_base64(encoded_data: &str, key_type: KeyType) -> Option<Self> {
|
||||
// strip newlines and other whitespace
|
||||
let cleaned: String = encoded_data.trim().split_whitespace().collect();
|
||||
let bytes = cleaned.as_bytes();
|
||||
|
||||
base64::decode(bytes)
|
||||
.ok()
|
||||
.and_then(|decoded| Self::from_slice(&decoded, key_type))
|
||||
@@ -138,61 +147,32 @@ impl Key {
|
||||
|
||||
pub fn from_self_public(
|
||||
context: &Context,
|
||||
self_addr: *const libc::c_char,
|
||||
sql: &SQLite,
|
||||
self_addr: impl AsRef<str>,
|
||||
sql: &Sql,
|
||||
) -> Option<Self> {
|
||||
if self_addr.is_null() {
|
||||
return None;
|
||||
}
|
||||
let addr = self_addr.as_ref();
|
||||
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
sql,
|
||||
b"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
|
||||
|
||||
let key = if unsafe { sqlite3_step(stmt) } == 100 {
|
||||
Self::from_stmt(stmt, 0, KeyType::Public)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
|
||||
key
|
||||
sql.query_row_col(
|
||||
context,
|
||||
"SELECT public_key FROM keypairs WHERE addr=? AND is_default=1;",
|
||||
&[addr],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Public))
|
||||
}
|
||||
|
||||
pub fn from_self_private(
|
||||
context: &Context,
|
||||
self_addr: *const libc::c_char,
|
||||
sql: &SQLite,
|
||||
self_addr: impl AsRef<str>,
|
||||
sql: &Sql,
|
||||
) -> Option<Self> {
|
||||
if self_addr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
sql,
|
||||
b"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
|
||||
|
||||
let key = if unsafe { sqlite3_step(stmt) } == 100 {
|
||||
Self::from_stmt(stmt, 0, KeyType::Private)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
|
||||
key
|
||||
sql.query_row_col(
|
||||
context,
|
||||
"SELECT private_key FROM keypairs WHERE addr=? AND is_default=1;",
|
||||
&[self_addr.as_ref()],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Self::from_slice(&blob, KeyType::Private))
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
@@ -226,16 +206,6 @@ impl Key {
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// the result must be freed
|
||||
pub fn to_base64_c(&self, break_every: usize) -> *mut libc::c_char {
|
||||
let res = self.to_base64(break_every);
|
||||
let res_c = CString::new(res.trim()).unwrap();
|
||||
|
||||
// need to use strdup to allocate the result with malloc
|
||||
// so it can be `free`d later.
|
||||
unsafe { strdup(res_c.as_ptr()) }
|
||||
}
|
||||
|
||||
pub fn to_armored_string(
|
||||
&self,
|
||||
headers: Option<&BTreeMap<String, String>>,
|
||||
@@ -330,57 +300,16 @@ pub fn dc_key_save_self_keypair(
|
||||
context: &Context,
|
||||
public_key: &Key,
|
||||
private_key: &Key,
|
||||
addr: *const libc::c_char,
|
||||
addr: impl AsRef<str>,
|
||||
is_default: libc::c_int,
|
||||
sql: &SQLite,
|
||||
sql: &Sql,
|
||||
) -> bool {
|
||||
if addr.is_null() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
sql::execute(
|
||||
context,
|
||||
sql,
|
||||
b"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);\x00"
|
||||
as *const u8 as *const libc::c_char
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
sqlite3_bind_text(stmt, 1, addr, -1, None);
|
||||
sqlite3_bind_int(stmt, 2, is_default)
|
||||
};
|
||||
let pub_bytes = public_key.to_bytes();
|
||||
let sec_bytes = private_key.to_bytes();
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
stmt,
|
||||
3,
|
||||
pub_bytes.as_ptr() as *const _,
|
||||
pub_bytes.len() as libc::c_int,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
stmt,
|
||||
4,
|
||||
sec_bytes.as_ptr() as *const _,
|
||||
sec_bytes.len() as libc::c_int,
|
||||
None,
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_int64(stmt, 5, time() as sqlite3_int64) };
|
||||
let success = if unsafe { sqlite3_step(stmt) } == 101 {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
|
||||
success
|
||||
"INSERT INTO keypairs (addr, is_default, public_key, private_key, created) VALUES (?,?,?,?,?);",
|
||||
params![addr.as_ref(), is_default, public_key.to_bytes(), private_key.to_bytes(), time()],
|
||||
).is_ok()
|
||||
}
|
||||
|
||||
/// Make a fingerprint human-readable, in hex format.
|
||||
@@ -436,6 +365,73 @@ mod tests {
|
||||
assert_eq!(fingerprint, "1234567890ABCDABCDEFABCDEF");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_armored_string() {
|
||||
let (private_key, _) = Key::from_armored_string(
|
||||
"-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xcLYBF0fgz4BCADnRUV52V4xhSsU56ZaAn3+3oG86MZhXy4X8w14WZZDf0VJGeTh
|
||||
oTtVwiw9rVN8FiUELqpO2CS2OwS9mAGMJmGIt78bvIy2EHIAjUilqakmb0ChJxC+
|
||||
ilSowab9slSdOgzQI1fzo+VZkhtczvRBq31cW8G05tuiLsnDSSS+sSH/GkvJqpzB
|
||||
BWu6tSrMzth58KBM2XwWmozpLzy6wlrUBOYT8J79UVvs81O/DhXpVYYOWj2h4n3O
|
||||
60qtK7SJBCjG7vGc2Ef8amsrjTDwUii0QQcF+BJN3ZuCI5AdOTpI39QuCDuD9UH2
|
||||
NOKI+jYPQ4KB8pA1aYXBZzYyjuwCHzryXXsXABEBAAEAB/0VkYBJPNxsAd9is7fv
|
||||
7QuTGW1AEPVvX1ENKr2226QH53auupt972t5NAKsPd3rVKVfHnsDn2TNGfP3OpXq
|
||||
XCn8diZ8j7kPwbjgFE0SJiCAVR/R57LIEl6S3nyUbG03vJI1VxZ8wmxBTj7/CM3+
|
||||
0d9/HY+TL3SMS5DFhazHm/1vrPbBz8FiNKtdTLHniW2/HUAN93aeALq0h4j7LKAC
|
||||
QaQOs4ej/UeIvL7dihTGc2SwXfUA/5BEPDnlrBVhhCZhWuu3dF7nMMcEVP9/gFOH
|
||||
khILR01b7fCfs+lxKHKxtAmHasOOi7xp26O61m3RQl//eid3CTdWpCNdxU4Y4kyp
|
||||
9KsBBAD0IMXzkJOM6epVuD+sm5QDyKBow1sODjlc+RNIGUiUUOD8Ho+ra4qC391L
|
||||
rn1T5xjJYExVqnnL//HVFGyGnkUZIwtztY5R8a2W9PnYQQedBL6XPnknI+6THEoe
|
||||
Od9fIdsUaWd+Ab+svfpSoEy3wrFpP2G8340EGNBEpDcPIzqr6wQA8oRulFUMx0cS
|
||||
ko65K4LCgpSpeEo6cI/PG/UNGM7Fb+eaF9UrF3Uq19ASiTPNAb6ZsJ007lmIW7+9
|
||||
bkynYu75t4nhVnkiikTDS2KOeFQpmQbdTrHEbm9w614BtnCQEg4BzZU43dtTIhZN
|
||||
Q50yYiAAhr5g+9H1QMOZ99yMzCIt/oUEAKZEISt1C6lf8iLpzCdKRlOEANmf7SyQ
|
||||
P+7JZ4BXmaZEbFKGGQpWm1P3gYkYIT5jwnQsKsHdIAFiGfAZS4SPezesfRPlc4RB
|
||||
9qLA0hDROrM47i5XK+kQPY3GPU7zNjbU9t60GyBhTzPAh+ikhUzNCBGj+3CqE8/3
|
||||
NRMrGNvzhUwXOunNBzxoZWxsbz7CwIkEEAEIADMCGQEFAl0fg18CGwMECwkIBwYV
|
||||
CAkKCwIDFgIBFiEEaeHEHjiV97rB+YeLMKMg0aJs7GIACgkQMKMg0aJs7GKh1gf+
|
||||
Jx9A/7z5A3N6bzCjolnDMepktdVRAaW2Z/YDQ9eNxA3N0HHTN0StXGg55BVIrGZQ
|
||||
2MbB++qx0nBQI4YM31RsWUIUfXm1EfPI8/07RAtrGdjfCsiG8Fi4YEEzDOgCRgQl
|
||||
+cwioVPmcPWbQaZxpm6Z0HPG54VX3Pt/NXvc80GB6++13KMr+V87XWxsDjAnuo5+
|
||||
edFWtreNq/qLE81xIwHSYgmzJbSAOhzhXfRYyWz8YM2YbEy0Ad3Zm1vkgQmC5q9m
|
||||
Ge7qWdG+z2sYEy1TfM0evSO5B6/0YDeeNkyR6qXASMw9Yhsz8oxwzOfKdI270qaN
|
||||
q6zaRuul7d5p3QJY2D0HIMfC2ARdH4M+AQgArioPOJsOhTcZfdPh/7I6f503YY3x
|
||||
jqQ02WzcjzsJD4RHPXmF2l+N3F4vgxVe/voPPbvYDIu2leAnPoi7JWrBMSXH3Y5+
|
||||
/TCC/I1JyhOG5r+OYiNmI7dgwfbuP41nDDb2sxbBUG/1HGNqVvwgayirgeJb4WEq
|
||||
Gpk8dznS9Fb/THz5IUosnxeNjH3jyTDAL7c+L5i2DDCBi5JixX/EeV1wlH3xLiHB
|
||||
YWEHMQ5S64ASWmnuvzrHKDQv0ClwDiP1o9FBiBsbcxszbvohyy+AmCiWV/D4ZGI9
|
||||
nUid8MwLs0J+8jToqIhjiFmSIDPGpXOANHQLzSCxEN9Yj1G0d5B89NveiQARAQAB
|
||||
AAf/XJ3LOFvkjdzuNmaNoS8DQse1IrCcCzGxVQo6BATt3Y2HYN6V2rnDs7N2aqvb
|
||||
t5X8suSIkKtfbjYkSHHnq48oq10e+ugDCdtZXLo5yjc2HtExA2k1sLqcvqj0q2Ej
|
||||
snAsIrJwHLlczDrl2tn612FqSwi3uZO1Ey335KMgVoVJAD/4nAj2Ku+Aqpw/nca5
|
||||
w3mSx+YxmB/pwHIrr/0hfYLyVPy9QPJ/BqXVlAmSyZxzv7GOipCSouBLTibuEAsC
|
||||
pI0TYRHtAnonY9F+8hiERda6qa+xXLaEwj1hiorEt62KaWYfiCC1Xr+Rlmo3GAwV
|
||||
08X0yYFhdFMQ6wMhDdrHtB3iAQQA04O09JiUwIbNb7kjd3TpjUebjR2Vw5OT3a2/
|
||||
4+73ESZPexDVJ/8dQAuRGDKx7UkLYsPJnU3Lc2IT456o4D0wytZJuGzwbMLo2Kn9
|
||||
hAe+5KaN+/+MipsUcmC98zIMcRNDirIQV6vYmFo6WZVUsx1c+bH1EV7CmJuuY4+G
|
||||
JKz0HMEEANLLWy/9enOvSpznYIUdtXxNG6evRHClkf7jZimM/VrAc4ICW4hqICK3
|
||||
k5VMcRxVOa9hKZgg8vLfO8BRPRUB6Bc3SrK2jCKSli0FbtliNZS/lUBO1A7HRtY6
|
||||
3coYUJBKqzmObLkh4C3RFQ5n/I6cJEvD7u9jzgpW71HtdI64NQvJBAC+88Q5irPg
|
||||
07UZH9by8EVsCij8NFzChGmysHHGqeAMVVuI+rOqDqBsQA1n2aqxQ1uz5NZ9+ztu
|
||||
Dn13hMEm8U2a9MtZdBhwlJrso3RzRf570V3E6qfdFqrQLoHDdRGRS9DMcUgMayo3
|
||||
Hod6MFYzFVmbrmc822KmhaS3lBzLVpgkmEeJwsB2BBgBCAAgBQJdH4NfAhsMFiEE
|
||||
aeHEHjiV97rB+YeLMKMg0aJs7GIACgkQMKMg0aJs7GLItQgAqKF63+HwAsjoPMBv
|
||||
T9RdKdCaYV0MvxZyc7eM2pSk8cyfj6IPnxD8DPT699SMIzBfsrdGcfDYYgSODHL+
|
||||
XsV31J215HfYBh/Nkru8fawiVxr+sJG2IDAeA9SBjsDCogfzW4PwLXgTXRqNFLVr
|
||||
fK6hf6wpF56STV2U2D60b9xJeSAbBWlZFzCCQw3mPtGf/EGMHFxnJUE7MLEaaTEf
|
||||
V2Fclh+G0sWp7F2ZS3nt0vX1hYG8TMIzM8Bj2eMsdXATOji9ST7EUxk/BpFax86D
|
||||
i8pcjGO+IZffvyZJVRWfVooBJmWWbPB1pueo3tx8w3+fcuzpxz+RLFKaPyqXO+dD
|
||||
7yPJeQ==
|
||||
=KZk/
|
||||
-----END PGP PRIVATE KEY BLOCK-----",
|
||||
KeyType::Private,
|
||||
)
|
||||
.expect("failed to decode"); // NOTE: if you take out the ===GU1/ part, everything passes!
|
||||
let binary = private_key.to_bytes();
|
||||
Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_fingerprint() {
|
||||
let fingerprint = dc_format_fingerprint("1234567890ABCDABCDEFABCDEF1234567890ABCD");
|
||||
@@ -447,9 +443,9 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_from_slice_roundtrip() {
|
||||
let (public_key, private_key) =
|
||||
crate::pgp::dc_pgp_create_keypair(CString::new("hello").unwrap().as_ptr()).unwrap();
|
||||
let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap();
|
||||
|
||||
let binary = public_key.to_bytes();
|
||||
let public_key2 = Key::from_slice(&binary, KeyType::Public).expect("invalid public key");
|
||||
@@ -459,4 +455,21 @@ mod tests {
|
||||
let private_key2 = Key::from_slice(&binary, KeyType::Private).expect("invalid private key");
|
||||
assert_eq!(private_key, private_key2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_ascii_roundtrip() {
|
||||
let (public_key, private_key) = crate::pgp::dc_pgp_create_keypair("hello").unwrap();
|
||||
|
||||
let s = public_key.to_armored_string(None).unwrap();
|
||||
let (public_key2, _) =
|
||||
Key::from_armored_string(&s, KeyType::Public).expect("invalid public key");
|
||||
assert_eq!(public_key, public_key2);
|
||||
|
||||
let s = private_key.to_armored_string(None).unwrap();
|
||||
println!("{}", &s);
|
||||
let (private_key2, _) =
|
||||
Key::from_armored_string(&s, KeyType::Private).expect("invalid private key");
|
||||
assert_eq!(private_key, private_key2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,8 @@ use std::borrow::Cow;
|
||||
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::key::*;
|
||||
use crate::types::*;
|
||||
use crate::sql::Sql;
|
||||
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub struct Keyring<'a> {
|
||||
@@ -31,29 +30,17 @@ impl<'a> Keyring<'a> {
|
||||
pub fn load_self_private_for_decrypting(
|
||||
&mut self,
|
||||
context: &Context,
|
||||
self_addr: *const libc::c_char,
|
||||
sql: &SQLite,
|
||||
self_addr: impl AsRef<str>,
|
||||
sql: &Sql,
|
||||
) -> bool {
|
||||
// Can we prevent keyring and self_addr to be null?
|
||||
if self_addr.is_null() {
|
||||
return false;
|
||||
}
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
sql,
|
||||
b"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;\x00"
|
||||
as *const u8 as *const libc::c_char,
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_text(stmt, 1, self_addr, -1, None) };
|
||||
while unsafe { sqlite3_step(stmt) == 100 } {
|
||||
if let Some(key) = Key::from_stmt(stmt, 0, KeyType::Private) {
|
||||
self.add_owned(key);
|
||||
}
|
||||
}
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
|
||||
true
|
||||
sql.query_row_col(
|
||||
context,
|
||||
"SELECT private_key FROM keypairs ORDER BY addr=? DESC, is_default DESC;",
|
||||
&[self_addr.as_ref()],
|
||||
0,
|
||||
)
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Private))
|
||||
.map(|key| self.add_owned(key))
|
||||
.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,13 +14,17 @@ extern crate failure_derive;
|
||||
extern crate num_derive;
|
||||
#[macro_use]
|
||||
extern crate smallvec;
|
||||
#[macro_use]
|
||||
extern crate rusqlite;
|
||||
|
||||
#[macro_use]
|
||||
pub mod dc_log;
|
||||
mod log;
|
||||
|
||||
pub mod aheader;
|
||||
pub mod config;
|
||||
pub mod constants;
|
||||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod imap;
|
||||
pub mod key;
|
||||
pub mod keyhistory;
|
||||
@@ -29,6 +33,7 @@ pub mod oauth2;
|
||||
pub mod peerstate;
|
||||
pub mod pgp;
|
||||
pub mod smtp;
|
||||
pub mod sql;
|
||||
pub mod types;
|
||||
pub mod x;
|
||||
|
||||
@@ -55,7 +60,6 @@ pub mod dc_receive_imf;
|
||||
pub mod dc_saxparser;
|
||||
pub mod dc_securejoin;
|
||||
pub mod dc_simplify;
|
||||
pub mod dc_sqlite3;
|
||||
pub mod dc_stock;
|
||||
pub mod dc_strencode;
|
||||
pub mod dc_token;
|
||||
|
||||
63
src/log.rs
Normal file
63
src/log.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
#[macro_export]
|
||||
macro_rules! info {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
info!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warn {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
warn!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void) ;
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
error!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! log_event {
|
||||
($ctx:expr, $data1:expr, $msg:expr) => {
|
||||
log_event!($ctx, $data1, $msg,)
|
||||
};
|
||||
($ctx:expr, $event:expr, $data1:expr, $msg:expr, $($args:expr),* $(,)?) => {
|
||||
#[allow(unused_unsafe)]
|
||||
unsafe {
|
||||
let formatted = format!($msg, $($args),*);
|
||||
let formatted_c = $crate::dc_tools::to_cstring(formatted);
|
||||
$ctx.call_cb($event, $data1 as libc::uintptr_t,
|
||||
formatted_c as libc::uintptr_t);
|
||||
libc::free(formatted_c as *mut libc::c_void);
|
||||
}};
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CString;
|
||||
|
||||
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::types::*;
|
||||
|
||||
const OAUTH2_GMAIL: Oauth2 = Oauth2 {
|
||||
client_id: "959970109878-4mvtgf6feshskf7695nfln6002mom908.apps.googleusercontent.com",
|
||||
@@ -51,11 +48,17 @@ pub fn dc_get_oauth2_url(
|
||||
redirect_uri: impl AsRef<str>,
|
||||
) -> Option<String> {
|
||||
if let Some(oauth2) = Oauth2::from_address(addr) {
|
||||
set_config(
|
||||
context,
|
||||
"oauth2_pending_redirect_uri",
|
||||
redirect_uri.as_ref(),
|
||||
);
|
||||
if context
|
||||
.sql
|
||||
.set_config(
|
||||
context,
|
||||
"oauth2_pending_redirect_uri",
|
||||
Some(redirect_uri.as_ref()),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let oauth2_url = replace_in_uri(&oauth2.get_code, "$CLIENT_ID", &oauth2.client_id);
|
||||
let oauth2_url = replace_in_uri(&oauth2_url, "$REDIRECT_URI", redirect_uri.as_ref());
|
||||
|
||||
@@ -79,16 +82,18 @@ pub fn dc_get_oauth2_access_token(
|
||||
|
||||
// read generated token
|
||||
if 0 == flags & 0x1 && !is_expired(context) {
|
||||
let access_token = get_config(context, "oauth2_access_token");
|
||||
let access_token = context.sql.get_config(context, "oauth2_access_token");
|
||||
if access_token.is_some() {
|
||||
// success
|
||||
return access_token;
|
||||
}
|
||||
}
|
||||
|
||||
let refresh_token = get_config(context, "oauth2_refresh_token");
|
||||
let refresh_token_for =
|
||||
get_config(context, "oauth2_refresh_token_for").unwrap_or_else(|| "unset".into());
|
||||
let refresh_token = context.sql.get_config(context, "oauth2_refresh_token");
|
||||
let refresh_token_for = context
|
||||
.sql
|
||||
.get_config(context, "oauth2_refresh_token_for")
|
||||
.unwrap_or_else(|| "unset".into());
|
||||
|
||||
let (redirect_uri, token_url, update_redirect_uri_on_success) =
|
||||
if refresh_token.is_none() || refresh_token_for != code.as_ref() {
|
||||
@@ -97,7 +102,9 @@ pub fn dc_get_oauth2_access_token(
|
||||
0, "Generate OAuth2 refresh_token and access_token...",
|
||||
);
|
||||
(
|
||||
get_config(context, "oauth2_pending_redirect_uri")
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "oauth2_pending_redirect_uri")
|
||||
.unwrap_or_else(|| "unset".into()),
|
||||
oauth2.init_token,
|
||||
true,
|
||||
@@ -108,7 +115,10 @@ pub fn dc_get_oauth2_access_token(
|
||||
0, "Regenerate OAuth2 access_token by refresh_token...",
|
||||
);
|
||||
(
|
||||
get_config(context, "oauth2_redirect_uri").unwrap_or_else(|| "unset".into()),
|
||||
context
|
||||
.sql
|
||||
.get_config(context, "oauth2_redirect_uri")
|
||||
.unwrap_or_else(|| "unset".into()),
|
||||
oauth2.refresh_token,
|
||||
false,
|
||||
)
|
||||
@@ -151,23 +161,38 @@ pub fn dc_get_oauth2_access_token(
|
||||
println!("response: {:?}", &parsed);
|
||||
let response = parsed.unwrap();
|
||||
if let Some(ref token) = response.refresh_token {
|
||||
set_config(context, "oauth2_refresh_token", token);
|
||||
set_config(context, "oauth2_refresh_token_for", code.as_ref());
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "oauth2_refresh_token", Some(token))
|
||||
.ok();
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "oauth2_refresh_token_for", Some(code.as_ref()))
|
||||
.ok();
|
||||
}
|
||||
|
||||
// after that, save the access token.
|
||||
// if it's unset, we may get it in the next round as we have the refresh_token now.
|
||||
if let Some(ref token) = response.access_token {
|
||||
set_config(context, "oauth2_access_token", token);
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "oauth2_access_token", Some(token))
|
||||
.ok();
|
||||
let expires_in = response
|
||||
.expires_in
|
||||
// refresh a bet before
|
||||
.map(|t| time() + t as i64 - 5)
|
||||
.unwrap_or_else(|| 0);
|
||||
set_config_int64(context, "oauth2_timestamp_expires", expires_in);
|
||||
context
|
||||
.sql
|
||||
.set_config_int64(context, "oauth2_timestamp_expires", expires_in)
|
||||
.ok();
|
||||
|
||||
if update_redirect_uri_on_success {
|
||||
set_config(context, "oauth2_redirect_uri", redirect_uri.as_ref());
|
||||
context
|
||||
.sql
|
||||
.set_config(context, "oauth2_redirect_uri", Some(redirect_uri.as_ref()))
|
||||
.ok();
|
||||
}
|
||||
} else {
|
||||
warn!(context, 0, "Failed to find OAuth2 access token");
|
||||
@@ -279,35 +304,11 @@ impl Oauth2 {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_config(context: &Context, key: &str) -> Option<String> {
|
||||
let key_c = CString::new(key).unwrap();
|
||||
let res =
|
||||
unsafe { dc_sqlite3_get_config(context, &context.sql, key_c.as_ptr(), std::ptr::null()) };
|
||||
if res.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(to_string(res))
|
||||
}
|
||||
|
||||
fn set_config(context: &Context, key: &str, value: &str) {
|
||||
let key_c = CString::new(key).unwrap();
|
||||
let value_c = CString::new(value).unwrap();
|
||||
unsafe { dc_sqlite3_set_config(context, &context.sql, key_c.as_ptr(), value_c.as_ptr()) };
|
||||
}
|
||||
|
||||
fn set_config_int64(context: &Context, key: &str, value: i64) {
|
||||
let key_c = CString::new(key).unwrap();
|
||||
unsafe { dc_sqlite3_set_config_int64(context, &context.sql, key_c.as_ptr(), value) };
|
||||
}
|
||||
|
||||
fn is_expired(context: &Context) -> bool {
|
||||
let expire_timestamp = dc_sqlite3_get_config_int64(
|
||||
context,
|
||||
&context.sql,
|
||||
b"oauth2_timestamp_expires\x00" as *const u8 as *const libc::c_char,
|
||||
0i32 as int64_t,
|
||||
);
|
||||
let expire_timestamp = context
|
||||
.sql
|
||||
.get_config_int64(context, "oauth2_timestamp_expires")
|
||||
.unwrap_or_default();
|
||||
|
||||
if expire_timestamp <= 0 {
|
||||
return false;
|
||||
|
||||
358
src/peerstate.rs
358
src/peerstate.rs
@@ -1,5 +1,4 @@
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
use std::fmt;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
@@ -8,22 +7,20 @@ use crate::aheader::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_chat::*;
|
||||
use crate::dc_sqlite3::*;
|
||||
use crate::dc_tools::{to_cstring, to_string};
|
||||
use crate::key::*;
|
||||
use crate::types::*;
|
||||
use crate::sql::{self, Sql};
|
||||
|
||||
/// Peerstate represents the state of an Autocrypt peer.
|
||||
pub struct Peerstate<'a> {
|
||||
pub context: &'a Context,
|
||||
pub addr: Option<String>,
|
||||
pub last_seen: u64,
|
||||
pub last_seen_autocrypt: u64,
|
||||
pub last_seen: i64,
|
||||
pub last_seen_autocrypt: i64,
|
||||
pub prefer_encrypt: EncryptPreference,
|
||||
pub public_key: Option<Key>,
|
||||
pub public_key_fingerprint: Option<String>,
|
||||
pub gossip_key: Option<Key>,
|
||||
pub gossip_timestamp: u64,
|
||||
pub gossip_timestamp: i64,
|
||||
pub gossip_key_fingerprint: Option<String>,
|
||||
verified_key: VerifiedKey,
|
||||
pub verified_key_fingerprint: Option<String>,
|
||||
@@ -141,7 +138,7 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_header(context: &'a Context, header: &Aheader, message_time: u64) -> Self {
|
||||
pub fn from_header(context: &'a Context, header: &Aheader, message_time: i64) -> Self {
|
||||
let mut res = Self::new(context);
|
||||
|
||||
res.addr = Some(header.addr.clone());
|
||||
@@ -155,7 +152,7 @@ impl<'a> Peerstate<'a> {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: u64) -> Self {
|
||||
pub fn from_gossip(context: &'a Context, gossip_header: &Aheader, message_time: i64) -> Self {
|
||||
let mut res = Self::new(context);
|
||||
|
||||
res.addr = Some(gossip_header.addr.clone());
|
||||
@@ -167,88 +164,70 @@ impl<'a> Peerstate<'a> {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn from_addr(context: &'a Context, sql: &SQLite, addr: &str) -> Option<Self> {
|
||||
let mut res = None;
|
||||
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;";
|
||||
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
sql,
|
||||
b"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;\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
};
|
||||
let addr_c = CString::new(addr.as_bytes()).unwrap();
|
||||
unsafe { sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None) };
|
||||
if unsafe { sqlite3_step(stmt) } == 100 {
|
||||
res = Some(Self::from_stmt(context, stmt));
|
||||
}
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
res
|
||||
Self::from_stmt(context, query, &[addr])
|
||||
}
|
||||
|
||||
pub fn from_fingerprint(context: &'a Context, sql: &SQLite, fingerprint: &str) -> Option<Self> {
|
||||
let mut res = None;
|
||||
pub fn from_fingerprint(context: &'a Context, _sql: &Sql, fingerprint: &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 public_key_fingerprint=? COLLATE NOCASE \
|
||||
OR gossip_key_fingerprint=? COLLATE NOCASE \
|
||||
ORDER BY public_key_fingerprint=? DESC;";
|
||||
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
context,
|
||||
sql,
|
||||
b"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 public_key_fingerprint=? COLLATE NOCASE OR gossip_key_fingerprint=? COLLATE NOCASE ORDER BY public_key_fingerprint=? DESC;\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
};
|
||||
|
||||
let fp_c = CString::new(fingerprint.as_bytes()).unwrap();
|
||||
unsafe {
|
||||
sqlite3_bind_text(stmt, 1, fp_c.as_ptr(), -1, None);
|
||||
sqlite3_bind_text(stmt, 2, fp_c.as_ptr(), -1, None);
|
||||
sqlite3_bind_text(stmt, 3, fp_c.as_ptr(), -1, None);
|
||||
}
|
||||
if unsafe { sqlite3_step(stmt) == 100 } {
|
||||
res = Some(Self::from_stmt(context, stmt));
|
||||
}
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
|
||||
res
|
||||
let fp = fingerprint.as_bytes();
|
||||
Self::from_stmt(context, query, params![fp, fp, fp])
|
||||
}
|
||||
|
||||
fn from_stmt(context: &'a Context, stmt: *mut sqlite3_stmt) -> Self {
|
||||
let mut res = Self::new(context);
|
||||
fn from_stmt<P>(context: &'a Context, query: &str, params: P) -> Option<Self>
|
||||
where
|
||||
P: IntoIterator,
|
||||
P::Item: rusqlite::ToSql,
|
||||
{
|
||||
context
|
||||
.sql
|
||||
.query_row(query, params, |row| {
|
||||
let mut res = Self::new(context);
|
||||
|
||||
res.addr = Some(to_string(unsafe {
|
||||
sqlite3_column_text(stmt, 0) as *const _
|
||||
}));
|
||||
res.last_seen = unsafe { sqlite3_column_int64(stmt, 1) } as u64;
|
||||
res.last_seen_autocrypt = unsafe { sqlite3_column_int64(stmt, 2) } as u64;
|
||||
res.prefer_encrypt =
|
||||
EncryptPreference::from_i32(unsafe { sqlite3_column_int(stmt, 3) }).unwrap_or_default();
|
||||
res.gossip_timestamp = unsafe { sqlite3_column_int(stmt, 5) } as u64;
|
||||
let pkf = to_string(unsafe { sqlite3_column_text(stmt, 7) as *const _ });
|
||||
res.public_key_fingerprint = if pkf.is_empty() { None } else { Some(pkf) };
|
||||
let gkf = to_string(unsafe { sqlite3_column_text(stmt, 8) as *const _ });
|
||||
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
|
||||
let vkf = to_string(unsafe { sqlite3_column_text(stmt, 10) as *const _ });
|
||||
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
|
||||
res.addr = Some(row.get(0)?);
|
||||
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 gkf: String = row.get(8)?;
|
||||
res.gossip_key_fingerprint = if gkf.is_empty() { None } else { Some(gkf) };
|
||||
let vkf: String = row.get(10)?;
|
||||
res.verified_key_fingerprint = if vkf.is_empty() { None } else { Some(vkf) };
|
||||
|
||||
if unsafe { sqlite3_column_type(stmt, 4) } != 5 {
|
||||
res.public_key = Key::from_stmt(stmt, 4, KeyType::Public);
|
||||
}
|
||||
if unsafe { sqlite3_column_type(stmt, 6) } != 5 {
|
||||
res.gossip_key = Key::from_stmt(stmt, 6, KeyType::Public);
|
||||
}
|
||||
if unsafe { sqlite3_column_type(stmt, 9) } != 5 {
|
||||
let vk = Key::from_stmt(stmt, 9, KeyType::Public);
|
||||
res.verified_key = if vk == res.gossip_key {
|
||||
VerifiedKey::Gossip
|
||||
} else if vk == res.public_key {
|
||||
VerifiedKey::Public
|
||||
} else {
|
||||
VerifiedKey::None
|
||||
};
|
||||
}
|
||||
res.public_key = row
|
||||
.get(4)
|
||||
.ok()
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
|
||||
res.gossip_key = row
|
||||
.get(6)
|
||||
.ok()
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
|
||||
let vk = row
|
||||
.get(9)
|
||||
.ok()
|
||||
.and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public));
|
||||
res.verified_key = if vk == res.gossip_key {
|
||||
VerifiedKey::Gossip
|
||||
} else if vk == res.public_key {
|
||||
VerifiedKey::Public
|
||||
} else {
|
||||
VerifiedKey::None
|
||||
};
|
||||
|
||||
res
|
||||
Ok(res)
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
|
||||
pub fn recalc_fingerprint(&mut self) {
|
||||
@@ -283,7 +262,7 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn degrade_encryption(&mut self, message_time: u64) {
|
||||
pub fn degrade_encryption(&mut self, message_time: i64) {
|
||||
if self.prefer_encrypt == EncryptPreference::Mutual {
|
||||
self.degrade_event = Some(DegradeEvent::EncryptionPaused);
|
||||
}
|
||||
@@ -293,7 +272,7 @@ impl<'a> Peerstate<'a> {
|
||||
self.to_save = Some(ToSave::All);
|
||||
}
|
||||
|
||||
pub fn apply_header(&mut self, header: &Aheader, message_time: u64) {
|
||||
pub fn apply_header(&mut self, header: &Aheader, message_time: i64) {
|
||||
if self.addr.is_none()
|
||||
|| self.addr.as_ref().unwrap().to_lowercase() != header.addr.to_lowercase()
|
||||
{
|
||||
@@ -325,7 +304,7 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: u64) {
|
||||
pub fn apply_gossip(&mut self, gossip_header: &Aheader, message_time: i64) {
|
||||
if self.addr.is_none()
|
||||
|| self.addr.as_ref().unwrap().to_lowercase() != gossip_header.addr.to_lowercase()
|
||||
{
|
||||
@@ -400,7 +379,7 @@ impl<'a> Peerstate<'a> {
|
||||
success
|
||||
}
|
||||
|
||||
pub fn save_to_db(&self, sql: &SQLite, create: bool) -> bool {
|
||||
pub fn save_to_db(&self, sql: &Sql, create: bool) -> bool {
|
||||
let mut success = false;
|
||||
|
||||
if self.addr.is_none() {
|
||||
@@ -408,158 +387,59 @@ impl<'a> Peerstate<'a> {
|
||||
}
|
||||
|
||||
if create {
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
self.context,
|
||||
sql,
|
||||
b"INSERT INTO acpeerstates (addr) VALUES(?);\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
)
|
||||
};
|
||||
let addr_c = to_cstring(self.addr.as_ref().unwrap());
|
||||
unsafe {
|
||||
sqlite3_bind_text(stmt, 1, addr_c.as_ptr(), -1, None);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
if sql::execute(
|
||||
self.context,
|
||||
sql,
|
||||
"INSERT INTO acpeerstates (addr) VALUES(?);",
|
||||
params![self.addr.as_ref().unwrap()],
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if self.to_save == Some(ToSave::All) || create {
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
self.context, sql,
|
||||
b"UPDATE acpeerstates \
|
||||
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
|
||||
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, verified_key=?, verified_key_fingerprint=? \
|
||||
WHERE addr=?;\x00"
|
||||
as *const u8 as *const libc::c_char
|
||||
)
|
||||
};
|
||||
|
||||
unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) };
|
||||
unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) };
|
||||
unsafe { sqlite3_bind_int64(stmt, 3, self.prefer_encrypt as sqlite3_int64) };
|
||||
|
||||
let pub_bytes = self
|
||||
.public_key
|
||||
.as_ref()
|
||||
.map(|k| k.to_bytes())
|
||||
.unwrap_or_default();
|
||||
let gossip_bytes = self
|
||||
.gossip_key
|
||||
.as_ref()
|
||||
.map(|k| k.to_bytes())
|
||||
.unwrap_or_default();
|
||||
let ver_bytes = self
|
||||
.verified_key()
|
||||
.as_ref()
|
||||
.map(|k| k.to_bytes())
|
||||
.unwrap_or_default();
|
||||
|
||||
let pkc = self
|
||||
.public_key_fingerprint
|
||||
.as_ref()
|
||||
.map(to_cstring)
|
||||
.unwrap_or_default();
|
||||
|
||||
let pkc_ptr = if self.public_key_fingerprint.is_some() {
|
||||
pkc.as_ptr()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
|
||||
let gkc = self
|
||||
.gossip_key_fingerprint
|
||||
.as_ref()
|
||||
.map(to_cstring)
|
||||
.unwrap_or_default();
|
||||
|
||||
let gkc_ptr = if self.gossip_key_fingerprint.is_some() {
|
||||
gkc.as_ptr()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
let vkc = self
|
||||
.verified_key_fingerprint
|
||||
.as_ref()
|
||||
.map(to_cstring)
|
||||
.unwrap_or_default();
|
||||
let vkc_ptr = if self.verified_key_fingerprint.is_some() {
|
||||
vkc.as_ptr()
|
||||
} else {
|
||||
std::ptr::null_mut()
|
||||
};
|
||||
let addr: String = self.addr.clone().unwrap_or_default();
|
||||
let addr_c = to_cstring(addr);
|
||||
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
stmt,
|
||||
4,
|
||||
pub_bytes.as_ptr() as *const _,
|
||||
pub_bytes.len() as libc::c_int,
|
||||
SQLITE_TRANSIENT(),
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_int64(stmt, 5, self.gossip_timestamp as sqlite3_int64) };
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
stmt,
|
||||
6,
|
||||
gossip_bytes.as_ptr() as *const _,
|
||||
gossip_bytes.len() as libc::c_int,
|
||||
SQLITE_TRANSIENT(),
|
||||
)
|
||||
};
|
||||
unsafe { sqlite3_bind_text(stmt, 7, pkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
|
||||
unsafe { sqlite3_bind_text(stmt, 8, gkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
|
||||
unsafe {
|
||||
sqlite3_bind_blob(
|
||||
stmt,
|
||||
9,
|
||||
ver_bytes.as_ptr() as *const _,
|
||||
ver_bytes.len() as libc::c_int,
|
||||
SQLITE_TRANSIENT(),
|
||||
)
|
||||
};
|
||||
|
||||
unsafe { sqlite3_bind_text(stmt, 10, vkc_ptr as *const _, -1, SQLITE_TRANSIENT()) };
|
||||
unsafe { sqlite3_bind_text(stmt, 11, addr_c.as_ptr(), -1, SQLITE_TRANSIENT()) };
|
||||
|
||||
if unsafe { sqlite3_step(stmt) } == 101 {
|
||||
success = true;
|
||||
}
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
success = sql::execute(
|
||||
self.context,
|
||||
sql,
|
||||
"UPDATE acpeerstates \
|
||||
SET last_seen=?, last_seen_autocrypt=?, prefer_encrypted=?, \
|
||||
public_key=?, gossip_timestamp=?, gossip_key=?, public_key_fingerprint=?, gossip_key_fingerprint=?, \
|
||||
verified_key=?, verified_key_fingerprint=? \
|
||||
WHERE addr=?;",
|
||||
params![
|
||||
self.last_seen,
|
||||
self.last_seen_autocrypt,
|
||||
self.prefer_encrypt as i64,
|
||||
self.public_key.as_ref().map(|k| k.to_bytes()),
|
||||
self.gossip_timestamp,
|
||||
self.gossip_key.as_ref().map(|k| k.to_bytes()),
|
||||
&self.public_key_fingerprint,
|
||||
&self.gossip_key_fingerprint,
|
||||
self.verified_key().map(|k| k.to_bytes()),
|
||||
&self.verified_key_fingerprint,
|
||||
&self.addr,
|
||||
],
|
||||
).is_ok();
|
||||
} else if self.to_save == Some(ToSave::Timestamps) {
|
||||
let stmt = unsafe {
|
||||
dc_sqlite3_prepare(
|
||||
&self.context,sql,
|
||||
b"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? WHERE addr=?;\x00"
|
||||
as *const u8 as *const libc::c_char)
|
||||
};
|
||||
|
||||
let c_addr = self.addr.as_ref().map(to_cstring).unwrap_or_default();
|
||||
let addr_ptr = if self.addr.is_some() {
|
||||
c_addr.as_ptr()
|
||||
} else {
|
||||
std::ptr::null()
|
||||
};
|
||||
|
||||
unsafe { sqlite3_bind_int64(stmt, 1, self.last_seen as sqlite3_int64) };
|
||||
unsafe { sqlite3_bind_int64(stmt, 2, self.last_seen_autocrypt as sqlite3_int64) };
|
||||
unsafe { sqlite3_bind_int64(stmt, 3, self.gossip_timestamp as sqlite3_int64) };
|
||||
unsafe { sqlite3_bind_text(stmt, 4, addr_ptr, -1, SQLITE_TRANSIENT()) };
|
||||
|
||||
if unsafe { sqlite3_step(stmt) } == 101 {
|
||||
success = true;
|
||||
}
|
||||
|
||||
unsafe { sqlite3_finalize(stmt) };
|
||||
success = sql::execute(
|
||||
self.context,
|
||||
sql,
|
||||
"UPDATE acpeerstates SET last_seen=?, last_seen_autocrypt=?, gossip_timestamp=? \
|
||||
WHERE addr=?;",
|
||||
params![
|
||||
self.last_seen,
|
||||
self.last_seen_autocrypt,
|
||||
self.gossip_timestamp,
|
||||
&self.addr
|
||||
],
|
||||
)
|
||||
.is_ok();
|
||||
}
|
||||
|
||||
if self.to_save == Some(ToSave::All) || create {
|
||||
unsafe { dc_reset_gossiped_timestamp(self.context, 0 as uint32_t) };
|
||||
dc_reset_gossiped_timestamp(self.context, 0);
|
||||
}
|
||||
|
||||
success
|
||||
@@ -586,6 +466,8 @@ mod tests {
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::context::*;
|
||||
use crate::dc_tools::to_cstring;
|
||||
use crate::x::free;
|
||||
|
||||
#[test]
|
||||
fn test_peerstate_save_to_db() {
|
||||
@@ -631,25 +513,25 @@ mod tests {
|
||||
unsafe extern "C" fn cb(
|
||||
_context: &Context,
|
||||
_event: Event,
|
||||
_data1: uintptr_t,
|
||||
_data2: uintptr_t,
|
||||
) -> uintptr_t {
|
||||
_data1: libc::uintptr_t,
|
||||
_data2: libc::uintptr_t,
|
||||
) -> libc::uintptr_t {
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn create_test_context() -> TestContext {
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
|
||||
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
|
||||
assert_eq!(
|
||||
dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()),
|
||||
dc_open(&mut ctx, dbfile, std::ptr::null()),
|
||||
1,
|
||||
"Failed to open {}",
|
||||
CStr::from_ptr(dbfile.as_ptr() as *const libc::c_char)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
CStr::from_ptr(dbfile as *const _).to_str().unwrap()
|
||||
);
|
||||
|
||||
free(dbfile as *mut _);
|
||||
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +137,8 @@ pub unsafe fn dc_split_armored_data(
|
||||
}
|
||||
|
||||
/// Create a new key pair.
|
||||
pub fn dc_pgp_create_keypair(addr: *const libc::c_char) -> Option<(Key, Key)> {
|
||||
let user_id = format!("<{}>", unsafe { CStr::from_ptr(addr).to_str().unwrap() });
|
||||
pub fn dc_pgp_create_keypair(addr: impl AsRef<str>) -> Option<(Key, Key)> {
|
||||
let user_id = format!("<{}>", addr.as_ref());
|
||||
|
||||
let key_params = SecretKeyParamsBuilder::default()
|
||||
.key_type(PgpKeyType::Rsa(2048))
|
||||
|
||||
85
src/smtp.rs
85
src/smtp.rs
@@ -1,14 +1,10 @@
|
||||
use std::ffi::CStr;
|
||||
|
||||
use lettre::smtp::client::net::*;
|
||||
use lettre::*;
|
||||
|
||||
use crate::constants::Event;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_log::*;
|
||||
use crate::dc_loginparam::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::oauth2::*;
|
||||
|
||||
pub struct Smtp {
|
||||
@@ -47,37 +43,17 @@ impl Smtp {
|
||||
}
|
||||
|
||||
/// Connect using the provided login params
|
||||
pub fn connect(&mut self, context: &Context, lp: *const dc_loginparam_t) -> usize {
|
||||
if lp.is_null() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn connect(&mut self, context: &Context, lp: &dc_loginparam_t) -> bool {
|
||||
if self.is_connected() {
|
||||
warn!(context, 0, "SMTP already connected.");
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Safe because we checked for null pointer above.
|
||||
let lp = unsafe { *lp };
|
||||
|
||||
if lp.addr.is_null() || lp.send_server.is_null() || lp.send_port == 0 {
|
||||
unsafe {
|
||||
dc_log_event(
|
||||
context,
|
||||
Event::ERROR_NETWORK,
|
||||
0,
|
||||
b"SMTP bad parameters.\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
}
|
||||
if lp.send_server.is_empty() || lp.send_port == 0 {
|
||||
log_event!(context, Event::ERROR_NETWORK, 0, "SMTP bad parameters.",);
|
||||
}
|
||||
|
||||
let raw_addr = unsafe {
|
||||
CStr::from_ptr(lp.addr)
|
||||
.to_str()
|
||||
.expect("invalid from address")
|
||||
.to_string()
|
||||
};
|
||||
self.from = if let Ok(addr) = EmailAddress::new(raw_addr) {
|
||||
self.from = if let Ok(addr) = EmailAddress::new(lp.addr.clone()) {
|
||||
Some(addr)
|
||||
} else {
|
||||
None
|
||||
@@ -85,14 +61,10 @@ impl Smtp {
|
||||
|
||||
if self.from.is_none() {
|
||||
// TODO: print error
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
let domain = unsafe {
|
||||
CStr::from_ptr(lp.send_server)
|
||||
.to_str()
|
||||
.expect("invalid send server")
|
||||
};
|
||||
let domain = &lp.send_server;
|
||||
let port = lp.send_port as u16;
|
||||
|
||||
let tls = native_tls::TlsConnector::builder()
|
||||
@@ -106,19 +78,19 @@ impl Smtp {
|
||||
|
||||
let creds = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
|
||||
// oauth2
|
||||
let addr = as_str(lp.addr);
|
||||
let send_pw = as_str(lp.send_pw);
|
||||
let addr = &lp.addr;
|
||||
let send_pw = &lp.send_pw;
|
||||
let access_token = dc_get_oauth2_access_token(context, addr, send_pw, 0);
|
||||
if access_token.is_none() {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
let user = as_str(lp.send_user);
|
||||
let user = &lp.send_user;
|
||||
|
||||
lettre::smtp::authentication::Credentials::new(user.into(), access_token.unwrap())
|
||||
lettre::smtp::authentication::Credentials::new(user.to_string(), access_token.unwrap())
|
||||
} else {
|
||||
// plain
|
||||
let user = unsafe { CStr::from_ptr(lp.send_user).to_str().unwrap().to_string() };
|
||||
let pw = unsafe { CStr::from_ptr(lp.send_pw).to_str().unwrap().to_string() };
|
||||
let user = lp.send_user.clone();
|
||||
let pw = lp.send_pw.clone();
|
||||
lettre::smtp::authentication::Credentials::new(user, pw)
|
||||
};
|
||||
|
||||
@@ -130,18 +102,25 @@ impl Smtp {
|
||||
lettre::smtp::ClientSecurity::Wrapper(tls_parameters)
|
||||
};
|
||||
|
||||
match lettre::smtp::SmtpClient::new((domain, port), security) {
|
||||
match lettre::smtp::SmtpClient::new((domain.as_str(), port), security) {
|
||||
Ok(client) => {
|
||||
let client = client
|
||||
.smtp_utf8(true)
|
||||
.credentials(creds)
|
||||
.connection_reuse(lettre::smtp::ConnectionReuseParameters::ReuseUnlimited);
|
||||
self.transport = Some(client.transport());
|
||||
1
|
||||
log_event!(
|
||||
context,
|
||||
Event::SMTP_CONNECTED,
|
||||
0,
|
||||
"SMTP-LOGIN as {} ok",
|
||||
lp.send_user,
|
||||
);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "SMTP: failed to establish connection {:?}", err);
|
||||
0
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,21 +141,17 @@ impl Smtp {
|
||||
|
||||
match transport.send(mail) {
|
||||
Ok(_) => {
|
||||
unsafe {
|
||||
dc_log_event(
|
||||
context,
|
||||
Event::SMTP_MESSAGE_SENT,
|
||||
0,
|
||||
b"Message was sent to SMTP server\x00" as *const u8
|
||||
as *const libc::c_char,
|
||||
);
|
||||
}
|
||||
log_event!(
|
||||
context,
|
||||
Event::SMTP_MESSAGE_SENT,
|
||||
0,
|
||||
"Message was sent to SMTP server",
|
||||
);
|
||||
self.transport_connected = true;
|
||||
1
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, 0, "SMTP failed to send message: {}", err);
|
||||
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
1198
src/sql.rs
Normal file
1198
src/sql.rs
Normal file
File diff suppressed because it is too large
Load Diff
29
src/top_evil_rs.py
Executable file
29
src/top_evil_rs.py
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
import os
|
||||
|
||||
if __name__ == "__main__":
|
||||
filestats = []
|
||||
for fn in os.listdir():
|
||||
if fn.endswith(".rs"):
|
||||
s = open(fn).read()
|
||||
unsafe = s.count("unsafe")
|
||||
free = s.count("free(")
|
||||
gotoblocks = s.count("current_block =")
|
||||
filestats.append((fn, unsafe, free, gotoblocks))
|
||||
|
||||
sum_unsafe, sum_free, sum_gotoblocks = 0, 0, 0
|
||||
|
||||
for fn, unsafe, free, gotoblocks in reversed(sorted(filestats, key=lambda x: sum(x[1:]))):
|
||||
print("{0: <30} unsafe: {1: >3} free: {2: >3} goto-blocks: {3: >3}".format(fn, unsafe, free, gotoblocks))
|
||||
sum_unsafe += unsafe
|
||||
sum_free += free
|
||||
sum_gotoblocks += gotoblocks
|
||||
|
||||
|
||||
print()
|
||||
print("total unsafe:", sum_unsafe)
|
||||
print("total free:", sum_free)
|
||||
print("total gotoblocks:", sum_gotoblocks)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::constants::Event;
|
||||
use crate::context::Context;
|
||||
|
||||
pub use libsqlite3_sys::*;
|
||||
pub use mmime::carray::*;
|
||||
pub use mmime::clist::*;
|
||||
pub use rusqlite::ffi::*;
|
||||
|
||||
/// Callback function that should be given to dc_context_new().
|
||||
///
|
||||
@@ -22,7 +22,7 @@ pub type dc_receive_imf_t = unsafe fn(
|
||||
_: &Context,
|
||||
_: *const libc::c_char,
|
||||
_: size_t,
|
||||
_: *const libc::c_char,
|
||||
_: &str,
|
||||
_: uint32_t,
|
||||
_: uint32_t,
|
||||
) -> ();
|
||||
@@ -32,7 +32,7 @@ Context is only used for logging and to get information about
|
||||
the online state. */
|
||||
|
||||
pub type dc_precheck_imf_t =
|
||||
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char, _: u32) -> libc::c_int;
|
||||
unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int;
|
||||
pub type dc_set_config_t =
|
||||
unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> ();
|
||||
pub type dc_get_config_t =
|
||||
|
||||
6
src/x.rs
6
src/x.rs
@@ -45,12 +45,6 @@ extern "C" {
|
||||
unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int,
|
||||
>,
|
||||
);
|
||||
pub fn vsnprintf(
|
||||
_: *mut libc::c_char,
|
||||
_: libc::c_ulong,
|
||||
_: *const libc::c_char,
|
||||
_: ::std::ffi::VaList,
|
||||
) -> libc::c_int;
|
||||
|
||||
// -- DC Methods
|
||||
pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char;
|
||||
|
||||
121
tests/stress.rs
121
tests/stress.rs
@@ -1,15 +1,17 @@
|
||||
//! Stress some functions for testing; if used as a lib, this file is obsolete.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::ffi::CString;
|
||||
|
||||
use mmime::mailimf_types::*;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::constants::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::dc_array::*;
|
||||
use deltachat::dc_chat::*;
|
||||
use deltachat::dc_configure::*;
|
||||
use deltachat::dc_contact::*;
|
||||
use deltachat::dc_imex::*;
|
||||
use deltachat::dc_location::*;
|
||||
use deltachat::dc_lot::*;
|
||||
@@ -172,7 +174,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
"content"
|
||||
);
|
||||
|
||||
free(buf);
|
||||
free(buf as *mut _);
|
||||
assert_ne!(
|
||||
0,
|
||||
dc_delete_file(
|
||||
@@ -244,15 +246,8 @@ unsafe fn stress_functions(context: &Context) {
|
||||
free(fn0 as *mut libc::c_void);
|
||||
free(fn1 as *mut libc::c_void);
|
||||
}
|
||||
let keys = dc_get_config(
|
||||
context,
|
||||
b"sys.config_keys\x00" as *const u8 as *const libc::c_char,
|
||||
);
|
||||
assert!(!keys.is_null());
|
||||
assert_ne!(0, *keys.offset(0isize) as libc::c_int);
|
||||
|
||||
let res = format!(" {} ", as_str(keys));
|
||||
free(keys as *mut libc::c_void);
|
||||
let res = context.get_config(config::Config::SysConfigKeys).unwrap();
|
||||
|
||||
assert!(!res.contains(" probably_never_a_key "));
|
||||
assert!(res.contains(" addr "));
|
||||
@@ -643,6 +638,7 @@ unsafe fn stress_functions(context: &Context) {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore] // is too expensive
|
||||
fn test_encryption_decryption() {
|
||||
unsafe {
|
||||
let mut bad_data: [libc::c_uchar; 4096] = [0; 4096];
|
||||
@@ -668,13 +664,11 @@ fn test_encryption_decryption() {
|
||||
j += 1
|
||||
}
|
||||
|
||||
let (public_key, private_key) =
|
||||
dc_pgp_create_keypair(b"foo@bar.de\x00" as *const u8 as *const libc::c_char).unwrap();
|
||||
let (public_key, private_key) = dc_pgp_create_keypair("foo@bar.de").unwrap();
|
||||
|
||||
private_key.split_key().unwrap();
|
||||
|
||||
let (public_key2, private_key2) =
|
||||
dc_pgp_create_keypair(b"two@zwo.de\x00" as *const u8 as *const libc::c_char).unwrap();
|
||||
let (public_key2, private_key2) = dc_pgp_create_keypair("two@zwo.de").unwrap();
|
||||
|
||||
assert_ne!(public_key, public_key2);
|
||||
|
||||
@@ -696,7 +690,7 @@ fn test_encryption_decryption() {
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_signed_bytes = ctext.len();
|
||||
let ctext_signed = CString::new(ctext).unwrap();
|
||||
let ctext_signed = to_cstring(ctext);
|
||||
|
||||
let ctext = dc_pgp_pk_encrypt(
|
||||
original_text as *const libc::c_void,
|
||||
@@ -709,7 +703,7 @@ fn test_encryption_decryption() {
|
||||
assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----"));
|
||||
|
||||
let ctext_unsigned_bytes = ctext.len();
|
||||
let ctext_unsigned = CString::new(ctext).unwrap();
|
||||
let ctext_unsigned = to_cstring(ctext);
|
||||
|
||||
let mut keyring = Keyring::default();
|
||||
keyring.add_owned(private_key);
|
||||
@@ -723,7 +717,7 @@ fn test_encryption_decryption() {
|
||||
let mut valid_signatures: HashSet<String> = Default::default();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
@@ -738,7 +732,7 @@ fn test_encryption_decryption() {
|
||||
|
||||
let empty_keyring = Keyring::default();
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&empty_keyring,
|
||||
@@ -751,7 +745,7 @@ fn test_encryption_decryption() {
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
@@ -766,7 +760,7 @@ fn test_encryption_decryption() {
|
||||
public_keyring2.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring2,
|
||||
@@ -779,13 +773,15 @@ fn test_encryption_decryption() {
|
||||
valid_signatures.clear();
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_unsigned.as_ptr() as *const _,
|
||||
ctext_unsigned as *const _,
|
||||
ctext_unsigned_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
Some(&mut valid_signatures),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
free(ctext_unsigned as *mut _);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
|
||||
valid_signatures.clear();
|
||||
@@ -796,13 +792,15 @@ fn test_encryption_decryption() {
|
||||
public_keyring.add_ref(&public_key);
|
||||
|
||||
let plain = dc_pgp_pk_decrypt(
|
||||
ctext_signed.as_ptr() as *const _,
|
||||
ctext_signed as *const _,
|
||||
ctext_signed_bytes,
|
||||
&keyring,
|
||||
&public_keyring,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
free(ctext_signed as *mut _);
|
||||
assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),);
|
||||
}
|
||||
}
|
||||
@@ -825,14 +823,14 @@ struct TestContext {
|
||||
unsafe fn create_test_context() -> TestContext {
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap();
|
||||
let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap());
|
||||
assert_eq!(
|
||||
dc_open(&mut ctx, dbfile.as_ptr(), std::ptr::null()),
|
||||
dc_open(&mut ctx, dbfile, std::ptr::null()),
|
||||
1,
|
||||
"Failed to open {}",
|
||||
as_str(dbfile.as_ptr() as *const libc::c_char)
|
||||
as_str(dbfile as *const libc::c_char)
|
||||
);
|
||||
|
||||
free(dbfile as *mut _);
|
||||
TestContext { ctx: ctx, dir: dir }
|
||||
}
|
||||
|
||||
@@ -953,6 +951,77 @@ fn test_stress_tests() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_contacts() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
let name = to_cstring("some2");
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name);
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
free(name as *mut _);
|
||||
|
||||
let name = to_cstring("bob");
|
||||
let email = to_cstring("bob@mail.de");
|
||||
let id = dc_create_contact(&context.ctx, name, email);
|
||||
assert_ne!(id, 0);
|
||||
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name);
|
||||
assert_eq!(dc_array_get_cnt(contacts), 1);
|
||||
dc_array_unref(contacts);
|
||||
|
||||
let name2 = to_cstring("alice");
|
||||
let contacts = dc_get_contacts(&context.ctx, 0, name2);
|
||||
assert_eq!(dc_array_get_cnt(contacts), 0);
|
||||
dc_array_unref(contacts);
|
||||
|
||||
free(name as *mut _);
|
||||
free(name2 as *mut _);
|
||||
free(email as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chat() {
|
||||
unsafe {
|
||||
let context = create_test_context();
|
||||
let name = to_cstring("bob");
|
||||
let email = to_cstring("bob@mail.de");
|
||||
|
||||
let contact1 = dc_create_contact(&context.ctx, name, email);
|
||||
free(name as *mut _);
|
||||
free(email as *mut _);
|
||||
assert_ne!(contact1, 0);
|
||||
|
||||
let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
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 chat2_id = dc_create_chat_by_contact_id(&context.ctx, contact1);
|
||||
assert_eq!(chat2_id, chat_id);
|
||||
let chat2 = dc_chat_new(&context.ctx);
|
||||
assert!(dc_chat_load_from_db(chat2, chat2_id));
|
||||
|
||||
assert_eq!(as_str((*chat2).name), as_str((*chat).name));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wrong_db() {
|
||||
unsafe {
|
||||
let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut());
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
std::fs::write(&dbfile, b"123").unwrap();
|
||||
|
||||
let dbfile_c = to_cstring(dbfile.to_str().unwrap());
|
||||
let res = dc_open(&mut ctx, dbfile_c, std::ptr::null());
|
||||
free(dbfile_c as *mut _);
|
||||
assert_eq!(res, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arr_to_string() {
|
||||
let arr2: [uint32_t; 4] = [
|
||||
|
||||
Reference in New Issue
Block a user