diff --git a/Cargo.lock b/Cargo.lock index 93f5cbdb6..9a7559b4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,7 +35,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -44,7 +44,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -101,7 +101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", @@ -115,7 +115,7 @@ name = "backtrace-sys" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -189,16 +189,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "buf_redux" -version = "0.8.2" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -243,12 +243,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -295,7 +295,7 @@ name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -328,9 +328,9 @@ dependencies = [ "cookie 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "publicsuffix 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -369,21 +369,21 @@ name = "crossbeam-deque" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -391,12 +391,12 @@ name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -409,19 +409,19 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "curve25519-dalek" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -443,7 +443,7 @@ dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -453,7 +453,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -461,35 +461,37 @@ name = "deltachat" version = "1.0.0-alpha.3" dependencies = [ "addr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "imap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "mmime 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mmime 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "pgp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "r2d2_sqlite 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", + "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -506,6 +508,7 @@ dependencies = [ "deltachat 1.0.0-alpha.3", "human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -517,7 +520,7 @@ dependencies = [ "derive_builder_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -528,7 +531,7 @@ dependencies = [ "darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -538,7 +541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -575,7 +578,7 @@ version = "1.0.0-pre.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -609,7 +612,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -619,7 +622,7 @@ name = "error-chain" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -628,7 +631,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -639,7 +642,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -673,7 +676,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -737,25 +740,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "h2" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -785,7 +788,7 @@ dependencies = [ [[package]] name = "http" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -800,7 +803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-buf 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -814,11 +817,11 @@ name = "human-panic" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -841,13 +844,13 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -925,6 +928,14 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "itoa" version = "0.4.4" @@ -948,9 +959,6 @@ dependencies = [ name = "lazy_static" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "lettre" @@ -961,11 +969,11 @@ dependencies = [ "bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "fast_chemail 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -977,10 +985,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libsqlite3-sys" version = "0.16.0" -source = "git+http://github.com/dignifiedquire/rusqlite?branch=fix/text#d81b8cdb175f7c27a3ace9ad46818e2a01434cdc" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1016,7 +1024,7 @@ dependencies = [ [[package]] name = "log" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1050,7 +1058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1069,8 +1077,11 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.2.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "mime" @@ -1093,7 +1104,7 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1101,13 +1112,13 @@ dependencies = [ [[package]] name = "miniz_oxide_c_api" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1120,7 +1131,7 @@ dependencies = [ "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1140,7 +1151,7 @@ dependencies = [ [[package]] name = "mmime" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1159,7 +1170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1185,7 +1196,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1227,7 +1238,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1275,7 +1286,7 @@ dependencies = [ [[package]] name = "opaque-debug" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1298,10 +1309,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-src" -version = "111.3.0+1.1.1c" +version = "111.4.0+1.1.1c" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1310,10 +1321,10 @@ version = "0.9.48" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-src 111.3.0+1.1.1c (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-src 111.4.0+1.1.1c (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1374,7 +1385,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1407,7 +1418,7 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1440,7 +1451,7 @@ dependencies = [ "block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "buf_redux 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "buf_redux 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "cast5 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1457,7 +1468,7 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint-dig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1512,7 +1523,7 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1538,7 +1549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1556,7 +1567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "psl-codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1612,18 +1623,18 @@ name = "r2d2" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "scheduled-thread-pool 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "r2d2_sqlite" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", + "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1662,9 +1673,9 @@ name = "rand" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1680,10 +1691,9 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1706,7 +1716,7 @@ name = "rand_core" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1841,7 +1851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1856,14 +1866,14 @@ dependencies = [ "encoding_rs 0.8.17 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.33 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1874,7 +1884,7 @@ dependencies = [ "tokio-timer 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", - "winreg 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1884,7 +1894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1900,19 +1910,19 @@ dependencies = [ "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rusqlite" -version = "0.19.0" -source = "git+http://github.com/dignifiedquire/rusqlite?branch=fix/text#d81b8cdb175f7c27a3ace9ad46818e2a01434cdc" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libsqlite3-sys 0.16.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)", + "libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1938,7 +1948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1954,7 +1964,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "safemem" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2028,20 +2038,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2051,7 +2061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2061,7 +2071,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2073,7 +2083,7 @@ dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2084,7 +2094,7 @@ dependencies = [ "block-buffer 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2096,7 +2106,7 @@ dependencies = [ "byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2124,11 +2134,6 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "spin" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "stable_deref_trait" version = "1.1.1" @@ -2168,12 +2173,12 @@ dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "subtle" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2188,7 +2193,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.15.40" +version = "0.15.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2203,7 +2208,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2313,7 +2318,7 @@ name = "tokio-executor" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2324,7 +2329,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2332,10 +2337,10 @@ name = "tokio-reactor" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2374,9 +2379,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2388,7 +2393,7 @@ name = "tokio-timer" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-executor 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2399,7 +2404,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2427,7 +2432,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "block-cipher-trait 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2553,7 +2558,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2613,7 +2618,7 @@ dependencies = [ [[package]] name = "winreg" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2642,7 +2647,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "curve25519-dalek 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2661,7 +2666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] @@ -2677,7 +2682,7 @@ dependencies = [ "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" -"checksum backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)" = "88fb679bc9af8fa639198790a77f52d345fe13656c08b43afa9424c206b731c6" +"checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" @@ -2688,14 +2693,14 @@ dependencies = [ "checksum block-modes 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "31aa8410095e39fdb732909fb5730a48d5bd7c2e3cd76bd1b07b3dbea130c529" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum blowfish 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aeb80d00f2688459b8542068abd974cfb101e7a82182414a99b5026c0d85cc3" -"checksum buf_redux 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cb6b0b3650a857c5f3eb2083d6a51dc86a9967eafdd42919be63e3b3e6599752" +"checksum buf_redux 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" "checksum bufstream 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cast5 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce5759b4c52ca74f9a98421817c882f1fd9b0071ae41cd61ab9f9d059c04fd6" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" +"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" "checksum cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "190e7b55d3a27cf8879becf61035a141cbc783f3258a41d16d1706719f991345" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f426e64df1c3de26cbf44593c6ffff5dbfd43bbf9de0d075058558126b3fc73" @@ -2711,11 +2716,11 @@ dependencies = [ "checksum crc24 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" -"checksum curve25519-dalek 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5d4b820e8711c211745880150f5fac78ab07d6e3851d8ce9f5a02cedc199174c" +"checksum curve25519-dalek 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "019779982f8518304cfbabd515e77ad6372a9a28a57d22d39478164c5e2b32ee" "checksum darling 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfbcb0c5961907597a7d1148e3af036268f2b773886b8bb3eeb1e1281d3d3d6" "checksum darling_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6afc018370c3bff3eb51f89256a6bdb18b4fdcda72d577982a14954a7a0b402c" "checksum darling_macro 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6d8dac1c6f1d29a41c4712b4400f878cb4fcc4c7628f298dd75038e024998d1" @@ -2748,12 +2753,12 @@ dependencies = [ "checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -"checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" -"checksum h2 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "a539b63339fbbb00e081e84b6e11bd1d9634a82d91da2984a18ac74a8823f392" +"checksum getrandom 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8e190892c840661957ba9f32dacfb3eb405e657f9f9f60485605f0bb37d6f8" +"checksum h2 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "a5b34c246847f938a410a03c5458c7fee2274436675e76d8b903c08efc29c462" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -"checksum http 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "eed324f0f0daf6ec10c474f150505af2c143f251722bf9dbd1261bd1f2ee2c1a" +"checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21638c5955a6daf3ecc42cae702335fc37a72a4abcc6959ce457b31a7d43bbdd" @@ -2766,32 +2771,33 @@ dependencies = [ "checksum imap-proto 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4e77b1d61faf028893531b071cc5584cdd02b6186cebe7f7168ffd8d591339a" "checksum indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lettre 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c66afaa5dfadbb81d4e00fd1d1ab057c7cd4c799c5a44e0009386d553587e728" "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum libsqlite3-sys 0.16.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)" = "" +"checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed946d4529956a20f2d63ebe1b69996d5a2137c91913fe3ebbeff957f5bca7ff" "checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc" -"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" "checksum mime_guess 2.0.0-alpha.6 (registry+https://github.com/rust-lang/crates.io-index)" = "30de2e4613efcba1ec63d8133f344076952090c122992a903359be5a4f99c3ed" -"checksum miniz_oxide 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b6c3756d66cf286314d5f7ebe74886188a9a92f5eee68b06f31ac2b4f314c99d" -"checksum miniz_oxide_c_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5b78ca5446dd9fe0dab00e058731b6b08a8c1d2b9cdb8efb10876e24e9ae2494" +"checksum miniz_oxide 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c061edee74a88eb35d876ce88b94d77a0448a201de111c244b70d047f5820516" +"checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" -"checksum mmime 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d86dea2dd1de25d9087cd7b249214d631393f731896183a6729f747fa80018f" +"checksum mmime 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a1246fa340840c36f1fca1507db82463fbc4c2f7763fe84bfde666c7381e0593" "checksum native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b2df1a4c22fd44a62147fd8f13dd0f95c9d8ca7b2610299b2a2f9cf8964274e" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" "checksum nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b" @@ -2804,10 +2810,10 @@ dependencies = [ "checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum opaque-debug 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "93f5bb2e8e8dec81642920ccff6b61f1eb94fa3020c5a325c9851ff604152409" +"checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" "checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-src 111.3.0+1.1.1c (registry+https://github.com/rust-lang/crates.io-index)" = "53ed5f31d294bdf5f7a4ba0a206c2754b0f60e9a63b7e3076babc5317873c797" +"checksum openssl-src 111.4.0+1.1.1c (registry+https://github.com/rust-lang/crates.io-index)" = "783a3c5b3c1c28bdd7245e6b7a91de6168994948726b1dd4227199e9e6864383" "checksum openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)" = "b5ba300217253bcc5dc68bed23d782affa45000193866e025329aa8a7a9f05b8" "checksum os_type 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7edc011af0ae98b7f88cf7e4a83b70a54a75d2b8cb013d6efd02e5956207e9eb" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" @@ -2818,7 +2824,7 @@ dependencies = [ "checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" "checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9" "checksum parking_lot_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb88cb1cb3790baa6776844f968fea3be44956cf184fa1be5a03341f5491278c" -"checksum parking_lot_core 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a7bbaa05312363e0480e1efee133fff1a09ef4a6406b65e226b9a793c223a32" +"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum percent-encoding 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba4f28a6faf4ffea762ba8f4baef48c61a6db348647c73095034041fc79dd954" "checksum pgp 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb80b37b7debf9a98dc0caca3ed40ddf1d383691208763d0458df0b91521020f" @@ -2826,7 +2832,7 @@ dependencies = [ "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum pretty_env_logger 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df8b3f4e0475def7d9c2e5de8e5a1306949849761e107b360d03e98eafaffd61" @@ -2838,12 +2844,12 @@ dependencies = [ "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum r2d2 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bc42ce75d9f4447fb2a04bbe1ed5d18dd949104572850ec19b164e274919f81b" -"checksum r2d2_sqlite 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e09cea6a05f6f05d6f5e7dc52bd5b09d6a24fe7b42cf1d9d87f71f39d43c0b" +"checksum r2d2_sqlite 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "806e268035ce9e5a604bf617ac8a073ef28b59ef2e48e8338db0baf530caef33" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" @@ -2865,12 +2871,12 @@ dependencies = [ "checksum reqwest 0.9.19 (registry+https://github.com/rust-lang/crates.io-index)" = "1d0777154c2c3eb54f5c480db01de845652d941e47191277cc673634c3853939" "checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a" "checksum rsa 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6ad8d3632f6745bb671c8637e2aa44015537c5e384789d2ea3235739301ed1e0" -"checksum rusqlite 0.19.0 (git+http://github.com/dignifiedquire/rusqlite?branch=fix/text)" = "" +"checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f47ea1ceb347d2deae482d655dc8eef4bd82363d3329baffa3818bd76fea48b" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scheduled-thread-pool 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd07742e081ff6c077f5f6b283f12f32b9e7cc765b316160d66289b74546fbb3" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" @@ -2881,8 +2887,8 @@ dependencies = [ "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum sequence_trie 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1ee22067b7ccd072eeb64454b9c6e1b33b61cd0d49e895fd48676a184580e0c3" -"checksum serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "d46b3dfedb19360a74316866cef04687cd4d6a70df8e6a506c63512790769b72" -"checksum serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "c22a0820adfe2f257b098714323563dd06426502abbbce4f51b72ef544c5027f" +"checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" +"checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum serde_urlencoded 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "642dd69105886af2efd227f75a520ec9b44a820d65bc133a9131f7d229fd165a" "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" @@ -2892,16 +2898,15 @@ dependencies = [ "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum slice-deque 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ffddf594f5f597f63533d897427a570dbaa9feabaaa06595b74b71b7014507d7" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stream-cipher 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8861bc80f649f5b4c9bd38b696ae9af74499d479dbfb327f0607de6b326a36bc" "checksum string 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d24114bfcceb867ca7f71a0d3fe45d45619ec47a6fbfa98cb14e14250bfa5d6d" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum strum 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1c33039533f051704951680f1adfd468fd37ac46816ded0d9ee068e60f05f" "checksum strum_macros 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47cd23f5c7dee395a00fa20135e2ec0fffcdfa151c56182966d7a3261343432e" -"checksum subtle 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" +"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082" "checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741" -"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8" +"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" @@ -2952,7 +2957,7 @@ dependencies = [ "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" -"checksum winreg 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73f1f3c6c4d3cab118551b96c476a2caab920701e28875b64a458f2ecb96ec9d" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7daf138b6b14196e3830a588acf1e86966c694d3e8fb026fb105b8b5dca07e6e" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum x25519-dalek 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7ee1585dc1484373cbc1cee7aafda26634665cf449436fd6e24bfd1fad230538" diff --git a/Cargo.toml b/Cargo.toml index 2d42e6798..94ddf9337 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,14 +35,16 @@ failure_derive = "0.1.5" rustyline = "4.1.0" lazy_static = "1.3.0" regex = "1.1.6" -rusqlite = { version = "0.19", features = ["bundled"] } +rusqlite = { version = "0.20", features = ["bundled"] } addr = "0.2.0" -r2d2_sqlite = "0.11.0" +r2d2_sqlite = "0.12.0" r2d2 = "0.8.5" strum = "0.15.0" strum_macros = "0.15.0" thread-local-object = "0.1.0" backtrace = "0.3.33" +byteorder = "1.3.1" +itertools = "0.8.0" [dev-dependencies] tempfile = "3.0" @@ -54,10 +56,6 @@ members = [ "deltachat-ffi" ] -[patch.crates-io] -rusqlite = { git = "http://github.com/dignifiedquire/rusqlite", branch = "fix/text", features = ["bundled"] } - - [[example]] name = "simple" path = "examples/simple.rs" diff --git a/README.md b/README.md index 892fdf2bb..546731af3 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,11 @@ Single#10: yourfriends@email.org [yourfriends@email.org] Message sent. ``` +If `yourfriend@email.org` uses DeltaChat, but does not receive message just +sent, it is advisable to check `Spam` folder. It is known that at least +`gmx.com` treat such test messages as spam, unless told otherwise with web +interface. + List messages when inside a chat: ``` diff --git a/ci/run.sh b/ci/run.sh index 1a770dbfe..e61a4abbc 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -4,6 +4,7 @@ set -ex export RUST_TEST_THREADS=1 export RUST_BACKTRACE=1 +export RUSTFLAGS='--deny warnings' export OPT="--target=$TARGET" export OPT_RELEASE="--release ${OPT}" export OPT_FFI_RELEASE="--manifest-path=deltachat-ffi/Cargo.toml --release" diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index c4e0d8580..ad4a79228 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["cdylib", "staticlib"] deltachat = { path = "../", default-features = false } libc = "0.2" human-panic = "1.0.1" +num-traits = "0.2.6" [features] default = ["vendored", "nightly", "ringbuf"] diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 1b584d892..82098cb35 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -9,9 +9,13 @@ #[macro_use] extern crate human_panic; +extern crate num_traits; +use num_traits::{FromPrimitive, ToPrimitive}; use std::str::FromStr; +use deltachat::contact::Contact; +use deltachat::dc_tools::{as_str, StrExt}; use deltachat::*; // TODO: constants @@ -30,7 +34,13 @@ pub unsafe extern "C" fn dc_context_new( ) -> *mut dc_context_t { setup_panic!(); + let os_name = if os_name.is_null() { + None + } else { + Some(dc_tools::to_string_lossy(os_name)) + }; let ctx = context::dc_context_new(cb, userdata, os_name); + Box::into_raw(Box::new(ctx)) } @@ -38,7 +48,8 @@ pub unsafe extern "C" fn dc_context_new( pub unsafe extern "C" fn dc_context_unref(context: *mut dc_context_t) { assert!(!context.is_null()); let context = &mut *context; - context::dc_context_unref(context) + context::dc_context_unref(context); + Box::from_raw(context); } #[no_mangle] @@ -56,9 +67,16 @@ pub unsafe extern "C" fn dc_open( blobdir: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(!dbfile.is_null()); let context = &mut *context; - context::dc_open(context, dbfile, blobdir) + let dbfile_str = dc_tools::as_str(dbfile); + let blobdir_str = if blobdir.is_null() { + None + } else { + Some(dc_tools::as_str(blobdir)) + }; + context::dc_open(context, dbfile_str, blobdir_str) as libc::c_int } #[no_mangle] @@ -105,16 +123,14 @@ 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; - 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(), - } + assert!(!key.is_null(), "invalid key pointer"); + let key = config::Config::from_str(dc_tools::as_str(key)).expect("invalid key"); + + // TODO: Translating None to NULL would be more sensible than translating None + // to "", as it is now. + context.get_config(key).unwrap_or_default().strdup() } #[no_mangle] @@ -137,7 +153,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) => dc_tools::to_cstring(res), + Some(res) => res.strdup(), None => std::ptr::null_mut(), } } @@ -334,6 +350,7 @@ pub unsafe extern "C" fn dc_prepare_msg( msg: *mut dc_msg::dc_msg_t, ) -> u32 { assert!(!context.is_null()); + assert!(!msg.is_null()); let context = &*context; dc_chat::dc_prepare_msg(context, chat_id, msg) @@ -346,6 +363,7 @@ pub unsafe extern "C" fn dc_send_msg( msg: *mut dc_msg::dc_msg_t, ) -> u32 { assert!(!context.is_null()); + assert!(!msg.is_null()); let context = &*context; dc_chat::dc_send_msg(context, chat_id, msg) @@ -358,7 +376,9 @@ pub unsafe extern "C" fn dc_send_text_msg( text_to_send: *mut libc::c_char, ) -> u32 { assert!(!context.is_null()); + assert!(!text_to_send.is_null()); let context = &*context; + let text_to_send = dc_tools::to_string_lossy(text_to_send); dc_chat::dc_send_text_msg(context, chat_id, text_to_send) } @@ -444,6 +464,14 @@ pub unsafe extern "C" fn dc_marknoticed_all_chats(context: *mut dc_context_t) { dc_chat::dc_marknoticed_all_chats(context); } +fn from_prim(s: S) -> Option +where + T: FromPrimitive, + S: Into, +{ + FromPrimitive::from_i64(s.into()) +} + #[no_mangle] pub unsafe extern "C" fn dc_get_chat_media( context: *mut dc_context_t, @@ -455,6 +483,12 @@ pub unsafe extern "C" fn dc_get_chat_media( assert!(!context.is_null()); let context = &*context; + let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {}", msg_type)); + let or_msg_type2 = + from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {}", or_msg_type2)); + let or_msg_type3 = + from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); + dc_chat::dc_get_chat_media(context, chat_id, msg_type, or_msg_type2, or_msg_type3) } @@ -470,6 +504,12 @@ pub unsafe extern "C" fn dc_get_next_media( assert!(!context.is_null()); let context = &*context; + let msg_type = from_prim(msg_type).expect(&format!("invalid msg_type = {}", msg_type)); + let or_msg_type2 = + from_prim(or_msg_type2).expect(&format!("incorrect or_msg_type2 = {}", or_msg_type2)); + let or_msg_type3 = + from_prim(or_msg_type3).expect(&format!("incorrect or_msg_type3 = {}", or_msg_type3)); + dc_chat::dc_get_next_media(context, msg_id, dir, msg_type, or_msg_type2, or_msg_type3) } @@ -512,6 +552,7 @@ pub unsafe extern "C" fn dc_search_msgs( query: *mut libc::c_char, ) -> *mut dc_array::dc_array_t { assert!(!context.is_null()); + assert!(!query.is_null()); let context = &*context; context::dc_search_msgs(context, chat_id, query) @@ -535,6 +576,7 @@ pub unsafe extern "C" fn dc_create_group_chat( name: *mut libc::c_char, ) -> u32 { assert!(!context.is_null()); + assert!(!name.is_null()); let context = &*context; dc_chat::dc_create_group_chat(context, verified, name) @@ -583,6 +625,8 @@ pub unsafe extern "C" fn dc_set_chat_name( name: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(!name.is_null()); + assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; dc_chat::dc_set_chat_name(context, chat_id, name) @@ -595,6 +639,7 @@ pub unsafe extern "C" fn dc_set_chat_profile_image( image: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; dc_chat::dc_set_chat_profile_image(context, chat_id, image) @@ -629,6 +674,8 @@ pub unsafe extern "C" fn dc_delete_msgs( msg_cnt: libc::c_int, ) { assert!(!context.is_null()); + assert!(!msg_ids.is_null()); + assert!(msg_cnt > 0); let context = &*context; dc_msg::dc_delete_msgs(context, msg_ids, msg_cnt) @@ -642,6 +689,9 @@ pub unsafe extern "C" fn dc_forward_msgs( chat_id: u32, ) { assert!(!context.is_null()); + assert!(!msg_ids.is_null()); + assert!(msg_cnt > 0); + assert!(chat_id > constants::DC_CHAT_ID_LAST_SPECIAL as u32); let context = &*context; dc_chat::dc_forward_msgs(context, msg_ids, msg_cnt, chat_id) @@ -652,7 +702,7 @@ pub unsafe extern "C" fn dc_marknoticed_contact(context: *mut dc_context_t, cont assert!(!context.is_null()); let context = &*context; - dc_contact::dc_marknoticed_contact(context, contact_id) + Contact::mark_noticed(context, contact_id) } #[no_mangle] @@ -662,6 +712,8 @@ pub unsafe extern "C" fn dc_markseen_msgs( msg_cnt: libc::c_int, ) { assert!(!context.is_null()); + assert!(!msg_ids.is_null()); + assert!(msg_cnt > 0); let context = &*context; dc_msg::dc_markseen_msgs(context, msg_ids, msg_cnt as usize); @@ -675,6 +727,9 @@ pub unsafe extern "C" fn dc_star_msgs( star: libc::c_int, ) { assert!(!context.is_null()); + assert!(!msg_ids.is_null()); + assert!(msg_cnt > 0); + let context = &*context; dc_msg::dc_star_msgs(context, msg_ids, msg_cnt, star); @@ -693,7 +748,8 @@ pub unsafe extern "C" fn dc_get_msg<'a>( #[no_mangle] pub unsafe extern "C" fn dc_may_be_valid_addr(addr: *mut libc::c_char) -> libc::c_int { - dc_contact::dc_may_be_valid_addr(addr) as libc::c_int + assert!(!addr.is_null()); + contact::may_be_valid_addr(as_str(addr)) as libc::c_int } #[no_mangle] @@ -702,9 +758,10 @@ pub unsafe extern "C" fn dc_lookup_contact_id_by_addr( addr: *mut libc::c_char, ) -> u32 { assert!(!context.is_null()); + assert!(!addr.is_null()); let context = &*context; - dc_contact::dc_lookup_contact_id_by_addr(context, addr) + Contact::lookup_id_by_addr(context, as_str(addr)) } #[no_mangle] @@ -714,9 +771,16 @@ pub unsafe extern "C" fn dc_create_contact( addr: *mut libc::c_char, ) -> u32 { assert!(!context.is_null()); + assert!(!addr.is_null()); + let context = &*context; - dc_contact::dc_create_contact(context, name, addr) + let name = if name.is_null() { "" } else { as_str(name) }; + + match Contact::create(context, name, as_str(addr)) { + Ok(id) => id, + Err(_) => 0, + } } #[no_mangle] @@ -725,9 +789,13 @@ pub unsafe extern "C" fn dc_add_address_book( addr_book: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(!addr_book.is_null()); let context = &*context; - dc_contact::dc_add_address_book(context, addr_book) + match Contact::add_address_book(context, as_str(addr_book)) { + Ok(cnt) => cnt as libc::c_int, + Err(_) => 0, + } } #[no_mangle] @@ -739,7 +807,16 @@ pub unsafe extern "C" fn dc_get_contacts( assert!(!context.is_null()); let context = &*context; - dc_contact::dc_get_contacts(context, flags, query) + let query = if query.is_null() { + None + } else { + Some(as_str(query)) + }; + + match Contact::get_all(context, flags, query) { + Ok(contacts) => contacts, + Err(_) => std::ptr::null_mut(), + } } #[no_mangle] @@ -747,7 +824,7 @@ pub unsafe extern "C" fn dc_get_blocked_cnt(context: *mut dc_context_t) -> libc: assert!(!context.is_null()); let context = &*context; - dc_contact::dc_get_blocked_cnt(context) + Contact::get_blocked_cnt(context) as libc::c_int } #[no_mangle] @@ -757,7 +834,7 @@ pub unsafe extern "C" fn dc_get_blocked_contacts( assert!(!context.is_null()); let context = &*context; - dc_contact::dc_get_blocked_contacts(context) + Contact::get_all_blocked(context) } #[no_mangle] @@ -769,7 +846,11 @@ pub unsafe extern "C" fn dc_block_contact( assert!(!context.is_null()); let context = &*context; - dc_contact::dc_block_contact(context, contact_id, block) + if block == 0 { + Contact::unblock(context, contact_id); + } else { + Contact::block(context, contact_id); + } } #[no_mangle] @@ -780,7 +861,7 @@ pub unsafe extern "C" fn dc_get_contact_encrinfo( assert!(!context.is_null()); let context = &*context; - dc_contact::dc_get_contact_encrinfo(context, contact_id) + Contact::get_encrinfo(context, contact_id).strdup() } #[no_mangle] @@ -791,18 +872,23 @@ pub unsafe extern "C" fn dc_delete_contact( assert!(!context.is_null()); let context = &*context; - dc_contact::dc_delete_contact(context, contact_id) as libc::c_int + match Contact::delete(context, contact_id) { + Ok(_) => 1, + Err(_) => 0, + } } #[no_mangle] pub unsafe extern "C" fn dc_get_contact<'a>( context: *mut dc_context_t, contact_id: u32, -) -> *mut dc_contact::dc_contact_t<'a> { +) -> *mut dc_contact_t<'a> { assert!(!context.is_null()); let context = &*context; - dc_contact::dc_get_contact(context, contact_id) + Contact::get_by_id(context, contact_id) + .map(|contact| Box::into_raw(Box::new(contact))) + .unwrap_or_else(|_| std::ptr::null_mut()) } #[no_mangle] @@ -824,6 +910,7 @@ pub unsafe extern "C" fn dc_imex_has_backup( dir: *mut libc::c_char, ) -> *mut libc::c_char { assert!(!context.is_null()); + assert!(!dir.is_null()); let context = &*context; dc_imex::dc_imex_has_backup(context, dir) @@ -844,6 +931,7 @@ pub unsafe extern "C" fn dc_continue_key_transfer( setup_code: *mut libc::c_char, ) -> libc::c_int { assert!(!context.is_null()); + assert!(!setup_code.is_null()); let context = &*context; dc_imex::dc_continue_key_transfer(context, msg_id, setup_code) @@ -863,6 +951,7 @@ pub unsafe extern "C" fn dc_check_qr( qr: *mut libc::c_char, ) -> *mut dc_lot::dc_lot_t { assert!(!context.is_null()); + assert!(!qr.is_null()); let context = &*context; dc_qr::dc_check_qr(context, qr) @@ -885,6 +974,7 @@ pub unsafe extern "C" fn dc_join_securejoin( qr: *mut libc::c_char, ) -> u32 { assert!(!context.is_null()); + assert!(!qr.is_null()); let context = &*context; dc_securejoin::dc_join_securejoin(context, qr) @@ -961,24 +1051,34 @@ pub type dc_array_t = dc_array::dc_array_t; #[no_mangle] pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) { + assert!(!a.is_null()); + dc_array::dc_array_unref(a) } #[no_mangle] pub unsafe extern "C" fn dc_array_add_uint(array: *mut dc_array_t, item: libc::uintptr_t) { + assert!(!array.is_null()); + dc_array::dc_array_add_uint(array, item) } #[no_mangle] pub unsafe extern "C" fn dc_array_add_id(array: *mut dc_array_t, item: libc::c_uint) { + assert!(!array.is_null()); + dc_array::dc_array_add_id(array, item) } #[no_mangle] pub unsafe extern "C" fn dc_array_add_ptr(array: *mut dc_array_t, item: *mut libc::c_void) { + assert!(!array.is_null()); + dc_array::dc_array_add_ptr(array, item) } #[no_mangle] pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t { + assert!(!array.is_null()); + dc_array::dc_array_get_cnt(array) } #[no_mangle] @@ -986,6 +1086,8 @@ pub unsafe extern "C" fn dc_array_get_uint( array: *const dc_array_t, index: libc::size_t, ) -> libc::uintptr_t { + assert!(!array.is_null()); + dc_array::dc_array_get_uint(array, index) } #[no_mangle] @@ -993,6 +1095,8 @@ pub unsafe extern "C" fn dc_array_get_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { + assert!(!array.is_null()); + dc_array::dc_array_get_id(array, index) } #[no_mangle] @@ -1000,6 +1104,8 @@ pub unsafe extern "C" fn dc_array_get_ptr( array: *const dc_array_t, index: libc::size_t, ) -> *mut libc::c_void { + assert!(!array.is_null()); + dc_array::dc_array_get_ptr(array, index) } #[no_mangle] @@ -1007,6 +1113,8 @@ pub unsafe extern "C" fn dc_array_get_latitude( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { + assert!(!array.is_null()); + dc_array::dc_array_get_latitude(array, index) } #[no_mangle] @@ -1014,6 +1122,8 @@ pub unsafe extern "C" fn dc_array_get_longitude( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { + assert!(!array.is_null()); + dc_array::dc_array_get_longitude(array, index) } #[no_mangle] @@ -1021,6 +1131,8 @@ pub unsafe extern "C" fn dc_array_get_accuracy( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_double { + assert!(!array.is_null()); + dc_array::dc_array_get_accuracy(array, index) } #[no_mangle] @@ -1028,6 +1140,8 @@ pub unsafe extern "C" fn dc_array_get_timestamp( array: *const dc_array_t, index: libc::size_t, ) -> i64 { + assert!(!array.is_null()); + dc_array::dc_array_get_timestamp(array, index) } #[no_mangle] @@ -1035,6 +1149,8 @@ pub unsafe extern "C" fn dc_array_get_chat_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { + assert!(!array.is_null()); + dc_array::dc_array_get_chat_id(array, index) } #[no_mangle] @@ -1042,6 +1158,8 @@ pub unsafe extern "C" fn dc_array_get_contact_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { + assert!(!array.is_null()); + dc_array::dc_array_get_contact_id(array, index) } #[no_mangle] @@ -1049,6 +1167,8 @@ pub unsafe extern "C" fn dc_array_get_msg_id( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_uint { + assert!(!array.is_null()); + dc_array::dc_array_get_msg_id(array, index) } #[no_mangle] @@ -1056,6 +1176,8 @@ pub unsafe extern "C" fn dc_array_get_marker( array: *const dc_array_t, index: libc::size_t, ) -> *mut libc::c_char { + assert!(!array.is_null()); + dc_array::dc_array_get_marker(array, index) } @@ -1065,11 +1187,15 @@ pub unsafe extern "C" fn dc_array_search_id( needle: libc::c_uint, ret_index: *mut libc::size_t, ) -> libc::c_int { + assert!(!array.is_null()); + dc_array::dc_array_search_id(array, needle, ret_index) as libc::c_int } #[no_mangle] pub unsafe extern "C" fn dc_array_get_raw(array: *const dc_array_t) -> *const libc::size_t { + assert!(!array.is_null()); + dc_array::dc_array_get_raw(array) } @@ -1078,6 +1204,8 @@ pub unsafe fn dc_array_is_independent( array: *const dc_array_t, index: libc::size_t, ) -> libc::c_int { + assert!(!array.is_null()); + dc_array::dc_array_is_independent(array, index) } @@ -1152,61 +1280,85 @@ pub type dc_chat_t<'a> = dc_chat::Chat<'a>; #[no_mangle] pub unsafe extern "C" fn dc_chat_unref(chat: *mut dc_chat_t) { + assert!(!chat.is_null()); + dc_chat::dc_chat_unref(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_id(chat: *mut dc_chat_t) -> u32 { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_id(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_type(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_type(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_char { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_name(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_subtitle(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_profile_image(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_color(chat: *mut dc_chat_t) -> u32 { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_color(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_get_archived(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_get_archived(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_unpromoted(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_is_unpromoted(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_self_talk(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_is_self_talk(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_verified(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_is_verified(chat) } #[no_mangle] pub unsafe extern "C" fn dc_chat_is_sending_locations(chat: *mut dc_chat_t) -> libc::c_int { + assert!(!chat.is_null()); + dc_chat::dc_chat_is_sending_locations(chat) } @@ -1222,102 +1374,143 @@ pub unsafe extern "C" fn dc_msg_new<'a>( ) -> *mut dc_msg::dc_msg_t<'a> { assert!(!context.is_null()); let context = &*context; + let viewtype = from_prim(viewtype).expect(&format!("invalid viewtype = {}", viewtype)); dc_msg::dc_msg_new(context, viewtype) } #[no_mangle] pub unsafe extern "C" fn dc_msg_unref(msg: *mut dc_msg::dc_msg_t) { + assert!(!msg.is_null()); + dc_msg::dc_msg_unref(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_empty(msg: *mut dc_msg::dc_msg_t) { + assert!(!msg.is_null()); + dc_msg::dc_msg_empty(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_id(msg: *mut dc_msg::dc_msg_t) -> u32 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_id(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_from_id(msg: *mut dc_msg::dc_msg_t) -> u32 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_from_id(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_chat_id(msg: *mut dc_msg::dc_msg_t) -> u32 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_chat_id(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_viewtype(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_viewtype(msg) + .to_i64() + .expect("impossible: Viewtype -> i64 conversion failed") as libc::c_int } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_state(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_state(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_timestamp(msg: *mut dc_msg::dc_msg_t) -> i64 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_timestamp(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_received_timestamp(msg: *mut dc_msg::dc_msg_t) -> i64 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_received_timestamp(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_sort_timestamp(msg: *mut dc_msg::dc_msg_t) -> i64 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_sort_timestamp(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_text(msg: *mut dc_msg::dc_msg_t) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_text(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg::dc_msg_t) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_file(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_filename(msg: *mut dc_msg::dc_msg_t) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_filename(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_filemime(msg: *mut dc_msg::dc_msg_t) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_filemime(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_filebytes(msg: *mut dc_msg::dc_msg_t) -> u64 { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_filebytes(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_width(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_width(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_height(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_height(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_duration(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_duration(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_get_showpadlock(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_showpadlock(msg) } @@ -1326,6 +1519,8 @@ pub unsafe extern "C" fn dc_msg_get_summary<'a>( msg: *mut dc_msg::dc_msg_t<'a>, chat: *mut dc_chat_t<'a>, ) -> *mut dc_lot::dc_lot_t { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_summary(msg, chat) } @@ -1334,46 +1529,64 @@ pub unsafe extern "C" fn dc_msg_get_summarytext( msg: *mut dc_msg::dc_msg_t, approx_characters: libc::c_int, ) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_summarytext(msg, approx_characters) } #[no_mangle] pub unsafe extern "C" fn dc_msg_has_deviating_timestamp(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_has_deviating_timestamp(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_has_location(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_has_location(msg) as libc::c_int } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_sent(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_is_sent(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_starred(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { - dc_msg::dc_msg_is_starred(msg) + assert!(!msg.is_null()); + + dc_msg::dc_msg_is_starred(msg).into() } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_forwarded(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_is_forwarded(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_info(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_is_info(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_increation(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_is_increation(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg::dc_msg_t) -> libc::c_int { + assert!(!msg.is_null()); + dc_msg::dc_msg_is_setupmessage(msg) as libc::c_int } @@ -1381,11 +1594,16 @@ pub unsafe extern "C" fn dc_msg_is_setupmessage(msg: *mut dc_msg::dc_msg_t) -> l pub unsafe extern "C" fn dc_msg_get_setupcodebegin( msg: *mut dc_msg::dc_msg_t, ) -> *mut libc::c_char { + assert!(!msg.is_null()); + dc_msg::dc_msg_get_setupcodebegin(msg) } #[no_mangle] pub unsafe extern "C" fn dc_msg_set_text(msg: *mut dc_msg::dc_msg_t, text: *mut libc::c_char) { + assert!(!msg.is_null()); + + // TODO: {text} equal to NULL is treated as "", which is strange. Does anyone rely on it? dc_msg::dc_msg_set_text(msg, text) } @@ -1395,6 +1613,8 @@ pub unsafe extern "C" fn dc_msg_set_file( file: *mut libc::c_char, filemime: *mut libc::c_char, ) { + assert!(!msg.is_null()); + dc_msg::dc_msg_set_file(msg, file, filemime) } @@ -1404,11 +1624,15 @@ pub unsafe extern "C" fn dc_msg_set_dimension( width: libc::c_int, height: libc::c_int, ) { + assert!(!msg.is_null()); + dc_msg::dc_msg_set_dimension(msg, width, height) } #[no_mangle] pub unsafe extern "C" fn dc_msg_set_duration(msg: *mut dc_msg::dc_msg_t, duration: libc::c_int) { + assert!(!msg.is_null()); + dc_msg::dc_msg_set_duration(msg, duration) } @@ -1418,6 +1642,8 @@ pub unsafe extern "C" fn dc_msg_set_location( latitude: libc::c_double, longitude: libc::c_double, ) { + assert!(!msg.is_null()); + dc_msg::dc_msg_set_location(msg, latitude, longitude) } @@ -1428,83 +1654,111 @@ pub unsafe extern "C" fn dc_msg_latefiling_mediasize( height: libc::c_int, duration: libc::c_int, ) { + assert!(!msg.is_null()); + dc_msg::dc_msg_latefiling_mediasize(msg, width, height, duration) } // dc_contact_t #[no_mangle] -pub type dc_contact_t<'a> = dc_contact::dc_contact_t<'a>; +pub type dc_contact_t<'a> = contact::Contact<'a>; #[no_mangle] -pub unsafe extern "C" fn dc_contact_unref(contact: *mut dc_contact::dc_contact_t) { - dc_contact::dc_contact_unref(contact) +pub unsafe extern "C" fn dc_contact_unref(contact: *mut dc_contact_t) { + assert!(!contact.is_null()); + Box::from_raw(contact); } #[no_mangle] -pub unsafe extern "C" fn dc_contact_get_id(contact: *mut dc_contact::dc_contact_t) -> u32 { - dc_contact::dc_contact_get_id(contact) +pub unsafe extern "C" fn dc_contact_get_id(contact: *mut dc_contact_t) -> u32 { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_id() } #[no_mangle] -pub unsafe extern "C" fn dc_contact_get_addr( - contact: *mut dc_contact::dc_contact_t, -) -> *mut libc::c_char { - dc_contact::dc_contact_get_addr(contact) +pub unsafe extern "C" fn dc_contact_get_addr(contact: *mut dc_contact_t) -> *mut libc::c_char { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_addr().strdup() } #[no_mangle] -pub unsafe extern "C" fn dc_contact_get_name( - contact: *mut dc_contact::dc_contact_t, -) -> *mut libc::c_char { - dc_contact::dc_contact_get_name(contact) +pub unsafe extern "C" fn dc_contact_get_name(contact: *mut dc_contact_t) -> *mut libc::c_char { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_name().strdup() } #[no_mangle] pub unsafe extern "C" fn dc_contact_get_display_name( - contact: *mut dc_contact::dc_contact_t, + contact: *mut dc_contact_t, ) -> *mut libc::c_char { - dc_contact::dc_contact_get_display_name(contact) + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_display_name().strdup() } #[no_mangle] pub unsafe extern "C" fn dc_contact_get_name_n_addr( - contact: *mut dc_contact::dc_contact_t, + contact: *mut dc_contact_t, ) -> *mut libc::c_char { - dc_contact::dc_contact_get_name_n_addr(contact) + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_name_n_addr().strdup() } #[no_mangle] pub unsafe extern "C" fn dc_contact_get_first_name( - contact: *mut dc_contact::dc_contact_t, + contact: *mut dc_contact_t, ) -> *mut libc::c_char { - dc_contact::dc_contact_get_first_name(contact) + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_first_name().strdup() } #[no_mangle] pub unsafe extern "C" fn dc_contact_get_profile_image( - contact: *mut dc_contact::dc_contact_t, + contact: *mut dc_contact_t, ) -> *mut libc::c_char { - dc_contact::dc_contact_get_profile_image(contact) + assert!(!contact.is_null()); + let contact = &*contact; + + contact + .get_profile_image() + .map(|s| s.strdup()) + .unwrap_or_else(|| std::ptr::null_mut()) } #[no_mangle] -pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact::dc_contact_t) -> u32 { - dc_contact::dc_contact_get_color(contact) +pub unsafe extern "C" fn dc_contact_get_color(contact: *mut dc_contact_t) -> u32 { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.get_color() } #[no_mangle] -pub unsafe extern "C" fn dc_contact_is_blocked( - contact: *mut dc_contact::dc_contact_t, -) -> libc::c_int { - dc_contact::dc_contact_is_blocked(contact) +pub unsafe extern "C" fn dc_contact_is_blocked(contact: *mut dc_contact_t) -> libc::c_int { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.is_blocked() as libc::c_int } #[no_mangle] -pub unsafe extern "C" fn dc_contact_is_verified( - contact: *mut dc_contact::dc_contact_t, -) -> libc::c_int { - dc_contact::dc_contact_is_verified(contact) +pub unsafe extern "C" fn dc_contact_is_verified(contact: *mut dc_contact_t) -> libc::c_int { + assert!(!contact.is_null()); + let contact = &*contact; + + contact.is_verified() as libc::c_int } // dc_lot_t @@ -1519,41 +1773,57 @@ pub unsafe extern "C" fn dc_lot_new() -> *mut dc_lot::dc_lot_t { #[no_mangle] pub unsafe extern "C" fn dc_lot_empty(lot: *mut dc_lot::dc_lot_t) { + assert!(!lot.is_null()); + dc_lot::dc_lot_empty(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_unref(lot: *mut dc_lot::dc_lot_t) { + assert!(!lot.is_null()); + dc_lot::dc_lot_unref(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_text1(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_text1(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_text2(lot: *mut dc_lot::dc_lot_t) -> *mut libc::c_char { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_text2(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_text1_meaning(lot: *mut dc_lot::dc_lot_t) -> libc::c_int { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_text1_meaning(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_state(lot: *mut dc_lot::dc_lot_t) -> libc::c_int { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_state(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_id(lot: *mut dc_lot::dc_lot_t) -> u32 { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_id(lot) } #[no_mangle] pub unsafe extern "C" fn dc_lot_get_timestamp(lot: *mut dc_lot::dc_lot_t) -> i64 { + assert!(!lot.is_null()); + dc_lot::dc_lot_get_timestamp(lot) } diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 09c0225a6..b30f1e763 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -4,11 +4,11 @@ use std::str::FromStr; use deltachat::chatlist::*; use deltachat::config; use deltachat::constants::*; +use deltachat::contact::*; use deltachat::context::*; use deltachat::dc_array::*; use deltachat::dc_chat::*; use deltachat::dc_configure::*; -use deltachat::dc_contact::*; use deltachat::dc_imex::*; use deltachat::dc_job::*; use deltachat::dc_location::*; @@ -147,7 +147,7 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int } else { current_block = 7149356873433890176; } - real_spec = to_cstring(rs.unwrap_or_default()); + real_spec = rs.unwrap_or_default().strdup(); } match current_block { 8522321847195001863 => {} @@ -184,11 +184,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int if name.ends_with(".eml") { let path_plus_name = format!("{}/{}", as_str(real_spec), name); info!(context, 0, "Import: {}", path_plus_name); - let path_plus_name_c = to_cstring(path_plus_name); - if 0 != dc_poke_eml_file(context, path_plus_name_c) { + let path_plus_name_c = CString::yolo(path_plus_name); + if 0 != dc_poke_eml_file(context, path_plus_name_c.as_ptr()) { read_cnt += 1 } - free(path_plus_name_c as *mut _); } } current_block = 1622411330066726685; @@ -219,9 +218,10 @@ unsafe fn poke_spec(context: &Context, spec: *const libc::c_char) -> libc::c_int } unsafe fn log_msg(context: &Context, prefix: impl AsRef, msg: *mut dc_msg_t) { - let contact: *mut dc_contact_t = dc_get_contact(context, dc_msg_get_from_id(msg)); - let contact_name: *mut libc::c_char = dc_contact_get_name(contact); - let contact_id: libc::c_int = dc_contact_get_id(contact) as libc::c_int; + let contact = Contact::get_by_id(context, dc_msg_get_from_id(msg)).expect("invalid contact"); + let contact_name = contact.get_name(); + let contact_id = contact.get_id(); + let statestr = match dc_msg_get_state(msg) { DC_STATE_OUT_PENDING => " o", DC_STATE_OUT_DELIVERED => " √", @@ -229,8 +229,8 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef, msg: *mut dc_msg_t DC_STATE_OUT_FAILED => " !!", _ => "", }; - let temp2: *mut libc::c_char = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); - let msgtext: *mut libc::c_char = dc_msg_get_text(msg); + let temp2 = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); + let msgtext = dc_msg_get_text(msg); info!( context, 0, @@ -243,14 +243,10 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef, msg: *mut dc_msg_t "" }, if dc_msg_has_location(msg) { "📍" } else { "" }, - as_str(contact_name), + &contact_name, contact_id, as_str(msgtext), - if 0 != dc_msg_is_starred(msg) { - "★" - } else { - "" - }, + if dc_msg_is_starred(msg) { "★" } else { "" }, if dc_msg_get_from_id(msg) == 1 as libc::c_uint { "" } else if dc_msg_get_state(msg) == DC_STATE_IN_SEEN { @@ -266,12 +262,9 @@ unsafe fn log_msg(context: &Context, prefix: impl AsRef, msg: *mut dc_msg_t "" }, statestr, - as_str(temp2), + &temp2, ); free(msgtext as *mut libc::c_void); - free(temp2 as *mut libc::c_void); - free(contact_name as *mut libc::c_void); - dc_contact_unref(contact); } unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) { @@ -309,7 +302,6 @@ unsafe fn log_msglist(context: &Context, msglist: *mut dc_array_t) { } unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) { - let mut contact: *mut dc_contact_t; if !dc_array_search_id(contacts, 1 as uint32_t, 0 as *mut size_t) { dc_array_add_id(contacts, 1 as uint32_t); } @@ -318,13 +310,12 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) { let contact_id = dc_array_get_id(contacts, i as size_t); let line; let mut line2 = "".to_string(); - contact = dc_get_contact(context, contact_id); - if !contact.is_null() { - let name: *mut libc::c_char = dc_contact_get_name(contact); - let addr: *mut libc::c_char = dc_contact_get_addr(contact); - let verified_state: libc::c_int = dc_contact_is_verified(contact); - let verified_str = if 0 != verified_state { - if verified_state == 2 { + if let Ok(contact) = Contact::get_by_id(context, contact_id) { + let name = contact.get_name(); + let addr = contact.get_addr(); + let verified_state = contact.is_verified(); + let verified_str = if VerifiedStatus::Unverified != verified_state { + if verified_state == VerifiedStatus::BidirectVerified { " √√" } else { " √" @@ -334,28 +325,26 @@ unsafe fn log_contactlist(context: &Context, contacts: *mut dc_array_t) { }; line = format!( "{}{} <{}>", - if !name.is_null() && 0 != *name.offset(0isize) as libc::c_int { - as_str(name) + if !name.is_empty() { + &name } else { "" }, verified_str, - if !addr.is_null() && 0 != *addr.offset(0isize) as libc::c_int { - as_str(addr) + if !addr.is_empty() { + &addr } else { "addr unset" } ); - let peerstate = Peerstate::from_addr(context, &context.sql, as_str(addr)); + let peerstate = Peerstate::from_addr(context, &context.sql, &addr); if peerstate.is_some() && contact_id != 1 as libc::c_uint { line2 = format!( ", prefer-encrypt={}", peerstate.as_ref().unwrap().prefer_encrypt ); } - dc_contact_unref(contact); - free(name as *mut libc::c_void); - free(addr as *mut libc::c_void); + info!(context, 0, "Contact#{}: {}{}", contact_id, line, line2); } } @@ -391,13 +380,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E let arg1_c = if arg1.is_empty() { std::ptr::null() } else { - to_cstring(arg1) as *const _ + arg1.strdup() as *const _ }; let arg2 = args.next().unwrap_or_default(); let arg2_c = if arg2.is_empty() { std::ptr::null() } else { - to_cstring(arg2) as *const _ + arg2.strdup() as *const _ }; match arg0 { @@ -503,10 +492,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "open" => { ensure!(!arg1.is_empty(), "Argument missing"); dc_close(context); - ensure!( - 0 != dc_open(context, arg1_c, 0 as *const libc::c_char), - "Open failed" - ); + ensure!(dc_open(context, arg1, None), "Open failed"); } "close" => { dc_close(context); @@ -570,30 +556,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "export-setup" => { let setup_code = dc_create_setup_code(context); - let setup_code_c = CString::new(setup_code.clone()).unwrap(); let file_name: *mut libc::c_char = dc_mprintf( b"%s/autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char, context.get_blobdir(), ); - let file_content: *mut libc::c_char; - file_content = dc_render_setup_file(context, setup_code_c.as_ptr()); - if !file_content.is_null() - && 0 != dc_write_file( - context, - file_name, - file_content as *const libc::c_void, - strlen(file_content), - ) - { - println!( - "Setup message written to: {}\nSetup code: {}", - as_str(file_name), - &setup_code, - ) - } else { - bail!(""); - } - free(file_content as *mut libc::c_void); + let file_content = dc_render_setup_file(context, &setup_code)?; + std::fs::write(as_str(file_name), file_content)?; + println!( + "Setup message written to: {}\nSetup code: {}", + as_str(file_name), + &setup_code, + ); free(file_name as *mut libc::c_void); } "poke" => { @@ -682,7 +655,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E if !text1.is_null() { ": " } else { "" }, to_string(text2), statestr, - as_str(timestr), + ×tr, if 0 != dc_chat_is_sending_locations(chat) { "📍" } else { @@ -691,7 +664,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ); free(text1 as *mut libc::c_void); free(text2 as *mut libc::c_void); - free(timestr as *mut libc::c_void); dc_lot_unref(lot); dc_chat_unref(chat); info!( @@ -886,7 +858,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E 0, "Loc#{}: {}: lat={} lng={} acc={} Chat#{} Contact#{} Msg#{} {}", dc_array_get_id(loc, j as size_t), - as_str(timestr_0), + ×tr_0, dc_array_get_latitude(loc, j as size_t), dc_array_get_longitude(loc, j as size_t), dc_array_get_accuracy(loc, j as size_t), @@ -899,7 +871,6 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "-" }, ); - free(timestr_0 as *mut libc::c_void); free(marker as *mut libc::c_void); j += 1 } @@ -938,32 +909,17 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!sel_chat.is_null(), "No chat selected."); ensure!(!arg1.is_empty(), "No message text given."); - let msg = to_cstring(format!("{} {}", arg1, arg2)); + let msg = format!("{} {}", arg1, arg2); if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg) { println!("Message sent."); - free(msg as *mut _); } else { - free(msg as *mut _); bail!("Sending failed."); } } - "send-garbage" => { - ensure!(!sel_chat.is_null(), "No chat selected."); - let msg = b"\xff\x00"; // NUL-terminated C-string, that is malformed utf-8 - if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), msg.as_ptr().cast()) { - println!("Malformed utf-8 succesfully send. Not nice."); - } else { - bail!("Garbage sending failed, as expected."); - } - } "sendempty" => { ensure!(!sel_chat.is_null(), "No chat selected."); - if 0 != dc_send_text_msg( - context, - dc_chat_get_id(sel_chat), - b"\x00" as *const u8 as *const libc::c_char, - ) { + if 0 != dc_send_text_msg(context, dc_chat_get_id(sel_chat), "".into()) { println!("Message sent."); } else { bail!("Sending failed."); @@ -973,7 +929,14 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!sel_chat.is_null(), "No chat selected."); ensure!(!arg1.is_empty() && !arg2.is_empty(), "No file given."); - let msg_0 = dc_msg_new(context, if arg0 == "sendimage" { 20 } else { 60 }); + let msg_0 = dc_msg_new( + context, + if arg0 == "sendimage" { + Viewtype::Image + } else { + Viewtype::File + }, + ); dc_msg_set_file(msg_0, arg1_c, 0 as *const libc::c_char); dc_msg_set_text(msg_0, arg2_c); dc_send_msg(context, dc_chat_get_id(sel_chat), msg_0); @@ -1000,7 +963,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!sel_chat.is_null(), "No chat selected."); if !arg1.is_empty() { - let draft_0 = dc_msg_new(context, 10); + let draft_0 = dc_msg_new(context, Viewtype::Text); dc_msg_set_text(draft_0, arg1_c); dc_set_draft(context, dc_chat_get_id(sel_chat), draft_0); dc_msg_unref(draft_0); @@ -1013,7 +976,13 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E "listmedia" => { ensure!(!sel_chat.is_null(), "No chat selected."); - let images = dc_get_chat_media(context, dc_chat_get_id(sel_chat), 20, 21, 50); + let images = dc_get_chat_media( + context, + dc_chat_get_id(sel_chat), + Viewtype::Image, + Viewtype::Gif, + Viewtype::Video, + ); let icnt: libc::c_int = dc_array_get_cnt(images) as libc::c_int; println!("{} images or videos: ", icnt); for i in 0..icnt { @@ -1086,15 +1055,15 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E dc_delete_msgs(context, ids.as_mut_ptr(), 1); } "listcontacts" | "contacts" | "listverified" => { - let contacts = dc_get_contacts( + let contacts = Contact::get_all( context, if arg0 == "listverified" { 0x1 | 0x2 } else { 0x2 }, - arg1_c, - ); + Some(arg1), + )?; if !contacts.is_null() { log_contactlist(context, contacts); println!("{} contacts.", dc_array_get_cnt(contacts) as libc::c_int,); @@ -1107,33 +1076,22 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E ensure!(!arg1.is_empty(), "Arguments [] expected."); if !arg2.is_empty() { - let book = dc_mprintf( - b"%s\n%s\x00" as *const u8 as *const libc::c_char, - arg1_c, - arg2_c, - ); - dc_add_address_book(context, book); - free(book as *mut libc::c_void); + let book = format!("{}\n{}", arg1, arg2); + Contact::add_address_book(context, book)?; } else { - if 0 == dc_create_contact(context, 0 as *const libc::c_char, arg1_c) { - bail!("Failed to create contact"); - } + Contact::create(context, "", arg1)?; } } "contactinfo" => { ensure!(!arg1.is_empty(), "Argument missing."); let contact_id = arg1.parse()?; - let contact = dc_get_contact(context, contact_id); - let name_n_addr = dc_contact_get_name_n_addr(contact); + let contact = Contact::get_by_id(context, contact_id)?; + let name_n_addr = contact.get_name_n_addr(); - let mut res = format!("Contact info for: {}:\n\n", as_str(name_n_addr),); - free(name_n_addr as *mut libc::c_void); - dc_contact_unref(contact); + let mut res = format!("Contact info for: {}:\n\n", name_n_addr); - let encrinfo = dc_get_contact_encrinfo(context, contact_id); - res += as_str(encrinfo); - free(encrinfo as *mut libc::c_void); + res += &Contact::get_encrinfo(context, contact_id); let chatlist = Chatlist::try_load(context, 0, None, Some(contact_id))?; let chatlist_cnt = chatlist.len(); @@ -1156,9 +1114,7 @@ pub unsafe fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::E } "delcontact" => { ensure!(!arg1.is_empty(), "Argument missing."); - if !dc_delete_contact(context, arg1.parse()?) { - bail!("Failed to delete contact"); - } + Contact::delete(context, arg1.parse()?)?; } "checkqr" => { ensure!(!arg1.is_empty(), "Argument missing."); diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 7cc700af8..c3a47ccec 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -291,7 +291,7 @@ const DB_COMMANDS: [&'static str; 11] = [ "housekeeping", ]; -const CHAT_COMMANDS: [&'static str; 25] = [ +const CHAT_COMMANDS: [&'static str; 24] = [ "listchats", "listarchived", "chat", @@ -309,7 +309,6 @@ const CHAT_COMMANDS: [&'static str; 25] = [ "dellocations", "getlocations", "send", - "send-garbage", "sendimage", "sendfile", "draft", @@ -393,18 +392,13 @@ fn main_0(args: Vec) -> Result<(), failure::Error> { let mut context = dc_context_new( Some(receive_event), 0 as *mut libc::c_void, - b"CLI\x00" as *const u8 as *const libc::c_char, + Some("CLI".into()), ); unsafe { dc_cmdline_skip_auth() }; if args.len() == 2 { - if 0 == unsafe { - let a = to_cstring(&args[1]); - let res = dc_open(&mut context, a, 0 as *const _); - free(a as *mut _); - res - } { + if unsafe { !dc_open(&mut context, &args[1], None) } { println!("Error: Cannot open {}.", args[0],); } } else if args.len() != 1 { @@ -486,7 +480,7 @@ unsafe fn handle_cmd(line: &str, ctx: Arc>) -> Result us fn main() { unsafe { - let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None); let running = Arc::new(RwLock::new(true)); let info = dc_get_info(&ctx); let info_s = CStr::from_ptr(info); @@ -76,11 +76,11 @@ fn main() { }); let dir = tempdir().unwrap(); - let dbfile = CString::new(dir.path().join("db.sqlite").to_str().unwrap()).unwrap(); + let dbfile = dir.path().join("db.sqlite"); println!("opening database {:?}", dbfile); - assert_eq!(dc_open(&ctx, dbfile.as_ptr(), std::ptr::null()), 1); + assert!(dc_open(&ctx, dbfile.to_str().unwrap(), None)); println!("configuring"); let args = std::env::args().collect::>(); @@ -93,12 +93,11 @@ fn main() { thread::sleep(duration); - let email = CString::new("dignifiedquire@gmail.com").unwrap(); println!("sending a message"); - let contact_id = dc_create_contact(&ctx, std::ptr::null(), email.as_ptr()); + let contact_id = + Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap(); let chat_id = dc_create_chat_by_contact_id(&ctx, contact_id); - let msg_text = CString::new("Hi, here is my first message!").unwrap(); - dc_send_text_msg(&ctx, chat_id, msg_text.as_ptr()); + dc_send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()); println!("fetching chats.."); let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap(); diff --git a/python/src/deltachat/message.py b/python/src/deltachat/message.py index 8f62dabde..6e3d05625 100644 --- a/python/src/deltachat/message.py +++ b/python/src/deltachat/message.py @@ -20,6 +20,7 @@ class Message(object): self._dc_context = account._dc_context assert isinstance(self._dc_context, ffi.CData) assert isinstance(dc_msg, ffi.CData) + assert dc_msg != ffi.NULL self._dc_msg = dc_msg self.id = lib.dc_msg_get_id(dc_msg) assert self.id is not None and self.id >= 0, repr(self.id) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index fca88e94a..2ee4ad7fd 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -120,6 +120,8 @@ def acfactory(pytestconfig, tmpdir, request): pytest.skip("specify a --liveconfig file to run tests with real accounts") self.live_count += 1 configdict = self.configlist.pop(0) + if "e2ee_enabled" not in configdict: + configdict["e2ee_enabled"] = "1" tmpdb = tmpdir.join("livedb%d" % self.live_count) ac = Account(tmpdb.strpath, logid="ac{}".format(self.live_count)) ac._evlogger.init_time = self.init_time diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 6e6c1e2c2..5acf972c4 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -421,10 +421,43 @@ class TestOnlineAccount: lp.sec("mark message as seen on ac2, wait for changes on ac1") ac2.mark_seen_messages([msg_in]) lp.step("1") - ac1._evlogger.get_matching("DC_EVENT_MSG_READ") + ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ") + assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL + assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL lp.step("2") assert msg_out.is_out_mdn_received() + def test_send_and_receive_will_encrypt_decrypt(self, acfactory, lp): + lp.sec("starting accounts, waiting for configuration") + ac1 = acfactory.get_online_configuring_account() + ac2 = acfactory.get_online_configuring_account() + c2 = ac1.create_contact(email=ac2.get_config("addr")) + chat = ac1.create_chat_by_contact(c2) + assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL + + wait_configuration_progress(ac1, 1000) + wait_configuration_progress(ac2, 1000) + + lp.sec("sending text message from ac1 to ac2") + msg_out = chat.send_text("message1") + + lp.sec("wait for ac2 to receive message") + ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev[2] == msg_out.id + msg_in = ac2.get_message_by_id(msg_out.id) + assert msg_in.text == "message1" + + lp.sec("create new chat with contact and send back (encrypted) message") + chat2b = ac2.create_chat_by_message(msg_in) + chat2b.send_text("message-back") + + lp.sec("wait for ac1 to receive message") + ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG") + assert ev[1] == chat.id + assert ev[2] > msg_out.id + msg_back = ac1.get_message_by_id(ev[2]) + assert msg_back.text == "message-back" + def test_saved_mime_on_received_message(self, acfactory, lp): lp.sec("starting accounts, waiting for configuration") ac1 = acfactory.get_online_configuring_account() diff --git a/src/aheader.rs b/src/aheader.rs index 35fcb25ca..20a2daa2c 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -6,7 +6,7 @@ use std::{fmt, str}; use mmime::mailimf_types::*; use crate::constants::*; -use crate::dc_contact::*; +use crate::contact::*; use crate::dc_tools::as_str; use crate::key::*; @@ -94,7 +94,7 @@ impl Aheader { match Self::from_str(value) { Ok(test) => { - if dc_addr_cmp(&test.addr, as_str(wanted_from)) { + if addr_cmp(&test.addr, as_str(wanted_from)) { if fine_header.is_none() { fine_header = Some(test); } else { diff --git a/src/chatlist.rs b/src/chatlist.rs index 112363098..018aff4ab 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -1,7 +1,7 @@ use crate::constants::*; +use crate::contact::*; use crate::context::*; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_lot::*; use crate::dc_msg::*; use crate::dc_tools::*; @@ -155,7 +155,7 @@ impl<'a> Chatlist<'a> { let query = query.trim().to_string(); ensure!(!query.is_empty(), "missing query"); - let strLikeCmd = format!("%{}%", query); + let str_like_cmd = format!("%{}%", query); context.sql.query_map( "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m \ ON c.id=m.chat_id \ @@ -164,7 +164,7 @@ impl<'a> Chatlist<'a> { 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], + params![str_like_cmd], process_row, process_rows, )? @@ -256,18 +256,18 @@ impl<'a> Chatlist<'a> { let mut ret = dc_lot_new(); if index >= self.ids.len() { - (*ret).text2 = to_cstring("ErrBadChatlistIndex"); + (*ret).text2 = "ErrBadChatlistIndex".strdup(); return ret; } let lastmsg_id = self.ids[index].1; - let mut lastcontact = 0 as *mut dc_contact_t; + let mut lastcontact = None; if chat.is_null() { chat = dc_chat_new(self.context); let chat_to_delete = chat; if !dc_chat_load_from_db(chat, self.ids[index].0) { - (*ret).text2 = to_cstring("ErrCannotReadChat"); + (*ret).text2 = "ErrCannotReadChat".strdup(); dc_chat_unref(chat_to_delete); return ret; @@ -282,8 +282,7 @@ impl<'a> Chatlist<'a> { && ((*chat).type_0 == DC_CHAT_TYPE_GROUP || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP) { - lastcontact = dc_contact_new(self.context); - dc_contact_load_from_db(lastcontact, &self.context.sql, (*lastmsg).from_id); + lastcontact = Contact::load_from_db(self.context, (*lastmsg).from_id).ok(); } lastmsg } else { @@ -292,14 +291,13 @@ impl<'a> Chatlist<'a> { if (*chat).id == DC_CHAT_ID_ARCHIVED_LINK as u32 { (*ret).text2 = dc_strdup(0 as *const libc::c_char) - } else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_SELF as u32 { - (*ret).text2 = to_cstring(self.context.stock_str(StockMessage::NoMessages)); + } else if lastmsg.is_null() || (*lastmsg).from_id == DC_CONTACT_ID_UNDEFINED as u32 { + (*ret).text2 = self.context.stock_str(StockMessage::NoMessages).strdup(); } else { - dc_lot_fill(ret, lastmsg, chat, lastcontact, self.context); + dc_lot_fill(ret, lastmsg, chat, lastcontact.as_ref(), self.context); } dc_msg_unref(lastmsg); - dc_contact_unref(lastcontact); ret } diff --git a/src/config.rs b/src/config.rs index ff7aa0905..459d0f949 100644 --- a/src/config.rs +++ b/src/config.rs @@ -7,7 +7,6 @@ use crate::dc_job::*; use crate::dc_tools::*; use crate::error::Error; use crate::stock::StockMessage; -use crate::x::*; /// The available configuration keys. #[derive( @@ -70,17 +69,7 @@ impl Context { let value = match key { Config::Selfavatar => { let rel_path = self.sql.get_config(self, key); - rel_path.map(|p| { - let v = unsafe { - let n = to_cstring(p); - let res = dc_get_abs_path(self, n); - free(n as *mut libc::c_void); - res - }; - let r = to_string(v); - unsafe { free(v as *mut _) }; - r - }) + rel_path.map(|p| dc_get_abs_path_safe(self, &p).to_str().unwrap().to_string()) } Config::SysVersion => Some(std::str::from_utf8(DC_VERSION_STR).unwrap().into()), Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)), diff --git a/src/constants.rs b/src/constants.rs index e1eb5b835..a5180de98 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,5 +1,8 @@ -#![allow(non_camel_case_types)] //! Constants +#![allow(non_camel_case_types)] +use num_traits::{FromPrimitive, ToPrimitive}; +use rusqlite as sql; +use rusqlite::types::*; pub const DC_VERSION_STR: &'static [u8; 14] = b"1.0.0-alpha.3\x00"; @@ -86,6 +89,7 @@ pub const DC_MAX_GET_TEXT_LEN: usize = 30000; /// approx. max. length returned by dc_get_msg_info() pub const DC_MAX_GET_INFO_LEN: usize = 100000; +pub const DC_CONTACT_ID_UNDEFINED: usize = 0; pub const DC_CONTACT_ID_SELF: usize = 1; pub const DC_CONTACT_ID_DEVICE: usize = 2; pub const DC_CONTACT_ID_LAST_SPECIAL: usize = 9; @@ -96,46 +100,6 @@ pub const DC_TEXT1_SELF: usize = 3; pub const DC_CREATE_MVBOX: usize = 1; -/// Text message. -/// The text of the message is set using dc_msg_set_text() -/// and retrieved with dc_msg_get_text(). -pub const DC_MSG_TEXT: i32 = 10; - -/// Image message. -/// If the image is an animated GIF, the type DC_MSG_GIF should be used. -/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension -/// and retrieved via dc_msg_set_file(), dc_msg_set_dimension(). -pub const DC_MSG_IMAGE: i32 = 20; - -/// Animated GIF message. -/// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension() -/// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height(). -pub const DC_MSG_GIF: i32 = 21; - -/// Message containing an Audio file. -/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration() -/// and retrieved via dc_msg_get_file(), dc_msg_get_duration(). -pub const DC_MSG_AUDIO: i32 = 40; - -/// A voice message that was directly recorded by the user. -/// For all other audio messages, the type #DC_MSG_AUDIO should be used. -/// File and duration are set via dc_msg_set_file(), dc_msg_set_duration() -/// and retrieved via dc_msg_get_file(), dc_msg_get_duration() -pub const DC_MSG_VOICE: i32 = 41; - -/// Video messages. -/// File, width, height and durarion -/// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration() -/// and retrieved via -/// dc_msg_get_file(), dc_msg_get_width(), -/// dc_msg_get_height(), dc_msg_get_duration(). -pub const DC_MSG_VIDEO: i32 = 50; - -/// Message containing any file, eg. a PDF. -/// The file is set via dc_msg_set_file() -/// and retrieved via dc_msg_get_file(). -pub const DC_MSG_FILE: i32 = 60; - // Flags for configuring IMAP and SMTP servers. // These flags are optional // and may be set together with the username, password etc. @@ -183,6 +147,78 @@ pub const DC_LP_IMAP_SOCKET_FLAGS: usize = pub const DC_LP_SMTP_SOCKET_FLAGS: usize = (DC_LP_SMTP_SOCKET_STARTTLS | DC_LP_SMTP_SOCKET_SSL | DC_LP_SMTP_SOCKET_PLAIN); +#[derive(Debug, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)] +#[repr(i32)] +pub enum Viewtype { + Unknown = 0, + /// Text message. + /// The text of the message is set using dc_msg_set_text() + /// and retrieved with dc_msg_get_text(). + Text = 10, + + /// Image message. + /// If the image is an animated GIF, the type DC_MSG_GIF should be used. + /// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension + /// and retrieved via dc_msg_set_file(), dc_msg_set_dimension(). + Image = 20, + + /// Animated GIF message. + /// File, width and height are set via dc_msg_set_file(), dc_msg_set_dimension() + /// and retrieved via dc_msg_get_file(), dc_msg_get_width(), dc_msg_get_height(). + Gif = 21, + + /// Message containing an Audio file. + /// File and duration are set via dc_msg_set_file(), dc_msg_set_duration() + /// and retrieved via dc_msg_get_file(), dc_msg_get_duration(). + Audio = 40, + + /// A voice message that was directly recorded by the user. + /// For all other audio messages, the type #DC_MSG_AUDIO should be used. + /// File and duration are set via dc_msg_set_file(), dc_msg_set_duration() + /// and retrieved via dc_msg_get_file(), dc_msg_get_duration() + Voice = 41, + + /// Video messages. + /// File, width, height and durarion + /// are set via dc_msg_set_file(), dc_msg_set_dimension(), dc_msg_set_duration() + /// and retrieved via + /// dc_msg_get_file(), dc_msg_get_width(), + /// dc_msg_get_height(), dc_msg_get_duration(). + Video = 50, + + /// Message containing any file, eg. a PDF. + /// The file is set via dc_msg_set_file() + /// and retrieved via dc_msg_get_file(). + File = 60, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn derive_display_works_as_expected() { + assert_eq!(format!("{}", Viewtype::Audio), "Audio"); + } +} + +impl ToSql for Viewtype { + fn to_sql(&self) -> sql::Result { + let num: i64 = self + .to_i64() + .expect("impossible: Viewtype -> i64 conversion failed"); + + Ok(ToSqlOutput::Owned(Value::Integer(num))) + } +} + +impl FromSql for Viewtype { + fn column_result(col: ValueRef) -> FromSqlResult { + let inner = FromSql::column_result(col)?; + FromPrimitive::from_i64(inner).ok_or(FromSqlError::InvalidType) + } +} + // These constants are used as events // reported to the callback given to dc_context_new(). // If you do not want to handle an event, it is always safe to return 0, diff --git a/src/contact.rs b/src/contact.rs new file mode 100644 index 000000000..30d4ed42c --- /dev/null +++ b/src/contact.rs @@ -0,0 +1,1093 @@ +use itertools::Itertools; +use num_traits::{FromPrimitive, ToPrimitive}; +use rusqlite; +use rusqlite::types::*; + +use crate::aheader::EncryptPreference; +use crate::config::Config; +use crate::constants::*; +use crate::context::Context; +use crate::dc_array::*; +use crate::dc_e2ee::*; +use crate::dc_loginparam::*; +use crate::dc_tools::*; +use crate::error::Result; +use crate::key::*; +use crate::peerstate::*; +use crate::sql; +use crate::stock::StockMessage; +use crate::types::*; + +const DC_GCL_VERIFIED_ONLY: u32 = 0x01; + +/// Contacts with at least this origin value are shown in the contact list. +const DC_ORIGIN_MIN_CONTACT_LIST: i32 = 0x100; + +/// An object representing a single contact in memory. +/// The contact object is not updated. +/// If you want an update, you have to recreate the object. +/// +/// The library makes sure +/// only to use names _authorized_ by the contact in `To:` or `Cc:`. +/// _Given-names _as "Daddy" or "Honey" are not used there. +/// For this purpose, internally, two names are tracked - +/// authorized-name and given-name. +/// By default, these names are equal, but functions working with contact names +pub struct Contact<'a> { + context: &'a Context, + /// The contact ID. + /// + /// Special message IDs: + /// - DC_CONTACT_ID_SELF (1) - this is the owner of the account with the email-address set by + /// `dc_set_config` using "addr". + /// + /// Normal contact IDs are larger than these special ones (larger than DC_CONTACT_ID_LAST_SPECIAL). + pub id: u32, + /// Contact name. It is recommended to use `Contact::get_name`, + /// `Contact::get_display_name` or `Contact::get_name_n_addr` to access this field. + /// May be empty, initially set to `authname`. + name: String, + /// Name authorized by the contact himself. Only this name may be spread to others, + /// e.g. in To:-lists. May be empty. It is recommended to use `Contact::get_name`, + /// `Contact::get_display_name` or `Contact::get_name_n_addr` to access this field. + authname: String, + /// E-Mail-Address of the contact. It is recommended to use `Contact::get_addr`` to access this field. + addr: String, + /// Blocked state. Use dc_contact_is_blocked to access this field. + blocked: bool, + /// The origin/source of the contact. + pub origin: Origin, +} + +/// Possible origins of a contact. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromPrimitive, ToPrimitive)] +#[repr(i32)] +pub enum Origin { + Unknown = 0, + /// From: of incoming messages of unknown sender + IncomingUnknownFrom = 0x10, + /// Cc: of incoming messages of unknown sender + IncomingUnknownCc = 0x20, + /// To: of incoming messages of unknown sender + IncomingUnknownTo = 0x40, + /// address scanned but not verified + UnhandledQrScan = 0x80, + /// Reply-To: of incoming message of known sender + IncomingReplyTo = 0x100, + /// Cc: of incoming message of known sender + IncomingCc = 0x200, + /// additional To:'s of incoming message of known sender + IncomingTo = 0x400, + /// a chat was manually created for this user, but no message yet sent + CreateChat = 0x800, + /// message sent by us + OutgoingBcc = 0x1000, + /// message sent by us + OutgoingCc = 0x2000, + /// message sent by us + OutgoingTo = 0x4000, + /// internal use + Internal = 0x40000, + /// address is in our address book + AdressBook = 0x80000, + /// set on Alice's side for contacts like Bob that have scanned the QR code offered by her. Only means the contact has once been established using the "securejoin" procedure in the past, getting the current key verification status requires calling dc_contact_is_verified() ! + SecurejoinInvited = 0x1000000, + /// set on Bob's side for contacts scanned and verified from a QR code. Only means the contact has once been established using the "securejoin" procedure in the past, getting the current key verification status requires calling dc_contact_is_verified() ! + SecurejoinJoined = 0x2000000, + /// contact added mannually by dc_create_contact(), this should be the largets origin as otherwise the user cannot modify the names + ManuallyCreated = 0x4000000, +} + +impl ToSql for Origin { + fn to_sql(&self) -> rusqlite::Result { + let num: i64 = self + .to_i64() + .expect("impossible: Origin -> i64 conversion failed"); + + Ok(ToSqlOutput::Owned(Value::Integer(num))) + } +} + +impl FromSql for Origin { + fn column_result(col: ValueRef) -> FromSqlResult { + let inner = FromSql::column_result(col)?; + FromPrimitive::from_i64(inner).ok_or(FromSqlError::InvalidType) + } +} + +impl Origin { + /// Contacts that start a new "normal" chat, defaults to off. + pub fn is_start_new_chat(self) -> bool { + self as i32 >= 0x7FFFFFFF + } + + /// Contacts that are verified and known not to be spam. + pub fn is_verified(self) -> bool { + self as i32 >= 0x100 + } + + /// Contacts that are shown in the contact list. + pub fn include_in_contactlist(self) -> bool { + self as i32 >= DC_ORIGIN_MIN_CONTACT_LIST + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum Modifier { + None, + Modified, + Created, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[repr(u8)] +pub enum VerifiedStatus { + /// Contact is not verified. + Unverified = 0, + // TODO: is this a thing? + Verified = 1, + /// SELF and contact have verified their fingerprints in both directions; in the UI typically checkmarks are shown. + BidirectVerified = 2, +} + +impl<'a> Contact<'a> { + pub fn load_from_db(context: &'a Context, contact_id: u32) -> Result { + if contact_id == DC_CONTACT_ID_SELF as u32 { + let contact = Contact { + context, + id: contact_id, + name: context.stock_str(StockMessage::SelfMsg).into(), + authname: "".into(), + addr: context + .get_config(Config::ConfiguredAddr) + .unwrap_or_default(), + blocked: false, + origin: Origin::Unknown, + }; + + return Ok(contact); + } + + context.sql.query_row( + "SELECT c.name, c.addr, c.origin, c.blocked, c.authname FROM contacts c WHERE c.id=?;", + params![contact_id as i32], + |row| { + let contact = Self { + context, + id: contact_id, + name: row.get::<_, String>(0)?, + authname: row.get::<_, String>(4)?, + addr: row.get::<_, String>(1)?, + blocked: row.get::<_, Option>(3)?.unwrap_or_default() != 0, + origin: row.get(2)?, + }; + Ok(contact) + } + ) + } + + /// Returns `true` if this contact is blocked. + pub fn is_blocked(&self) -> bool { + self.blocked + } + + /// Check if a contact is blocked. + pub fn is_blocked_load(context: &'a Context, id: u32) -> bool { + Self::load_from_db(context, id) + .map(|contact| contact.blocked) + .unwrap_or_default() + } + + /// Block the given contact. + pub fn block(context: &Context, id: u32) { + set_block_contact(context, id, true); + } + + /// Unblock the given contact. + pub fn unblock(context: &Context, id: u32) { + set_block_contact(context, id, false); + } + + /// Add a single contact as a result of an _explicit_ user action. + /// + /// We assume, the contact name, if any, is entered by the user and is used "as is" therefore, + /// normalize() is _not_ called for the name. If the contact is blocked, it is unblocked. + /// + /// To add a number of contacts, see `dc_add_address_book()` which is much faster for adding + /// a bunch of addresses. + /// + /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event. + pub fn create(context: &Context, name: impl AsRef, addr: impl AsRef) -> Result { + ensure!( + !addr.as_ref().is_empty(), + "Cannot create contact with empty address" + ); + + let (contact_id, sth_modified) = + Contact::add_or_lookup(context, name, addr, Origin::ManuallyCreated)?; + let blocked = Contact::is_blocked_load(context, contact_id); + context.call_cb( + Event::CONTACTS_CHANGED, + (if sth_modified == Modifier::Created { + contact_id + } else { + 0 + }) as uintptr_t, + 0 as uintptr_t, + ); + if blocked { + Contact::unblock(context, contact_id); + } + + Ok(contact_id) + } + + /// Mark all messages sent by the given contact + /// as _noticed_. See also dc_marknoticed_chat() and dc_markseen_msgs() + /// + /// Calling this function usually results in the event `#DC_EVENT_MSGS_CHANGED`. + pub fn mark_noticed(context: &Context, id: u32) { + if sql::execute( + context, + &context.sql, + "UPDATE msgs SET state=? WHERE from_id=? AND state=?;", + params![DC_STATE_IN_NOTICED, id as i32, DC_STATE_IN_FRESH], + ) + .is_ok() + { + context.call_cb(Event::MSGS_CHANGED, 0, 0); + } + } + + /// Check if an e-mail address belongs to a known and unblocked contact. + /// Known and unblocked contacts will be returned by `dc_get_contacts()`. + /// + /// To validate an e-mail address independently of the contact database + /// use `dc_may_be_valid_addr()`. + pub fn lookup_id_by_addr(context: &Context, addr: impl AsRef) -> u32 { + if addr.as_ref().is_empty() { + return 0; + } + + let addr_normalized = addr_normalize(addr.as_ref()); + let addr_self = context + .get_config(Config::ConfiguredAddr) + .unwrap_or_default(); + + if addr_normalized == addr_self { + return 1; + } + + context.sql.query_row_col( + context, + "SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;", + params![ + addr_normalized, + DC_CONTACT_ID_LAST_SPECIAL as i32, + DC_ORIGIN_MIN_CONTACT_LIST, + ], + 0 + ).unwrap_or_default() + } + + /// Lookup a contact and create it if it does not exist yet. + /// + /// Returns the contact_id and a `Modifier` value indicating if a modification occured. + pub fn add_or_lookup( + context: &Context, + name: impl AsRef, + addr: impl AsRef, + origin: Origin, + ) -> Result<(u32, Modifier)> { + let mut sth_modified = Modifier::None; + + ensure!( + !addr.as_ref().is_empty(), + "Can not add_or_lookup empty address" + ); + ensure!(origin != Origin::Unknown, "Missing valid origin"); + + let addr = addr_normalize(addr.as_ref()); + let addr_self = context + .get_config(Config::ConfiguredAddr) + .unwrap_or_default(); + + if addr == addr_self { + return Ok((1, sth_modified)); + } + + if !may_be_valid_addr(&addr) { + warn!( + context, + 0, + "Bad address \"{}\" for contact \"{}\".", + addr, + if !name.as_ref().is_empty() { + name.as_ref() + } else { + "" + }, + ); + bail!("Bad address supplied"); + } + + let mut update_addr = false; + let mut update_name = false; + let mut update_authname = false; + let mut row_id = 0; + + if let Ok((id, row_name, row_addr, row_origin, row_authname)) = context.sql.query_row( + "SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;", + params![addr], + |row| { + let row_id = row.get(0)?; + let row_name: String = row.get(1)?; + let row_addr: String = row.get(2)?; + let row_origin = row.get(3)?; + let row_authname: String = row.get(4)?; + + if !name.as_ref().is_empty() && !row_name.is_empty() { + if origin >= row_origin && name.as_ref() != row_name { + update_name = true; + } + } else { + update_name = true; + } + if origin == Origin::IncomingUnknownFrom && name.as_ref() != row_authname { + update_authname = true; + } + Ok((row_id, row_name, row_addr, row_origin, row_authname)) + }, + ) { + row_id = id; + if origin as i32 >= row_origin as i32 && addr != row_addr { + update_addr = true; + } + if update_name || update_authname || update_addr || origin > row_origin { + sql::execute( + context, + &context.sql, + "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;", + params![ + if update_name { + name.as_ref() + } else { + &row_name + }, + if update_addr { addr } else { &row_addr }, + if origin > row_origin { + origin + } else { + row_origin + }, + if update_authname { + name.as_ref() + } else { + &row_authname + }, + row_id + ], + ) + .ok(); + + if update_name { + sql::execute( + context, + &context.sql, + "UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);", + params![name.as_ref(), 100, row_id] + ).ok(); + } + sth_modified = Modifier::Modified; + } + } else { + if sql::execute( + context, + &context.sql, + "INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);", + params![name.as_ref(), addr, origin,], + ) + .is_ok() + { + row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr); + sth_modified = Modifier::Created; + } else { + error!(context, 0, "Cannot add contact."); + } + } + + Ok((row_id, sth_modified)) + } + + /// Add a number of contacts. + /// + /// Typically used to add the whole address book from the OS. As names here are typically not + /// well formatted, we call `normalize()` for each name given. + /// + /// No email-address is added twice. + /// Trying to add email-addresses that are already in the contact list, + /// results in updating the name unless the name was changed manually by the user. + /// If any email-address or any name is really updated, + /// the event `DC_EVENT_CONTACTS_CHANGED` is sent. + /// + /// To add a single contact entered by the user, you should prefer `Contact::create`, + /// however, for adding a bunch of addresses, this function is _much_ faster. + /// + /// The `adr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`. + /// + /// Returns the number of modified contacts. + pub fn add_address_book(context: &Context, adr_book: impl AsRef) -> Result { + let mut modify_cnt = 0; + + for chunk in &adr_book.as_ref().lines().chunks(2) { + let chunk = chunk.collect::>(); + if chunk.len() < 2 { + break; + } + let name = chunk[0]; + let addr = chunk[1]; + let name = normalize_name(name); + let (_, modified) = Contact::add_or_lookup(context, name, addr, Origin::AdressBook)?; + if modified != Modifier::None { + modify_cnt += 1 + } + } + if modify_cnt > 0 { + context.call_cb(Event::CONTACTS_CHANGED, 0 as uintptr_t, 0 as uintptr_t); + } + + Ok(modify_cnt) + } + + /// Returns known and unblocked contacts. + /// + /// To get information about a single contact, see dc_get_contact(). + /// + /// `listflags` is a combination of flags: + /// - if the flag DC_GCL_ADD_SELF is set, SELF is added to the list unless filtered by other parameters + /// - if the flag DC_GCL_VERIFIED_ONLY is set, only verified contacts are returned. + /// if DC_GCL_VERIFIED_ONLY is not set, verified and unverified contacts are returned. + /// `query` is a string to filter the list. + pub fn get_all( + context: &Context, + listflags: u32, + query: Option>, + ) -> Result<*mut dc_array_t> { + let self_addr = context + .get_config(Config::ConfiguredAddr) + .unwrap_or_default(); + + let mut add_self = false; + let mut ret = dc_array_t::new(100); + + if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || query.is_some() { + let s3str_like_cmd = format!( + "%{}%", + query + .as_ref() + .map(|s| s.as_ref().to_string()) + .unwrap_or_default() + ); + context.sql.query_map( + "SELECT c.id FROM contacts c \ + LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ + WHERE c.addr!=?1 \ + AND c.id>?2 \ + AND c.origin>=?3 \ + AND c.blocked=0 \ + AND (c.name LIKE ?4 OR c.addr LIKE ?5) \ + AND (1=?6 OR LENGTH(ps.verified_key_fingerprint)!=0) \ + ORDER BY LOWER(c.name||c.addr),c.id;", + params![ + self_addr, + DC_CONTACT_ID_LAST_SPECIAL as i32, + 0x100, + &s3str_like_cmd, + &s3str_like_cmd, + if 0 != listflags & 0x1 { 0 } else { 1 }, + ], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + ret.add_id(id? as u32); + } + Ok(()) + }, + )?; + + let self_name = context.get_config(Config::Displayname).unwrap_or_default(); + let self_name2 = context.stock_str(StockMessage::SelfMsg); + + if let Some(query) = query { + if self_addr.contains(query.as_ref()) + || self_name.contains(query.as_ref()) + || self_name2.contains(query.as_ref()) + { + add_self = true; + } + } else { + add_self = true; + } + } else { + add_self = true; + + context.sql.query_map( + "SELECT id FROM contacts WHERE addr!=?1 AND id>?2 AND origin>=?3 AND blocked=0 ORDER BY LOWER(name||addr),id;", + params![self_addr, DC_CONTACT_ID_LAST_SPECIAL as i32, 0x100], + |row| row.get::<_, i32>(0), + |ids| { + for id in ids { + ret.add_id(id? as u32); + } + Ok(()) + } + )?; + } + + if 0 != listflags & DC_GCL_ADD_SELF as u32 && add_self { + ret.add_id(DC_CONTACT_ID_SELF as u32); + } + + Ok(ret.into_raw()) + } + + pub fn get_blocked_cnt(context: &Context) -> usize { + context + .sql + .query_row_col::<_, isize>( + context, + "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0", + params![DC_CONTACT_ID_LAST_SPECIAL as i32], + 0, + ) + .unwrap_or_default() as usize + } + + /// Get blocked contacts. + pub fn get_all_blocked(context: &Context) -> *mut dc_array_t { + context + .sql + .query_map( + "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", + params![DC_CONTACT_ID_LAST_SPECIAL as i32], + |row| row.get::<_, i32>(0), + |ids| { + let mut ret = dc_array_t::new(100); + + for id in ids { + ret.add_id(id? as u32); + } + + Ok(ret.into_raw()) + }, + ) + .unwrap_or_else(|_| std::ptr::null_mut()) + } + + pub fn get_encrinfo(context: &Context, contact_id: u32) -> String { + let mut ret = String::new(); + + if let Ok(contact) = Contact::load_from_db(context, contact_id) { + let peerstate = Peerstate::from_addr(context, &context.sql, &contact.addr); + let loginparam = dc_loginparam_read(context, &context.sql, "configured_"); + + let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); + + if peerstate.is_some() && peerstate.as_ref().and_then(|p| p.peek_key(0)).is_some() { + let peerstate = peerstate.as_ref().unwrap(); + let p = + context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual { + StockMessage::E2ePreferred + } else { + StockMessage::E2eAvailable + }); + ret += &p; + if self_key.is_none() { + unsafe { dc_ensure_secret_key_exists(context) }; + self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); + } + let p = context.stock_str(StockMessage::FingerPrints); + ret += &format!(" {}:", p); + + let fingerprint_self = self_key + .map(|k| k.formatted_fingerprint()) + .unwrap_or_default(); + let fingerprint_other_verified = peerstate + .peek_key(2) + .map(|k| k.formatted_fingerprint()) + .unwrap_or_default(); + let fingerprint_other_unverified = peerstate + .peek_key(0) + .map(|k| k.formatted_fingerprint()) + .unwrap_or_default(); + if peerstate.addr.is_some() && &loginparam.addr < peerstate.addr.as_ref().unwrap() { + cat_fingerprint(&mut ret, &loginparam.addr, &fingerprint_self, ""); + cat_fingerprint( + &mut ret, + peerstate.addr.as_ref().unwrap(), + &fingerprint_other_verified, + &fingerprint_other_unverified, + ); + } else { + cat_fingerprint( + &mut ret, + peerstate.addr.as_ref().unwrap(), + &fingerprint_other_verified, + &fingerprint_other_unverified, + ); + cat_fingerprint(&mut ret, &loginparam.addr, &fingerprint_self, ""); + } + } else if 0 == loginparam.server_flags & DC_LP_IMAP_SOCKET_PLAIN as i32 + && 0 == loginparam.server_flags & DC_LP_SMTP_SOCKET_PLAIN as i32 + { + ret += &context.stock_str(StockMessage::EncrTransp); + } else { + ret += &context.stock_str(StockMessage::EncrNone); + } + } + + ret + } + + /// Delete a contact. The contact is deleted from the local device. It may happen that this is not + /// possible as the contact is in use. In this case, the contact can be blocked. + /// + /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event. + pub fn delete(context: &Context, contact_id: u32) -> Result<()> { + ensure!( + contact_id > DC_CONTACT_ID_LAST_SPECIAL as u32, + "Can not delete special contact" + ); + + let count_contacts: i32 = context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;", + params![contact_id as i32], + 0, + ) + .unwrap_or_default(); + + let count_msgs: i32 = if count_contacts > 0 { + context + .sql + .query_row_col( + context, + "SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;", + params![contact_id as i32, contact_id as i32], + 0, + ) + .unwrap_or_default() + } else { + 0 + }; + + if count_msgs == 0 { + match sql::execute( + context, + &context.sql, + "DELETE FROM contacts WHERE id=?;", + params![contact_id as i32], + ) { + Ok(_) => { + context.call_cb(Event::CONTACTS_CHANGED, 0, 0); + return Ok(()); + } + Err(err) => { + error!(context, 0, "delete_contact {} failed ({})", contact_id, err); + return Err(err); + } + } + } + + info!( + context, + 0, "could not delete contact {}, there are {} messages with it", contact_id, count_msgs + ); + bail!("Could not delete contact with messages in it"); + } + + /// Get a single contact object. For a list, see eg. dc_get_contacts(). + /// + /// For contact DC_CONTACT_ID_SELF (1), the function returns sth. + /// like "Me" in the selected language and the email address + /// defined by dc_set_config(). + pub fn get_by_id(context: &Context, contact_id: u32) -> Result { + Contact::load_from_db(context, contact_id) + } + + /// Get the ID of the contact. + pub fn get_id(&self) -> u32 { + self.id + } + + /// Get email address. The email address is always set for a contact. + pub fn get_addr(&self) -> &str { + &self.addr + } + + pub fn get_authname(&self) -> &str { + &self.authname + } + + /// Get the contact name. This is the name as defined by the contact himself or + /// modified by the user. May be an empty string. + /// + /// This name is typically used in a form where the user can edit the name of a contact. + /// To get a fine name to display in lists etc., use `Contact::get_display_name` or `Contact::get_name_n_addr`. + pub fn get_name(&self) -> &str { + &self.name + } + + /// Get display name. This is the name as defined by the contact himself, + /// modified by the user or, if both are unset, the email address. + /// + /// This name is typically used in lists. + /// To get the name editable in a formular, use `Contact::get_name`. + pub fn get_display_name(&self) -> &str { + if !self.name.is_empty() { + return &self.name; + } + &self.addr + } + + /// Get a summary of name and address. + /// + /// The returned string is either "Name (email@domain.com)" or just + /// "email@domain.com" if the name is unset. + /// + /// The summary is typically used when asking the user something about the contact. + /// The attached email address makes the question unique, eg. "Chat with Alan Miller (am@uniquedomain.com)?" + pub fn get_name_n_addr(&self) -> String { + if !self.name.is_empty() { + return format!("{} ({})", self.name, self.addr); + } + (&self.addr).into() + } + + /// Get the part of the name before the first space. In most languages, this seems to be + /// the prename. If there is no space, the full display name is returned. + /// If the display name is not set, the e-mail address is returned. + pub fn get_first_name(&self) -> &str { + if !self.name.is_empty() { + return get_first_name(&self.name); + } + &self.addr + } + + /// Get the contact's profile image. + /// This is the image set by each remote user on their own + /// using dc_set_config(context, "selfavatar", image). + pub fn get_profile_image(&self) -> Option { + if self.id == DC_CONTACT_ID_SELF as u32 { + return self.context.get_config(Config::Selfavatar); + } + // TODO: else get image_abs from contact param + None + } + + /// Get a color for the contact. + /// The color is calculated from the contact's email address + /// and can be used for an fallback avatar with white initials + /// as well as for headlines in bubbles of group chats. + pub fn get_color(&self) -> u32 { + dc_str_to_color_safe(&self.addr) + } + + /// Check if a contact was verified. E.g. by a secure-join QR code scan + /// and if the key has not changed since this verification. + /// + /// The UI may draw a checkbox or something like that beside verified contacts. + /// + pub fn is_verified(&self) -> VerifiedStatus { + self.is_verified_ex(None) + } + + /// Same as `Contact::is_verified` but allows speeding up things + /// by adding the peerstate belonging to the contact. + /// If you do not have the peerstate available, it is loaded automatically. + pub fn is_verified_ex(&self, peerstate: Option<&Peerstate<'a>>) -> VerifiedStatus { + // We're always sort of secured-verified as we could verify the key on this device any time with the key + // on this device + if self.id == DC_CONTACT_ID_SELF as u32 { + return VerifiedStatus::BidirectVerified; + } + + if let Some(peerstate) = peerstate { + if peerstate.verified_key().is_some() { + return VerifiedStatus::BidirectVerified; + } + } + + let peerstate = Peerstate::from_addr(self.context, &self.context.sql, &self.addr); + if let Some(ps) = peerstate { + if ps.verified_key().is_some() { + return VerifiedStatus::BidirectVerified; + } + } + + VerifiedStatus::Unverified + } + + pub fn addr_equals_contact(context: &Context, addr: impl AsRef, contact_id: u32) -> bool { + if addr.as_ref().is_empty() { + return false; + } + + if let Ok(contact) = Contact::load_from_db(context, contact_id) { + if !contact.addr.is_empty() { + let normalized_addr = addr_normalize(addr.as_ref()); + if &contact.addr == &normalized_addr { + return true; + } + } + } + + false + } + + pub fn get_real_cnt(context: &Context) -> usize { + if !context.sql.is_open() { + return 0; + } + + context + .sql + .query_row_col::<_, isize>( + context, + "SELECT COUNT(*) FROM contacts WHERE id>?;", + params![DC_CONTACT_ID_LAST_SPECIAL as i32], + 0, + ) + .unwrap_or_default() as usize + } + + pub fn get_origin_by_id(context: &Context, contact_id: u32, ret_blocked: &mut i32) -> Origin { + let mut ret = Origin::Unknown; + *ret_blocked = 0; + + if let Ok(contact) = Contact::load_from_db(context, contact_id) { + /* we could optimize this by loading only the needed fields */ + if contact.blocked { + *ret_blocked = 1; + } else { + ret = contact.origin; + } + } + + ret + } + + pub fn real_exists_by_id(context: &Context, contact_id: u32) -> bool { + if !context.sql.is_open() || contact_id <= 9 { + return false; + } + + context + .sql + .exists( + "SELECT id FROM contacts WHERE id=?;", + params![contact_id as i32], + ) + .unwrap_or_default() + } + + pub fn scaleup_origin_by_id(context: &Context, contact_id: u32, origin: Origin) -> bool { + context + .sql + .execute( + "UPDATE contacts SET origin=? WHERE id=? AND origin(full_name: &'a str) -> &'a str { + full_name.splitn(2, ' ').next().unwrap_or_default() +} + +/// Returns false if addr is an invalid address, otherwise true. +pub fn may_be_valid_addr(addr: &str) -> bool { + if addr.is_empty() { + return false; + } + + let at = addr.find('@').unwrap_or_default(); + if at < 1 { + return false; + } + let dot = addr.find('.').unwrap_or_default(); + if dot < 1 || dot > addr.len() - 3 || dot < at + 2 { + return false; + } + + true +} + +pub fn addr_normalize(addr: &str) -> &str { + let norm = addr.trim(); + + if norm.starts_with("mailto:") { + return &norm[7..]; + } + + norm +} + +fn set_block_contact(context: &Context, contact_id: u32, new_blocking: bool) { + if contact_id <= 9 { + return; + } + + if let Ok(contact) = Contact::load_from_db(context, contact_id) { + if contact.blocked != new_blocking { + if sql::execute( + context, + &context.sql, + "UPDATE contacts SET blocked=? WHERE id=?;", + params![new_blocking as i32, contact_id as i32], + ) + .is_ok() + { + // also (un)block all chats with _only_ this contact - we do not delete them to allow a + // non-destructive blocking->unblocking. + // (Maybe, beside normal chats (type=100) we should also block group chats with only this user. + // However, I'm not sure about this point; it may be confusing if the user wants to add other people; + // this would result in recreating the same group...) + if sql::execute( + context, + &context.sql, + "UPDATE chats SET blocked=? WHERE type=? AND id IN (SELECT chat_id FROM chats_contacts WHERE contact_id=?);", + params![new_blocking, 100, contact_id as i32], + ).is_ok() { + Contact::mark_noticed(context, contact_id); + context.call_cb( + Event::CONTACTS_CHANGED, + 0, + 0, + ); + } + } + } + } +} + +/// Normalize a name. +/// +/// - Remove quotes (come from some bad MUA implementations) +/// - Convert names as "Petersen, Björn" to "Björn Petersen" +/// - Trims the resulting string +/// +/// Typically, this function is not needed as it is called implicitly by `Contact::add_address_book`. +pub fn normalize_name(full_name: impl AsRef) -> String { + let mut full_name = full_name.as_ref().trim(); + if full_name.is_empty() { + return full_name.into(); + } + + let len = full_name.len(); + if len > 0 { + let firstchar = full_name.as_bytes()[0]; + let lastchar = full_name.as_bytes()[len - 1]; + if firstchar == '\'' as u8 && lastchar == '\'' as u8 + || firstchar == '\"' as u8 && lastchar == '\"' as u8 + || firstchar == '<' as u8 && lastchar == '>' as u8 + { + full_name = &full_name[1..len - 1]; + } + } + + if let Some(p1) = full_name.find(',') { + let (last_name, first_name) = full_name.split_at(p1); + + let last_name = last_name.trim(); + let first_name = (&first_name[1..]).trim(); + + return format!("{} {}", first_name, last_name); + } + + full_name.trim().into() +} + +fn cat_fingerprint( + ret: &mut String, + addr: impl AsRef, + fingerprint_verified: impl AsRef, + fingerprint_unverified: impl AsRef, +) { + *ret += &format!( + "\n\n{}:\n{}", + addr.as_ref(), + if !fingerprint_verified.as_ref().is_empty() { + fingerprint_verified.as_ref() + } else { + fingerprint_unverified.as_ref() + }, + ); + if !fingerprint_verified.as_ref().is_empty() + && !fingerprint_unverified.as_ref().is_empty() + && fingerprint_verified.as_ref() != fingerprint_unverified.as_ref() + { + *ret += &format!( + "\n\n{} (alternative):\n{}", + addr.as_ref(), + fingerprint_unverified.as_ref() + ); + } +} + +pub fn addr_cmp(addr1: impl AsRef, addr2: impl AsRef) -> bool { + let norm1 = addr_normalize(addr1.as_ref()); + let norm2 = addr_normalize(addr2.as_ref()); + + norm1 == norm2 +} + +pub fn addr_equals_self(context: &Context, addr: impl AsRef) -> bool { + if !addr.as_ref().is_empty() { + let normalized_addr = addr_normalize(addr.as_ref()); + if let Some(self_addr) = context.get_config(Config::ConfiguredAddr) { + return normalized_addr == self_addr; + } + } + false +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_may_be_valid_addr() { + assert_eq!(may_be_valid_addr(""), false); + assert_eq!(may_be_valid_addr("user@domain.tld"), true); + assert_eq!(may_be_valid_addr("uuu"), false); + assert_eq!(may_be_valid_addr("dd.tt"), false); + assert_eq!(may_be_valid_addr("tt.dd@uu"), false); + assert_eq!(may_be_valid_addr("u@d"), false); + assert_eq!(may_be_valid_addr("u@d."), false); + assert_eq!(may_be_valid_addr("u@d.t"), false); + assert_eq!(may_be_valid_addr("u@d.tt"), true); + assert_eq!(may_be_valid_addr("u@.tt"), false); + assert_eq!(may_be_valid_addr("@d.tt"), false); + } + + #[test] + fn test_normalize_name() { + assert_eq!(&normalize_name("Doe, John"), "John Doe"); + assert_eq!(&normalize_name(" hello world "), "hello world"); + } + + #[test] + fn test_normalize_addr() { + assert_eq!(addr_normalize("mailto:john@doe.com"), "john@doe.com"); + assert_eq!(addr_normalize(" hello@world.com "), "hello@world.com"); + } + + #[test] + fn test_get_first_name() { + assert_eq!(get_first_name("John Doe"), "John"); + } +} diff --git a/src/context.rs b/src/context.rs index 58d585292..013f13f5d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,9 +1,9 @@ use std::sync::{Arc, Condvar, Mutex, RwLock}; use crate::constants::*; +use crate::contact::*; use crate::dc_array::*; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_jobthread::*; use crate::dc_loginparam::*; @@ -27,15 +27,15 @@ pub struct Context { pub blobdir: Arc>, pub sql: Sql, pub inbox: Arc>, - pub perform_inbox_jobs_needed: Arc>, - pub probe_imap_network: Arc>, + pub perform_inbox_jobs_needed: Arc>, + pub probe_imap_network: Arc>, pub sentbox_thread: Arc>, pub mvbox_thread: Arc>, pub smtp: Arc>, pub smtp_state: Arc<(Mutex, Condvar)>, pub oauth2_critical: Arc>, pub cb: Option, - pub os_name: *mut libc::c_char, + pub os_name: Option, pub cmdline_sel_chat_id: Arc>, pub bob: Arc>, pub last_smeared_timestamp: Arc>, @@ -106,17 +106,17 @@ impl Default for BobStatus { #[derive(Default, Debug)] pub struct SmtpState { pub idle: bool, - pub suspended: i32, - pub doing_jobs: i32, + pub suspended: bool, + pub doing_jobs: bool, pub perform_jobs_needed: i32, - pub probe_network: i32, + pub probe_network: bool, } // create/open/config/information pub fn dc_context_new( cb: Option, userdata: *mut libc::c_void, - os_name: *const libc::c_char, + os_name: Option, ) -> Context { Context { blobdir: Arc::new(RwLock::new(std::ptr::null_mut())), @@ -131,7 +131,7 @@ pub fn dc_context_new( })), userdata, cb, - os_name: unsafe { dc_strdup_keep_null(os_name) }, + os_name: os_name, running_state: Arc::new(RwLock::new(Default::default())), sql: Sql::new(), smtp: Arc::new(Mutex::new(Smtp::new())), @@ -160,8 +160,18 @@ pub fn dc_context_new( cb_receive_imf, ), ))), - probe_imap_network: Arc::new(RwLock::new(0)), - perform_inbox_jobs_needed: Arc::new(RwLock::new(0)), + probe_imap_network: Arc::new(RwLock::new(false)), + perform_inbox_jobs_needed: Arc::new(RwLock::new(false)), + } +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn no_crashes_on_context_deref() { + let mut ctx = dc_context_new(None, std::ptr::null_mut(), Some("Test OS".into())); + unsafe { dc_context_unref(&mut ctx) }; } } @@ -233,13 +243,8 @@ unsafe fn cb_precheck_imf( return rfc724_mid_exists; } -unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *const libc::c_char) { - let v = if value.is_null() { - None - } else { - Some(as_str(value)) - }; - context.sql.set_config(context, as_str(key), v).ok(); +fn cb_set_config(context: &Context, key: &str, value: Option<&str>) { + context.sql.set_config(context, key, value).ok(); } /* * @@ -249,24 +254,14 @@ unsafe fn cb_set_config(context: &Context, key: *const libc::c_char, value: *con * * @private @memberof Context */ -unsafe fn cb_get_config( - context: &Context, - key: *const libc::c_char, - def: *const libc::c_char, -) -> *mut libc::c_char { - let res = context - .sql - .get_config(context, as_str(key)) - .unwrap_or_else(|| to_string(def)); - to_cstring(res) +fn cb_get_config(context: &Context, key: &str) -> Option { + context.sql.get_config(context, key) } pub unsafe fn dc_context_unref(context: &mut Context) { if 0 != dc_is_open(context) { dc_close(context); } - - free(context.os_name as *mut libc::c_void); } pub unsafe fn dc_close(context: &Context) { @@ -310,32 +305,26 @@ pub unsafe fn dc_get_userdata(context: &mut Context) -> *mut libc::c_void { context.userdata as *mut _ } -pub unsafe fn dc_open( - context: &Context, - dbfile: *const libc::c_char, - blobdir: *const libc::c_char, -) -> libc::c_int { - let mut success = 0; +pub unsafe fn dc_open(context: &Context, dbfile: &str, blobdir: Option<&str>) -> bool { + let mut success = false; if 0 != dc_is_open(context) { - return 0; + return false; } - if !dbfile.is_null() { - *context.dbfile.write().unwrap() = dc_strdup(dbfile); - if !blobdir.is_null() && 0 != *blobdir.offset(0isize) as libc::c_int { - let dir = dc_strdup(blobdir); - dc_ensure_no_slash(dir); - *context.blobdir.write().unwrap() = dir; - } else { - let dir = dc_mprintf(b"%s-blobs\x00" as *const u8 as *const libc::c_char, dbfile); - dc_create_folder(context, dir); - *context.blobdir.write().unwrap() = dir; - } - // Create/open sqlite database, this may already use the blobdir - if context.sql.open(context, as_path(dbfile), 0) { - success = 1i32 - } + *context.dbfile.write().unwrap() = dbfile.strdup(); + if blobdir.is_some() && blobdir.unwrap().len() > 0 { + let dir = dc_ensure_no_slash_safe(blobdir.unwrap()).strdup(); + *context.blobdir.write().unwrap() = dir; + } else { + let dir = (dbfile.to_string() + "-blobs").strdup(); + dc_create_folder(context, dir); + *context.blobdir.write().unwrap() = dir; } - if 0 == success { + // Create/open sqlite database, this may already use the blobdir + let dbfile_path = std::path::Path::new(dbfile); + if context.sql.open(context, dbfile_path, 0) { + success = true + } + if !success { dc_close(context); } success @@ -357,7 +346,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { let chats = dc_get_chat_cnt(context) as usize; let real_msgs = dc_get_real_msg_cnt(context) as usize; let deaddrop_msgs = dc_get_deaddrop_msg_cnt(context) as usize; - let contacts = dc_get_real_contact_cnt(context) as usize; + let contacts = Contact::get_real_cnt(context) as usize; let is_configured = context .sql .get_config_int(context, "configured") @@ -494,7 +483,7 @@ pub unsafe fn dc_get_info(context: &Context) -> *mut libc::c_char { fingerprint_str, ); - to_cstring(res) + res.strdup() } pub unsafe fn dc_get_version_str() -> *mut libc::c_char { diff --git a/src/dc_array.rs b/src/dc_array.rs index b2834233a..224ef9cc8 100644 --- a/src/dc_array.rs +++ b/src/dc_array.rs @@ -4,6 +4,7 @@ use crate::types::*; /* * the structure behind dc_array_t */ #[derive(Clone)] +#[allow(non_camel_case_types)] pub enum dc_array_t { Locations(Vec), Uint(Vec), @@ -136,6 +137,20 @@ impl dc_array_t { panic!("Attempt to search for id in array of other type"); } } + + pub fn sort_ids(&mut self) { + if let dc_array_t::Uint(v) = self { + v.sort(); + } else { + panic!("Attempt to sort array of something other than uints"); + } + } +} + +impl From> for dc_array_t { + fn from(array: Vec) -> Self { + dc_array_t::Locations(array) + } } pub unsafe fn dc_array_unref(array: *mut dc_array_t) { @@ -256,7 +271,7 @@ pub unsafe fn dc_array_get_marker(array: *const dc_array_t, index: size_t) -> *m if let dc_array_t::Locations(v) = &*array { if let Some(s) = &v[index].marker { - to_cstring(s) + s.strdup() } else { std::ptr::null_mut() } @@ -339,17 +354,6 @@ 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).len() <= 1 { - return; - } - if let dc_array_t::Uint(v) = &mut *array { - v.sort(); - } else { - panic!("Attempt to sort array of something other than uints"); - } -} - pub unsafe fn dc_array_get_string( array: *const dc_array_t, sep: *const libc::c_char, @@ -371,7 +375,7 @@ pub unsafe fn dc_array_get_string( res + sep + &n.to_string() } }); - to_cstring(res) + res.strdup() } else { panic!("Attempt to get string from array of other type"); } @@ -416,7 +420,7 @@ mod tests { dc_array_add_id(arr, 0 as uint32_t); dc_array_add_id(arr, 5000 as uint32_t); - dc_array_sort_ids(arr); + (*arr).sort_ids(); assert_eq!(dc_array_get_id(arr, 0 as size_t), 0); assert_eq!(dc_array_get_id(arr, 1 as size_t), 7); diff --git a/src/dc_chat.rs b/src/dc_chat.rs index 18d0d6922..5497ad927 100644 --- a/src/dc_chat.rs +++ b/src/dc_chat.rs @@ -2,9 +2,9 @@ use std::ffi::CString; use crate::chatlist::*; use crate::constants::*; +use crate::contact::*; use crate::context::Context; use crate::dc_array::*; -use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_tools::*; @@ -52,7 +52,7 @@ pub unsafe fn dc_create_chat_by_msg_id(context: &Context, msg_id: uint32_t) -> u dc_unblock_chat(context, (*chat).id); send_event = 1i32 } - dc_scaleup_contact_origin(context, (*msg).from_id, 0x800i32); + Contact::scaleup_origin_by_id(context, (*msg).from_id, Origin::CreateChat); } dc_msg_unref(msg); @@ -137,14 +137,8 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { c.id = row.get(0)?; c.type_0 = row.get(1)?; - c.name = { - let raw: String = row.get(2)?; - unsafe { to_cstring(raw) } - }; - c.grpid = { - let raw: String = row.get(3)?; - unsafe { to_cstring(raw) } - }; + c.name = unsafe { row.get::<_, String>(2)?.strdup() }; + c.grpid = unsafe { row.get::<_, String>(3)?.strdup() }; c.param = row.get::<_, String>(4)?.parse().unwrap_or_default(); c.archived = row.get(5)?; @@ -172,24 +166,27 @@ pub fn dc_chat_load_from_db(chat: *mut Chat, chat_id: u32) -> bool { match c.id { 1 => unsafe { free((*chat).name as *mut libc::c_void); - (*chat).name = to_cstring((*chat).context.stock_str(StockMessage::DeadDrop)); + (*chat).name = (*chat).context.stock_str(StockMessage::DeadDrop).strdup(); }, 6 => unsafe { free((*chat).name as *mut libc::c_void); let tempname = (*chat).context.stock_str(StockMessage::ArchivedChats); let cnt = dc_get_archived_cnt((*chat).context); - (*chat).name = to_cstring(format!("{} ({})", tempname, cnt)); + (*chat).name = format!("{} ({})", tempname, cnt).strdup(); }, 5 => unsafe { free((*chat).name as *mut libc::c_void); - (*chat).name = to_cstring((*chat).context.stock_str(StockMessage::StarredMsgs)); + (*chat).name = (*chat) + .context + .stock_str(StockMessage::StarredMsgs) + .strdup(); }, _ => { if unsafe { &(*chat).param }.exists(Param::Selftalk) { unsafe { free((*chat).name as *mut libc::c_void); (*chat).name = - to_cstring((*chat).context.stock_str(StockMessage::SelfMsg)); + (*chat).context.stock_str(StockMessage::SelfMsg).strdup(); } } } @@ -209,7 +206,9 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 dc_unblock_chat(context, chat_id); send_event = 1i32 } - } else if !dc_real_contact_exists(context, contact_id) && contact_id != 1i32 as libc::c_uint { + } else if !Contact::real_exists_by_id(context, contact_id) + && contact_id != DC_CONTACT_ID_SELF as u32 + { warn!( context, 0, "Cannot create chat, contact {} does not exist.", contact_id as libc::c_int, @@ -225,7 +224,7 @@ pub unsafe fn dc_create_chat_by_contact_id(context: &Context, contact_id: uint32 if 0 != chat_id { send_event = 1; } - dc_scaleup_contact_origin(context, contact_id, 0x800i32); + Contact::scaleup_origin_by_id(context, contact_id, Origin::CreateChat); } if 0 != send_event { context.call_cb(Event::MSGS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t); @@ -242,8 +241,6 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( ) { let mut chat_id = 0; let mut chat_blocked = 0; - let contact: *mut dc_contact_t; - let chat_name: *mut libc::c_char; if !ret_chat_id.is_null() { *ret_chat_id = 0; @@ -267,14 +264,8 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( } return; } - contact = dc_contact_new(context); - if dc_contact_load_from_db(contact, &context.sql, contact_id) { - chat_name = - if !(*contact).name.is_null() && 0 != *(*contact).name.offset(0isize) as libc::c_int { - (*contact).name - } else { - (*contact).addr - }; + if let Ok(contact) = Contact::load_from_db(context, contact_id) { + let chat_name = contact.get_display_name(); if sql::execute( context, @@ -282,10 +273,10 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( format!( "INSERT INTO chats (type, name, param, blocked, grpid) VALUES({}, '{}', '{}', {}, '{}')", 100, - as_str(chat_name), - if contact_id == 1 { "K=1" } else { "" }, + chat_name, + if contact_id == DC_CONTACT_ID_SELF as u32 { "K=1" } else { "" }, create_blocked, - as_str((*contact).addr), + contact.get_addr(), ), params![], ).is_ok() { @@ -294,7 +285,7 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( &context.sql, "chats", "grpid", - as_str((*contact).addr), + contact.get_addr(), ); sql::execute( @@ -306,7 +297,6 @@ pub unsafe fn dc_create_or_lookup_nchat_by_contact_id( } } - dc_contact_unref(contact); if !ret_chat_id.is_null() { *ret_chat_id = chat_id } @@ -371,14 +361,14 @@ pub unsafe fn dc_prepare_msg<'a>( return msg_id; } -pub fn msgtype_has_file(msgtype: i32) -> bool { +pub fn msgtype_has_file(msgtype: Viewtype) -> bool { match msgtype { - DC_MSG_IMAGE => true, - DC_MSG_GIF => true, - DC_MSG_AUDIO => true, - DC_MSG_VOICE => true, - DC_MSG_VIDEO => true, - DC_MSG_FILE => true, + Viewtype::Image => true, + Viewtype::Gif => true, + Viewtype::Audio => true, + Viewtype::Voice => true, + Viewtype::Video => true, + Viewtype::File => true, _ => false, } } @@ -392,13 +382,13 @@ unsafe fn prepare_msg_common<'a>( let mut OK_TO_CONTINUE = true; (*msg).id = 0i32 as uint32_t; (*msg).context = context; - if (*msg).type_0 == DC_MSG_TEXT { + if (*msg).type_0 == Viewtype::Text { /* the caller should check if the message text is empty */ } else if msgtype_has_file((*msg).type_0) { let mut pathNfilename = (*msg) .param .get(Param::File) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); if pathNfilename.is_null() { error!( @@ -417,16 +407,16 @@ unsafe fn prepare_msg_common<'a>( OK_TO_CONTINUE = false; } else { (*msg).param.set(Param::File, as_str(pathNfilename)); - if (*msg).type_0 == DC_MSG_FILE || (*msg).type_0 == DC_MSG_IMAGE { + if (*msg).type_0 == Viewtype::File || (*msg).type_0 == Viewtype::Image { /* Correct the type, take care not to correct already very special formats as GIF or VOICE. Typical conversions: - from FILE to AUDIO/VIDEO/IMAGE - from FILE/IMAGE to GIF */ - let mut better_type = 0; + let mut better_type = Viewtype::Unknown; let mut better_mime = std::ptr::null_mut(); dc_msg_guess_msgtype_from_suffix(pathNfilename, &mut better_type, &mut better_mime); - if 0 != better_type && !better_mime.is_null() { + if Viewtype::Unknown != better_type && !better_mime.is_null() { (*msg).type_0 = better_type; (*msg).param.set(Param::MimeType, as_str(better_mime)); } @@ -436,7 +426,7 @@ unsafe fn prepare_msg_common<'a>( dc_msg_guess_msgtype_from_suffix( pathNfilename, - 0 as *mut libc::c_int, + 0 as *mut Viewtype, &mut better_mime, ); @@ -516,16 +506,15 @@ unsafe fn prepare_msg_raw( if from.is_none() { error!(context, 0, "Cannot send message, not configured.",); } else { - let from_c = to_cstring(from.unwrap()); + let from_c = CString::yolo(from.unwrap()); new_rfc724_mid = dc_create_outgoing_rfc724_mid( if (*chat).type_0 == 120 || (*chat).type_0 == 130 { (*chat).grpid } else { 0 as *mut libc::c_char }, - from_c, + from_c.as_ptr(), ); - free(from_c as *mut _); if (*chat).type_0 == DC_CHAT_TYPE_SINGLE { if let Some(id) = context.sql.query_row_col( @@ -729,7 +718,7 @@ unsafe fn prepare_msg_raw( timestamp, (*msg).type_0, (*msg).state, - if !(*msg).text.is_null() { Some(as_str((*msg).text)) } else { None }, + (*msg).text, (*msg).param.to_string(), (*msg).hidden, to_string(new_in_reply_to), @@ -780,18 +769,19 @@ unsafe fn get_parent_mime_headers( || parent_in_reply_to.is_null() || parent_references.is_null()) { + // prefer a last message that isn't from us success = (*chat) .context .sql .query_row( "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE timestamp=(SELECT max(timestamp) \ - FROM msgs WHERE chat_id=? AND from_id!=?);", - params![(*chat).id as i32, 1], + FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT max(timestamp) \ + FROM msgs WHERE chat_id=?1 AND from_id!=?2);", + params![(*chat).id as i32, DC_CONTACT_ID_SELF as i32], |row| { - *parent_rfc724_mid = to_cstring(row.get::<_, String>(0)?); - *parent_in_reply_to = to_cstring(row.get::<_, String>(1)?); - *parent_references = to_cstring(row.get::<_, String>(2)?); + *parent_rfc724_mid = row.get::<_, String>(0)?.strdup(); + *parent_in_reply_to = row.get::<_, String>(1)?.strdup(); + *parent_references = row.get::<_, String>(2)?.strdup(); Ok(()) }, ) @@ -803,13 +793,13 @@ unsafe fn get_parent_mime_headers( .sql .query_row( "SELECT rfc724_mid, mime_in_reply_to, mime_references \ - FROM msgs WHERE timestamp=(SELECT min(timestamp) \ - FROM msgs WHERE chat_id=? AND from_id==?);", - params![(*chat).id as i32, 1], + FROM msgs WHERE chat_id=?1 AND timestamp=(SELECT min(timestamp) \ + FROM msgs WHERE chat_id=?1 AND from_id==?2);", + params![(*chat).id as i32, DC_CONTACT_ID_SELF as i32], |row| { - *parent_rfc724_mid = to_cstring(row.get::<_, String>(0)?); - *parent_in_reply_to = to_cstring(row.get::<_, String>(1)?); - *parent_references = to_cstring(row.get::<_, String>(2)?); + *parent_rfc724_mid = row.get::<_, String>(0)?.strdup(); + *parent_in_reply_to = row.get::<_, String>(1)?.strdup(); + *parent_references = row.get::<_, String>(2)?.strdup(); Ok(()) }, ) @@ -954,7 +944,7 @@ pub unsafe fn dc_send_msg<'a>( pub unsafe fn dc_send_text_msg( context: &Context, chat_id: uint32_t, - text_to_send: *const libc::c_char, + text_to_send: String, ) -> uint32_t { if chat_id <= 9 { warn!( @@ -964,18 +954,8 @@ pub unsafe fn dc_send_text_msg( return 0; } - if text_to_send.is_null() { - warn!(context, 0, "dc_send_text_msg: text_to_send is emtpy"); - return 0; - } - - if let Err(err) = as_str_safe(text_to_send) { - warn!(context, 0, "{}", err); - return 0; - } - - let mut msg = dc_msg_new(context, 10); - (*msg).text = dc_strdup(text_to_send); + let mut msg = dc_msg_new(context, Viewtype::Text); + (*msg).text = Some(text_to_send); let ret = dc_send_msg(context, chat_id, msg); dc_msg_unref(msg); ret @@ -1004,15 +984,13 @@ unsafe fn set_draft_raw(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t } // save new draft if !msg.is_null() { - if (*msg).type_0 == DC_MSG_TEXT { - if (*msg).text.is_null() || *(*msg).text.offset(0isize) as libc::c_int == 0i32 { - OK_TO_CONTINUE = false; - } + if (*msg).type_0 == Viewtype::Text { + OK_TO_CONTINUE = (*msg).text.as_ref().map_or(false, |s| !s.is_empty()); } else if msgtype_has_file((*msg).type_0) { let mut pathNfilename = (*msg) .param .get(Param::File) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); if pathNfilename.is_null() { OK_TO_CONTINUE = false; @@ -1040,11 +1018,7 @@ unsafe fn set_draft_raw(context: &Context, chat_id: uint32_t, msg: *mut dc_msg_t time(), (*msg).type_0, DC_STATE_OUT_DRAFT, - if !(*msg).text.is_null() { - as_str((*msg).text) - } else { - "" - }, + (*msg).text.as_ref().map(String::as_str).unwrap_or(""), (*msg).param.to_string(), 1, ], @@ -1258,20 +1232,20 @@ pub fn dc_marknoticed_all_chats(context: &Context) -> bool { pub fn dc_get_chat_media( context: &Context, chat_id: uint32_t, - msg_type: libc::c_int, - msg_type2: libc::c_int, - msg_type3: libc::c_int, + msg_type: Viewtype, + msg_type2: Viewtype, + msg_type3: Viewtype, ) -> *mut dc_array_t { context.sql.query_map( "SELECT id FROM msgs WHERE chat_id=? AND (type=? OR type=? OR type=?) ORDER BY timestamp, id;", params![ chat_id as i32, msg_type, - if msg_type2 > 0 { + if msg_type2 != Viewtype::Unknown { msg_type2 } else { msg_type - }, if msg_type3 > 0 { + }, if msg_type3 != Viewtype::Unknown { msg_type3 } else { msg_type @@ -1292,9 +1266,9 @@ pub unsafe fn dc_get_next_media( context: &Context, curr_msg_id: uint32_t, dir: libc::c_int, - msg_type: libc::c_int, - msg_type2: libc::c_int, - msg_type3: libc::c_int, + msg_type: Viewtype, + msg_type2: Viewtype, + msg_type3: Viewtype, ) -> uint32_t { let mut ret_msg_id: uint32_t = 0i32 as uint32_t; let msg: *mut dc_msg_t = dc_msg_new_untyped(context); @@ -1306,7 +1280,7 @@ pub unsafe fn dc_get_next_media( list = dc_get_chat_media( context, (*msg).chat_id, - if msg_type > 0i32 { + if msg_type != Viewtype::Unknown { msg_type } else { (*msg).type_0 @@ -1494,7 +1468,7 @@ pub unsafe fn dc_create_group_chat( let draft_txt = CString::new(context.stock_string_repl_str(StockMessage::NewGroupDraft, as_str(chat_name))) .unwrap(); - let grpid = as_str(dc_create_id()); + let grpid = dc_create_id(); if sql::execute( context, &context.sql, @@ -1510,7 +1484,7 @@ pub unsafe fn dc_create_group_chat( chat_id = sql::get_rowid(context, &context.sql, "chats", "grpid", grpid); if chat_id != 0 { if 0 != dc_add_to_chat_contacts_table(context, chat_id, 1) { - let draft_msg = dc_msg_new(context, 10); + let draft_msg = dc_msg_new(context, Viewtype::Text); dc_msg_set_text(draft_msg, draft_txt.as_ptr()); set_draft_raw(context, chat_id, draft_msg); dc_msg_unref(draft_msg); @@ -1561,15 +1535,18 @@ pub unsafe fn dc_add_contact_to_chat_ex( ) -> libc::c_int { let mut OK_TO_CONTINUE = true; let mut success: libc::c_int = 0; - let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); + let contact = Contact::get_by_id(context, contact_id); let chat: *mut Chat = dc_chat_new(context); let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - if !(contact.is_null() || chat_id <= 9 as libc::c_uint) { + if !(contact.is_err() || chat_id <= 9 as libc::c_uint) { dc_reset_gossiped_timestamp(context, chat_id); + let contact = contact.unwrap(); + /*this also makes sure, not contacts are added to special or normal chats*/ if !(0 == real_group_exists(context, chat_id) - || !dc_real_contact_exists(context, contact_id) && contact_id != 1 as libc::c_uint + || !Contact::real_exists_by_id(context, contact_id) + && contact_id != DC_CONTACT_ID_SELF as u32 || !dc_chat_load_from_db(chat, chat_id)) { if !(dc_is_contact_in_chat(context, chat_id, 1 as uint32_t) == 1) { @@ -1591,7 +1568,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( .sql .get_config(context, "configured_addr") .unwrap_or_default(); - if as_str((*contact).addr) != &self_addr { + if contact.get_addr() != &self_addr { // ourself is added using DC_CONTACT_ID_SELF, do not add it explicitly. // if SELF is not in the group, members cannot be added at all. @@ -1603,7 +1580,7 @@ pub unsafe fn dc_add_contact_to_chat_ex( } else { // else continue and send status mail if (*chat).type_0 == 130 { - if dc_contact_is_verified(contact) != 2 { + if contact.is_verified() != VerifiedStatus::BidirectVerified { error!( context, 0, "Only bidirectional verified contacts can be added to verified groups." @@ -1619,17 +1596,15 @@ pub unsafe fn dc_add_contact_to_chat_ex( } if OK_TO_CONTINUE { if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = to_cstring(context.stock_system_msg( + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( StockMessage::MsgAddMember, - as_str((*contact).addr), + contact.get_addr(), "", DC_CONTACT_ID_SELF as uint32_t, )); (*msg).param.set_int(Param::Cmd, 4); - if !(*contact).addr.is_null() { - (*msg).param.set(Param::Arg, as_str((*contact).addr)); - } + (*msg).param.set(Param::Arg, contact.get_addr()); (*msg).param.set_int(Param::Arg2, flags); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( @@ -1646,7 +1621,6 @@ pub unsafe fn dc_add_contact_to_chat_ex( } } dc_chat_unref(chat); - dc_contact_unref(contact); dc_msg_unref(msg); success @@ -1708,13 +1682,12 @@ pub unsafe fn dc_remove_contact_from_chat( chat_id: u32, contact_id: u32, ) -> libc::c_int { - let mut success: libc::c_int = 0; - let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); + let mut success = 0; let chat: *mut Chat = dc_chat_new(context); let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); if !(chat_id <= 9 as libc::c_uint - || contact_id <= 9 as libc::c_uint && contact_id != 1 as libc::c_uint) + || contact_id <= 9 as libc::c_uint && contact_id != DC_CONTACT_ID_SELF as u32) { /* we do not check if "contact_id" exists but just delete all records with the id from chats_contacts */ /* this allows to delete pending references to deleted contacts. Of course, this should _not_ happen. */ @@ -1728,29 +1701,27 @@ pub unsafe fn dc_remove_contact_from_chat( ); } else { /* we should respect this - whatever we send to the group, it gets discarded anyway! */ - if !contact.is_null() { + if let Ok(contact) = Contact::get_by_id(context, contact_id) { if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = DC_MSG_TEXT; - if (*contact).id == 1 as libc::c_uint { + (*msg).type_0 = Viewtype::Text; + if contact.id == DC_CONTACT_ID_SELF as u32 { dc_set_group_explicitly_left(context, (*chat).grpid); - (*msg).text = to_cstring(context.stock_system_msg( + (*msg).text = Some(context.stock_system_msg( StockMessage::MsgGroupLeft, "", "", DC_CONTACT_ID_SELF as u32, )); } else { - (*msg).text = to_cstring(context.stock_system_msg( + (*msg).text = Some(context.stock_system_msg( StockMessage::MsgDelMember, - as_str((*contact).addr), + contact.get_addr(), "", DC_CONTACT_ID_SELF as u32, )); } (*msg).param.set_int(Param::Cmd, 5); - if !(*contact).addr.is_null() { - (*msg).param.set(Param::Arg, as_str((*contact).addr)); - } + (*msg).param.set(Param::Arg, contact.get_addr()); (*msg).id = dc_send_msg(context, chat_id, msg); context.call_cb( Event::MSGS_CHANGED, @@ -1775,7 +1746,6 @@ pub unsafe fn dc_remove_contact_from_chat( } dc_chat_unref(chat); - dc_contact_unref(contact); dc_msg_unref(msg); success @@ -1845,8 +1815,8 @@ pub unsafe fn dc_set_chat_name( .is_ok() { if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { - (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = to_cstring(context.stock_system_msg( + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( StockMessage::MsgGrpName, as_str((*chat).name), as_str(new_name), @@ -1917,8 +1887,8 @@ pub unsafe fn dc_set_chat_profile_image( if (*chat).param.get_int(Param::Unpromoted).unwrap_or_default() == 0 { (*msg).param.set_int(Param::Cmd, 3); (*msg).param.set(Param::Arg, as_str(new_image_rel)); - (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = to_cstring(context.stock_system_msg( + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(context.stock_system_msg( if !new_image_rel.is_null() { StockMessage::MsgGrpImgChanged } else { @@ -1966,8 +1936,7 @@ pub unsafe fn dc_forward_msgs( let msg = dc_msg_new_untyped(context); let chat = dc_chat_new(context); - let contact = dc_contact_new(context); - let created_db_entries = carray_new(16); + let mut created_db_entries = Vec::new(); let mut curr_timestamp: i64; dc_unarchive_chat(context, chat_id); @@ -2000,7 +1969,7 @@ pub unsafe fn dc_forward_msgs( break; } let original_param = (*msg).param.clone(); - if (*msg).from_id != 1 { + if (*msg).from_id != DC_CONTACT_ID_SELF as u32 { (*msg).param.set_int(Param::Forwarded, 1); } (*msg).param.remove(Param::GuranteeE2ee); @@ -2034,33 +2003,18 @@ pub unsafe fn dc_forward_msgs( new_msg_id = prepare_msg_raw(context, chat, msg, fresh10); dc_job_send_msg(context, new_msg_id); } - carray_add( - created_db_entries, - chat_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); - carray_add( - created_db_entries, - new_msg_id as uintptr_t as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); + created_db_entries.push(chat_id); + created_db_entries.push(new_msg_id); } } - if !created_db_entries.is_null() { - let mut i = 0u32; - let icnt = carray_count(created_db_entries); - while i < icnt { - context.call_cb( - Event::MSGS_CHANGED, - carray_get(created_db_entries, i) as uintptr_t, - carray_get(created_db_entries, i.wrapping_add(1)) as uintptr_t, - ); - i = i.wrapping_add(2); - } - carray_free(created_db_entries); + for i in (0..created_db_entries.len()).step_by(2) { + context.call_cb( + Event::MSGS_CHANGED, + created_db_entries[i] as uintptr_t, + created_db_entries[i + 1] as uintptr_t, + ); } - dc_contact_unref(contact); dc_msg_unref(msg); dc_chat_unref(chat); } @@ -2094,7 +2048,10 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { let mut ret: *mut libc::c_char = std::ptr::null_mut(); if (*chat).type_0 == 100 && (*chat).param.exists(Param::Selftalk) { - ret = to_cstring((*chat).context.stock_str(StockMessage::SelfTalkSubTitle)); + ret = (*chat) + .context + .stock_str(StockMessage::SelfTalkSubTitle) + .strdup(); } else if (*chat).type_0 == 100 { let ret_raw: String = (*chat) .context @@ -2108,17 +2065,16 @@ pub unsafe fn dc_chat_get_subtitle(chat: *const Chat) -> *mut libc::c_char { 0, ) .unwrap_or_else(|| "Err".into()); - ret = to_cstring(ret_raw); + ret = ret_raw.strdup(); } else if (*chat).type_0 == 120 || (*chat).type_0 == 130 { if (*chat).id == 1 { - ret = to_cstring((*chat).context.stock_str(StockMessage::DeadDrop)); + ret = (*chat).context.stock_str(StockMessage::DeadDrop).strdup(); } else { let cnt = dc_get_chat_contact_cnt((*chat).context, (*chat).id); - ret = to_cstring( - (*chat) - .context - .stock_string_repl_int(StockMessage::Member, cnt), - ); + ret = (*chat) + .context + .stock_string_repl_int(StockMessage::Member, cnt) + .strdup(); } } return if !ret.is_null() { @@ -2144,23 +2100,29 @@ pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char let mut image_rel: *mut libc::c_char = 0 as *mut libc::c_char; let mut image_abs: *mut libc::c_char = 0 as *mut libc::c_char; let mut contacts: *mut dc_array_t = 0 as *mut dc_array_t; - let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t; + if !(chat.is_null() || (*chat).magic != 0xc4a7c4a7u32) { - image_rel = to_cstring((*chat).param.get(Param::ProfileImage).unwrap_or_default()); + image_rel = (*chat) + .param + .get(Param::ProfileImage) + .unwrap_or_default() + .strdup(); if !image_rel.is_null() && 0 != *image_rel.offset(0isize) as libc::c_int { image_abs = dc_get_abs_path((*chat).context, image_rel) } else if (*chat).type_0 == 100i32 { contacts = dc_get_chat_contacts((*chat).context, (*chat).id); if !(*contacts).is_empty() { - contact = dc_get_contact((*chat).context, (*contacts).get_id(0)); - image_abs = dc_contact_get_profile_image(contact) + if let Ok(contact) = Contact::get_by_id((*chat).context, (*contacts).get_id(0)) { + if let Some(img) = contact.get_profile_image() { + image_abs = img.strdup(); + } + } } } } free(image_rel as *mut libc::c_void); dc_array_unref(contacts); - dc_contact_unref(contact); image_abs } @@ -2168,13 +2130,14 @@ pub unsafe fn dc_chat_get_profile_image(chat: *const Chat) -> *mut libc::c_char pub unsafe fn dc_chat_get_color(chat: *const Chat) -> uint32_t { let mut color: uint32_t = 0i32 as uint32_t; let mut contacts: *mut dc_array_t = 0 as *mut dc_array_t; - let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t; + if !(chat.is_null() || (*chat).magic != 0xc4a7c4a7u32) { if (*chat).type_0 == 100i32 { contacts = dc_get_chat_contacts((*chat).context, (*chat).id); if !(*contacts).is_empty() { - contact = dc_get_contact((*chat).context, (*contacts).get_id(0)); - color = dc_str_to_color((*contact).addr) as uint32_t + if let Ok(contact) = Contact::get_by_id((*chat).context, (*contacts).get_id(0)) { + color = contact.get_color(); + } } } else { color = dc_str_to_color((*chat).name) as uint32_t @@ -2182,7 +2145,6 @@ pub unsafe fn dc_chat_get_color(chat: *const Chat) -> uint32_t { } dc_array_unref(contacts); - dc_contact_unref(contact); color } @@ -2287,7 +2249,7 @@ pub fn dc_add_device_msg(context: &Context, chat_id: uint32_t, text: *const libc 2, 2, unsafe {dc_create_smeared_timestamp(context)}, - DC_MSG_TEXT, + Viewtype::Text, DC_STATE_IN_NOTICED, as_str(text), as_str(rfc724_mid), diff --git a/src/dc_configure.rs b/src/dc_configure.rs index a1397742a..73f449e7b 100644 --- a/src/dc_configure.rs +++ b/src/dc_configure.rs @@ -1102,14 +1102,14 @@ unsafe fn moz_autoconfigure( tag_config: 0, }; - let url_c = to_cstring(url); + let url_c = url.strdup(); let xml_raw = read_autoconf_file(context, url_c); free(url_c as *mut libc::c_void); if xml_raw.is_null() { return None; } - moz_ac.in_emaillocalpart = to_cstring(¶m_in.addr); + moz_ac.in_emaillocalpart = param_in.addr.strdup(); let p = strchr(moz_ac.in_emaillocalpart, '@' as i32); if p.is_null() { @@ -1166,7 +1166,7 @@ unsafe fn moz_autoconfigure_text_cb( let mut moz_ac: *mut moz_autoconfigure_t = userdata as *mut moz_autoconfigure_t; let mut val: *mut libc::c_char = dc_strdup(text); dc_trim(val); - let addr = to_cstring(&(*moz_ac).in_0.addr); + let addr = (*moz_ac).in_0.addr.strdup(); dc_str_replace( &mut val, b"%EMAILADDRESS%\x00" as *const u8 as *const libc::c_char, @@ -1306,7 +1306,7 @@ fn read_autoconf_file(context: &Context, url: *const libc::c_char) -> *mut libc: .send() .and_then(|mut res| res.text()) { - Ok(res) => unsafe { to_cstring(res) }, + Ok(res) => unsafe { res.strdup() }, Err(_err) => { info!(context, 0, "Can\'t read file.",); @@ -1322,7 +1322,7 @@ unsafe fn outlk_autodiscover( ) -> Option { let current_block: u64; let mut xml_raw: *mut libc::c_char = 0 as *mut libc::c_char; - let mut url = to_cstring(url__); + let mut url = url__.strdup(); let mut outlk_ad = outlk_autodiscover_t { in_0: param_in, out: dc_loginparam_new(), diff --git a/src/dc_contact.rs b/src/dc_contact.rs deleted file mode 100644 index 1beeb3a81..000000000 --- a/src/dc_contact.rs +++ /dev/null @@ -1,1147 +0,0 @@ -use std::ffi::CString; - -use crate::aheader::EncryptPreference; -use crate::config; -use crate::constants::*; -use crate::context::Context; -use crate::dc_array::*; -use crate::dc_e2ee::*; -use crate::dc_loginparam::*; -use crate::dc_tools::*; -use crate::key::*; -use crate::peerstate::*; -use crate::sql::{self, Sql}; -use crate::stock::StockMessage; -use crate::types::*; -use crate::x::*; - -const DC_GCL_VERIFIED_ONLY: u32 = 0x01; - -#[derive(Copy, Clone)] -#[repr(C)] -pub struct dc_contact_t<'a> { - pub magic: uint32_t, - pub context: &'a Context, - pub id: uint32_t, - pub name: *mut libc::c_char, - pub authname: *mut libc::c_char, - pub addr: *mut libc::c_char, - pub blocked: libc::c_int, - pub origin: libc::c_int, -} - -pub fn dc_marknoticed_contact(context: &Context, contact_id: u32) { - if sql::execute( - context, - &context.sql, - "UPDATE msgs SET state=? WHERE from_id=? AND state=?;", - params![DC_STATE_IN_NOTICED, contact_id as i32, DC_STATE_IN_FRESH], - ) - .is_ok() - { - context.call_cb(Event::MSGS_CHANGED, 0, 0); - } -} - -/// Returns false if addr is an invalid address, otherwise true. -pub unsafe fn dc_may_be_valid_addr(addr: *const libc::c_char) -> bool { - if addr.is_null() { - return false; - } - let at: *const libc::c_char = strchr(addr, '@' as i32); - if at.is_null() || at.wrapping_offset_from(addr) < 1 { - return false; - } - let dot: *const libc::c_char = strchr(at, '.' as i32); - if dot.is_null() - || dot.wrapping_offset_from(at) < 2 - || *dot.offset(1isize) as libc::c_int == 0i32 - || *dot.offset(2isize) as libc::c_int == 0i32 - { - return false; - } - - true -} - -pub unsafe fn dc_lookup_contact_id_by_addr( - context: &Context, - addr: *const libc::c_char, -) -> uint32_t { - if addr.is_null() || *addr.offset(0) as libc::c_int == 0 { - return 0; - } - - let addr_normalized_c = dc_addr_normalize(addr); - let addr_normalized = as_str(addr_normalized_c); - let addr_self = context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(); - - let contact_id = if addr_normalized == addr_self { - 1 - } else { - context.sql.query_row_col( - context, - "SELECT id FROM contacts WHERE addr=?1 COLLATE NOCASE AND id>?2 AND origin>=?3 AND blocked=0;", - params![addr_normalized, 9, 0x100], - 0 - ).unwrap_or_default() - }; - free(addr_normalized_c as *mut libc::c_void); - - contact_id -} - -pub unsafe fn dc_addr_normalize(addr: *const libc::c_char) -> *mut libc::c_char { - let mut addr_normalized: *mut libc::c_char = dc_strdup(addr); - dc_trim(addr_normalized); - if strncmp( - addr_normalized, - b"mailto:\x00" as *const u8 as *const libc::c_char, - 7, - ) == 0i32 - { - let old: *mut libc::c_char = addr_normalized; - addr_normalized = dc_strdup(&mut *old.offset(7isize)); - free(old as *mut libc::c_void); - dc_trim(addr_normalized); - } - - addr_normalized -} - -pub fn dc_addr_normalize_safe(addr: &str) -> &str { - let norm = addr.trim(); - - if norm.starts_with("mailto:") { - return &norm[7..]; - } - - norm -} - -pub unsafe fn dc_create_contact( - context: &Context, - name: *const libc::c_char, - addr: *const libc::c_char, -) -> uint32_t { - let mut contact_id: uint32_t = 0i32 as uint32_t; - let mut sth_modified: libc::c_int = 0i32; - let blocked: bool; - if !(addr.is_null() || *addr.offset(0isize) as libc::c_int == 0i32) { - contact_id = dc_add_or_lookup_contact(context, name, addr, 0x4000000i32, &mut sth_modified); - blocked = dc_is_contact_blocked(context, contact_id); - context.call_cb( - Event::CONTACTS_CHANGED, - (if sth_modified == 2i32 { - contact_id - } else { - 0i32 as libc::c_uint - }) as uintptr_t, - 0i32 as uintptr_t, - ); - if blocked { - dc_block_contact(context, contact_id, 0i32); - } - } - - contact_id -} - -pub unsafe fn dc_block_contact(context: &Context, contact_id: uint32_t, new_blocking: libc::c_int) { - if contact_id <= 9 { - return; - } - - let contact = dc_contact_new(context); - - if dc_contact_load_from_db(contact, &context.sql, contact_id) - && (*contact).blocked != new_blocking - { - if sql::execute( - context, - &context.sql, - "UPDATE contacts SET blocked=? WHERE id=?;", - params![new_blocking, contact_id as i32], - ) - .is_ok() - { - // also (un)block all chats with _only_ this contact - we do not delete them to allow a - // non-destructive blocking->unblocking. - // (Maybe, beside normal chats (type=100) we should also block group chats with only this user. - // However, I'm not sure about this point; it may be confusing if the user wants to add other people; - // this would result in recreating the same group...) - if sql::execute( - context, - &context.sql, - "UPDATE chats SET blocked=? WHERE type=? AND id IN (SELECT chat_id FROM chats_contacts WHERE contact_id=?);", - params![new_blocking, 100, contact_id as i32], - ).is_ok() { - dc_marknoticed_contact(context, contact_id); - context.call_cb( - Event::CONTACTS_CHANGED, - 0, - 0, - ); - } - } - } - - dc_contact_unref(contact); -} - -/** - * @class dc_contact_t - * - * An object representing a single contact in memory. - * The contact object is not updated. - * If you want an update, you have to recreate the object. - * - * The library makes sure - * only to use names _authorized_ by the contact in `To:` or `Cc:`. - * _Given-names _as "Daddy" or "Honey" are not used there. - * For this purpose, internally, two names are tracked - - * authorized-name and given-name. - * By default, these names are equal, - * but functions working with contact names - * (eg. dc_contact_get_name(), dc_contact_get_display_name(), - * dc_contact_get_name_n_addr(), dc_contact_get_first_name(), - * dc_create_contact() or dc_add_address_book()) - * only affect the given-name. - */ -pub unsafe fn dc_contact_new<'a>(context: &'a Context) -> *mut dc_contact_t<'a> { - let mut contact: *mut dc_contact_t; - contact = calloc(1, ::std::mem::size_of::()) as *mut dc_contact_t; - assert!(!contact.is_null()); - - (*contact).magic = 0xc047ac7i32 as uint32_t; - (*contact).context = context; - - contact -} - -pub unsafe fn dc_contact_unref(contact: *mut dc_contact_t) { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return; - } - dc_contact_empty(contact); - (*contact).magic = 0i32 as uint32_t; - free(contact as *mut libc::c_void); -} - -pub unsafe fn dc_contact_empty(mut contact: *mut dc_contact_t) { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return; - } - (*contact).id = 0i32 as uint32_t; - free((*contact).name as *mut libc::c_void); - (*contact).name = 0 as *mut libc::c_char; - free((*contact).authname as *mut libc::c_void); - (*contact).authname = 0 as *mut libc::c_char; - free((*contact).addr as *mut libc::c_void); - (*contact).addr = 0 as *mut libc::c_char; - (*contact).origin = 0i32; - (*contact).blocked = 0i32; -} - -/* From: of incoming messages of unknown sender */ -/* Cc: of incoming messages of unknown sender */ -/* To: of incoming messages of unknown sender */ -/* address scanned but not verified */ -/* Reply-To: of incoming message of known sender */ -/* Cc: of incoming message of known sender */ -/* additional To:'s of incoming message of known sender */ -/* a chat was manually created for this user, but no message yet sent */ -/* message sent by us */ -/* message sent by us */ -/* message sent by us */ -/* internal use */ -/* address is in our address book */ -/* set on Alice's side for contacts like Bob that have scanned the QR code offered by her. Only means the contact has once been established using the "securejoin" procedure in the past, getting the current key verification status requires calling dc_contact_is_verified() ! */ -/* set on Bob's side for contacts scanned and verified from a QR code. Only means the contact has once been established using the "securejoin" procedure in the past, getting the current key verification status requires calling dc_contact_is_verified() ! */ -/* contact added manually by dc_create_contact(), this should be the largets origin as otherwise the user cannot modify the names */ -/* contacts with at least this origin value are shown in the contact list */ -/* contacts with at least this origin value are verified and known not to be spam */ -/* contacts with at least this origin value start a new "normal" chat, defaults to off */ -pub unsafe fn dc_contact_load_from_db( - contact: *mut dc_contact_t, - sql: &Sql, - contact_id: u32, -) -> bool { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return false; - } - - dc_contact_empty(contact); - - if contact_id == 1 as libc::c_uint { - (*contact).id = contact_id; - (*contact).name = to_cstring((*contact).context.stock_str(StockMessage::SelfMsg)); - (*contact).addr = to_cstring( - (*contact) - .context - .sql - .get_config((*contact).context, "configured_addr") - .unwrap_or_default(), - ); - true - } else { - sql.query_row( - "SELECT c.name, c.addr, c.origin, c.blocked, c.authname FROM contacts c WHERE c.id=?;", - params![contact_id as i32], - |row| { - (*contact).id = contact_id; - (*contact).name = to_cstring(row.get::<_, String>(0)?); - (*contact).addr = to_cstring(row.get::<_, String>(1)?); - (*contact).origin = row.get(2)?; - (*contact).blocked = row.get::<_, Option>(3)?.unwrap_or_default(); - (*contact).authname = to_cstring(row.get::<_, String>(4)?); - Ok(()) - } - ).is_ok() - } -} - -pub unsafe fn dc_is_contact_blocked(context: &Context, contact_id: uint32_t) -> bool { - let mut is_blocked = false; - let contact: *mut dc_contact_t = dc_contact_new(context); - if dc_contact_load_from_db(contact, &context.sql, contact_id) { - if 0 != (*contact).blocked { - is_blocked = true - } - } - dc_contact_unref(contact); - - is_blocked -} - -/*can be NULL*/ -pub fn dc_add_or_lookup_contact( - context: &Context, - name: *const libc::c_char, - addr__: *const libc::c_char, - origin: libc::c_int, - mut sth_modified: *mut libc::c_int, -) -> uint32_t { - let mut dummy = 0; - - if sth_modified.is_null() { - sth_modified = &mut dummy; - } - unsafe { *sth_modified = 0 }; - - if addr__.is_null() || origin <= 0 { - return 0; - } - - let addr_c = unsafe { dc_addr_normalize(addr__) }; - let addr = as_str(addr_c); - let addr_self = context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(); - - if addr == addr_self { - return 1; - } - - if !unsafe { dc_may_be_valid_addr(addr_c) } { - warn!( - context, - 0, - "Bad address \"{}\" for contact \"{}\".", - addr, - if !name.is_null() { - as_str(name) - } else { - "" - }, - ); - return 0; - } - - let mut update_addr = false; - let mut update_name = false; - let mut update_authname = false; - let mut row_id = 0; - - if let Ok((id, row_name, row_addr, row_origin, row_authname)) = context.sql.query_row( - "SELECT id, name, addr, origin, authname FROM contacts WHERE addr=? COLLATE NOCASE;", - params![addr], - |row| { - let row_id = row.get(0)?; - let row_name: String = row.get(1)?; - let row_addr: String = row.get(2)?; - let row_origin = row.get(3)?; - let row_authname: String = row.get(4)?; - - if !name.is_null() && 0 != unsafe { *name.offset(0) as libc::c_int } { - if !row_name.is_empty() { - if origin >= row_origin && as_str(name) != row_name { - update_name = true; - } - } - } else { - update_name = true; - } - if origin == 0x10 && !name.is_null() && as_str(name) != row_authname { - update_authname = true; - } - Ok((row_id, row_name, row_addr, row_origin, row_authname)) - }, - ) { - row_id = id; - if origin >= row_origin && addr != row_addr { - update_addr = true; - } - if update_name || update_authname || update_addr || origin > row_origin { - sql::execute( - context, - &context.sql, - "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;", - params![ - if update_name { - to_string(name) - } else { - row_name - }, - if update_addr { addr } else { &row_addr }, - if origin > row_origin { - origin - } else { - row_origin - }, - if update_authname { - to_string(name) - } else { - row_authname - }, - row_id - ], - ) - .ok(); - - if update_name { - sql::execute( - context, - &context.sql, - "UPDATE chats SET name=? WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?);", - params![to_string(name), 100, row_id] - ).ok(); - } - unsafe { *sth_modified = 1 }; - } - } else { - if sql::execute( - context, - &context.sql, - "INSERT INTO contacts (name, addr, origin) VALUES(?, ?, ?);", - params![to_string(name), addr, origin,], - ) - .is_ok() - { - row_id = sql::get_rowid(context, &context.sql, "contacts", "addr", addr); - unsafe { *sth_modified = 2 }; - } else { - error!(context, 0, "Cannot add contact."); - } - } - - unsafe { free(addr_c as *mut libc::c_void) }; - - row_id -} - -#[allow(non_snake_case)] -pub unsafe fn dc_add_address_book(context: &Context, adr_book: *const libc::c_char) -> libc::c_int { - let mut lines: *mut carray = 0 as *mut carray; - let mut i: size_t; - let iCnt: size_t; - let mut sth_modified: libc::c_int = 0i32; - let mut modify_cnt: libc::c_int = 0i32; - if !(adr_book.is_null()) { - lines = dc_split_into_lines(adr_book); - if !lines.is_null() { - iCnt = carray_count(lines) as size_t; - i = 0i32 as size_t; - while i.wrapping_add(1) < iCnt { - let name: *mut libc::c_char = - carray_get(lines, i as libc::c_uint) as *mut libc::c_char; - let addr: *mut libc::c_char = - carray_get(lines, i.wrapping_add(1) as libc::c_uint) as *mut libc::c_char; - dc_normalize_name(name); - dc_add_or_lookup_contact(context, name, addr, 0x80000i32, &mut sth_modified); - if 0 != sth_modified { - modify_cnt += 1 - } - i = (i as libc::c_ulong).wrapping_add(2i32 as libc::c_ulong) as size_t as size_t - } - if 0 != modify_cnt { - context.call_cb( - Event::CONTACTS_CHANGED, - 0i32 as uintptr_t, - 0i32 as uintptr_t, - ); - } - } - } - dc_free_splitted_lines(lines); - - modify_cnt -} - -// Working with names -pub unsafe fn dc_normalize_name(full_name: *mut libc::c_char) { - if full_name.is_null() { - return; - } - dc_trim(full_name); - let len: libc::c_int = strlen(full_name) as libc::c_int; - if len > 0i32 { - let firstchar: libc::c_char = *full_name.offset(0isize); - let lastchar: libc::c_char = *full_name.offset((len - 1i32) as isize); - if firstchar as libc::c_int == '\'' as i32 && lastchar as libc::c_int == '\'' as i32 - || firstchar as libc::c_int == '\"' as i32 && lastchar as libc::c_int == '\"' as i32 - || firstchar as libc::c_int == '<' as i32 && lastchar as libc::c_int == '>' as i32 - { - *full_name.offset(0isize) = ' ' as i32 as libc::c_char; - *full_name.offset((len - 1i32) as isize) = ' ' as i32 as libc::c_char - } - } - let p1: *mut libc::c_char = strchr(full_name, ',' as i32); - if !p1.is_null() { - *p1 = 0i32 as libc::c_char; - let last_name: *mut libc::c_char = dc_strdup(full_name); - let first_name: *mut libc::c_char = dc_strdup(p1.offset(1isize)); - dc_trim(last_name); - dc_trim(first_name); - strcpy(full_name, first_name); - strcat(full_name, b" \x00" as *const u8 as *const libc::c_char); - strcat(full_name, last_name); - free(last_name as *mut libc::c_void); - free(first_name as *mut libc::c_void); - } else { - dc_trim(full_name); - }; -} - -#[allow(non_snake_case)] -pub fn dc_get_contacts( - context: &Context, - listflags: u32, - query: *const libc::c_char, -) -> *mut dc_array_t { - let self_addr = context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(); - - let mut add_self = false; - let mut ret = dc_array_t::new(100); - - if (listflags & DC_GCL_VERIFIED_ONLY) > 0 || !query.is_null() { - let s3strLikeCmd = format!("%{}%", if !query.is_null() { as_str(query) } else { "" }); - eprintln!("query '{}'", &s3strLikeCmd); - context - .sql - .query_map( - "SELECT c.id FROM contacts c \ - LEFT JOIN acpeerstates ps ON c.addr=ps.addr \ - WHERE c.addr!=?1 \ - AND c.id>?2 \ - AND c.origin>=?3 \ - AND c.blocked=0 \ - AND (c.name LIKE ?4 OR c.addr LIKE ?5) \ - AND (1=?6 OR LENGTH(ps.verified_key_fingerprint)!=0) \ - ORDER BY LOWER(c.name||c.addr),c.id;", - params![ - self_addr, - 9, - 0x100, - &s3strLikeCmd, - &s3strLikeCmd, - if 0 != listflags & 0x1 { 0 } else { 1 }, - ], - |row| row.get::<_, i32>(0), - |ids| { - for id in ids { - ret.add_id(id? as u32); - } - Ok(()) - }, - ) - .unwrap(); // TODO: Better error handling - - let self_name = context - .sql - .get_config(context, "displayname") - .unwrap_or_default(); - - let self_name2 = CString::new(context.stock_str(StockMessage::SelfMsg).as_ref()).unwrap(); - - if query.is_null() - || self_addr.contains(as_str(query)) - || self_name.contains(as_str(query)) - || 0 != unsafe { dc_str_contains(self_name2.as_ptr(), query) } - { - add_self = true; - } - } else { - add_self = true; - - context.sql.query_map( - "SELECT id FROM contacts WHERE addr!=?1 AND id>?2 AND origin>=?3 AND blocked=0 ORDER BY LOWER(name||addr),id;", - params![self_addr, 9, 0x100], - |row| row.get::<_, i32>(0), - |ids| { - for id in ids { - ret.add_id(id? as u32); - } - Ok(()) - } - ).unwrap(); // TODO: better error handling - } - - if 0 != listflags & 0x2 && add_self { - ret.add_id(1); - } - - ret.into_raw() -} - -pub fn dc_get_blocked_cnt(context: &Context) -> libc::c_int { - context - .sql - .query_row_col( - context, - "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0", - params![9], - 0, - ) - .unwrap_or_default() -} - -pub fn dc_get_blocked_contacts(context: &Context) -> *mut dc_array_t { - context - .sql - .query_map( - "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY LOWER(name||addr),id;", - params![9], - |row| row.get::<_, i32>(0), - |ids| { - let mut ret = dc_array_t::new(100); - - for id in ids { - ret.add_id(id? as u32); - } - - Ok(ret.into_raw()) - }, - ) - .unwrap_or_else(|_| std::ptr::null_mut()) -} - -pub unsafe fn dc_get_contact_encrinfo( - context: &Context, - contact_id: uint32_t, -) -> *mut libc::c_char { - let mut ret = String::new(); - let contact = dc_contact_new(context); - - let mut fingerprint_self = 0 as *mut libc::c_char; - let mut fingerprint_other_verified = 0 as *mut libc::c_char; - let mut fingerprint_other_unverified = 0 as *mut libc::c_char; - - if !(!dc_contact_load_from_db(contact, &context.sql, contact_id)) { - let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); - let loginparam = dc_loginparam_read(context, &context.sql, "configured_"); - - let mut self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); - - if peerstate.is_some() && peerstate.as_ref().and_then(|p| p.peek_key(0)).is_some() { - let peerstate = peerstate.as_ref().unwrap(); - let p = context.stock_str(if peerstate.prefer_encrypt == EncryptPreference::Mutual { - StockMessage::E2ePreferred - } else { - StockMessage::E2eAvailable - }); - ret += &p; - if self_key.is_none() { - dc_ensure_secret_key_exists(context); - self_key = Key::from_self_public(context, &loginparam.addr, &context.sql); - } - let p = context.stock_str(StockMessage::FingerPrints); - ret += &format!(" {}:", p); - - fingerprint_self = self_key - .map(|k| k.formatted_fingerprint_c()) - .unwrap_or(std::ptr::null_mut()); - fingerprint_other_verified = peerstate - .peek_key(2) - .map(|k| k.formatted_fingerprint_c()) - .unwrap_or(std::ptr::null_mut()); - fingerprint_other_unverified = peerstate - .peek_key(0) - .map(|k| k.formatted_fingerprint_c()) - .unwrap_or(std::ptr::null_mut()); - if peerstate.addr.is_some() && &loginparam.addr < peerstate.addr.as_ref().unwrap() { - cat_fingerprint( - &mut ret, - &loginparam.addr, - fingerprint_self, - 0 as *const libc::c_char, - ); - cat_fingerprint( - &mut ret, - peerstate.addr.as_ref().unwrap(), - fingerprint_other_verified, - fingerprint_other_unverified, - ); - } else { - cat_fingerprint( - &mut ret, - peerstate.addr.as_ref().unwrap(), - fingerprint_other_verified, - fingerprint_other_unverified, - ); - cat_fingerprint( - &mut ret, - &loginparam.addr, - fingerprint_self, - 0 as *const libc::c_char, - ); - } - } else if 0 == loginparam.server_flags & 0x400 && 0 == loginparam.server_flags & 0x40000 { - ret += &context.stock_str(StockMessage::EncrTransp); - } else { - ret += &context.stock_str(StockMessage::EncrNone); - } - } - - dc_contact_unref(contact); - - free(fingerprint_self as *mut libc::c_void); - free(fingerprint_other_verified as *mut libc::c_void); - free(fingerprint_other_unverified as *mut libc::c_void); - - to_cstring(ret) -} - -unsafe fn cat_fingerprint( - ret: &mut String, - addr: impl AsRef, - fingerprint_verified: *const libc::c_char, - fingerprint_unverified: *const libc::c_char, -) { - *ret += &format!( - "\n\n{}:\n{}", - addr.as_ref(), - if !fingerprint_verified.is_null() - && 0 != *fingerprint_verified.offset(0isize) as libc::c_int - { - as_str(fingerprint_verified) - } else { - as_str(fingerprint_unverified) - }, - ); - if !fingerprint_verified.is_null() - && 0 != *fingerprint_verified.offset(0isize) as libc::c_int - && !fingerprint_unverified.is_null() - && 0 != *fingerprint_unverified.offset(0isize) as libc::c_int - && strcmp(fingerprint_verified, fingerprint_unverified) != 0 - { - *ret += &format!( - "\n\n{} (alternative):\n{}", - addr.as_ref(), - as_str(fingerprint_unverified) - ); - } -} - -pub fn dc_delete_contact(context: &Context, contact_id: u32) -> bool { - if contact_id <= 9 { - return false; - } - - let count_contacts: i32 = context - .sql - .query_row_col( - context, - "SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?;", - params![contact_id as i32], - 0, - ) - .unwrap_or_default(); - - let count_msgs: i32 = if count_contacts > 0 { - context - .sql - .query_row_col( - context, - "SELECT COUNT(*) FROM msgs WHERE from_id=? OR to_id=?;", - params![contact_id as i32, contact_id as i32], - 0, - ) - .unwrap_or_default() - } else { - 0 - }; - - if count_msgs == 0 { - if sql::execute( - context, - &context.sql, - "DELETE FROM contacts WHERE id=?;", - params![contact_id as i32], - ) - .is_ok() - { - context.call_cb(Event::CONTACTS_CHANGED, 0, 0); - true - } else { - error!(context, 0, "delete_contact {} failed", contact_id); - false - } - } else { - info!( - context, - 0, "could not delete contact {}, there are {} messages with it", contact_id, count_msgs - ); - false - } -} - -pub unsafe fn dc_get_contact(context: &Context, contact_id: uint32_t) -> *mut dc_contact_t { - let mut ret: *mut dc_contact_t = dc_contact_new(context); - if !dc_contact_load_from_db(ret, &context.sql, contact_id) { - dc_contact_unref(ret); - ret = 0 as *mut dc_contact_t - } - ret -} - -pub unsafe fn dc_contact_get_id(contact: *const dc_contact_t) -> uint32_t { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return 0i32 as uint32_t; - } - (*contact).id -} - -pub unsafe fn dc_contact_get_addr(contact: *const dc_contact_t) -> *mut libc::c_char { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return dc_strdup(0 as *const libc::c_char); - } - dc_strdup((*contact).addr) -} - -pub unsafe fn dc_contact_get_name(contact: *const dc_contact_t) -> *mut libc::c_char { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return dc_strdup(0 as *const libc::c_char); - } - dc_strdup((*contact).name) -} - -pub unsafe fn dc_contact_get_display_name(contact: *const dc_contact_t) -> *mut libc::c_char { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return dc_strdup(0 as *const libc::c_char); - } - if !(*contact).name.is_null() && 0 != *(*contact).name.offset(0isize) as libc::c_int { - return dc_strdup((*contact).name); - } - dc_strdup((*contact).addr) -} - -pub unsafe fn dc_contact_get_name_n_addr(contact: *const dc_contact_t) -> *mut libc::c_char { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return dc_strdup(0 as *const libc::c_char); - } - if !(*contact).name.is_null() && 0 != *(*contact).name.offset(0isize) as libc::c_int { - return dc_mprintf( - b"%s (%s)\x00" as *const u8 as *const libc::c_char, - (*contact).name, - (*contact).addr, - ); - } - dc_strdup((*contact).addr) -} - -pub unsafe fn dc_contact_get_first_name(contact: *const dc_contact_t) -> *mut libc::c_char { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return dc_strdup(0 as *const libc::c_char); - } - if !(*contact).name.is_null() && 0 != *(*contact).name.offset(0isize) as libc::c_int { - return dc_get_first_name((*contact).name); - } - dc_strdup((*contact).addr) -} - -pub unsafe fn dc_get_first_name(full_name: *const libc::c_char) -> *mut libc::c_char { - let mut first_name: *mut libc::c_char = dc_strdup(full_name); - let p1: *mut libc::c_char = strchr(first_name, ' ' as i32); - if !p1.is_null() { - *p1 = 0i32 as libc::c_char; - dc_rtrim(first_name); - if *first_name.offset(0isize) as libc::c_int == 0i32 { - free(first_name as *mut libc::c_void); - first_name = dc_strdup(full_name) - } - } - first_name -} - -pub fn dc_contact_get_profile_image(contact: *const dc_contact_t) -> *mut libc::c_char { - let mut image_abs = 0 as *mut libc::c_char; - - if contact.is_null() || unsafe { (*contact).magic != 0xc047ac7 } { - return image_abs; - } - - if unsafe { (*contact).id } == 1 { - let context = unsafe { (*contact) }.context; - if let Some(avatar) = context.get_config(config::Config::Selfavatar) { - image_abs = unsafe { to_cstring(avatar) }; - } - } - // TODO: else get image_abs from contact param - image_abs -} - -pub unsafe fn dc_contact_get_color(contact: *const dc_contact_t) -> uint32_t { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return 0i32 as uint32_t; - } - dc_str_to_color((*contact).addr) as uint32_t -} - -pub unsafe fn dc_contact_is_blocked(contact: *const dc_contact_t) -> libc::c_int { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return 0i32; - } - (*contact).blocked -} - -/// Check if a contact was verified. E.g. by a secure-join QR code scan -/// and if the key has not changed since this verification. -/// -/// The UI may draw a checkbox or something like that beside verified contacts. -/// -/// Returns -/// - 0: contact is not verified. -/// - 2: SELF and contact have verified their fingerprints in both directions; in the UI typically checkmarks are shown. -pub unsafe fn dc_contact_is_verified(contact: *mut dc_contact_t) -> libc::c_int { - dc_contact_is_verified_ex(contact, None) -} - -/// Same as dc_contact_is_verified() but allows speeding up things -/// by adding the peerstate belonging to the contact. -/// If you do not have the peerstate available, it is loaded automatically. -pub unsafe fn dc_contact_is_verified_ex<'a>( - contact: *mut dc_contact_t<'a>, - peerstate: Option<&Peerstate<'a>>, -) -> libc::c_int { - if contact.is_null() || (*contact).magic != 0xc047ac7i32 as libc::c_uint { - return 0; - } - - // we're always sort of secured-verified as we could verify the key on this device any time with the key - // on this device - if (*contact).id == 1 as libc::c_uint { - return 2; - } - - if let Some(peerstate) = peerstate { - if peerstate.verified_key().is_some() { - 2 - } else { - 0 - } - } else { - let peerstate = Peerstate::from_addr( - (*contact).context, - &(*contact).context.sql, - as_str((*contact).addr), - ); - - let res = if let Some(ps) = peerstate { - if ps.verified_key().is_some() { - 2 - } else { - 0 - } - } else { - 0 - }; - - res - } -} - -// Working with e-mail-addresses -pub fn dc_addr_cmp(addr1: impl AsRef, addr2: impl AsRef) -> bool { - let norm1 = dc_addr_normalize_safe(addr1.as_ref()); - let norm2 = dc_addr_normalize_safe(addr2.as_ref()); - - norm1 == norm2 -} - -pub fn dc_addr_equals_self(context: &Context, addr: *const libc::c_char) -> libc::c_int { - let mut ret = 0; - - if !addr.is_null() { - let normalized_addr = unsafe { dc_addr_normalize(addr) }; - if let Some(self_addr) = context.sql.get_config(context, "configured_addr") { - ret = (as_str(normalized_addr) == self_addr) as libc::c_int; - } - unsafe { free(normalized_addr as *mut libc::c_void) }; - } - - ret -} - -pub unsafe fn dc_addr_equals_contact( - context: &Context, - addr: impl AsRef, - contact_id: u32, -) -> bool { - if addr.as_ref().is_empty() { - return false; - } - - let contact = dc_contact_new(context); - let mut addr_are_equal = false; - - if dc_contact_load_from_db(contact, &context.sql, contact_id) { - if !(*contact).addr.is_null() { - let normalized_addr = dc_addr_normalize_safe(addr.as_ref()); - if as_str((*contact).addr) == normalized_addr { - addr_are_equal = true; - } - } - dc_contact_unref(contact); - } - - addr_are_equal -} - -// Context functions to work with contacts -pub fn dc_get_real_contact_cnt(context: &Context) -> usize { - if !context.sql.is_open() { - return 0; - } - - context - .sql - .query_row_col::<_, isize>( - context, - "SELECT COUNT(*) FROM contacts WHERE id>?;", - params![9], - 0, - ) - .unwrap_or_default() as usize -} - -pub unsafe fn dc_get_contact_origin( - context: &Context, - contact_id: uint32_t, - mut ret_blocked: *mut libc::c_int, -) -> libc::c_int { - let mut ret: libc::c_int = 0i32; - let mut dummy: libc::c_int = 0i32; - if ret_blocked.is_null() { - ret_blocked = &mut dummy - } - let contact: *mut dc_contact_t = dc_contact_new(context); - *ret_blocked = 0i32; - if dc_contact_load_from_db(contact, &context.sql, contact_id) { - /* we could optimize this by loading only the needed fields */ - if 0 != (*contact).blocked { - *ret_blocked = 1i32 - } else { - ret = (*contact).origin - } - } - dc_contact_unref(contact); - ret -} - -pub fn dc_real_contact_exists(context: &Context, contact_id: u32) -> bool { - if !context.sql.is_open() || contact_id <= 9 { - return false; - } - - context - .sql - .exists( - "SELECT id FROM contacts WHERE id=?;", - params![contact_id as i32], - ) - .unwrap_or_default() -} - -pub fn dc_scaleup_contact_origin(context: &Context, contact_id: u32, origin: libc::c_int) -> bool { - context - .sql - .execute( - "UPDATE contacts SET origin=? WHERE id=? AND origin, } #[derive(Debug, PartialEq)] @@ -32,7 +32,7 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char let mut dehtml = Dehtml { strbuilder: String::with_capacity(strlen(buf_terminated)), add_text: AddText::YesRemoveLineEnds, - last_href: 0 as *mut libc::c_char, + last_href: None, }; let mut saxparser = dc_saxparser_t { starttag_cb: None, @@ -51,9 +51,8 @@ pub unsafe fn dc_dehtml(buf_terminated: *mut libc::c_char) -> *mut libc::c_char ); dc_saxparser_set_text_handler(&mut saxparser, Some(dehtml_text_cb)); dc_saxparser_parse(&mut saxparser, buf_terminated); - free(dehtml.last_href as *mut libc::c_void); - to_cstring(dehtml.strbuilder) + dehtml.strbuilder.strdup() } unsafe fn dehtml_text_cb( @@ -66,7 +65,11 @@ unsafe fn dehtml_text_cb( if dehtml.add_text == AddText::YesPreserveLineEnds || dehtml.add_text == AddText::YesRemoveLineEnds { - let last_added = std::ffi::CStr::from_ptr(text).to_string_lossy(); + let last_added = std::ffi::CStr::from_ptr(text) + .to_str() + .expect("invalid utf8"); + // TODO: why does len does not match? + // assert_eq!(last_added.len(), len as usize); if dehtml.add_text == AddText::YesRemoveLineEnds { dehtml.strbuilder += LINE_RE.replace_all(last_added.as_ref(), "\r").as_ref(); @@ -86,14 +89,10 @@ unsafe fn dehtml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char dehtml.add_text = AddText::YesRemoveLineEnds; } "a" => { - if !dehtml.last_href.is_null() { + if let Some(ref last_href) = dehtml.last_href.take() { dehtml.strbuilder += "]("; - dehtml.strbuilder += std::ffi::CStr::from_ptr((*dehtml).last_href) - .to_string_lossy() - .as_ref(); + dehtml.strbuilder += last_href; dehtml.strbuilder += ")"; - free(dehtml.last_href as *mut libc::c_void); - dehtml.last_href = 0 as *mut libc::c_char; } } "b" | "strong" => { @@ -131,12 +130,13 @@ unsafe fn dehtml_starttag_cb( dehtml.add_text = AddText::YesPreserveLineEnds; } "a" => { - free(dehtml.last_href as *mut libc::c_void); - dehtml.last_href = dc_strdup_keep_null(dc_attr_find( + let text_c = std::ffi::CStr::from_ptr(dc_attr_find( attr, b"href\x00" as *const u8 as *const libc::c_char, )); - if !dehtml.last_href.is_null() { + let text_r = text_c.to_str().expect("invalid utf8"); + if !text_r.is_empty() { + dehtml.last_href = Some(text_r.to_string()); dehtml.strbuilder += "["; } } diff --git a/src/dc_e2ee.rs b/src/dc_e2ee.rs index 3232a9a91..db0c15a89 100644 --- a/src/dc_e2ee.rs +++ b/src/dc_e2ee.rs @@ -66,7 +66,7 @@ pub unsafe fn dc_e2ee_encrypt( mut in_out_message: *mut mailmime, helper: &mut dc_e2ee_helper_t, ) { - let mut current_block: u64 = 0; + let mut ok_to_continue = true; let mut col: libc::c_int = 0i32; let mut do_encrypt: libc::c_int = 0i32; /*just a pointer into mailmime structure, must not be freed*/ @@ -115,11 +115,22 @@ pub unsafe fn dc_e2ee_encrypt( || 0 != e2ee_guaranteed) { let peerstate = peerstate.unwrap(); + info!( + context, + 0, "dc_e2ee_encrypt {} has peerstate", recipient_addr + ); if let Some(key) = peerstate.peek_key(min_verified as usize) { keyring.add_owned(key.clone()); peerstates.push(peerstate); } } else { + info!( + context, + 0, + "dc_e2ee_encrypt {} HAS NO peerstate {}", + recipient_addr, + peerstate.is_some() + ); do_encrypt = 0i32; /* if we cannot encrypt to a single recipient, we cannot encrypt the message at all */ break; @@ -177,16 +188,12 @@ pub unsafe fn dc_e2ee_encrypt( let p = peerstates[i as usize] .render_gossip_header(min_verified as usize); - if p.is_some() { - let header = to_cstring(p.unwrap()); + if let Some(header) = p { mailimf_fields_add( imffields_encrypted, mailimf_field_new_custom( - strdup( - b"Autocrypt-Gossip\x00" as *const u8 - as *const libc::c_char, - ), - header, + "Autocrypt-Gossip".strdup(), + header.strdup(), ), ); } @@ -287,7 +294,7 @@ pub unsafe fn dc_e2ee_encrypt( ); mailmime_write_mem(plain, &mut col, message_to_encrypt); if (*plain).str_0.is_null() || (*plain).len <= 0 { - current_block = 14181132614457621749; + ok_to_continue = false; } else { if let Some(ctext_v) = dc_pgp_pk_encrypt( (*plain).str_0 as *const libc::c_void, @@ -296,8 +303,8 @@ pub unsafe fn dc_e2ee_encrypt( sign_key.as_ref(), ) { let ctext_bytes = ctext_v.len(); - let ctext = to_cstring(ctext_v); - (*helper).cdata_to_free = ctext as *mut _; + let ctext = ctext_v.strdup(); + helper.cdata_to_free = ctext as *mut _; /* create MIME-structure that will contain the encrypted text */ let mut encrypted_part: *mut mailmime = new_data_part( @@ -343,27 +350,19 @@ pub unsafe fn dc_e2ee_encrypt( (*in_out_message).mm_data.mm_message.mm_msg_mime = encrypted_part; (*encrypted_part).mm_parent = in_out_message; mailmime_free(message_to_encrypt); - (*helper).encryption_successfull = 1i32; - current_block = 13824533195664196414; + helper.encryption_successfull = 1i32; } } - } else { - current_block = 13824533195664196414; } - match current_block { - 14181132614457621749 => {} - _ => { - let aheader = Aheader::new(addr, public_key, prefer_encrypt); - let rendered = to_cstring(aheader.to_string()); - - mailimf_fields_add( - imffields_unprotected, - mailimf_field_new_custom( - strdup(b"Autocrypt\x00" as *const u8 as *const libc::c_char), - rendered, - ), - ); - } + if ok_to_continue { + let aheader = Aheader::new(addr, public_key, prefer_encrypt); + mailimf_fields_add( + imffields_unprotected, + mailimf_field_new_custom( + "Autocrypt".strdup(), + aheader.to_string().strdup(), + ), + ); } } } @@ -384,7 +383,7 @@ unsafe fn new_data_part( default_content_type: *mut libc::c_char, default_encoding: libc::c_int, ) -> *mut mailmime { - let mut current_block: u64; + let mut ok_to_continue = true; //char basename_buf[PATH_MAX]; let mut encoding: *mut mailmime_mechanism; let content: *mut mailmime_content; @@ -404,7 +403,7 @@ unsafe fn new_data_part( } content = mailmime_content_new_with_str(content_type_str); if content.is_null() { - current_block = 16266721588079097885; + ok_to_continue = false; } else { do_encoding = 1i32; if (*(*content).ct_type).tp_type == MAILMIME_TYPE_COMPOSITE_TYPE as libc::c_int { @@ -432,54 +431,44 @@ unsafe fn new_data_part( } encoding = mailmime_mechanism_new(encoding_type, 0 as *mut libc::c_char); if encoding.is_null() { - current_block = 16266721588079097885; - } else { - current_block = 11057878835866523405; + ok_to_continue = false; } - } else { - current_block = 11057878835866523405; } - match current_block { - 16266721588079097885 => {} - _ => { - mime_fields = mailmime_fields_new_with_data( - encoding, - 0 as *mut libc::c_char, - 0 as *mut libc::c_char, - 0 as *mut mailmime_disposition, - 0 as *mut mailmime_language, - ); - if mime_fields.is_null() { - current_block = 16266721588079097885; + if ok_to_continue { + mime_fields = mailmime_fields_new_with_data( + encoding, + 0 as *mut libc::c_char, + 0 as *mut libc::c_char, + 0 as *mut mailmime_disposition, + 0 as *mut mailmime_language, + ); + if mime_fields.is_null() { + ok_to_continue = false; + } else { + mime = mailmime_new_empty(content, mime_fields); + if mime.is_null() { + mailmime_fields_free(mime_fields); + mailmime_content_free(content); } else { - mime = mailmime_new_empty(content, mime_fields); - if mime.is_null() { - mailmime_fields_free(mime_fields); - mailmime_content_free(content); - } else { - if !data.is_null() - && data_bytes > 0 - && (*mime).mm_type == MAILMIME_SINGLE as libc::c_int - { - mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes); - } - return mime; + if !data.is_null() + && data_bytes > 0 + && (*mime).mm_type == MAILMIME_SINGLE as libc::c_int + { + mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes); } - current_block = 13668317689588454213; + return mime; } } } } - match current_block { - 16266721588079097885 => { - if !encoding.is_null() { - mailmime_mechanism_free(encoding); - } - if !content.is_null() { - mailmime_content_free(content); - } + + if ok_to_continue == false { + if !encoding.is_null() { + mailmime_mechanism_free(encoding); + } + if !content.is_null() { + mailmime_content_free(content); } - _ => {} } return 0 as *mut mailmime; } @@ -596,7 +585,7 @@ pub unsafe fn dc_e2ee_decrypt( } } else if let Some(ref header) = autocryptheader { let p = Peerstate::from_header(context, header, message_time); - p.save_to_db(&context.sql, true); + assert!(p.save_to_db(&context.sql, true)); peerstate = Some(p); } } @@ -841,7 +830,7 @@ unsafe fn decrypt_part( ret_valid_signatures: &mut HashSet, ret_decrypted_mime: *mut *mut mailmime, ) -> libc::c_int { - let current_block: u64; + let mut ok_to_continue = true; let mime_data: *mut mailmime_data; let mut mime_transfer_encoding: libc::c_int = MAILMIME_MECHANISM_BINARY as libc::c_int; /* mmap_string_unref()'d if set */ @@ -889,9 +878,7 @@ unsafe fn decrypt_part( decoded_data_bytes = (*mime_data).dt_data.dt_text.dt_length; if decoded_data.is_null() || decoded_data_bytes <= 0 { /* no error - but no data */ - current_block = 2554982661806928548; - } else { - current_block = 4488286894823169796; + ok_to_continue = false; } } else { let r: libc::c_int; @@ -908,53 +895,49 @@ unsafe fn decrypt_part( || transfer_decoding_buffer.is_null() || decoded_data_bytes <= 0 { - current_block = 2554982661806928548; + ok_to_continue = false; } else { decoded_data = transfer_decoding_buffer; - current_block = 4488286894823169796; } } - match current_block { - 2554982661806928548 => {} - _ => { - /* encrypted, decoded data in decoded_data now ... */ - if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int)) - { - let add_signatures = if ret_valid_signatures.is_empty() { - Some(ret_valid_signatures) - } else { - None - }; + if ok_to_continue { + /* encrypted, decoded data in decoded_data now ... */ + if !(0 == has_decrypted_pgp_armor(decoded_data, decoded_data_bytes as libc::c_int)) { + let add_signatures = if ret_valid_signatures.is_empty() { + Some(ret_valid_signatures) + } else { + None + }; - /*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */ - if let Some(plain) = dc_pgp_pk_decrypt( - decoded_data as *const libc::c_void, - decoded_data_bytes, - &private_keyring, - &public_keyring_for_validate, - add_signatures, - ) { - let plain_bytes = plain.len(); - let plain_buf = plain.as_ptr() as *const libc::c_char; + /*if we already have fingerprints, do not add more; this ensures, only the fingerprints from the outer-most part are collected */ + if let Some(plain) = dc_pgp_pk_decrypt( + decoded_data as *const libc::c_void, + decoded_data_bytes, + &private_keyring, + &public_keyring_for_validate, + add_signatures, + ) { + let plain_bytes = plain.len(); + let plain_buf = plain.as_ptr() as *const libc::c_char; - let mut index: size_t = 0i32 as size_t; - let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime; - if mailmime_parse( - plain_buf as *const _, - plain_bytes, - &mut index, - &mut decrypted_mime, - ) != MAIL_NO_ERROR as libc::c_int - || decrypted_mime.is_null() - { - if !decrypted_mime.is_null() { - mailmime_free(decrypted_mime); - } - } else { - *ret_decrypted_mime = decrypted_mime; - sth_decrypted = 1i32 + let mut index: size_t = 0i32 as size_t; + let mut decrypted_mime: *mut mailmime = 0 as *mut mailmime; + if mailmime_parse( + plain_buf as *const _, + plain_bytes, + &mut index, + &mut decrypted_mime, + ) != MAIL_NO_ERROR as libc::c_int + || decrypted_mime.is_null() + { + if !decrypted_mime.is_null() { + mailmime_free(decrypted_mime); } + } else { + *ret_decrypted_mime = decrypted_mime; + sth_decrypted = 1i32 } + std::mem::forget(plain); } } } @@ -1084,3 +1067,69 @@ pub unsafe fn dc_ensure_secret_key_exists(context: &Context) -> libc::c_int { success } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mailmime_parse() { + let plain = b"Chat-Disposition-Notification-To: holger@deltachat.de +Chat-Group-ID: CovhGgau8M- +Chat-Group-Name: Delta Chat Dev +Subject: =?utf-8?Q?Chat=3A?= Delta Chat =?utf-8?Q?Dev=3A?= sidenote for + =?utf-8?Q?all=3A?= rust core master ... +Content-Type: text/plain; charset=\"utf-8\"; protected-headers=\"v1\" +Content-Transfer-Encoding: quoted-printable + +sidenote for all: rust core master is broken currently ... so dont recomm= +end to try to run with desktop or ios unless you are ready to hunt bugs + +-- =20 +Sent with my Delta Chat Messenger: https://delta.chat"; + let plain_bytes = plain.len(); + let plain_buf = plain.as_ptr() as *const libc::c_char; + + let mut index = 0; + let mut decrypted_mime = std::ptr::null_mut(); + + let res = unsafe { + mailmime_parse( + plain_buf as *const _, + plain_bytes, + &mut index, + &mut decrypted_mime, + ) + }; + unsafe { + let msg1 = (*decrypted_mime).mm_data.mm_message.mm_msg_mime; + let mut decoded_data = 0 as *const libc::c_char; + let mut decoded_data_bytes = 0; + let mut transfer_decoding_buffer: *mut libc::c_char = 0 as *mut libc::c_char; + + assert_eq!( + mailmime_transfer_decode( + msg1, + &mut decoded_data, + &mut decoded_data_bytes, + &mut transfer_decoding_buffer, + ), + 1 + ); + println!( + "{:?}", + String::from_utf8_lossy(std::slice::from_raw_parts( + decoded_data as *const u8, + decoded_data_bytes as usize, + )) + ); + + free(decoded_data as *mut _); + } + + assert_eq!(res, 0); + assert!(!decrypted_mime.is_null()); + + unsafe { free(decrypted_mime as *mut _) }; + } +} diff --git a/src/dc_imex.rs b/src/dc_imex.rs index c414fdbf1..259c7237d 100644 --- a/src/dc_imex.rs +++ b/src/dc_imex.rs @@ -5,6 +5,7 @@ use mmime::mmapstring::*; use mmime::other::*; use rand::{thread_rng, Rng}; +use crate::config::Config; use crate::constants::*; use crate::context::Context; use crate::dc_chat::*; @@ -13,6 +14,7 @@ use crate::dc_e2ee::*; use crate::dc_job::*; use crate::dc_msg::*; use crate::dc_tools::*; +use crate::error::*; use crate::key::*; use crate::param::*; use crate::pgp::*; @@ -98,101 +100,85 @@ pub unsafe fn dc_imex_has_backup( } pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { - let current_block: u64; - let mut success: libc::c_int = 0i32; - let mut setup_code: *mut libc::c_char; - let mut setup_file_content: *mut libc::c_char = 0 as *mut libc::c_char; let mut setup_file_name: *mut libc::c_char = 0 as *mut libc::c_char; - let chat_id: uint32_t; let mut msg: *mut dc_msg_t = 0 as *mut dc_msg_t; - let msg_id: uint32_t; - if 0 == dc_alloc_ongoing(context) { - return 0 as *mut libc::c_char; + if dc_alloc_ongoing(context) == 0 { + return std::ptr::null_mut(); } - setup_code = to_cstring(dc_create_setup_code(context)); - if !setup_code.is_null() { - /* this may require a keypair to be created. this may take a second ... */ - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - setup_file_content = dc_render_setup_file(context, setup_code); - if !setup_file_content.is_null() { - /* encrypting may also take a while ... */ - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - setup_file_name = dc_get_fine_pathNfilename( + let setup_code = dc_create_setup_code(context); + /* this may require a keypair to be created. this may take a second ... */ + if !context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { + if let Ok(setup_file_content) = dc_render_setup_file(context, &setup_code) { + let setup_file_content_c = CString::yolo(setup_file_content.as_str()); + /* encrypting may also take a while ... */ + if !context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { + setup_file_name = dc_get_fine_pathNfilename( + context, + b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, + b"autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char, + ); + if !(setup_file_name.is_null() + || 0 == dc_write_file( context, - b"$BLOBDIR\x00" as *const u8 as *const libc::c_char, - b"autocrypt-setup-message.html\x00" as *const u8 as *const libc::c_char, - ); - if !(setup_file_name.is_null() - || 0 == dc_write_file( - context, - setup_file_name, - setup_file_content as *const libc::c_void, - strlen(setup_file_content), - )) - { - chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t); - if !(chat_id == 0i32 as libc::c_uint) { - msg = dc_msg_new_untyped(context); - (*msg).type_0 = DC_MSG_FILE; - (*msg).param.set(Param::File, as_str(setup_file_name)); + setup_file_name, + setup_file_content_c.as_ptr() as *const libc::c_void, + setup_file_content_c.as_bytes().len(), + )) + { + let chat_id = dc_create_chat_by_contact_id(context, 1i32 as uint32_t); + if !(chat_id == 0i32 as libc::c_uint) { + msg = dc_msg_new_untyped(context); + (*msg).type_0 = Viewtype::File; + (*msg).param.set(Param::File, as_str(setup_file_name)); - (*msg) - .param - .set(Param::MimeType, "application/autocrypt-setup"); - (*msg).param.set_int(Param::Cmd, 6); - (*msg).param.set_int(Param::ForcePlaintext, 2); + (*msg) + .param + .set(Param::MimeType, "application/autocrypt-setup"); + (*msg).param.set_int(Param::Cmd, 6); + (*msg).param.set_int(Param::ForcePlaintext, 2); - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - msg_id = dc_send_msg(context, chat_id, msg); - if !(msg_id == 0i32 as libc::c_uint) { + if !context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { + let msg_id = dc_send_msg(context, chat_id, msg); + if msg_id != 0 { + dc_msg_unref(msg); + msg = 0 as *mut dc_msg_t; + info!(context, 0, "Wait for setup message being sent ...",); + loop { + if context + .running_state + .clone() + .read() + .unwrap() + .shall_stop_ongoing + { + break; + } + std::thread::sleep(std::time::Duration::from_secs(1)); + msg = dc_get_msg(context, msg_id); + if 0 != dc_msg_is_sent(msg) { + info!(context, 0, "... setup message sent.",); + break; + } dc_msg_unref(msg); - msg = 0 as *mut dc_msg_t; - info!(context, 0, "Wait for setup message being sent ...",); - loop { - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - current_block = 6116957410927263949; - break; - } - std::thread::sleep(std::time::Duration::from_secs(1)); - msg = dc_get_msg(context, msg_id); - if 0 != dc_msg_is_sent(msg) { - current_block = 6450636197030046351; - break; - } - dc_msg_unref(msg); - msg = 0 as *mut dc_msg_t - } - match current_block { - 6116957410927263949 => {} - _ => { - info!(context, 0, "... setup message sent.",); - success = 1; - } - } + msg = 0 as *mut dc_msg_t } } } @@ -201,88 +187,77 @@ pub unsafe fn dc_initiate_key_transfer(context: &Context) -> *mut libc::c_char { } } } - if 0 == success { - free(setup_code as *mut libc::c_void); - setup_code = 0 as *mut libc::c_char - } free(setup_file_name as *mut libc::c_void); - free(setup_file_content as *mut libc::c_void); dc_msg_unref(msg); dc_free_ongoing(context); - setup_code + setup_code.strdup() } -pub unsafe extern "C" fn dc_render_setup_file( - context: &Context, - passphrase: *const libc::c_char, -) -> *mut libc::c_char { - let stmt: *mut sqlite3_stmt = 0 as *mut sqlite3_stmt; - - let mut passphrase_begin: [libc::c_char; 8] = [0; 8]; - let mut ret_setupfilecontent: *mut libc::c_char = 0 as *mut libc::c_char; - if !(passphrase.is_null() || strlen(passphrase) < 2) { - strncpy(passphrase_begin.as_mut_ptr(), passphrase, 2); - passphrase_begin[2usize] = 0i32 as libc::c_char; - /* create the payload */ - if !(0 == dc_ensure_secret_key_exists(context)) { - let self_addr = context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(); - let curr_private_key = Key::from_self_private(context, self_addr, &context.sql); - let e2ee_enabled = context - .sql - .get_config_int(context, "e2ee_enabled") - .unwrap_or_else(|| 1); - - let headers = if 0 != e2ee_enabled { - Some(("Autocrypt-Prefer-Encrypt", "mutual")) - } else { - None - }; - - if let Some(payload_key_asc) = curr_private_key.map(|k| k.to_asc_c(headers)) { - if let Some(encr) = dc_pgp_symm_encrypt( - passphrase, - payload_key_asc as *const libc::c_void, - strlen(payload_key_asc), - ) { - let encr_string_c = CString::new(encr).unwrap(); - let mut encr_string = strdup(encr_string_c.as_ptr()); - - free(payload_key_asc as *mut libc::c_void); - let replacement: *mut libc::c_char = - dc_mprintf(b"-----BEGIN PGP MESSAGE-----\r\nPassphrase-Format: numeric9x4\r\nPassphrase-Begin: %s\x00" - as *const u8 as *const libc::c_char, - passphrase_begin.as_mut_ptr()); - dc_str_replace( - &mut encr_string, - b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, - replacement, - ); - free(replacement as *mut libc::c_void); - let setup_message_title = - CString::new(context.stock_str(StockMessage::AcSetupMsgSubject).as_ref()) - .unwrap(); - let setup_message_body = context.stock_str(StockMessage::AcSetupMsgBody); - let msg_body_head: &str = setup_message_body.split('\r').next().unwrap(); - let msg_body_html = CString::new(msg_body_head.replace("\n", "
")).unwrap(); - ret_setupfilecontent = - dc_mprintf(b"\r\n\r\n\r\n%s\r\n\r\n\r\n

%s

\r\n

%s

\r\n
\r\n%s\r\n
\r\n\r\n\r\n\x00" - as *const u8 as *const libc::c_char, - setup_message_title.as_ptr(), - setup_message_title.as_ptr(), - msg_body_html.as_ptr(), - encr_string); - free(encr_string as *mut libc::c_void); - } - } - } +pub fn dc_render_setup_file(context: &Context, passphrase: &str) -> Result { + ensure!( + passphrase.len() >= 2, + "Passphrase must be at least 2 chars long." + ); + unsafe { + ensure!( + !(dc_ensure_secret_key_exists(context) == 0), + "No secret key available." + ); } - sqlite3_finalize(stmt); + let self_addr = context + .get_config(Config::ConfiguredAddr) + .ok_or(format_err!("Failed to get self address."))?; + let private_key = Key::from_self_private(context, self_addr, &context.sql) + .ok_or(format_err!("Failed to get private key."))?; + let ac_headers = match context + .sql + .get_config_int(context, Config::E2eeEnabled) + .unwrap_or(1) + { + 0 => None, + _ => Some(("Autocrypt-Prefer-Encrypt", "mutual")), + }; + let private_key_asc = private_key.to_asc(ac_headers); + let encr = { + let private_key_asc_c = CString::yolo(private_key_asc); + let passphrase_c = CString::yolo(passphrase); + dc_pgp_symm_encrypt( + passphrase_c.as_ptr(), + private_key_asc_c.as_ptr() as *const libc::c_void, + private_key_asc_c.as_bytes().len(), + ) + .ok_or(format_err!("Failed to encrypt private key."))? + }; + let replacement = format!( + concat!( + "-----BEGIN PGP MESSAGE-----\r\n", + "Passphrase-Format: numeric9x4\r\n", + "Passphrase-Begin: {}" + ), + &passphrase[..2] + ); + let pgp_msg = encr.replace("-----BEGIN PGP MESSAGE-----", &replacement); - ret_setupfilecontent + let msg_subj = context.stock_str(StockMessage::AcSetupMsgSubject); + let msg_body = context.stock_str(StockMessage::AcSetupMsgBody); + let msg_body_html = msg_body.replace("\r", "").replace("\n", "
"); + Ok(format!( + concat!( + "\r\n", + "\r\n", + " \r\n", + " {}\r\n", + " \r\n", + " \r\n", + "

{}

\r\n", + "

{}

\r\n", + "
\r\n{}\r\n
\r\n", + " \r\n", + "\r\n" + ), + msg_subj, msg_subj, msg_body_html, pgp_msg + )) } pub fn dc_create_setup_code(_context: &Context) -> String { @@ -351,7 +326,7 @@ pub unsafe fn dc_continue_key_transfer( armored_key = dc_decrypt_setup_file(context, norm_sc, filecontent); if armored_key.is_null() { warn!(context, 0, "Cannot decrypt Autocrypt Setup Message.",); - } else if !(0 == set_self_key(context, armored_key, 1i32)) { + } else if set_self_key(context, armored_key, 1) { /*set default*/ /* error already logged */ success = 1i32 @@ -368,12 +343,11 @@ pub unsafe fn dc_continue_key_transfer( success } -// TODO should return bool /rtn fn set_self_key( context: &Context, armored_c: *const libc::c_char, set_default: libc::c_int, -) -> libc::c_int { +) -> bool { assert!(!armored_c.is_null(), "invalid buffer"); let armored = as_str(armored_c); @@ -383,7 +357,7 @@ fn set_self_key( if keys.is_none() { error!(context, 0, "File does not contain a valid private key.",); - return 0; + return false; } let (private_key, public_key, header) = keys.unwrap(); @@ -397,7 +371,7 @@ fn set_self_key( ) .is_err() { - return 0; + return false; } if 0 != set_default { @@ -409,7 +383,7 @@ fn set_self_key( ) .is_err() { - return 0; + return false; } } else { error!(context, 0, "File does not contain a private key.",); @@ -419,7 +393,7 @@ fn set_self_key( if self_addr.is_none() { error!(context, 0, "Missing self addr"); - return 0; + return false; } if !dc_key_save_self_keypair( @@ -431,20 +405,20 @@ fn set_self_key( &context.sql, ) { error!(context, 0, "Cannot save keypair."); - return 0; + return false; } match preferencrypt.map(|s| s.as_str()) { - Some("") => 0, + Some("") => false, Some("nopreference") => context .sql .set_config_int(context, "e2ee_enabled", 0) - .is_ok() as libc::c_int, + .is_ok(), Some("mutual") => context .sql .set_config_int(context, "e2ee_enabled", 1) - .is_ok() as libc::c_int, - _ => 1, + .is_ok(), + _ => true, } } @@ -462,20 +436,18 @@ pub unsafe fn dc_decrypt_setup_file( let mut payload: *mut libc::c_char = 0 as *mut libc::c_char; fc_buf = dc_strdup(filecontent); - if !(0 - == dc_split_armored_data( - fc_buf, - &mut fc_headerline, - 0 as *mut *const libc::c_char, - 0 as *mut *const libc::c_char, - &mut fc_base64, - ) - || fc_headerline.is_null() - || strcmp( + if dc_split_armored_data( + fc_buf, + &mut fc_headerline, + 0 as *mut *const libc::c_char, + 0 as *mut *const libc::c_char, + &mut fc_base64, + ) && !fc_headerline.is_null() + && strcmp( fc_headerline, b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, - ) != 0i32 - || fc_base64.is_null()) + ) == 0 + && !fc_base64.is_null() { /* convert base64 to binary */ /*must be freed using mmap_string_unref()*/ @@ -536,7 +508,7 @@ pub unsafe fn dc_normalize_setup_code( p1 = p1.offset(1); } - to_cstring(out) + out.strdup() } #[allow(non_snake_case)] @@ -545,16 +517,14 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) let mut success: libc::c_int = 0; let mut ongoing_allocated_here: libc::c_int = 0; let what: libc::c_int; - let mut param1 = 0 as *mut libc::c_char; - let mut param2 = 0 as *mut libc::c_char; if !(0 == dc_alloc_ongoing(context)) { ongoing_allocated_here = 1; what = (*job).param.get_int(Param::Cmd).unwrap_or_default(); - param1 = to_cstring((*job).param.get(Param::Arg).unwrap_or_default()); - param2 = to_cstring((*job).param.get(Param::Arg2).unwrap_or_default()); + let param1 = CString::yolo((*job).param.get(Param::Arg).unwrap_or_default()); + let _param2 = CString::yolo((*job).param.get(Param::Arg2).unwrap_or_default()); - if strlen(param1) == 0 { + if strlen(param1.as_ptr()) == 0 { error!(context, 0, "No Import/export dir/file given.",); } else { info!(context, 0, "Import/export process started.",); @@ -572,7 +542,7 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) ); current_block = 3568988166330621280; } else { - dc_create_folder(context, param1); + dc_create_folder(context, param1.as_ptr()); current_block = 4495394744059808450; } } else { @@ -585,28 +555,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) current_block = 10991094515395304355; match current_block { 2973387206439775448 => { - if 0 == import_backup(context, param1) { + if 0 == import_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 11250025114629486028 => { - if 0 == import_self_keys(context, param1) { + if 0 == import_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 12669919903773909120 => { - if 0 == export_backup(context, param1) { + if 0 == export_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } _ => { - if 0 == export_self_keys(context, param1) { + if 0 == export_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; @@ -625,28 +595,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) current_block = 11250025114629486028; match current_block { 2973387206439775448 => { - if 0 == import_backup(context, param1) { + if 0 == import_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 11250025114629486028 => { - if 0 == import_self_keys(context, param1) { + if 0 == import_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 12669919903773909120 => { - if 0 == export_backup(context, param1) { + if 0 == export_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } _ => { - if 0 == export_self_keys(context, param1) { + if 0 == export_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; @@ -665,28 +635,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) current_block = 12669919903773909120; match current_block { 2973387206439775448 => { - if 0 == import_backup(context, param1) { + if 0 == import_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 11250025114629486028 => { - if 0 == import_self_keys(context, param1) { + if 0 == import_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 12669919903773909120 => { - if 0 == export_backup(context, param1) { + if 0 == export_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } _ => { - if 0 == export_self_keys(context, param1) { + if 0 == export_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; @@ -705,28 +675,28 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) current_block = 2973387206439775448; match current_block { 2973387206439775448 => { - if 0 == import_backup(context, param1) { + if 0 == import_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 11250025114629486028 => { - if 0 == import_self_keys(context, param1) { + if 0 == import_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } 12669919903773909120 => { - if 0 == export_backup(context, param1) { + if 0 == export_backup(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; } } _ => { - if 0 == export_self_keys(context, param1) { + if 0 == export_self_keys(context, param1.as_ptr()) { current_block = 3568988166330621280; } else { current_block = 1118134448028020070; @@ -748,8 +718,6 @@ pub unsafe fn dc_job_do_DC_JOB_IMEX_IMAP(context: &Context, job: *mut dc_job_t) } } - free(param1 as *mut libc::c_void); - free(param2 as *mut libc::c_void); if 0 != ongoing_allocated_here { dc_free_ongoing(context); } @@ -896,9 +864,8 @@ unsafe fn export_backup(context: &Context, dir: *const libc::c_char) -> libc::c_ let res = chrono::NaiveDateTime::from_timestamp(now as i64, 0) .format("delta-chat-%Y-%m-%d.bak") .to_string(); - let buffer = to_cstring(res); - let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer); - free(buffer as *mut _); + let buffer = CString::yolo(res); + let dest_pathNfilename = dc_get_fine_pathNfilename(context, dir, buffer.as_ptr()); if dest_pathNfilename.is_null() { error!(context, 0, "Cannot get backup file name.",); @@ -1097,7 +1064,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let mut imported_cnt: libc::c_int = 0; let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; let mut path_plus_name: *mut libc::c_char = 0 as *mut libc::c_char; - let mut name_c: *mut libc::c_char = 0 as *mut libc::c_char; let mut set_default: libc::c_int; let mut buf: *mut libc::c_char = 0 as *mut libc::c_char; let mut buf_bytes: size_t = 0 as size_t; @@ -1125,9 +1091,8 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> let entry = entry.unwrap(); free(suffix as *mut libc::c_void); let name_f = entry.file_name(); - free(name_c as *mut libc::c_void); - name_c = to_cstring(name_f.to_string_lossy()); - suffix = dc_get_filesuffix_lc(name_c); + let name_c = name_f.to_c_string().unwrap(); + suffix = dc_get_filesuffix_lc(name_c.as_ptr()); if suffix.is_null() || strcmp(suffix, b"asc\x00" as *const u8 as *const libc::c_char) != 0 { @@ -1137,7 +1102,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> path_plus_name = dc_mprintf( b"%s/%s\x00" as *const u8 as *const libc::c_char, dir_name, - name_c, + name_c.as_ptr(), ); info!(context, 0, "Checking: {}", as_str(path_plus_name)); free(buf as *mut libc::c_void); @@ -1154,7 +1119,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> private_key = buf; free(buf2 as *mut libc::c_void); buf2 = dc_strdup(buf); - if 0 != dc_split_armored_data( + if dc_split_armored_data( buf2, &mut buf2_headerline, 0 as *mut *const libc::c_char, @@ -1175,7 +1140,12 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> } } set_default = 1; - if !strstr(name_c, b"legacy\x00" as *const u8 as *const libc::c_char).is_null() { + if !strstr( + name_c.as_ptr(), + b"legacy\x00" as *const u8 as *const libc::c_char, + ) + .is_null() + { info!( context, 0, @@ -1184,7 +1154,7 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> ); set_default = 0i32 } - if 0 == set_self_key(context, private_key, set_default) { + if !set_self_key(context, private_key, set_default) { continue; } imported_cnt += 1 @@ -1200,7 +1170,6 @@ unsafe fn import_self_keys(context: &Context, dir_name: *const libc::c_char) -> } } - free(name_c as *mut libc::c_void); free(suffix as *mut libc::c_void); free(path_plus_name as *mut libc::c_void); free(buf as *mut libc::c_void); @@ -1310,3 +1279,201 @@ unsafe fn export_key_to_asc_file( success } + +#[cfg(test)] +mod tests { + use super::*; + + use std::ffi::CStr; + + use num_traits::ToPrimitive; + + use crate::config::Config; + use crate::key; + use crate::test_utils::*; + + unsafe extern "C" fn logging_cb( + _ctx: &Context, + evt: Event, + _d1: uintptr_t, + d2: uintptr_t, + ) -> uintptr_t { + let to_str = |x| CStr::from_ptr(x as *const libc::c_char).to_str().unwrap(); + match evt { + Event::INFO => println!("I: {}", to_str(d2)), + Event::WARNING => println!("W: {}", to_str(d2)), + Event::ERROR => println!("E: {}", to_str(d2)), + _ => (), + } + 0 + } + + /// Create Alice with a pre-generated keypair. + fn create_alice_keypair(ctx: &Context) { + ctx.set_config(Config::ConfiguredAddr, Some("alice@example.org")) + .unwrap(); + + // The keypair was created using: + // let (public, private) = crate::pgp::dc_pgp_create_keypair("alice@example.com") + // .unwrap(); + // println!("{}", public.to_base64(64)); + // println!("{}", private.to_base64(64)); + let public = key::Key::from_base64( + concat!( + "xsBNBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l", + "FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX", + "AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4", + "Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9", + "iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw", + "oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAHNEzxhbGljZUBleGFtcGxl", + "LmNvbT7CwIkEEAEIADMCGQEFAl086fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iai", + "x4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9", + "OHUl3MrXtZ7QmHyOAFvbXE/6n5Eeh+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkK", + "A8e4cJqwDOHsyAnvQXZ7WNje9+BMzcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea", + "6zjGF0/qljTdoxTtsYpv5wXYuhwbYklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6", + "GkquJN814Y+xny4xhZzGOfue6SeP12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUK", + "u5wO9FFbgDySOSlEjByGejSGuBmho0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxD", + "Fc7ATQRdPOnsAQgA5oLxXRLnyugzOmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG", + "9JzDeQql+sYXgUSxOoIayItuXtnFn7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av", + "62n18Venlm0yNKpROPcZ6M/sc4m6uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/R", + "noW+/fhmwIg08dQ5m8hQe3GEOZEeLrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q", + "4zW8vk2ztB8ngwbnqYy8zrN1DCICN1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAm", + "jxLZfVDcoObFH3Cv2GB7BEYxv86KC2Y6T74Q/wARAQABwsB2BBgBCAAgBQJdPOn4", + "AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/dXshJnoW", + "qEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJywJoupwX", + "FNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJuiCQvR9m", + "MjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6RDXIeYJf", + "qrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMlammDliPw", + "sK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKObzPqgJCGw", + "jTglkixw+aSTXw==" + ), + KeyType::Public, + ) + .unwrap(); + let private = key::Key::from_base64( + concat!( + "xcLYBF086ewBCACmJKuoxIO6T87yi4Q3MyNpMch3Y8KrtHDQyUszU36eqM3Pmd1l", + "FrbcCd8ZWo2pq6OJSwsM/jjRGn1zo2YOaQeJRRrC+KrKGqSUtRSYQBPrPjE2YjSX", + "AMbu8jBI6VVUhHeghriBkK79PY9O/oRhIJC0p14IJe6CQ6OI2fTmTUHF9i/nJ3G4", + "Wb3/K1bU3yVfyPZjTZQPYPvvh03vxHULKurtYkX5DTEMSWsF4qzLMps+l87VuLV9", + "iQnbN7YMRLHHx2KkX5Ivi7JCefhCa54M0K3bDCCxuVWAM5wjQwNZjzR+WL+dYchw", + "oFvuF8NrlzjM9dSv+2rn+J7f99ijSXarzPC7ABEBAAEACAChqzVOuErmVRqvcYtq", + "m1xt1H+ZjX20z5Sn1fhTLYAcq236AWMqJvwxCXoKlc8bt2UfB+Ls9cQb1YcVq353", + "r0QiExiDeK3YlCxqd/peXJwFYTNKFC3QcnUhtpG9oS/jWjN+BRotGbjtu6Vj3M68", + "JJAq+mHJ0/9OyrqrREvGfo7uLZt7iMGemDlrDakvrbIyZrPLgay+nZ3dEFKeOQ6F", + "FrU05jyUVdoHBy0Tqx/6VpFUX9+IHcMHL2lTJB0nynBj+XZ/G4aX3WYoo3YlixHb", + "Iu35fGFA0TChoGaGPzqcI/kg2Z+b/BryG9NM3LA2cO8iGrGXAE1nPFp91jmCrQ3V", + "WushBADERP+uojjjfdO5J+RkmcFe9mFYDdtkhN+kV+LdePjiNNtcXMBhasstio0S", + "ut0GKnE7DFRhX7mkN9w2apJ2ooeFeVVWot18eSdp6Rzh6/1Z7TmhYFJ3oUxxLbnQ", + "sWIXIec1SzqWBFJUCn3IP0mCnJktFg/uGW6yLs01r5ds52uSBQQA2LSWiTwk9tEm", + "dr9mz3tHnmrkyGiyKhKGM1Z7Rch63D5yQc1s4kUMBlyuLL2QtM/e4dtaz2JAkO8k", + "QrYCnNgJ+2roTAK3kDZgYtymjdvK3HpQNtjVo7dds5RJVb6U618phZwU5WNFAEJW", + "yyImmycGfjLv+18cW/3mq0QVZejkM78D/2kHaIeJAowtBOFY2zDrKyDRoBHaUSgj", + "5BjGoviRC5rYihWDEyYDQ6mBJQstAD0Ty3MYzyUxl6ruB/BMWnMDFq5+TqtdBzu3", + "jCtZ8OEyH8A5Kdo68Wzo/PGxzMtusOdNj9+3PBmSq4yibJxbLSrn59aVUYpGLjeG", + "Kyvm9OTKkrOGN27NEzxhbGljZUBleGFtcGxlLmNvbT7CwIkEEAEIADMCGQEFAl08", + "6fgCGwMECwkIBwYVCAkKCwIDFgIBFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQ", + "k6DcNkbrcei3ogf/cruUmQ+th52TFHTHdkw9OHUl3MrXtZ7QmHyOAFvbXE/6n5Ee", + "h+eZoF8MWWV72m14Wbs+vTcNQkFVTdOPptkKA8e4cJqwDOHsyAnvQXZ7WNje9+BM", + "zcoipIUawHP4ORFaPDsKLZQ0b4wBbKn8ziea6zjGF0/qljTdoxTtsYpv5wXYuhwb", + "YklrLOqgSa5M7LXUe7E3g9mbg+9iX1GuB8m6GkquJN814Y+xny4xhZzGOfue6SeP", + "12jJMNSjSP7416dRq7794VGnkkW9V/7oFEUKu5wO9FFbgDySOSlEjByGejSGuBmh", + "o0iJSjcPjZ7EY/j3M3orq4dpza5C82OeSvxDFcfC2ARdPOnsAQgA5oLxXRLnyugz", + "OmNCy7dxV3JrDZscA6JNlJlDWIShT0YSs+zG9JzDeQql+sYXgUSxOoIayItuXtnF", + "n7tstwGoOnYvadm/e5/7V5fKAQRtCtdN51av62n18Venlm0yNKpROPcZ6M/sc4m6", + "uU6YRZ/a1idal8VGY0wLKlghjIBuIiBoVQ/RnoW+/fhmwIg08dQ5m8hQe3GEOZEe", + "LrTWL/9awPlXK7Y+DmJOoR4qbHWEcRfbzS6q4zW8vk2ztB8ngwbnqYy8zrN1DCIC", + "N1gYdlU++uVw6Bb1XfY8Cdldh1VLKpF35mAmjxLZfVDcoObFH3Cv2GB7BEYxv86K", + "C2Y6T74Q/wARAQABAAgAhSvFEYZoj1sSrXrHDjZOrryViGjCCH9t3pmkxLDrGIdd", + "KsFyN8ORUo6KUZS745yx3yFnI9EZ1IZvm9aF+jxk2lGJFtgLvfoxFOvGckwCSy8T", + "/MCiJZkz01hWo5s2VCLJheWL/GqTKjS5wXDcm+y8Wtilh+UawycdlDsSNr/D4MZL", + "j3Chq9K03l5UIR8DcC7SavNi55R2oGOfboXsdvwOlrNZdCkZOlXDI4ZKFwbDHCtp", + "Do5FS30hnJi2TecUPZWB1CaGFWnevINd4ikugVjcAoZj/QAIvfrOCgqisF/Ylg9u", + "RMUPBapmcJUueILwd0iQqvGG0aCqtchvSmlg15/lQQQA9G1NNjNAH+NQrXvDJFJe", + "/V1U3F3pz7jCjQa69c0dxSBUeNX1pG8XXD6tSkkd4Ni1mzZGcZXOmVUM6cA9I7RH", + "95RqV+QIfnXVneCRrlCjV8m6OBlkivkESXc3nW5wtCIfw7oKg9w1xuVNUaAlbCt9", + "QVLaxXJiY7ad0f5U9XJ1+w8EAPFs+M/+GZK1wOZYBL1vo7x0gL9ZggmjC4B+viBJ", + "8Q60mqTrphYFsbXHuwKV0g9aIoZMucKyEE0QLR7imttiLEz1nD8bfEScbGy9ZG//", + "wRfyJmCVAjA0pQ6LtB93d70PSVzzJrMHgbLKrDuSd6RChl7n9BIEdVyk7LEph0Yg", + "9UsRBADm6DvpKL+P3lQ0eLTfAgcQTOqLZDYmI3PvqqSkHb1kHChqOXXs8hGOSSwK", + "Gjcd4CZeNOGWR42rZyRhVgtkt6iYviIaVAWUfme6K+sLQBCeyMlmEGtykAA+LmPB", + "f4zdyUNADfoxgZF3EKHf6I3nlVn5cdT+o/9vjdY2XAOwcls1RzaFwsB2BBgBCAAg", + "BQJdPOn4AhsMFiEE+iaix4d0doj87Q0ek6DcNkbrcegACgkQk6DcNkbrcegLxwf/", + "dXshJnoWqEnRsf6rVb9/Mc66ti+NVQLfNd275dybh/QIJdK3NmSxdnTPIEJRVspJ", + "ywJoupwXFNrnHG2Ff6QPvHqI+/oNu986r9d7Z1oQibbLHKt8t6kpOfg/xGxotagJ", + "uiCQvR9mMjD1DqsdB37SjDxGupZOOJSXWi6KX60IE+uM+QOBfeOZziQwuFmA5wV6", + "RDXIeYJfqrcbeXeR1d0nfNpPHQR1gBiqmxNb6KBbdXD2+EXW60axC7D2b1APmzMl", + "ammDliPwsK9U1rc9nuquEBvGDOJf4K+Dzn+mDCqRpP6uAuQ7RKHyim4uyN0wwKOb", + "zPqgJCGwjTglkixw+aSTXw==" + ), + KeyType::Private, + ) + .unwrap(); + let saved = key::dc_key_save_self_keypair( + &ctx, + &public, + &private, + "alice@example.org", + 1, + &ctx.sql, + ); + assert_eq!(saved, true, "Failed to save Alice's key"); + } + + #[test] + fn test_render_setup_file() { + let t = test_context(Some(logging_cb)); + + create_alice_keypair(&t.ctx); // Trick things to think we're configured. + let msg = dc_render_setup_file(&t.ctx, "hello").unwrap(); + println!("{}", &msg); + // Check some substrings, indicating things got substituted. + // In particular note the mixing of `\r\n` and `\n` depending + // on who generated the stings. + assert!(msg.contains("Autocrypt Setup Message</title")); + assert!(msg.contains("<h1>Autocrypt Setup Message</h1>")); + assert!(msg.contains("<p>This is the Autocrypt Setup Message used to")); + assert!(msg.contains("-----BEGIN PGP MESSAGE-----\r\n")); + assert!(msg.contains("Passphrase-Format: numeric9x4\r\n")); + assert!(msg.contains("Passphrase-Begin: he\n")); + assert!(msg.contains("==\n")); + assert!(msg.contains("-----END PGP MESSAGE-----\n")); + } + + unsafe extern "C" fn ac_setup_msg_cb( + ctx: &Context, + evt: Event, + d1: uintptr_t, + d2: uintptr_t, + ) -> uintptr_t { + if evt == Event::GET_STRING && d1 == StockMessage::AcSetupMsgBody.to_usize().unwrap() { + "hello\r\nthere".strdup() as usize + } else { + logging_cb(ctx, evt, d1, d2) + } + } + + #[test] + fn test_render_setup_file_newline_replace() { + let t = test_context(Some(ac_setup_msg_cb)); + create_alice_keypair(&t.ctx); + let msg = dc_render_setup_file(&t.ctx, "pw").unwrap(); + println!("{}", &msg); + assert!(msg.contains("<p>hello<br>there</p>")); + } + + #[test] + fn test_create_setup_code() { + let t = dummy_context(); + let setupcode = dc_create_setup_code(&t.ctx); + assert_eq!(setupcode.len(), 44); + assert_eq!(setupcode.chars().nth(4).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(9).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(14).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(19).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(24).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(29).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(34).unwrap(), '-'); + assert_eq!(setupcode.chars().nth(39).unwrap(), '-'); + } +} diff --git a/src/dc_job.rs b/src/dc_job.rs index e5501bc77..e253fabad 100644 --- a/src/dc_job.rs +++ b/src/dc_job.rs @@ -17,12 +17,14 @@ use crate::dc_mimefactory::*; use crate::dc_msg::*; use crate::dc_tools::*; use crate::imap::*; -use crate::keyhistory::*; use crate::param::*; use crate::sql; use crate::types::*; use crate::x::*; +const DC_IMAP_THREAD: libc::c_int = 100; +const DC_SMTP_THREAD: libc::c_int = 5000; + // thread IDs // jobs in the INBOX-thread, range from DC_IMAP_THREAD..DC_IMAP_THREAD+999 // low priority ... @@ -51,15 +53,15 @@ pub unsafe fn dc_perform_imap_jobs(context: &Context) { info!(context, 0, "dc_perform_imap_jobs starting.",); let probe_imap_network = *context.probe_imap_network.clone().read().unwrap(); - *context.probe_imap_network.write().unwrap() = 0; - *context.perform_inbox_jobs_needed.write().unwrap() = 0; + *context.probe_imap_network.write().unwrap() = false; + *context.perform_inbox_jobs_needed.write().unwrap() = false; - dc_job_perform(context, 100, probe_imap_network); + dc_job_perform(context, DC_IMAP_THREAD, probe_imap_network); info!(context, 0, "dc_perform_imap_jobs ended.",); } -unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: libc::c_int) { - let query = if probe_network == 0 { +unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: bool) { + let query = if !probe_network { // processing for first-try and after backoff-timeouts: // process jobs in the order they were added. "SELECT id, action, foreign_id, param, added_timestamp, desired_timestamp, tries \ @@ -74,7 +76,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: let params_no_probe = params![thread as i64, time()]; let params_probe = params![thread as i64]; - let params: &[&dyn rusqlite::ToSql] = if probe_network == 0 { + let params: &[&dyn rusqlite::ToSql] = if !probe_network { params_no_probe } else { params_probe @@ -116,7 +118,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: context, 0, "{}-job #{}, action {} started...", - if thread == 100 { "INBOX" } else { "SMTP" }, + if thread == DC_IMAP_THREAD { + "INBOX" + } else { + "SMTP" + }, job.job_id, job.action, ); @@ -129,7 +135,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: dc_job_kill_action(context, job.action); dc_jobthread_suspend(context, &context.sentbox_thread.clone().read().unwrap(), 1); dc_jobthread_suspend(context, &context.mvbox_thread.clone().read().unwrap(), 1); - dc_suspend_smtp_thread(context, 1); + dc_suspend_smtp_thread(context, true); } let mut tries = 0; @@ -167,7 +173,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: &mut context.mvbox_thread.clone().read().unwrap(), 0, ); - dc_suspend_smtp_thread(context, 0); + dc_suspend_smtp_thread(context, false); break; } else if job.try_again == 2 { // just try over next loop unconditionally, the ui typically interrupts idle when the file (video) is ready @@ -175,7 +181,11 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: context, 0, "{}-job #{} not yet ready and will be delayed.", - if thread == 100 { "INBOX" } else { "SMTP" }, + if thread == DC_IMAP_THREAD { + "INBOX" + } else { + "SMTP" + }, job.job_id ); } else if job.try_again == -1 || job.try_again == 3 { @@ -189,13 +199,17 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: context, 0, "{}-job #{} not succeeded on try #{}, retry in ADD_TIME+{} (in {} seconds).", - if thread == 100 { "INBOX" } else { "SMTP" }, + if thread == DC_IMAP_THREAD { + "INBOX" + } else { + "SMTP" + }, job.job_id as libc::c_int, tries, time_offset, job.added_timestamp + time_offset - time() ); - if thread == 5000 && tries < 17 - 1 { + if thread == DC_SMTP_THREAD && tries < 17 - 1 { context .smtp_state .clone() @@ -210,7 +224,7 @@ unsafe fn dc_job_perform(context: &Context, thread: libc::c_int, probe_network: } dc_job_delete(context, &mut job); } - if 0 == probe_network { + if !probe_network { continue; } // on dc_maybe_network() we stop trying here; @@ -236,7 +250,7 @@ fn dc_job_delete(context: &Context, job: &dc_job_t) -> bool { * Tools ******************************************************************************/ #[allow(non_snake_case)] -unsafe fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 { +fn get_backoff_time_offset(c_tries: libc::c_int) -> i64 { // results in ~3 weeks for the last backoff timespan let mut N = 2_i32.pow((c_tries - 1) as u32); N = N * 60; @@ -264,11 +278,11 @@ fn dc_job_update(context: &Context, job: &dc_job_t) -> bool { .is_ok() } -unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: libc::c_int) { +unsafe fn dc_suspend_smtp_thread(context: &Context, suspend: bool) { context.smtp_state.0.lock().unwrap().suspended = suspend; - if 0 != suspend { + if suspend { loop { - if context.smtp_state.0.lock().unwrap().doing_jobs == 0 { + if !context.smtp_state.0.lock().unwrap().doing_jobs { return; } std::thread::sleep(std::time::Duration::from_micros(300 * 1000)); @@ -299,7 +313,7 @@ unsafe fn dc_job_do_DC_JOB_SEND(context: &Context, job: &mut dc_job_t) { } match current_block { 13109137661213826276 => { - filename = to_cstring(job.param.get(Param::File).unwrap_or_default()); + filename = job.param.get(Param::File).unwrap_or_default().strdup(); if strlen(filename) == 0 { warn!(context, 0, "Missing file name for job {}", job.job_id,); } else if !(0 == dc_read_file(context, filename, &mut buf, &mut buf_bytes)) { @@ -772,7 +786,7 @@ unsafe fn dc_add_smtp_job( b"\x1e\x00" as *const u8 as *const libc::c_char, ); param.set(Param::File, as_str(pathNfilename)); - param.set(Param::File, as_str(recipients)); + param.set(Param::Recipients, as_str(recipients)); dc_job_add( context, action, @@ -801,10 +815,10 @@ pub unsafe fn dc_job_add( delay_seconds: libc::c_int, ) { let timestamp = time(); - let thread = if action >= 100 && action < 100 + 1000 { - 100 - } else if action >= 5000 && action < 5000 + 1000 { - 5000 + let thread = if action >= DC_IMAP_THREAD && action < DC_IMAP_THREAD + 1000 { + DC_IMAP_THREAD + } else if action >= DC_SMTP_THREAD && action < DC_SMTP_THREAD + 1000 { + DC_SMTP_THREAD } else { return; }; @@ -823,7 +837,7 @@ pub unsafe fn dc_job_add( ] ).ok(); - if thread == 100 { + if thread == DC_IMAP_THREAD { dc_interrupt_imap_idle(context); } else { dc_interrupt_smtp_idle(context); @@ -844,7 +858,7 @@ pub unsafe fn dc_interrupt_smtp_idle(context: &Context) { pub unsafe fn dc_interrupt_imap_idle(context: &Context) { info!(context, 0, "Interrupting IMAP-IDLE...",); - *context.perform_inbox_jobs_needed.write().unwrap() = 1; + *context.perform_inbox_jobs_needed.write().unwrap() = true; context.inbox.read().unwrap().interrupt_idle(); } @@ -952,7 +966,7 @@ pub fn dc_perform_imap_idle(context: &Context) { connect_to_inbox(context, &inbox); - if 0 != *context.perform_inbox_jobs_needed.clone().read().unwrap() { + if *context.perform_inbox_jobs_needed.clone().read().unwrap() { info!( context, 0, "INBOX-IDLE will not be started because of waiting jobs." @@ -1027,26 +1041,26 @@ pub unsafe fn dc_perform_smtp_jobs(context: &Context) { let mut state = lock.lock().unwrap(); let probe_smtp_network = state.probe_network; - state.probe_network = 0; + state.probe_network = false; state.perform_jobs_needed = 0; - if 0 != state.suspended { + if state.suspended { info!(context, 0, "SMTP-jobs suspended.",); return; } - state.doing_jobs = 1; + state.doing_jobs = true; probe_smtp_network }; info!(context, 0, "SMTP-jobs started...",); - dc_job_perform(context, 5000, probe_smtp_network); + dc_job_perform(context, DC_SMTP_THREAD, probe_smtp_network); info!(context, 0, "SMTP-jobs ended."); { let &(ref lock, _) = &*context.smtp_state.clone(); let mut state = lock.lock().unwrap(); - state.doing_jobs = 0; + state.doing_jobs = false; } } @@ -1062,7 +1076,7 @@ pub unsafe fn dc_perform_smtp_idle(context: &Context) { 0, "SMTP-idle will not be started because of waiting jobs.", ); } else { - let dur = get_next_wakeup_time(context, 5000); + let dur = get_next_wakeup_time(context, DC_SMTP_THREAD); loop { let res = cvar.wait_timeout(state, dur).unwrap(); @@ -1108,9 +1122,9 @@ pub unsafe fn dc_maybe_network(context: &Context) { { let &(ref lock, _) = &*context.smtp_state.clone(); let mut state = lock.lock().unwrap(); - state.probe_network = 1; + state.probe_network = true; - *context.probe_imap_network.write().unwrap() = 1; + *context.probe_imap_network.write().unwrap() = true; } dc_interrupt_smtp_idle(context); @@ -1162,15 +1176,14 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in } else { // no redo, no IMAP. moreover, as the data does not exist, there is no need in calling dc_set_msg_failed() if msgtype_has_file((*mimefactory.msg).type_0) { - let pathNfilename = to_cstring( - (*mimefactory.msg) - .param - .get(Param::File) - .unwrap_or_default(), - ); + let pathNfilename = (*mimefactory.msg) + .param + .get(Param::File) + .unwrap_or_default() + .strdup(); if strlen(pathNfilename) > 0 { - if ((*mimefactory.msg).type_0 == DC_MSG_IMAGE - || (*mimefactory.msg).type_0 == DC_MSG_GIF) + if ((*mimefactory.msg).type_0 == Viewtype::Image + || (*mimefactory.msg).type_0 == Viewtype::Gif) && !(*mimefactory.msg).param.exists(Param::Width) { let mut buf = 0 as *mut libc::c_uchar; @@ -1263,13 +1276,6 @@ pub unsafe fn dc_job_send_msg(context: &Context, msg_id: uint32_t) -> libc::c_in (*mimefactory.msg).param.set_int(Param::GuranteeE2ee, 1); dc_msg_save_param_to_disk(mimefactory.msg); } - dc_add_to_keyhistory( - context, - 0 as *const libc::c_char, - 0, - 0 as *const libc::c_char, - 0 as *const libc::c_char, - ); success = dc_add_smtp_job(context, 5901i32, &mut mimefactory); } } diff --git a/src/dc_location.rs b/src/dc_location.rs index bdef8671e..5e6e599ca 100644 --- a/src/dc_location.rs +++ b/src/dc_location.rs @@ -1,6 +1,7 @@ use std::ffi::CString; use crate::constants::Event; +use crate::constants::*; use crate::context::*; use crate::dc_array::*; use crate::dc_chat::*; @@ -51,7 +52,7 @@ impl dc_location { #[allow(non_camel_case_types)] pub struct dc_kml_t { pub addr: *mut libc::c_char, - pub locations: *mut dc_array_t, + pub locations: Option<Vec<dc_location>>, pub tag: libc::c_int, pub curr: dc_location, } @@ -60,7 +61,7 @@ impl dc_kml_t { pub fn new() -> Self { dc_kml_t { addr: std::ptr::null_mut(), - locations: std::ptr::null_mut(), + locations: None, tag: 0, curr: dc_location::new(), } @@ -98,13 +99,9 @@ pub unsafe fn dc_send_locations_to_chat( .is_ok() { if 0 != seconds && !is_sending_locations_before { - msg = dc_msg_new(context, 10i32); - (*msg).text = to_cstring(context.stock_system_msg( - StockMessage::MsgLocationEnabled, - "", - "", - 0, - )); + msg = dc_msg_new(context, Viewtype::Text); + (*msg).text = + Some(context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0)); (*msg).param.set_int(Param::Cmd, 8); dc_send_msg(context, chat_id, msg); } else if 0 == seconds && is_sending_locations_before { @@ -250,12 +247,12 @@ pub fn dc_get_locations( Ok(loc) }, |locations| { - let mut ret = dc_array_t::new_locations(500); + let mut ret = Vec::new(); for location in locations { - ret.add_location(location?); + ret.push(location?); } - Ok(ret.into_raw()) + Ok(dc_array_t::from(ret).into_raw()) }, ) .unwrap_or_else(|_| std::ptr::null_mut()) @@ -350,7 +347,7 @@ pub fn dc_get_location_kml( } if 0 != success { - unsafe { to_cstring(ret) } + unsafe { ret.strdup() } } else { std::ptr::null_mut() } @@ -364,7 +361,7 @@ unsafe fn get_kml_timestamp(utc: i64) -> *mut libc::c_char { let res = chrono::NaiveDateTime::from_timestamp(utc, 0) .format("%Y-%m-%dT%H:%M:%SZ") .to_string(); - to_cstring(res) + res.strdup() } pub unsafe fn dc_get_message_kml( @@ -422,13 +419,14 @@ pub unsafe fn dc_save_locations( context: &Context, chat_id: u32, contact_id: u32, - locations: *const dc_array_t, + locations_opt: &Option<Vec<dc_location>>, independent: libc::c_int, ) -> u32 { - if chat_id <= 9 || locations.is_null() { + if chat_id <= 9 || locations_opt.is_none() { return 0; } + let locations = locations_opt.as_ref().unwrap(); context .sql .prepare2( @@ -440,31 +438,29 @@ pub unsafe fn dc_save_locations( let mut newest_timestamp = 0; let mut newest_location_id = 0; - for i in 0..dc_array_get_cnt(locations) { - let location = dc_array_get_ptr(locations, i as size_t) as *mut dc_location; - + for location in locations { let exists = - stmt_test.exists(params![(*location).timestamp, contact_id as i32])?; + stmt_test.exists(params![location.timestamp, contact_id as i32])?; if 0 != independent || !exists { stmt_insert.execute(params![ - (*location).timestamp, + location.timestamp, contact_id as i32, chat_id as i32, - (*location).latitude, - (*location).longitude, - (*location).accuracy, + location.latitude, + location.longitude, + location.accuracy, independent, ])?; - if (*location).timestamp > newest_timestamp { - newest_timestamp = (*location).timestamp; + if location.timestamp > newest_timestamp { + newest_timestamp = location.timestamp; newest_location_id = sql::get_rowid2_with_conn( context, conn, "locations", "timestamp", - (*location).timestamp, + location.timestamp, "from_id", contact_id as i32, ); @@ -499,7 +495,7 @@ pub unsafe fn dc_kml_parse( } else { content_nullterminated = dc_null_terminate(content, content_bytes as libc::c_int); if !content_nullterminated.is_null() { - kml.locations = dc_array_new_locations(100); + kml.locations = Some(Vec::with_capacity(100)); dc_saxparser_init( &mut saxparser, &mut kml as *mut dc_kml_t as *mut libc::c_void, @@ -585,7 +581,7 @@ unsafe fn kml_endtag_cb(userdata: *mut libc::c_void, tag: *const libc::c_char) { && 0. != (*kml).curr.longitude { let location = (*kml).curr.clone(); - (*(*kml).locations).add_location(location); + ((*kml).locations.as_mut().unwrap()).push(location); } (*kml).tag = 0 }; @@ -636,11 +632,7 @@ unsafe fn kml_starttag_cb( }; } -pub unsafe fn dc_kml_unref(kml: *mut dc_kml_t) { - if kml.is_null() { - return; - } - dc_array_unref((*kml).locations); +pub unsafe fn dc_kml_unref(kml: &mut dc_kml_t) { free((*kml).addr as *mut libc::c_void); } @@ -707,7 +699,7 @@ pub unsafe fn dc_job_do_DC_JOB_MAYBE_SEND_LOCATIONS(context: &Context, _job: *mu // the easiest way to determine this, is to check for an empty message queue. // (might not be 100%, however, as positions are sent combined later // and dc_set_location() is typically called periodically, this is ok) - let mut msg = dc_msg_new(context, 10); + let mut msg = dc_msg_new(context, Viewtype::Text); (*msg).hidden = 1; (*msg).param.set_int(Param::Cmd, 9); dc_send_msg(context, chat_id as u32, msg); diff --git a/src/dc_lot.rs b/src/dc_lot.rs index 429859636..87ef926a1 100644 --- a/src/dc_lot.rs +++ b/src/dc_lot.rs @@ -1,6 +1,6 @@ +use crate::contact::*; use crate::context::Context; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_msg::*; use crate::dc_tools::*; use crate::stock::StockMessage; @@ -127,41 +127,58 @@ pub unsafe fn dc_lot_fill( mut lot: *mut dc_lot_t, msg: *mut dc_msg_t, chat: *const Chat, - contact: *const dc_contact_t, + contact: Option<&Contact>, context: &Context, ) { if lot.is_null() || (*lot).magic != 0x107107i32 as libc::c_uint || msg.is_null() { return; } if (*msg).state == 19i32 { - (*lot).text1 = to_cstring(context.stock_str(StockMessage::Draft)); + (*lot).text1 = context.stock_str(StockMessage::Draft).strdup(); (*lot).text1_meaning = 1i32 } else if (*msg).from_id == 1i32 as libc::c_uint { if 0 != dc_msg_is_info(msg) || 0 != dc_chat_is_self_talk(chat) { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else { - (*lot).text1 = to_cstring(context.stock_str(StockMessage::SelfMsg)); + (*lot).text1 = context.stock_str(StockMessage::SelfMsg).strdup(); (*lot).text1_meaning = 3i32 } } else if chat.is_null() { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else if (*chat).type_0 == 120i32 || (*chat).type_0 == 130i32 { - if 0 != dc_msg_is_info(msg) || contact.is_null() { + if 0 != dc_msg_is_info(msg) || contact.is_none() { (*lot).text1 = 0 as *mut libc::c_char; (*lot).text1_meaning = 0i32 } else { if !chat.is_null() && (*chat).id == 1i32 as libc::c_uint { - (*lot).text1 = dc_contact_get_display_name(contact) + if let Some(contact) = contact { + (*lot).text1 = contact.get_display_name().strdup(); + } else { + (*lot).text1 = std::ptr::null_mut(); + } } else { - (*lot).text1 = dc_contact_get_first_name(contact) + if let Some(contact) = contact { + (*lot).text1 = contact.get_first_name().strdup(); + } else { + (*lot).text1 = std::ptr::null_mut(); + } } - (*lot).text1_meaning = 2i32 + (*lot).text1_meaning = 2i32; } } - (*lot).text2 = - dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, &mut (*msg).param, 160, context); + + let message_text = (*msg).text.as_ref().unwrap(); + + (*lot).text2 = dc_msg_get_summarytext_by_raw( + (*msg).type_0, + message_text.strdup(), + &mut (*msg).param, + 160, + context, + ); + (*lot).timestamp = dc_msg_get_timestamp(msg); (*lot).state = (*msg).state; } diff --git a/src/dc_mimefactory.rs b/src/dc_mimefactory.rs index 8dc83f8d6..dfbb84f86 100644 --- a/src/dc_mimefactory.rs +++ b/src/dc_mimefactory.rs @@ -9,11 +9,12 @@ use mmime::mailmime_types_helper::*; use mmime::mailmime_write_mem::*; use mmime::mmapstring::*; use mmime::other::*; +use std::ptr; use crate::constants::*; +use crate::contact::*; use crate::context::Context; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_e2ee::*; use crate::dc_location::*; use crate::dc_msg::*; @@ -158,13 +159,13 @@ pub unsafe fn dc_mimefactory_load_msg( |rows| { for row in rows { let (authname, addr) = row?; - let addr_c = to_cstring(addr); + let addr_c = addr.strdup(); if clist_search_string_nocase((*factory).recipients_addr, addr_c) == 0 { clist_insert_after( (*factory).recipients_names, (*(*factory).recipients_names).last, if !authname.is_empty() { - to_cstring(authname) + authname.strdup() } else { std::ptr::null_mut() } as *mut libc::c_void, @@ -188,7 +189,7 @@ pub unsafe fn dc_mimefactory_load_msg( if command == 5 { let email_to_remove = (*(*factory).msg).param.get(Param::Arg).unwrap_or_default(); - let email_to_remove_c = to_cstring(email_to_remove); + let email_to_remove_c = email_to_remove.strdup(); let self_addr = context .sql @@ -234,8 +235,8 @@ pub unsafe fn dc_mimefactory_load_msg( ); match row { Ok((in_reply_to, references)) => { - (*factory).in_reply_to = to_cstring(in_reply_to); - (*factory).references = to_cstring(references); + (*factory).in_reply_to = in_reply_to.strdup(); + (*factory).references = references.strdup(); } Err(err) => { error!( @@ -259,27 +260,28 @@ pub unsafe fn dc_mimefactory_load_msg( unsafe fn load_from(mut factory: *mut dc_mimefactory_t) { let context = (*factory).context; - (*factory).from_addr = to_cstring( - context - .sql - .get_config(context, "configured_addr") - .unwrap_or_default(), - ); + (*factory).from_addr = context + .sql + .get_config(context, "configured_addr") + .unwrap_or_default() + .strdup(); - (*factory).from_displayname = to_cstring( - context - .sql - .get_config(context, "displayname") - .unwrap_or_default(), - ); - (*factory).selfstatus = to_cstring( - context - .sql - .get_config(context, "selfstatus") - .unwrap_or_default(), - ); + (*factory).from_displayname = context + .sql + .get_config(context, "displayname") + .unwrap_or_default() + .strdup(); + + (*factory).selfstatus = context + .sql + .get_config(context, "selfstatus") + .unwrap_or_default() + .strdup(); if (*factory).selfstatus.is_null() { - (*factory).selfstatus = to_cstring((*factory).context.stock_str(StockMessage::StatusLine)); + (*factory).selfstatus = (*factory) + .context + .stock_str(StockMessage::StatusLine) + .strdup(); }; } @@ -292,7 +294,6 @@ pub unsafe fn dc_mimefactory_load_mdn( } let mut success = 0; - let mut contact = 0 as *mut dc_contact_t; (*factory).recipients_names = clist_new(); (*factory).recipients_addr = clist_new(); @@ -304,24 +305,19 @@ pub unsafe fn dc_mimefactory_load_mdn( .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, - (*(*factory).msg).from_id, - )) - { - if !(0 != (*contact).blocked || (*(*factory).msg).chat_id <= 9 as libc::c_uint) { + if !dc_msg_load_from_db((*factory).msg, (*factory).context, msg_id) { + return success; + } + + if let Ok(contact) = Contact::load_from_db((*factory).context, (*(*factory).msg).from_id) { + if !(contact.is_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) + (if !contact.get_authname().is_empty() { + contact.get_authname().strdup() } else { 0 as *mut libc::c_char }) as *mut libc::c_void, @@ -329,7 +325,7 @@ pub unsafe fn dc_mimefactory_load_mdn( clist_insert_after( (*factory).recipients_addr, (*(*factory).recipients_addr).last, - dc_strdup((*contact).addr) as *mut libc::c_void, + contact.get_addr().strdup() as *mut libc::c_void, ); load_from(factory); (*factory).timestamp = dc_create_smeared_timestamp((*factory).context); @@ -344,15 +340,13 @@ pub unsafe fn dc_mimefactory_load_mdn( } } - dc_contact_unref(contact); - success } // TODO should return bool /rtn pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc::c_int { let subject: *mut mailimf_subject; - let mut current_block: u64; + let mut ok_to_continue = true; let imf_fields: *mut mailimf_fields; let mut message: *mut mailmime = 0 as *mut mailmime; let mut message_text: *mut libc::c_char = 0 as *mut libc::c_char; @@ -477,26 +471,26 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: references_list, 0 as *mut libc::c_char, ); + + let os_name = &(*factory).context.os_name; + let os_part = os_name + .as_ref() + .map(|s| format!("/{}", s)) + .unwrap_or_default(); + let os_part = CString::new(os_part).expect("String -> CString conversion failed"); + mailimf_fields_add( imf_fields, mailimf_field_new_custom( strdup(b"X-Mailer\x00" as *const u8 as *const libc::c_char), dc_mprintf( - b"Delta Chat Core %s%s%s\x00" as *const u8 as *const libc::c_char, + b"Delta Chat Core %s%s\x00" as *const u8 as *const libc::c_char, DC_VERSION_STR as *const u8 as *const libc::c_char, - if !(*(*factory).context).os_name.is_null() { - b"/\x00" as *const u8 as *const libc::c_char - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if !(*(*factory).context).os_name.is_null() { - (*(*factory).context).os_name - } else { - b"\x00" as *const u8 as *const libc::c_char - }, + os_part.as_ptr(), ), ), ); + mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -573,8 +567,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); if command == 5 { - let email_to_remove = - to_cstring((*msg).param.get(Param::Arg).unwrap_or_default()); + let email_to_remove = (*msg).param.get(Param::Arg).unwrap_or_default().strdup(); if strlen(email_to_remove) > 0 { mailimf_fields_add( imf_fields, @@ -589,7 +582,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } } else if command == 4 { do_gossip = 1; - let email_to_add = to_cstring((*msg).param.get(Param::Arg).unwrap_or_default()); + let email_to_add = (*msg).param.get(Param::Arg).unwrap_or_default().strdup(); if strlen(email_to_add) > 0 { mailimf_fields_add( imf_fields, @@ -619,7 +612,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ); } } else if command == 2 { - let value_to_add = to_cstring((*msg).param.get(Param::Arg).unwrap_or_default()); + let value_to_add = (*msg).param.get(Param::Arg).unwrap_or_default().strdup(); mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -661,11 +654,13 @@ 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 = - to_cstring((*factory).context.stock_str(StockMessage::AcSetupMsgBody)); + placeholdertext = (*factory) + .context + .stock_str(StockMessage::AcSetupMsgBody) + .strdup(); } if command == 7 { - let step = to_cstring((*msg).param.get(Param::Arg).unwrap_or_default()); + let step = (*msg).param.get(Param::Arg).unwrap_or_default().strdup(); if strlen(step) > 0 { info!( (*msg).context, @@ -680,7 +675,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: step, ), ); - let param2 = to_cstring((*msg).param.get(Param::Arg2).unwrap_or_default()); + let param2 = (*msg).param.get(Param::Arg2).unwrap_or_default().strdup(); if strlen(param2) > 0 { mailimf_fields_add( imf_fields, @@ -708,7 +703,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ), ); } - let fingerprint = to_cstring((*msg).param.get(Param::Arg3).unwrap_or_default()); + let fingerprint = (*msg).param.get(Param::Arg3).unwrap_or_default().strdup(); if strlen(fingerprint) > 0 { mailimf_fields_add( imf_fields, @@ -722,7 +717,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ); } let grpid = match (*msg).param.get(Param::Arg4) { - Some(id) => to_cstring(id), + Some(id) => id.strdup(), None => std::ptr::null_mut(), }; if !grpid.is_null() { @@ -738,10 +733,9 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } } } - if let Some(grpimage) = grpimage { let mut meta = dc_msg_new_untyped((*factory).context); - (*meta).type_0 = DC_MSG_IMAGE as libc::c_int; + (*meta).type_0 = Viewtype::Image; (*meta).param.set(Param::File, grpimage); let mut filename_as_sent = 0 as *mut libc::c_char; @@ -762,11 +756,11 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: dc_msg_unref(meta); } - if (*msg).type_0 == DC_MSG_VOICE - || (*msg).type_0 == DC_MSG_AUDIO - || (*msg).type_0 == DC_MSG_VIDEO + if (*msg).type_0 == Viewtype::Voice + || (*msg).type_0 == Viewtype::Audio + || (*msg).type_0 == Viewtype::Video { - if (*msg).type_0 == DC_MSG_VOICE { + if (*msg).type_0 == Viewtype::Voice { mailimf_fields_add( imf_fields, mailimf_field_new_custom( @@ -798,12 +792,17 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ) } - let mut final_text: *const libc::c_char = 0 as *const libc::c_char; - if !placeholdertext.is_null() { - final_text = placeholdertext - } else if !(*msg).text.is_null() && 0 != *(*msg).text.offset(0isize) as libc::c_int { - final_text = (*msg).text - } + let final_text = { + if !placeholdertext.is_null() { + to_string(placeholdertext) + } else if let Some(ref text) = (*msg).text { + text.clone() + } else { + "".into() + } + }; + let final_text = CString::yolo(final_text); + let footer: *mut libc::c_char = (*factory).selfstatus; message_text = dc_mprintf( b"%s%s%s%s%s\x00" as *const u8 as *const libc::c_char, @@ -812,12 +811,8 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: } else { b"\x00" as *const u8 as *const libc::c_char }, - if !final_text.is_null() { - final_text - } else { - b"\x00" as *const u8 as *const libc::c_char - }, - if !final_text.is_null() + final_text.as_ptr(), + if final_text != CString::yolo("") && !footer.is_null() && 0 != *footer.offset(0isize) as libc::c_int { @@ -844,7 +839,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: /* add attachment part */ if msgtype_has_file((*msg).type_0) { - if 0 == is_file_size_okay(msg) { + if !is_file_size_okay(msg) { let error: *mut libc::c_char = dc_mprintf( b"Message exceeds the recommended %i MB.\x00" as *const u8 as *const libc::c_char, @@ -852,7 +847,7 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ); set_error(factory, error); free(error as *mut libc::c_void); - current_block = 11328123142868406523; + ok_to_continue = false; } else { let file_part: *mut mailmime = build_body_file(msg, 0 as *const libc::c_char, 0 as *mut *mut libc::c_char); @@ -860,86 +855,73 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: mailmime_smart_add_part(message, file_part); parts += 1 } - current_block = 13000670339742628194; } - } else { - current_block = 13000670339742628194; } - match current_block { - 11328123142868406523 => {} - _ => { - if parts == 0 { - set_error( - factory, - b"Empty message.\x00" as *const u8 as *const libc::c_char, - ); - current_block = 11328123142868406523; - } else { - if !meta_part.is_null() { - mailmime_smart_add_part(message, meta_part); - } - if (*msg).param.exists(Param::SetLatitude) { - let latitude = (*msg) - .param - .get_float(Param::SetLatitude) - .unwrap_or_default(); - let longitude = (*msg) - .param - .get_float(Param::SetLongitude) - .unwrap_or_default(); - let kml_file = - dc_get_message_kml((*msg).timestamp_sort, latitude, longitude); - if !kml_file.is_null() { - let content_type = mailmime_content_new_with_str( - b"application/vnd.google-earth.kml+xml\x00" as *const u8 - as *const libc::c_char, - ); - let mime_fields = mailmime_fields_new_filename( - MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, - dc_strdup( - b"message.kml\x00" as *const u8 as *const libc::c_char, - ), - MAILMIME_MECHANISM_8BIT as libc::c_int, - ); - let kml_mime_part = mailmime_new_empty(content_type, mime_fields); - mailmime_set_body_text(kml_mime_part, kml_file, strlen(kml_file)); - - mailmime_smart_add_part(message, kml_mime_part); - } - } - - if 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, - &mut last_added_location_id, + if ok_to_continue { + if parts == 0 { + set_error( + factory, + b"Empty message.\x00" as *const u8 as *const libc::c_char, + ); + ok_to_continue = false; + } else { + if !meta_part.is_null() { + mailmime_smart_add_part(message, meta_part); + } + if (*msg).param.exists(Param::SetLatitude) { + let latitude = (*msg) + .param + .get_float(Param::SetLatitude) + .unwrap_or_default(); + let longitude = (*msg) + .param + .get_float(Param::SetLongitude) + .unwrap_or_default(); + let kml_file = + dc_get_message_kml((*msg).timestamp_sort, latitude, longitude); + if !kml_file.is_null() { + let content_type = mailmime_content_new_with_str( + b"application/vnd.google-earth.kml+xml\x00" as *const u8 + as *const libc::c_char, ); - if !kml_file.is_null() { - let content_type: *mut mailmime_content = - mailmime_content_new_with_str( - b"application/vnd.google-earth.kml+xml\x00" as *const u8 - as *const libc::c_char, - ); - let mime_fields: *mut mailmime_fields = - mailmime_fields_new_filename( - MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, - dc_strdup( - b"location.kml\x00" as *const u8 as *const libc::c_char, - ), - MAILMIME_MECHANISM_8BIT as libc::c_int, - ); - let kml_mime_part: *mut mailmime = - mailmime_new_empty(content_type, mime_fields); - mailmime_set_body_text(kml_mime_part, kml_file, strlen(kml_file)); - mailmime_smart_add_part(message, kml_mime_part); - if !(*msg).param.exists(Param::SetLatitude) { - // otherwise, the independent location is already filed - (*factory).out_last_added_location_id = last_added_location_id; - } + let mime_fields = mailmime_fields_new_filename( + MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, + dc_strdup(b"message.kml\x00" as *const u8 as *const libc::c_char), + MAILMIME_MECHANISM_8BIT as libc::c_int, + ); + let kml_mime_part = mailmime_new_empty(content_type, mime_fields); + mailmime_set_body_text(kml_mime_part, kml_file, strlen(kml_file)); + + mailmime_smart_add_part(message, kml_mime_part); + } + } + + if 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, + &mut last_added_location_id, + ); + if !kml_file.is_null() { + let content_type: *mut mailmime_content = mailmime_content_new_with_str( + b"application/vnd.google-earth.kml+xml\x00" as *const u8 + as *const libc::c_char, + ); + let mime_fields: *mut mailmime_fields = mailmime_fields_new_filename( + MAILMIME_DISPOSITION_TYPE_ATTACHMENT as libc::c_int, + dc_strdup(b"location.kml\x00" as *const u8 as *const libc::c_char), + MAILMIME_MECHANISM_8BIT as libc::c_int, + ); + let kml_mime_part: *mut mailmime = + mailmime_new_empty(content_type, mime_fields); + mailmime_set_body_text(kml_mime_part, kml_file, strlen(kml_file)); + mailmime_smart_add_part(message, kml_mime_part); + if !(*msg).param.exists(Param::SetLatitude) { + // otherwise, the independent location is already filed + (*factory).out_last_added_location_id = last_added_location_id; } } - current_block = 9952640327414195044; } } } @@ -959,25 +941,23 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: ) as *mut libc::c_void, ); mailmime_add_part(message, multipart); - let p1: *mut libc::c_char; - let p2: *mut libc::c_char; - if 0 != (*(*factory).msg) - .param - .get_int(Param::GuranteeE2ee) - .unwrap_or_default() + let p1 = if 0 + != (*(*factory).msg) + .param + .get_int(Param::GuranteeE2ee) + .unwrap_or_default() { - p1 = to_cstring((*factory).context.stock_str(StockMessage::EncryptedMsg)); - } else { - p1 = dc_msg_get_summarytext((*factory).msg, 32) - } - p2 = to_cstring( (*factory) .context - .stock_string_repl_str(StockMessage::ReadRcptMailBody, as_str(p1)), - ); - message_text = dc_mprintf(b"%s\r\n\x00" as *const u8 as *const libc::c_char, p2); - free(p1 as *mut libc::c_void); - free(p2 as *mut libc::c_void); + .stock_str(StockMessage::EncryptedMsg) + .into_owned() + } else { + to_string(dc_msg_get_summarytext((*factory).msg, 32)) + }; + let p2 = (*factory) + .context + .stock_string_repl_str(StockMessage::ReadRcptMailBody, p1); + message_text = format!("{}\r\n", p2).strdup(); let human_mime_part: *mut mailmime = build_body_text(message_text); mailmime_add_part(multipart, human_mime_part); message_text2 = @@ -995,86 +975,81 @@ pub unsafe fn dc_mimefactory_render(mut factory: *mut dc_mimefactory_t) -> libc: mailmime_set_body_text(mach_mime_part, message_text2, strlen(message_text2)); mailmime_add_part(multipart, mach_mime_part); force_plaintext = 2; - current_block = 9952640327414195044; } else { set_error( factory, b"No message loaded.\x00" as *const u8 as *const libc::c_char, ); - current_block = 11328123142868406523; + ok_to_continue = false; } - match current_block { - 11328123142868406523 => {} - _ => { - if (*factory).loaded as libc::c_uint - == DC_MF_MDN_LOADED as libc::c_int as libc::c_uint - { - let e = CString::new( - (*factory) - .context - .stock_str(StockMessage::ReadRcpt) - .as_ref(), - ) - .unwrap(); - subject_str = dc_mprintf( - b"Chat: %s\x00" as *const u8 as *const libc::c_char, - e.as_ptr(), - ); - } else { - subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email) - } - subject = mailimf_subject_new(dc_encode_header_words(subject_str)); - mailimf_fields_add( - imf_fields, - mailimf_field_new( - MAILIMF_FIELD_SUBJECT as libc::c_int, - 0 as *mut mailimf_return, - 0 as *mut mailimf_orig_date, - 0 as *mut mailimf_from, - 0 as *mut mailimf_sender, - 0 as *mut mailimf_to, - 0 as *mut mailimf_cc, - 0 as *mut mailimf_bcc, - 0 as *mut mailimf_message_id, - 0 as *mut mailimf_orig_date, - 0 as *mut mailimf_from, - 0 as *mut mailimf_sender, - 0 as *mut mailimf_reply_to, - 0 as *mut mailimf_to, - 0 as *mut mailimf_cc, - 0 as *mut mailimf_bcc, - 0 as *mut mailimf_message_id, - 0 as *mut mailimf_in_reply_to, - 0 as *mut mailimf_references, - subject, - 0 as *mut mailimf_comments, - 0 as *mut mailimf_keywords, - 0 as *mut mailimf_optional_field, - ), + if ok_to_continue { + if (*factory).loaded as libc::c_uint == DC_MF_MDN_LOADED as libc::c_int as libc::c_uint + { + let e = CString::new( + (*factory) + .context + .stock_str(StockMessage::ReadRcpt) + .as_ref(), + ) + .unwrap(); + subject_str = dc_mprintf( + b"Chat: %s\x00" as *const u8 as *const libc::c_char, + e.as_ptr(), ); - if force_plaintext != 2 { - dc_e2ee_encrypt( - (*factory).context, - (*factory).recipients_addr, - force_plaintext, - e2ee_guaranteed, - min_verified, - do_gossip, - message, - &mut e2ee_helper, - ); - } - if 0 != e2ee_helper.encryption_successfull { - (*factory).out_encrypted = 1; - if 0 != do_gossip { - (*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 = 1 + } else { + subject_str = get_subject((*factory).chat, (*factory).msg, afwd_email) } + subject = mailimf_subject_new(dc_encode_header_words(subject_str)); + mailimf_fields_add( + imf_fields, + mailimf_field_new( + MAILIMF_FIELD_SUBJECT as libc::c_int, + 0 as *mut mailimf_return, + 0 as *mut mailimf_orig_date, + 0 as *mut mailimf_from, + 0 as *mut mailimf_sender, + 0 as *mut mailimf_to, + 0 as *mut mailimf_cc, + 0 as *mut mailimf_bcc, + 0 as *mut mailimf_message_id, + 0 as *mut mailimf_orig_date, + 0 as *mut mailimf_from, + 0 as *mut mailimf_sender, + 0 as *mut mailimf_reply_to, + 0 as *mut mailimf_to, + 0 as *mut mailimf_cc, + 0 as *mut mailimf_bcc, + 0 as *mut mailimf_message_id, + 0 as *mut mailimf_in_reply_to, + 0 as *mut mailimf_references, + subject, + 0 as *mut mailimf_comments, + 0 as *mut mailimf_keywords, + 0 as *mut mailimf_optional_field, + ), + ); + if force_plaintext != 2 { + dc_e2ee_encrypt( + (*factory).context, + (*factory).recipients_addr, + force_plaintext, + e2ee_guaranteed, + min_verified, + do_gossip, + message, + &mut e2ee_helper, + ); + } + if 0 != e2ee_helper.encryption_successfull { + (*factory).out_encrypted = 1; + if 0 != do_gossip { + (*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 = 1 } } if !message.is_null() { @@ -1095,15 +1070,24 @@ unsafe fn get_subject( ) -> *mut libc::c_char { let context = (*chat).context; let ret: *mut libc::c_char; - let raw_subject = - dc_msg_get_summarytext_by_raw((*msg).type_0, (*msg).text, &mut (*msg).param, 32, context); + + let raw_subject = { + let msgtext_c = (*msg) + .text + .as_ref() + .map(|s| CString::yolo(String::as_str(s))); + let msgtext_ptr = msgtext_c.map_or(ptr::null(), |s| s.as_ptr()); + + dc_msg_get_summarytext_by_raw((*msg).type_0, msgtext_ptr, &mut (*msg).param, 32, context) + }; + let fwd = if 0 != afwd_email { b"Fwd: \x00" as *const u8 as *const libc::c_char } else { b"\x00" as *const u8 as *const libc::c_char }; if (*msg).param.get_int(Param::Cmd).unwrap_or_default() == 6 { - ret = to_cstring(context.stock_str(StockMessage::AcSetupMsgSubject)) + ret = context.stock_str(StockMessage::AcSetupMsgSubject).strdup() } else if (*chat).type_0 == DC_CHAT_TYPE_GROUP as libc::c_int || (*chat).type_0 == DC_CHAT_TYPE_VERIFIED_GROUP as libc::c_int { @@ -1166,12 +1150,12 @@ unsafe fn build_body_file( let pathNfilename = (*msg) .param .get(Param::File) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); let mut mimetype = (*msg) .param .get(Param::MimeType) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); let suffix = dc_get_filesuffix_lc(pathNfilename); @@ -1179,7 +1163,7 @@ unsafe fn build_body_file( let mut filename_encoded = 0 as *mut libc::c_char; if !pathNfilename.is_null() { - if (*msg).type_0 == DC_MSG_VOICE { + if (*msg).type_0 == Viewtype::Voice { let ts = chrono::Utc.timestamp((*msg).timestamp_sort as i64, 0); let suffix = if !suffix.is_null() { @@ -1190,10 +1174,10 @@ unsafe fn build_body_file( let res = ts .format(&format!("voice-message_%Y-%m-%d_%H-%M-%S.{}", suffix)) .to_string(); - filename_to_send = to_cstring(res); - } else if (*msg).type_0 == DC_MSG_AUDIO { + filename_to_send = res.strdup(); + } else if (*msg).type_0 == Viewtype::Audio { filename_to_send = dc_get_filename(pathNfilename) - } else if (*msg).type_0 == DC_MSG_IMAGE || (*msg).type_0 == DC_MSG_GIF { + } else if (*msg).type_0 == Viewtype::Image || (*msg).type_0 == Viewtype::Gif { if base_name.is_null() { base_name = b"image\x00" as *const u8 as *const libc::c_char } @@ -1206,7 +1190,7 @@ unsafe fn build_body_file( b"dat\x00" as *const u8 as *const libc::c_char }, ) - } else if (*msg).type_0 == DC_MSG_VIDEO { + } else if (*msg).type_0 == Viewtype::Video { filename_to_send = dc_mprintf( b"video.%s\x00" as *const u8 as *const libc::c_char, if !suffix.is_null() { @@ -1328,13 +1312,13 @@ unsafe fn build_body_file( * Render ******************************************************************************/ #[allow(non_snake_case)] -unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> libc::c_int { - let mut file_size_okay = 1; - let pathNfilename = to_cstring((*msg).param.get(Param::File).unwrap_or_default()); +unsafe fn is_file_size_okay(msg: *const dc_msg_t) -> bool { + let mut file_size_okay = true; + let pathNfilename = (*msg).param.get(Param::File).unwrap_or_default().strdup(); let bytes = dc_get_filebytes((*msg).context, pathNfilename); if bytes > (49 * 1024 * 1024 / 4 * 3) { - file_size_okay = 0; + file_size_okay = false; } free(pathNfilename as *mut _); diff --git a/src/dc_mimeparser.rs b/src/dc_mimeparser.rs index f5bc4cc64..0b40b1747 100644 --- a/src/dc_mimeparser.rs +++ b/src/dc_mimeparser.rs @@ -11,8 +11,8 @@ use mmime::mailmime_types::*; use mmime::mmapstring::*; use mmime::other::*; +use crate::contact::*; use crate::context::Context; -use crate::dc_contact::*; use crate::dc_e2ee::*; use crate::dc_location::*; use crate::dc_simplify::*; @@ -44,7 +44,7 @@ pub struct dc_mimepart_t { #[derive(Clone)] #[allow(non_camel_case_types)] pub struct dc_mimeparser_t<'a> { - pub parts: *mut carray, + pub parts: Vec<dc_mimepart_t>, pub mimeroot: *mut mailmime, pub header: HashMap<String, *mut mailimf_field>, pub header_root: *mut mailimf_fields, @@ -55,7 +55,7 @@ pub struct dc_mimeparser_t<'a> { pub e2ee_helper: dc_e2ee_helper_t, pub is_forwarded: libc::c_int, pub context: &'a Context, - pub reports: *mut carray, + pub reports: Vec<*mut mailmime>, pub is_system_message: libc::c_int, pub location_kml: Option<dc_kml_t>, pub message_kml: Option<dc_kml_t>, @@ -71,7 +71,7 @@ static mut S_GENERATE_COMPOUND_MSGS: libc::c_int = 1i32; pub unsafe fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t { dc_mimeparser_t { - parts: carray_new(16i32 as libc::c_uint), + parts: Vec::new(), mimeroot: std::ptr::null_mut(), header: Default::default(), header_root: std::ptr::null_mut(), @@ -82,7 +82,7 @@ pub unsafe fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t { e2ee_helper: Default::default(), is_forwarded: 0, context, - reports: carray_new(16i32 as libc::c_uint), + reports: Vec::new(), is_system_message: 0, location_kml: None, message_kml: None, @@ -91,58 +91,41 @@ pub unsafe fn dc_mimeparser_new(context: &Context) -> dc_mimeparser_t { pub unsafe fn dc_mimeparser_unref(mimeparser: &mut dc_mimeparser_t) { dc_mimeparser_empty(mimeparser); - if !(*mimeparser).parts.is_null() { - carray_free((*mimeparser).parts); - } - if !(*mimeparser).reports.is_null() { - carray_free((*mimeparser).reports); - } } pub unsafe fn dc_mimeparser_empty(mimeparser: &mut dc_mimeparser_t) { - if !(*mimeparser).parts.is_null() { - let mut i: libc::c_int; - let cnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int; - i = 0i32; - while i < cnt { - let part = carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t; - if !part.is_null() { - dc_mimepart_unref(*Box::from_raw(part)); - } - i += 1 - } - carray_set_size((*mimeparser).parts, 0i32 as libc::c_uint); + for part in mimeparser.parts.drain(..) { + dc_mimepart_unref(part); } - (*mimeparser).header_root = 0 as *mut mailimf_fields; - (*mimeparser).header.clear(); - if !(*mimeparser).header_protected.is_null() { - mailimf_fields_free((*mimeparser).header_protected); - (*mimeparser).header_protected = 0 as *mut mailimf_fields + assert!(mimeparser.parts.is_empty()); + mimeparser.header_root = 0 as *mut mailimf_fields; + mimeparser.header.clear(); + if !mimeparser.header_protected.is_null() { + mailimf_fields_free(mimeparser.header_protected); + mimeparser.header_protected = 0 as *mut mailimf_fields } - (*mimeparser).is_send_by_messenger = 0i32; - (*mimeparser).is_system_message = 0i32; - free((*mimeparser).subject as *mut libc::c_void); - (*mimeparser).subject = 0 as *mut libc::c_char; - if !(*mimeparser).mimeroot.is_null() { - mailmime_free((*mimeparser).mimeroot); - (*mimeparser).mimeroot = 0 as *mut mailmime + mimeparser.is_send_by_messenger = 0i32; + mimeparser.is_system_message = 0i32; + free(mimeparser.subject as *mut libc::c_void); + mimeparser.subject = 0 as *mut libc::c_char; + if !mimeparser.mimeroot.is_null() { + mailmime_free(mimeparser.mimeroot); + mimeparser.mimeroot = 0 as *mut mailmime } - (*mimeparser).is_forwarded = 0i32; - if !(*mimeparser).reports.is_null() { - carray_set_size((*mimeparser).reports, 0i32 as libc::c_uint); - } - (*mimeparser).decrypting_failed = 0i32; - dc_e2ee_thanks(&mut (*mimeparser).e2ee_helper); + mimeparser.is_forwarded = 0i32; + mimeparser.reports.clear(); + mimeparser.decrypting_failed = 0i32; + dc_e2ee_thanks(&mut mimeparser.e2ee_helper); - if let Some(location_kml) = (*mimeparser).location_kml.as_mut() { - dc_kml_unref(location_kml as *mut dc_kml_t); + if let Some(location_kml) = mimeparser.location_kml.as_mut() { + dc_kml_unref(location_kml); } - (*mimeparser).location_kml = None; + mimeparser.location_kml = None; - if let Some(message_kml) = (*mimeparser).message_kml.as_mut() { - dc_kml_unref(message_kml as *mut dc_kml_t); + if let Some(message_kml) = mimeparser.message_kml.as_mut() { + dc_kml_unref(message_kml); } - (*mimeparser).message_kml = None; + mimeparser.message_kml = None; } unsafe fn dc_mimepart_unref(mut mimepart: dc_mimepart_t) { @@ -165,19 +148,18 @@ pub unsafe fn dc_mimeparser_parse( body_not_terminated, body_bytes, &mut index, - &mut (*mimeparser).mimeroot, + &mut mimeparser.mimeroot, ); - if !(r != MAILIMF_NO_ERROR as libc::c_int || (*mimeparser).mimeroot.is_null()) { + if !(r != MAILIMF_NO_ERROR as libc::c_int || mimeparser.mimeroot.is_null()) { dc_e2ee_decrypt( - (*mimeparser).context, - (*mimeparser).mimeroot, - &mut (*mimeparser).e2ee_helper, + mimeparser.context, + mimeparser.mimeroot, + &mut mimeparser.e2ee_helper, ); - dc_mimeparser_parse_mime_recursive(mimeparser, (*mimeparser).mimeroot); + dc_mimeparser_parse_mime_recursive(mimeparser, mimeparser.mimeroot); 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) + mimeparser.subject = dc_decode_header_words((*(*field).fld_data.fld_subject).sbj_value) } if !dc_mimeparser_lookup_optional_field( mimeparser, @@ -185,32 +167,37 @@ pub unsafe fn dc_mimeparser_parse( ) .is_null() { - (*mimeparser).is_send_by_messenger = 1i32 + mimeparser.is_send_by_messenger = 1i32 } if !dc_mimeparser_lookup_field(mimeparser, "Autocrypt-Setup-Message").is_null() { - let mut i: libc::c_int; let mut has_setup_file: libc::c_int = 0i32; - i = 0i32; - while (i as libc::c_uint) < carray_count((*mimeparser).parts) { - let part: *mut dc_mimepart_t = - carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t; - if (*part).int_mimetype == 111i32 { + for part in &mimeparser.parts { + if part.int_mimetype == 111i32 { has_setup_file = 1i32 } - i += 1 } if 0 != has_setup_file { - (*mimeparser).is_system_message = 6i32; - i = 0i32; - while (i as libc::c_uint) < carray_count((*mimeparser).parts) { - let part_0 = - carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t; - if (*part_0).int_mimetype != 111i32 { - dc_mimepart_unref(*Box::from_raw(part_0)); - carray_delete_slow((*mimeparser).parts, i as libc::c_uint); - i -= 1 + mimeparser.is_system_message = 6i32; + + // TODO: replace the following code with this + // once drain_filter stabilizes. + // + // See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter + // and https://github.com/rust-lang/rust/issues/43244 + // + // mimeparser + // .parts + // .drain_filter(|part| part.int_mimetype != 111) + // .for_each(|part| dc_mimepart_unref(part)); + + let mut i = 0; + while i != mimeparser.parts.len() { + if mimeparser.parts[i].int_mimetype != 111 { + let part = mimeparser.parts.remove(i); + dc_mimepart_unref(part); + } else { + i += 1; } - i += 1 } } } else { @@ -224,56 +211,69 @@ pub unsafe fn dc_mimeparser_parse( b"location-streaming-enabled\x00" as *const u8 as *const libc::c_char, ) == 0i32 { - (*mimeparser).is_system_message = 8i32 + mimeparser.is_system_message = 8i32 } } } if !dc_mimeparser_lookup_field(mimeparser, "Chat-Group-Image").is_null() - && carray_count((*mimeparser).parts) >= 1i32 as libc::c_uint + && !mimeparser.parts.is_empty() { - let textpart: *mut dc_mimepart_t = - carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t; - if (*textpart).type_0 == 10i32 { - if carray_count((*mimeparser).parts) >= 2i32 as libc::c_uint { - let mut imgpart: *mut dc_mimepart_t = - carray_get((*mimeparser).parts, 1i32 as libc::c_uint) as *mut dc_mimepart_t; - if (*imgpart).type_0 == 20i32 { - (*imgpart).is_meta = 1i32 + let textpart = &mimeparser.parts[0]; + if textpart.type_0 == 10i32 { + if mimeparser.parts.len() >= 2 { + let imgpart = &mut mimeparser.parts[1]; + if imgpart.type_0 == 20i32 { + imgpart.is_meta = 1i32 } } } } - if 0 != (*mimeparser).is_send_by_messenger + if 0 != mimeparser.is_send_by_messenger && 0 != S_GENERATE_COMPOUND_MSGS - && carray_count((*mimeparser).parts) == 2i32 as libc::c_uint + && mimeparser.parts.len() == 2 { - let mut textpart_0 = carray_get((*mimeparser).parts, 0) as *mut dc_mimepart_t; - let mut filepart = carray_get((*mimeparser).parts, 1) as *mut dc_mimepart_t; - if (*textpart_0).type_0 == 10i32 - && ((*filepart).type_0 == 20i32 - || (*filepart).type_0 == 21i32 - || (*filepart).type_0 == 40i32 - || (*filepart).type_0 == 41i32 - || (*filepart).type_0 == 50i32 - || (*filepart).type_0 == 60i32) - && 0 == (*filepart).is_meta + let need_drop: bool; { - free((*filepart).msg as *mut libc::c_void); - (*filepart).msg = (*textpart_0).msg; - (*textpart_0).msg = 0 as *mut libc::c_char; - dc_mimepart_unref(*Box::from_raw(textpart_0)); - carray_delete_slow((*mimeparser).parts, 0i32 as libc::c_uint); + let textpart = &mimeparser.parts[0]; + let filepart = &mimeparser.parts[1]; + need_drop = textpart.type_0 == 10i32 + && (filepart.type_0 == 20i32 + || filepart.type_0 == 21i32 + || filepart.type_0 == 40i32 + || filepart.type_0 == 41i32 + || filepart.type_0 == 50i32 + || filepart.type_0 == 60i32) + && 0 == filepart.is_meta; + } + + if need_drop { + let mut filepart = mimeparser.parts.swap_remove(1); + + // clear old one + free(filepart.msg as *mut libc::c_void); + + // insert new one + filepart.msg = mimeparser.parts[0].msg; + + // forget the one we use now + mimeparser.parts[0].msg = std::ptr::null_mut(); + + // swap new with old + let old = std::mem::replace(&mut mimeparser.parts[0], filepart); + + // unref old one + dc_mimepart_unref(old); } } - if !(*mimeparser).subject.is_null() { + if !mimeparser.subject.is_null() { let mut prepend_subject: libc::c_int = 1i32; - if 0 == (*mimeparser).decrypting_failed { - let p: *mut libc::c_char = strchr((*mimeparser).subject, ':' as i32); - if p.wrapping_offset_from((*mimeparser).subject) == 2 - || p.wrapping_offset_from((*mimeparser).subject) == 3 - || 0 != (*mimeparser).is_send_by_messenger + if 0 == mimeparser.decrypting_failed { + let p: *mut libc::c_char = strchr(mimeparser.subject, ':' as i32); + if p.wrapping_offset_from(mimeparser.subject) == 2 + || p.wrapping_offset_from(mimeparser.subject) == 3 + || 0 != mimeparser.is_send_by_messenger || !strstr( - (*mimeparser).subject, + mimeparser.subject, b"Chat:\x00" as *const u8 as *const libc::c_char, ) .is_null() @@ -282,62 +282,48 @@ pub unsafe fn dc_mimeparser_parse( } } if 0 != prepend_subject { - let subj: *mut libc::c_char = dc_strdup((*mimeparser).subject); + let subj: *mut libc::c_char = dc_strdup(mimeparser.subject); let p_0: *mut libc::c_char = strchr(subj, '[' as i32); if !p_0.is_null() { *p_0 = 0i32 as libc::c_char } dc_trim(subj); if 0 != *subj.offset(0isize) { - let mut i_0: libc::c_int; - let icnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int; - i_0 = 0i32; - while i_0 < icnt { - let mut part_1: *mut dc_mimepart_t = - carray_get((*mimeparser).parts, i_0 as libc::c_uint) - as *mut dc_mimepart_t; - if (*part_1).type_0 == 10i32 { + for part in mimeparser.parts.iter_mut() { + if part.type_0 == 10i32 { let new_txt: *mut libc::c_char = dc_mprintf( b"%s \xe2\x80\x93 %s\x00" as *const u8 as *const libc::c_char, subj, - (*part_1).msg, + part.msg, ); - free((*part_1).msg as *mut libc::c_void); - (*part_1).msg = new_txt; + free(part.msg as *mut libc::c_void); + part.msg = new_txt; break; - } else { - i_0 += 1 } } } free(subj as *mut libc::c_void); } } - if 0 != (*mimeparser).is_forwarded { - let mut i_1: libc::c_int; - let icnt_0: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int; - i_1 = 0i32; - while i_1 < icnt_0 { - let part_2 = - carray_get((*mimeparser).parts, i_1 as libc::c_uint) as *mut dc_mimepart_t; - (*part_2).param.set_int(Param::Forwarded, 1); - i_1 += 1 + if 0 != mimeparser.is_forwarded { + for part in mimeparser.parts.iter_mut() { + part.param.set_int(Param::Forwarded, 1); } } - if carray_count((*mimeparser).parts) == 1i32 as libc::c_uint { - let mut part_3: *mut dc_mimepart_t = - carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t; - if (*part_3).type_0 == 40i32 { + if mimeparser.parts.len() == 1 { + if mimeparser.parts[0].type_0 == 40i32 { if !dc_mimeparser_lookup_optional_field( mimeparser, b"Chat-Voice-Message\x00" as *const u8 as *const libc::c_char, ) .is_null() { - (*part_3).type_0 = 41i32 + let part_mut = &mut mimeparser.parts[0]; + part_mut.type_0 = 41i32 } } - if (*part_3).type_0 == 40i32 || (*part_3).type_0 == 41i32 || (*part_3).type_0 == 50i32 { + let part = &mimeparser.parts[0]; + if part.type_0 == 40i32 || part.type_0 == 41i32 || part.type_0 == 50i32 { let field_0 = dc_mimeparser_lookup_optional_field( mimeparser, b"Chat-Duration\x00" as *const u8 as *const libc::c_char, @@ -345,17 +331,18 @@ 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 { - (*part_3).param.set_int(Param::Duration, duration_ms); + let part_mut = &mut mimeparser.parts[0]; + part_mut.param.set_int(Param::Duration, duration_ms); } } } } - if 0 == (*mimeparser).decrypting_failed { + if 0 == mimeparser.decrypting_failed { let dn_field: *const mailimf_optional_field = dc_mimeparser_lookup_optional_field( mimeparser, b"Chat-Disposition-Notification-To\x00" as *const u8 as *const libc::c_char, ); - if !dn_field.is_null() && !dc_mimeparser_get_last_nonmeta(mimeparser).is_null() { + if !dn_field.is_null() && dc_mimeparser_get_last_nonmeta(mimeparser).is_some() { let mut mb_list: *mut mailimf_mailbox_list = 0 as *mut mailimf_mailbox_list; let mut index_0: size_t = 0i32 as size_t; if mailimf_mailbox_list_parse( @@ -379,10 +366,9 @@ pub unsafe fn dc_mimeparser_parse( ); if !from_addr.is_null() { if strcmp(from_addr, dn_to_addr) == 0i32 { - let part_4: *mut dc_mimepart_t = - dc_mimeparser_get_last_nonmeta(mimeparser); - if !part_4.is_null() { - (*part_4).param.set_int(Param::WantsMdn, 1); + if let Some(part_4) = dc_mimeparser_get_last_nonmeta(mimeparser) + { + part_4.param.set_int(Param::WantsMdn, 1); } } free(from_addr as *mut libc::c_void); @@ -396,21 +382,15 @@ pub unsafe fn dc_mimeparser_parse( } } /* Cleanup - and try to create at least an empty part if there are no parts yet */ - if dc_mimeparser_get_last_nonmeta(mimeparser).is_null() - && carray_count((*mimeparser).reports) == 0i32 as libc::c_uint - { + if dc_mimeparser_get_last_nonmeta(mimeparser).is_none() && mimeparser.reports.is_empty() { let mut part_5 = dc_mimepart_new(); part_5.type_0 = 10i32; - if !(*mimeparser).subject.is_null() && 0 == (*mimeparser).is_send_by_messenger { - part_5.msg = dc_strdup((*mimeparser).subject) + if !mimeparser.subject.is_null() && 0 == mimeparser.is_send_by_messenger { + part_5.msg = dc_strdup(mimeparser.subject) } else { part_5.msg = dc_strdup(b"\x00" as *const u8 as *const libc::c_char) } - carray_add( - (*mimeparser).parts, - Box::into_raw(Box::new(part_5)) as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); + mimeparser.parts.push(part_5); }; } @@ -429,22 +409,14 @@ unsafe fn dc_mimepart_new() -> dc_mimepart_t { } } -pub unsafe fn dc_mimeparser_get_last_nonmeta(mimeparser: &dc_mimeparser_t) -> *mut dc_mimepart_t { - if !(*mimeparser).parts.is_null() { - let mut i: libc::c_int; - let icnt: libc::c_int = carray_count((*mimeparser).parts) as libc::c_int; - i = icnt - 1i32; - while i >= 0i32 { - let part: *mut dc_mimepart_t = - carray_get(mimeparser.parts, i as libc::c_uint) as *mut dc_mimepart_t; - if !part.is_null() && 0 == (*part).is_meta { - return part; - } - i -= 1 - } - } - - 0 as *mut dc_mimepart_t +pub fn dc_mimeparser_get_last_nonmeta<'a>( + mimeparser: &'a mut dc_mimeparser_t, +) -> Option<&'a mut dc_mimepart_t> { + mimeparser + .parts + .iter_mut() + .rev() + .find(|part| part.is_meta == 0) } /*the result must be freed*/ @@ -460,7 +432,7 @@ pub unsafe fn mailimf_find_first_addr(mb_list: *const mailimf_mailbox_list) -> * 0 as *mut libc::c_void }) as *mut mailimf_mailbox; if !mb.is_null() && !(*mb).mb_addr_spec.is_null() { - return dc_addr_normalize((*mb).mb_addr_spec); + return addr_normalize(as_str((*mb).mb_addr_spec)).strdup(); } cur = if !cur.is_null() { (*cur).next @@ -531,32 +503,32 @@ unsafe fn dc_mimeparser_parse_mime_recursive( ) == 0i32 { info!( - (*mimeparser).context, + mimeparser.context, 0, "Protected headers found in text/rfc822-headers attachment: Will be ignored.", ); return 0i32; } - if (*mimeparser).header_protected.is_null() { + if mimeparser.header_protected.is_null() { let mut dummy: size_t = 0i32 as size_t; if mailimf_envelope_and_optional_fields_parse( (*mime).mm_mime_start, (*mime).mm_length, &mut dummy, - &mut (*mimeparser).header_protected, + &mut mimeparser.header_protected, ) != MAILIMF_NO_ERROR as libc::c_int - || (*mimeparser).header_protected.is_null() + || mimeparser.header_protected.is_null() { - warn!((*mimeparser).context, 0, "Protected headers parsing error.",); + warn!(mimeparser.context, 0, "Protected headers parsing error.",); } else { hash_header( - &mut (*mimeparser).header, - (*mimeparser).header_protected, - (*mimeparser).context, + &mut mimeparser.header, + mimeparser.header_protected, + mimeparser.context, ); } } else { info!( - (*mimeparser).context, + mimeparser.context, 0, "Protected headers found in MIME header: Will be ignored as we already found an outer one." ); @@ -660,7 +632,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive( let mut part = dc_mimepart_new(); part.type_0 = 10i32; let msg_body = CString::new( - (*mimeparser) + mimeparser .context .stock_str(StockMessage::CantDecryptMsgBody) .as_ref(), @@ -671,13 +643,9 @@ unsafe fn dc_mimeparser_parse_mime_recursive( msg_body.as_ptr(), ); part.msg_raw = dc_strdup(part.msg); - carray_add( - (*mimeparser).parts, - Box::into_raw(Box::new(part)) as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); + mimeparser.parts.push(part); any_part_added = 1i32; - (*mimeparser).decrypting_failed = 1i32 + mimeparser.decrypting_failed = 1i32 } 46 => { cur = (*(*mime).mm_data.mm_multipart.mm_mp_list).first; @@ -705,11 +673,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive( b"disposition-notification\x00" as *const u8 as *const libc::c_char, ) == 0i32 { - carray_add( - (*mimeparser).reports, - mime as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); + mimeparser.reports.push(mime); } else { any_part_added = dc_mimeparser_parse_mime_recursive( mimeparser, @@ -759,7 +723,7 @@ unsafe fn dc_mimeparser_parse_mime_recursive( } if plain_cnt == 1i32 && html_cnt == 1i32 { warn!( - (*mimeparser).context, + mimeparser.context, 0i32, "HACK: multipart/mixed message found with PLAIN and HTML, we\'ll skip the HTML part as this seems to be unwanted." ); @@ -788,12 +752,12 @@ unsafe fn dc_mimeparser_parse_mime_recursive( } } 3 => { - if (*mimeparser).header_root.is_null() { - (*mimeparser).header_root = (*mime).mm_data.mm_message.mm_fields; + if mimeparser.header_root.is_null() { + mimeparser.header_root = (*mime).mm_data.mm_message.mm_fields; hash_header( - &mut (*mimeparser).header, - (*mimeparser).header_root, - (*mimeparser).context, + &mut mimeparser.header, + mimeparser.header_root, + mimeparser.context, ); } if !(*mime).mm_data.mm_message.mm_msg_mime.is_null() { @@ -1136,7 +1100,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( mime: *mut mailmime, ) -> libc::c_int { let mut current_block: u64; - let old_part_count: libc::c_int = carray_count(mimeparser.parts) as libc::c_int; + let old_part_count = mimeparser.parts.len(); let mime_type: libc::c_int; let mime_data: *mut mailmime_data; let file_suffix: *mut libc::c_char = 0 as *mut libc::c_char; @@ -1173,8 +1137,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( simplifier = Some(dc_simplify_t::new()); } /* get from `Content-Type: text/...; charset=utf-8`; must not be free()'d */ - let charset: *const libc::c_char = - mailmime_content_charset_get((*mime).mm_content_type); + let charset = mailmime_content_charset_get((*mime).mm_content_type); if !charset.is_null() && strcmp(charset, b"utf-8\x00" as *const u8 as *const libc::c_char) != 0i32 @@ -1190,12 +1153,16 @@ unsafe fn dc_mimeparser_add_single_part_if_known( ); let (res, _, _) = encoding.decode(data); + info!(mimeparser.context, 0, "decoded message: '{}'", res); if res.is_empty() { /* no error - but nothing to add */ current_block = 8795901732489102124; } else { - decoded_data_bytes = res.len(); - decoded_data = res.as_ptr() as *const libc::c_char; + let b = res.as_bytes(); + decoded_data = b.as_ptr() as *const libc::c_char; + decoded_data_bytes = b.len(); + std::mem::forget(res); + current_block = 17788412896529399552; } } else { @@ -1215,19 +1182,19 @@ unsafe fn dc_mimeparser_add_single_part_if_known( 8795901732489102124 => {} _ => { /* check header directly as is_send_by_messenger is not yet set up */ - let is_msgrmsg: libc::c_int = (dc_mimeparser_lookup_optional_field( + let is_msgrmsg = (!dc_mimeparser_lookup_optional_field( &mimeparser, b"Chat-Version\x00" as *const u8 as *const libc::c_char, - ) != 0 as *mut libc::c_void - as *mut mailimf_optional_field) + ) + .is_null()) as libc::c_int; - let simplified_txt: *mut libc::c_char = - simplifier.unwrap().simplify( - decoded_data, - decoded_data_bytes as libc::c_int, - if mime_type == 70i32 { 1i32 } else { 0i32 }, - is_msgrmsg, - ); + + let simplified_txt = simplifier.unwrap().simplify( + decoded_data, + decoded_data_bytes as libc::c_int, + if mime_type == 70i32 { 1i32 } else { 0i32 }, + is_msgrmsg, + ); if !simplified_txt.is_null() && 0 != *simplified_txt.offset(0isize) as libc::c_int { @@ -1242,7 +1209,7 @@ unsafe fn dc_mimeparser_add_single_part_if_known( free(simplified_txt as *mut libc::c_void); } if 0 != simplifier.unwrap().is_forwarded { - (*mimeparser).is_forwarded = 1i32 + mimeparser.is_forwarded = 1i32 } current_block = 10261677128829721533; } @@ -1323,9 +1290,8 @@ unsafe fn dc_mimeparser_add_single_part_if_known( } if !filename_parts.is_empty() { free(desired_filename as *mut libc::c_void); - let parts_c = to_cstring(filename_parts); - desired_filename = dc_decode_ext_header(parts_c); - free(parts_c as *mut _); + let parts_c = CString::yolo(filename_parts); + desired_filename = dc_decode_ext_header(parts_c.as_ptr()); } if desired_filename.is_null() { let param = mailmime_find_ct_parameter( @@ -1371,8 +1337,8 @@ unsafe fn dc_mimeparser_add_single_part_if_known( 4, ) == 0i32 { - (*mimeparser).location_kml = Some(dc_kml_parse( - (*mimeparser).context, + mimeparser.location_kml = Some(dc_kml_parse( + mimeparser.context, decoded_data, decoded_data_bytes, )); @@ -1390,8 +1356,8 @@ unsafe fn dc_mimeparser_add_single_part_if_known( 4, ) == 0i32 { - (*mimeparser).message_kml = Some(dc_kml_parse( - (*mimeparser).context, + mimeparser.message_kml = Some(dc_kml_parse( + mimeparser.context, decoded_data, decoded_data_bytes, )); @@ -1430,16 +1396,16 @@ unsafe fn dc_mimeparser_add_single_part_if_known( free(file_suffix as *mut libc::c_void); free(desired_filename as *mut libc::c_void); free(raw_mime as *mut libc::c_void); - return if carray_count((*mimeparser).parts) > old_part_count as libc::c_uint { - 1i32 + return if mimeparser.parts.len() > old_part_count { + 1 } else { - 0i32 + 0 }; } #[allow(non_snake_case)] unsafe fn do_add_single_file_part( - parser: &dc_mimeparser_t, + parser: &mut dc_mimeparser_t, msg_type: libc::c_int, mime_type: libc::c_int, raw_mime: *const libc::c_char, @@ -1488,17 +1454,13 @@ unsafe fn do_add_single_file_part( free(pathNfilename as *mut libc::c_void); } -unsafe fn do_add_single_part(parser: &dc_mimeparser_t, mut part: dc_mimepart_t) { +unsafe fn do_add_single_part(parser: &mut dc_mimeparser_t, mut part: dc_mimepart_t) { if 0 != (*parser).e2ee_helper.encrypted && (*parser).e2ee_helper.signatures.len() > 0 { part.param.set_int(Param::GuranteeE2ee, 1); } else if 0 != (*parser).e2ee_helper.encrypted { part.param.set_int(Param::ErroneousE2ee, 0x2); } - carray_add( - (*parser).parts, - Box::into_raw(Box::new(part)) as *mut libc::c_void, - 0 as *mut libc::c_uint, - ); + parser.parts.push(part); } // TODO should return bool /rtn @@ -1616,8 +1578,8 @@ pub unsafe fn dc_mimeparser_sender_equals_recipient(mimeparser: &dc_mimeparser_t let fld: *const mailimf_field; let mut fld_from: *const mailimf_from = 0 as *const mailimf_from; let mb: *mut mailimf_mailbox; - let mut from_addr_norm: *mut libc::c_char = 0 as *mut libc::c_char; - if !(*mimeparser).header_root.is_null() { + + if !mimeparser.header_root.is_null() { /* get From: and check there is exactly one sender */ fld = mailimf_find_field(mimeparser.header_root, MAILIMF_FIELD_FROM as libc::c_int); if !(fld.is_null() @@ -1635,17 +1597,16 @@ pub unsafe fn dc_mimeparser_sender_equals_recipient(mimeparser: &dc_mimeparser_t 0 as *mut libc::c_void }) as *mut mailimf_mailbox; if !mb.is_null() { - from_addr_norm = dc_addr_normalize((*mb).mb_addr_spec); + let from_addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); let recipients = mailimf_get_recipients(mimeparser.header_root); if recipients.len() == 1 { - if recipients.contains(as_str(from_addr_norm)) { + if recipients.contains(from_addr_norm) { sender_equals_recipient = 1i32; } } } } } - free(from_addr_norm as *mut libc::c_void); sender_equals_recipient } @@ -1742,15 +1703,15 @@ pub unsafe fn mailimf_get_recipients(imffields: *mut mailimf_fields) -> HashSet< /* ****************************************************************************** * low-level-tools for getting a list of all recipients ******************************************************************************/ + #[allow(non_snake_case)] unsafe fn mailimf_get_recipients__add_addr( recipients: &mut HashSet<String>, mb: *mut mailimf_mailbox, ) { if !mb.is_null() { - let addr_norm: *mut libc::c_char = dc_addr_normalize((*mb).mb_addr_spec); - recipients.insert(to_string(addr_norm)); - free(addr_norm as *mut libc::c_void); + let addr_norm = addr_normalize(as_str((*mb).mb_addr_spec)); + recipients.insert(addr_norm.into()); }; } @@ -1785,27 +1746,20 @@ pub unsafe fn mailimf_find_field( } pub unsafe fn dc_mimeparser_repl_msg_by_error( - mimeparser: &dc_mimeparser_t, + mimeparser: &mut dc_mimeparser_t, error_msg: *const libc::c_char, ) { - let mut part: *mut dc_mimepart_t; - let mut i: libc::c_int; - if (*mimeparser).parts.is_null() || carray_count((*mimeparser).parts) <= 0i32 as libc::c_uint { + if mimeparser.parts.is_empty() { return; } - part = carray_get((*mimeparser).parts, 0i32 as libc::c_uint) as *mut dc_mimepart_t; - (*part).type_0 = 10i32; - free((*part).msg as *mut libc::c_void); - (*part).msg = dc_mprintf(b"[%s]\x00" as *const u8 as *const libc::c_char, error_msg); - i = 1i32; - while (i as libc::c_uint) < carray_count((*mimeparser).parts) { - part = carray_get((*mimeparser).parts, i as libc::c_uint) as *mut dc_mimepart_t; - if !part.is_null() { - dc_mimepart_unref(*Box::from_raw(part)); - } - i += 1 + let part = &mut mimeparser.parts[0]; + part.type_0 = 10i32; + free(part.msg as *mut libc::c_void); + part.msg = dc_mprintf(b"[%s]\x00" as *const u8 as *const libc::c_char, error_msg); + for part in mimeparser.parts.drain(1..) { + dc_mimepart_unref(part); } - carray_set_size((*mimeparser).parts, 1i32 as libc::c_uint); + assert_eq!(mimeparser.parts.len(), 1); } /*the result is a pointer to mime, must not be freed*/ diff --git a/src/dc_msg.rs b/src/dc_msg.rs index b0110abe2..76ee51282 100644 --- a/src/dc_msg.rs +++ b/src/dc_msg.rs @@ -1,9 +1,9 @@ use std::ffi::CString; use crate::constants::*; +use crate::contact::*; use crate::context::*; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_lot::dc_lot_t; use crate::dc_lot::*; @@ -14,6 +14,7 @@ use crate::sql; use crate::stock::StockMessage; use crate::types::*; use crate::x::*; +use std::ptr; /* * the structure behind dc_msg_t */ #[derive(Clone)] @@ -25,13 +26,13 @@ pub struct dc_msg_t<'a> { pub to_id: uint32_t, pub chat_id: uint32_t, pub move_state: dc_move_state_t, - pub type_0: libc::c_int, + pub type_0: Viewtype, pub state: libc::c_int, pub hidden: libc::c_int, pub timestamp_sort: i64, pub timestamp_sent: i64, pub timestamp_rcvd: i64, - pub text: *mut libc::c_char, + pub text: Option<String>, pub context: &'a Context, pub rfc724_mid: *mut libc::c_char, pub in_reply_to: *mut libc::c_char, @@ -47,12 +48,10 @@ pub struct dc_msg_t<'a> { // handle messages pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_char { let msg = dc_msg_new_untyped(context); - let contact_from = dc_contact_new(context); let mut p: *mut libc::c_char; let mut ret = String::new(); dc_msg_load_from_db(msg, context, msg_id); - dc_contact_load_from_db(contact_from, &context.sql, (*msg).from_id); let rawtxt: Option<String> = context.sql.query_row_col( context, @@ -64,36 +63,35 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch if rawtxt.is_none() { ret += &format!("Cannot load message #{}.", msg_id as usize); dc_msg_unref(msg); - dc_contact_unref(contact_from); - return to_cstring(ret); + return ret.strdup(); } let rawtxt = rawtxt.unwrap(); let rawtxt = dc_truncate_str(rawtxt.trim(), 100000); - let fts = dc_timestamp_to_str_safe(dc_msg_get_timestamp(msg)); + let fts = dc_timestamp_to_str(dc_msg_get_timestamp(msg)); ret += &format!("Sent: {}", fts); - p = dc_contact_get_name_n_addr(contact_from); - ret += &format!(" by {}", to_string(p)); - free(p as *mut libc::c_void); + let name = Contact::load_from_db(context, (*msg).from_id) + .map(|contact| contact.get_name_n_addr()) + .unwrap_or_default(); + + ret += &format!(" by {}", name); ret += "\n"; - if (*msg).from_id != 1 as libc::c_uint { - p = dc_timestamp_to_str(if 0 != (*msg).timestamp_rcvd { + if (*msg).from_id != DC_CONTACT_ID_SELF as libc::c_uint { + let s = dc_timestamp_to_str(if 0 != (*msg).timestamp_rcvd { (*msg).timestamp_rcvd } else { (*msg).timestamp_sort }); - ret += &format!("Received: {}", as_str(p)); - free(p as *mut libc::c_void); + ret += &format!("Received: {}", &s); ret += "\n"; } if (*msg).from_id == 2 || (*msg).to_id == 2 { // device-internal message, no further details needed dc_msg_unref(msg); - dc_contact_unref(contact_from); - return to_cstring(ret); + return ret.strdup(); } context @@ -109,17 +107,14 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch |rows| { for row in rows { let (contact_id, ts) = row?; - let fts = dc_timestamp_to_str_safe(ts); + let fts = dc_timestamp_to_str(ts); ret += &format!("Read: {}", fts); - let contact = dc_contact_new(context); - dc_contact_load_from_db(contact, &context.sql, contact_id as u32); - - p = dc_contact_get_name_n_addr(contact); - ret += &format!(" by {}", as_str(p)); - free(p as *mut libc::c_void); - dc_contact_unref(contact); + let name = Contact::load_from_db(context, contact_id as u32) + .map(|contact| contact.get_name_n_addr()) + .unwrap_or_default(); + ret += &format!(" by {}", name); ret += "\n"; } Ok(()) @@ -178,17 +173,9 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch } free(p as *mut libc::c_void); - if (*msg).type_0 != DC_MSG_TEXT { + if (*msg).type_0 != Viewtype::Text { ret += "Type: "; - match (*msg).type_0 { - DC_MSG_AUDIO => ret += "Audio", - DC_MSG_FILE => ret += "File", - DC_MSG_GIF => ret += "GIF", - DC_MSG_IMAGE => ret += "Image", - DC_MSG_VIDEO => ret += "Video", - DC_MSG_VOICE => ret += "Voice", - _ => ret += &format!("{}", (*msg).type_0), - } + ret += &format!("{}", (*msg).type_0); ret += "\n"; p = dc_msg_get_filemime(msg); ret += &format!("Mimetype: {}\n", as_str(p)); @@ -218,12 +205,11 @@ pub unsafe fn dc_get_msg_info(context: &Context, msg_id: u32) -> *mut libc::c_ch } dc_msg_unref(msg); - dc_contact_unref(contact_from); - to_cstring(ret) + ret.strdup() } pub unsafe fn dc_msg_new_untyped<'a>(context: &'a Context) -> *mut dc_msg_t<'a> { - dc_msg_new(context, 0i32) + dc_msg_new(context, Viewtype::Unknown) } /* * @@ -236,7 +222,7 @@ pub unsafe fn dc_msg_new_untyped<'a>(context: &'a Context) -> *mut dc_msg_t<'a> // to check if a mail was sent, use dc_msg_is_sent() // approx. max. length returned by dc_msg_get_text() // approx. max. length returned by dc_get_msg_info() -pub unsafe fn dc_msg_new<'a>(context: &'a Context, viewtype: libc::c_int) -> *mut dc_msg_t<'a> { +pub unsafe fn dc_msg_new<'a>(context: &'a Context, viewtype: Viewtype) -> *mut dc_msg_t<'a> { let msg = dc_msg_t { magic: 0x11561156, id: 0, @@ -250,7 +236,7 @@ pub unsafe fn dc_msg_new<'a>(context: &'a Context, viewtype: libc::c_int) -> *mu timestamp_sort: 0, timestamp_sent: 0, timestamp_rcvd: 0, - text: std::ptr::null_mut(), + text: None, context, rfc724_mid: std::ptr::null_mut(), in_reply_to: std::ptr::null_mut(), @@ -279,8 +265,6 @@ pub unsafe fn dc_msg_empty(mut msg: *mut dc_msg_t) { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { return; } - free((*msg).text as *mut libc::c_void); - (*msg).text = 0 as *mut libc::c_char; free((*msg).rfc724_mid as *mut libc::c_void); (*msg).rfc724_mid = 0 as *mut libc::c_char; free((*msg).in_reply_to as *mut libc::c_void); @@ -297,18 +281,17 @@ pub unsafe fn dc_msg_get_filemime(msg: *const dc_msg_t) -> *mut libc::c_char { if !(msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint) { match (*msg).param.get(Param::MimeType) { Some(m) => { - ret = to_cstring(m); + ret = m.strdup(); } None => { if let Some(file) = (*msg).param.get(Param::File) { - let file_c = to_cstring(file); - dc_msg_guess_msgtype_from_suffix(file_c, 0 as *mut libc::c_int, &mut ret); + let file_c = CString::yolo(file); + dc_msg_guess_msgtype_from_suffix(file_c.as_ptr(), 0 as *mut Viewtype, &mut ret); if ret.is_null() { ret = dc_strdup( b"application/octet-stream\x00" as *const u8 as *const libc::c_char, ) } - free(file_c as *mut _); } } } @@ -324,11 +307,11 @@ pub unsafe fn dc_msg_get_filemime(msg: *const dc_msg_t) -> *mut libc::c_char { #[allow(non_snake_case)] pub unsafe fn dc_msg_guess_msgtype_from_suffix( pathNfilename: *const libc::c_char, - mut ret_msgtype: *mut libc::c_int, + mut ret_msgtype: *mut Viewtype, mut ret_mime: *mut *mut libc::c_char, ) { let mut suffix: *mut libc::c_char = 0 as *mut libc::c_char; - let mut dummy_msgtype: libc::c_int = 0; + let mut dummy_msgtype = Viewtype::Unknown; let mut dummy_buf: *mut libc::c_char = 0 as *mut libc::c_char; if !pathNfilename.is_null() { if ret_msgtype.is_null() { @@ -337,37 +320,37 @@ pub unsafe fn dc_msg_guess_msgtype_from_suffix( if ret_mime.is_null() { ret_mime = &mut dummy_buf } - *ret_msgtype = 0; + *ret_msgtype = Viewtype::Unknown; *ret_mime = 0 as *mut libc::c_char; suffix = dc_get_filesuffix_lc(pathNfilename); if !suffix.is_null() { if strcmp(suffix, b"mp3\x00" as *const u8 as *const libc::c_char) == 0i32 { - *ret_msgtype = DC_MSG_AUDIO; + *ret_msgtype = Viewtype::Audio; *ret_mime = dc_strdup(b"audio/mpeg\x00" as *const u8 as *const libc::c_char) } else if strcmp(suffix, b"aac\x00" as *const u8 as *const libc::c_char) == 0i32 { - *ret_msgtype = DC_MSG_AUDIO; + *ret_msgtype = Viewtype::Audio; *ret_mime = dc_strdup(b"audio/aac\x00" as *const u8 as *const libc::c_char) } else if strcmp(suffix, b"mp4\x00" as *const u8 as *const libc::c_char) == 0i32 { - *ret_msgtype = DC_MSG_VIDEO; + *ret_msgtype = Viewtype::Video; *ret_mime = dc_strdup(b"video/mp4\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 { - *ret_msgtype = DC_MSG_IMAGE; + *ret_msgtype = Viewtype::Image; *ret_mime = dc_strdup(b"image/jpeg\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 { - *ret_msgtype = DC_MSG_IMAGE; + *ret_msgtype = Viewtype::Image; *ret_mime = dc_strdup(b"image/png\x00" as *const u8 as *const libc::c_char) } else if strcmp(suffix, b"webp\x00" as *const u8 as *const libc::c_char) == 0i32 { - *ret_msgtype = DC_MSG_IMAGE; + *ret_msgtype = Viewtype::Image; *ret_mime = dc_strdup(b"image/webp\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 { - *ret_msgtype = DC_MSG_GIF; + *ret_msgtype = Viewtype::Gif; *ret_mime = dc_strdup(b"image/gif\x00" as *const u8 as *const libc::c_char) } else if strcmp(suffix, b"vcf\x00" as *const u8 as *const libc::c_char) == 0i32 || strcmp(suffix, b"vcard\x00" as *const u8 as *const libc::c_char) == 0i32 { - *ret_msgtype = DC_MSG_FILE; + *ret_msgtype = Viewtype::File; *ret_mime = dc_strdup(b"text/vcard\x00" as *const u8 as *const libc::c_char) } } @@ -381,9 +364,8 @@ pub unsafe fn dc_msg_get_file(msg: *const dc_msg_t) -> *mut libc::c_char { if !(msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint) { if let Some(file_rel) = (*msg).param.get(Param::File) { - let file_rel_c = to_cstring(file_rel); - file_abs = dc_get_abs_path((*msg).context, file_rel_c); - free(file_rel_c as *mut _); + let file_rel_c = CString::yolo(file_rel); + file_abs = dc_get_abs_path((*msg).context, file_rel_c.as_ptr()); } } if !file_abs.is_null() { @@ -473,12 +455,12 @@ pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: dc_msg_empty(msg); (*msg).id = row.get::<_, i32>(0)? as u32; - (*msg).rfc724_mid = to_cstring(row.get::<_, String>(1)?); + (*msg).rfc724_mid = row.get::<_, String>(1)?.strdup(); (*msg).in_reply_to = match row.get::<_, Option<String>>(2)? { - Some(s) => to_cstring(s), + Some(s) => s.strdup(), None => std::ptr::null_mut(), }; - (*msg).server_folder = to_cstring(row.get::<_, String>(3)?); + (*msg).server_folder = row.get::<_, String>(3)?.strdup(); (*msg).server_uid = row.get(4)?; (*msg).move_state = row.get(5)?; (*msg).chat_id = row.get(6)?; @@ -490,19 +472,25 @@ pub fn dc_msg_load_from_db<'a>(msg: *mut dc_msg_t<'a>, context: &'a Context, id: (*msg).type_0 = row.get(12)?; (*msg).state = row.get(13)?; (*msg).is_dc_message = row.get(14)?; - (*msg).text = to_cstring(row.get::<_, String>(15).unwrap_or_default()); + (*msg).text = row.get::<_, Option<String>>(15)?; (*msg).param = row.get::<_, String>(16)?.parse().unwrap_or_default(); (*msg).starred = row.get(17)?; (*msg).hidden = row.get(18)?; (*msg).location_id = row.get(19)?; (*msg).chat_blocked = row.get::<_, Option<i32>>(20)?.unwrap_or_default(); if (*msg).chat_blocked == 2 { - dc_truncate_n_unwrap_str((*msg).text, 256, 0); - } - } + if let Some(ref text) = (*msg).text { + let ptr = text.strdup(); + + dc_truncate_n_unwrap_str(ptr, 256, 0); + + (*msg).text = Some(to_string(ptr)); + free(ptr.cast()); + } + }; Ok(()) - } - ); + } + }); res.is_ok() } @@ -516,10 +504,8 @@ pub unsafe fn dc_get_mime_headers(context: &Context, msg_id: uint32_t) -> *mut l ); if let Some(headers) = headers { - let h = to_cstring(headers); - let res = dc_strdup_keep_null(h); - free(h as *mut _); - res + let h = CString::yolo(headers); + dc_strdup_keep_null(h.as_ptr()) } else { std::ptr::null_mut() } @@ -682,9 +668,9 @@ pub unsafe fn dc_msg_get_chat_id(msg: *const dc_msg_t) -> uint32_t { }; } -pub unsafe fn dc_msg_get_viewtype(msg: *const dc_msg_t) -> libc::c_int { +pub unsafe fn dc_msg_get_viewtype(msg: *const dc_msg_t) -> Viewtype { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { - return 0i32; + return Viewtype::Unknown; } (*msg).type_0 @@ -718,9 +704,11 @@ pub unsafe fn dc_msg_get_text(msg: *const dc_msg_t) -> *mut libc::c_char { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { return dc_strdup(0 as *const libc::c_char); } - - let res = dc_truncate_str(as_str((*msg).text), 30000); - to_cstring(res) + if let Some(ref text) = (*msg).text { + dc_truncate_str(text, 30000).strdup() + } else { + ptr::null_mut() + } } #[allow(non_snake_case)] @@ -729,9 +717,8 @@ pub unsafe fn dc_msg_get_filename(msg: *const dc_msg_t) -> *mut libc::c_char { if !(msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint) { if let Some(file) = (*msg).param.get(Param::File) { - let file_c = to_cstring(file); - ret = dc_get_filename(file_c); - free(file_c as *mut _); + let file_c = CString::yolo(file); + ret = dc_get_filename(file_c.as_ptr()); } } if !ret.is_null() { @@ -746,9 +733,8 @@ pub unsafe fn dc_msg_get_filebytes(msg: *const dc_msg_t) -> uint64_t { if !(msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint) { if let Some(file) = (*msg).param.get(Param::File) { - let file_c = to_cstring(file); - ret = dc_get_filebytes((*msg).context, file_c); - free(file_c as *mut _); + let file_c = CString::yolo(file); + ret = dc_get_filebytes((*msg).context, file_c.as_ptr()); } } @@ -802,8 +788,8 @@ pub unsafe fn dc_msg_get_summary<'a>( ) -> *mut dc_lot_t { let mut success = true; let ret: *mut dc_lot_t = dc_lot_new(); - let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t; let mut chat_to_delete: *mut Chat = 0 as *mut Chat; + if !(msg.is_null() || (*msg).magic != 0x11561156 as libc::c_uint) { if chat.is_null() { chat_to_delete = dc_get_chat((*msg).context, (*msg).chat_id); @@ -816,15 +802,18 @@ pub unsafe fn dc_msg_get_summary<'a>( success = false; } if success == false { - if (*msg).from_id != 1 as libc::c_uint + let contact = if (*msg).from_id != DC_CONTACT_ID_SELF as libc::c_uint && ((*chat).type_0 == 120 || (*chat).type_0 == 130) { - contact = dc_get_contact((*chat).context, (*msg).from_id) - } - dc_lot_fill(ret, msg, chat, contact, (*msg).context); + Contact::get_by_id((*chat).context, (*msg).from_id).ok() + } else { + None + }; + + dc_lot_fill(ret, msg, chat, contact.as_ref(), (*msg).context); } } - dc_contact_unref(contact); + dc_chat_unref(chat_to_delete); ret @@ -838,9 +827,15 @@ pub unsafe fn dc_msg_get_summarytext( return dc_strdup(0 as *const libc::c_char); } + let msgtext_c = (*msg) + .text + .as_ref() + .map(|s| CString::yolo(String::as_str(s))); + let msgtext_ptr = msgtext_c.map_or(ptr::null(), |s| s.as_ptr()); + dc_msg_get_summarytext_by_raw( (*msg).type_0, - (*msg).text, + msgtext_ptr, &mut (*msg).param, approx_characters, (*msg).context, @@ -850,7 +845,7 @@ pub unsafe fn dc_msg_get_summarytext( /* the returned value must be free()'d */ #[allow(non_snake_case)] pub unsafe fn dc_msg_get_summarytext_by_raw( - type_0: libc::c_int, + type_0: Viewtype, text: *const libc::c_char, param: &mut Params, approx_characters: libc::c_int, @@ -863,20 +858,23 @@ pub unsafe fn dc_msg_get_summarytext_by_raw( let mut value: *mut libc::c_char = 0 as *mut libc::c_char; let mut append_text: libc::c_int = 1i32; match type_0 { - 20 => prefix = to_cstring(context.stock_str(StockMessage::Image)), - 21 => prefix = to_cstring(context.stock_str(StockMessage::Gif)), - 50 => prefix = to_cstring(context.stock_str(StockMessage::Video)), - 41 => prefix = to_cstring(context.stock_str(StockMessage::VoiceMessage)), - 40 | 60 => { + Viewtype::Image => prefix = context.stock_str(StockMessage::Image).strdup(), + Viewtype::Gif => prefix = context.stock_str(StockMessage::Gif).strdup(), + Viewtype::Video => prefix = context.stock_str(StockMessage::Video).strdup(), + Viewtype::Voice => prefix = context.stock_str(StockMessage::VoiceMessage).strdup(), + Viewtype::Audio | Viewtype::File => { if param.get_int(Param::Cmd) == Some(6) { - prefix = to_cstring(context.stock_str(StockMessage::AcSetupMsgSubject)); + prefix = context.stock_str(StockMessage::AcSetupMsgSubject).strdup(); append_text = 0i32 } else { - pathNfilename = to_cstring(param.get(Param::File).unwrap_or_else(|| "ErrFilename")); + pathNfilename = param + .get(Param::File) + .unwrap_or_else(|| "ErrFilename") + .strdup(); value = dc_get_filename(pathNfilename); let label = CString::new( context - .stock_str(if type_0 == DC_MSG_AUDIO { + .stock_str(if type_0 == Viewtype::Audio { StockMessage::Audio } else { StockMessage::File @@ -893,7 +891,7 @@ pub unsafe fn dc_msg_get_summarytext_by_raw( } _ => { if param.get_int(Param::Cmd) == Some(9) { - prefix = to_cstring(context.stock_str(StockMessage::Location)); + prefix = context.stock_str(StockMessage::Location).strdup(); append_text = 0; } } @@ -946,12 +944,11 @@ pub unsafe fn dc_msg_is_sent(msg: *const dc_msg_t) -> libc::c_int { } } -// TODO should return bool /rtn -pub unsafe fn dc_msg_is_starred(msg: *const dc_msg_t) -> libc::c_int { +pub unsafe fn dc_msg_is_starred(msg: *const dc_msg_t) -> bool { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { - return 0i32; + return false; } - return if 0 != (*msg).starred { 1i32 } else { 0i32 }; + 0 != (*msg).starred } // TODO should return bool /rtn @@ -998,7 +995,7 @@ pub unsafe fn dc_msg_is_increation(msg: *const dc_msg_t) -> libc::c_int { pub unsafe fn dc_msg_is_setupmessage(msg: *const dc_msg_t) -> bool { if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint - || (*msg).type_0 != DC_MSG_FILE as libc::c_int + || (*msg).type_0 != Viewtype::File { return false; } @@ -1028,19 +1025,17 @@ pub unsafe fn dc_msg_get_setupcodebegin(msg: *const dc_msg_t) -> *mut libc::c_ch || buf.is_null() || buf_bytes <= 0) { - if !(0 - == dc_split_armored_data( - buf, - &mut buf_headerline, - &mut buf_setupcodebegin, - 0 as *mut *const libc::c_char, - 0 as *mut *const libc::c_char, - ) - || strcmp( - buf_headerline, - b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, - ) != 0i32 - || buf_setupcodebegin.is_null()) + if dc_split_armored_data( + buf, + &mut buf_headerline, + &mut buf_setupcodebegin, + 0 as *mut *const libc::c_char, + 0 as *mut *const libc::c_char, + ) && strcmp( + buf_headerline, + b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, + ) == 0 + && !buf_setupcodebegin.is_null() { ret = dc_strdup(buf_setupcodebegin) } @@ -1060,8 +1055,11 @@ pub unsafe fn dc_msg_set_text(mut msg: *mut dc_msg_t, text: *const libc::c_char) if msg.is_null() || (*msg).magic != 0x11561156i32 as libc::c_uint { return; } - free((*msg).text as *mut libc::c_void); - (*msg).text = dc_strdup(text); + (*msg).text = if text.is_null() { + None + } else { + Some(to_string(text)) + }; } pub unsafe fn dc_msg_set_file( @@ -1395,7 +1393,7 @@ pub fn dc_rfc724_mid_exists( &[as_str(rfc724_mid)], |row| { if !ret_server_folder.is_null() { - unsafe { *ret_server_folder = to_cstring(row.get::<_, String>(0)?) }; + unsafe { *ret_server_folder = row.get::<_, String>(0)?.strdup() }; } if !ret_server_uid.is_null() { unsafe { *ret_server_uid = row.get(1)? }; @@ -1437,12 +1435,13 @@ pub fn dc_update_server_uid( #[cfg(test)] mod tests { use super::*; + use crate::test_utils as test; use std::ffi::CStr; #[test] fn test_dc_msg_guess_msgtype_from_suffix() { unsafe { - let mut type_0: libc::c_int = 0; + let mut type_0 = Viewtype::Unknown; let mut mime_0: *mut libc::c_char = 0 as *mut libc::c_char; dc_msg_guess_msgtype_from_suffix( @@ -1450,7 +1449,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_AUDIO as libc::c_int); + assert_eq!(type_0, Viewtype::Audio); assert_eq!(as_str(mime_0 as *const libc::c_char), "audio/mpeg"); free(mime_0 as *mut libc::c_void); @@ -1459,7 +1458,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_AUDIO as libc::c_int); + assert_eq!(type_0, Viewtype::Audio); assert_eq!(as_str(mime_0 as *const libc::c_char), "audio/aac"); free(mime_0 as *mut libc::c_void); @@ -1468,7 +1467,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_VIDEO as libc::c_int); + assert_eq!(type_0, Viewtype::Video); assert_eq!(as_str(mime_0 as *const libc::c_char), "video/mp4"); free(mime_0 as *mut libc::c_void); @@ -1477,7 +1476,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_IMAGE as libc::c_int); + assert_eq!(type_0, Viewtype::Image); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1491,7 +1490,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_IMAGE as libc::c_int); + assert_eq!(type_0, Viewtype::Image); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1505,7 +1504,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_IMAGE as libc::c_int); + assert_eq!(type_0, Viewtype::Image); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1519,7 +1518,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_IMAGE as libc::c_int); + assert_eq!(type_0, Viewtype::Image); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1533,7 +1532,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_GIF as libc::c_int); + assert_eq!(type_0, Viewtype::Gif); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1547,7 +1546,7 @@ mod tests { &mut type_0, &mut mime_0, ); - assert_eq!(type_0, DC_MSG_FILE as libc::c_int); + assert_eq!(type_0, Viewtype::File); assert_eq!( CStr::from_ptr(mime_0 as *const libc::c_char) .to_str() @@ -1557,4 +1556,32 @@ mod tests { free(mime_0 as *mut libc::c_void); } } + + #[test] + pub fn test_prepare_message_and_send() { + use crate::config::Config; + + unsafe { + let d = test::dummy_context(); + let ctx = &d.ctx; + + let contact = + Contact::create(ctx, "", "dest@example.com").expect("failed to create contact"); + + let res = ctx.set_config(Config::ConfiguredAddr, Some("self@example.com")); + assert!(res.is_ok()); + + let chat = dc_create_chat_by_contact_id(ctx, contact); + assert!(chat != 0); + + let msg = dc_msg_new(ctx, Viewtype::Text); + assert!(!msg.is_null()); + + let msg_id = dc_prepare_msg(ctx, chat, msg); + assert!(msg_id != 0); + + let msg2 = dc_get_msg(ctx, msg_id); + assert!(!msg2.is_null()); + } + } } diff --git a/src/dc_qr.rs b/src/dc_qr.rs index ac9ad1ffe..251b3bd2a 100644 --- a/src/dc_qr.rs +++ b/src/dc_qr.rs @@ -1,8 +1,8 @@ use percent_encoding::percent_decode_str; +use crate::contact::*; use crate::context::Context; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_lot::*; use crate::dc_strencode::*; use crate::dc_tools::*; @@ -59,34 +59,33 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc let param: Params = as_str(fragment).parse().expect("invalid params"); addr = param .get(Param::Forwarded) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); if !addr.is_null() { if let Some(ref name_enc) = param.get(Param::SetLongitude) { let name_r = percent_decode_str(name_enc) .decode_utf8() .expect("invalid name"); - name = to_cstring(name_r); - dc_normalize_name(name); + name = normalize_name(name_r).strdup(); } invitenumber = param .get(Param::ProfileImage) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); auth = param .get(Param::Auth) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); grpid = param .get(Param::GroupId) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); if !grpid.is_null() { if let Some(grpname_enc) = param.get(Param::GroupName) { let grpname_r = percent_decode_str(grpname_enc) .decode_utf8() .expect("invalid groupname"); - grpname = to_cstring(grpname_r); + grpname = grpname_r.strdup(); } } } @@ -152,11 +151,8 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc strlen(b"BEGIN:VCARD\x00" as *const u8 as *const libc::c_char), ) == 0i32 { - let lines: *mut carray = dc_split_into_lines(qr); - let mut i: libc::c_int = 0i32; - while (i as libc::c_uint) < carray_count(lines) { - let key: *mut libc::c_char = - carray_get(lines, i as libc::c_uint) as *mut libc::c_char; + let lines = dc_split_into_lines(qr); + for &key in &lines { dc_trim(key); let mut value: *mut libc::c_char = strchr(key, ':' as i32); if !value.is_null() { @@ -189,10 +185,9 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc b";\x00" as *const u8 as *const libc::c_char, b",\x00" as *const u8 as *const libc::c_char, ); - dc_normalize_name(name); + name = normalize_name(as_str(name)).strdup(); } } - i += 1 } dc_free_splitted_lines(lines); } @@ -208,10 +203,10 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc let mut temp: *mut libc::c_char = dc_urldecode(addr); free(addr as *mut libc::c_void); addr = temp; - temp = dc_addr_normalize(addr); + temp = addr_normalize(as_str(addr)).strdup(); free(addr as *mut libc::c_void); addr = temp; - if !dc_may_be_valid_addr(addr) { + if !may_be_valid_addr(as_str(addr)) { (*qr_parsed).state = 400i32; (*qr_parsed).text1 = dc_strdup( b"Bad e-mail address.\x00" as *const u8 as *const libc::c_char, @@ -252,19 +247,19 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc if addr.is_null() || invitenumber.is_null() || auth.is_null() { if let Some(peerstate) = peerstate { (*qr_parsed).state = 210i32; - let addr_ptr = if let Some(ref addr) = peerstate.addr { - to_cstring(addr) - } else { - std::ptr::null() - }; - (*qr_parsed).id = dc_add_or_lookup_contact( + let addr = peerstate + .addr + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_else(|| ""); + (*qr_parsed).id = Contact::add_or_lookup( context, - 0 as *const libc::c_char, - addr_ptr, - 0x80i32, - 0 as *mut libc::c_int, - ); - free(addr_ptr as *mut _); + "", + addr, + Origin::UnhandledQrScan, + ) + .map(|(id, _)| id) + .unwrap_or_default(); dc_create_or_lookup_nchat_by_contact_id( context, (*qr_parsed).id, @@ -290,26 +285,28 @@ pub unsafe fn dc_check_qr(context: &Context, qr: *const libc::c_char) -> *mut dc } else { (*qr_parsed).state = 200i32 } - (*qr_parsed).id = dc_add_or_lookup_contact( + (*qr_parsed).id = Contact::add_or_lookup( context, - name, - addr, - 0x80i32, - 0 as *mut libc::c_int, - ); + as_str(name), + as_str(addr), + Origin::UnhandledQrScan, + ) + .map(|(id, _)| id) + .unwrap_or_default(); (*qr_parsed).fingerprint = dc_strdup(fingerprint); (*qr_parsed).invitenumber = dc_strdup(invitenumber); (*qr_parsed).auth = dc_strdup(auth) } } else if !addr.is_null() { (*qr_parsed).state = 320i32; - (*qr_parsed).id = dc_add_or_lookup_contact( + (*qr_parsed).id = Contact::add_or_lookup( context, - name, - addr, - 0x80i32, - 0 as *mut libc::c_int, + as_str(name), + as_str(addr), + Origin::UnhandledQrScan, ) + .map(|(id, _)| id) + .unwrap_or_default(); } else if strstr( qr, b"http://\x00" as *const u8 as *const libc::c_char, diff --git a/src/dc_receive_imf.rs b/src/dc_receive_imf.rs index 35964de91..47c1e3004 100644 --- a/src/dc_receive_imf.rs +++ b/src/dc_receive_imf.rs @@ -8,10 +8,10 @@ use mmime::other::*; use sha2::{Digest, Sha256}; use crate::constants::*; +use crate::contact::*; use crate::context::Context; use crate::dc_array::*; use crate::dc_chat::*; -use crate::dc_contact::*; use crate::dc_job::*; use crate::dc_location::*; use crate::dc_mimeparser::*; @@ -38,7 +38,7 @@ pub unsafe fn dc_receive_imf( let mut current_block: u64; /* the function returns the number of created messages in the database */ let mut incoming: libc::c_int = 1; - let mut incoming_origin: libc::c_int = 0; + let mut incoming_origin = Origin::Unknown; let mut to_self: libc::c_int = 0; let mut from_id: uint32_t = 0 as uint32_t; let mut from_id_blocked: libc::c_int = 0; @@ -51,8 +51,6 @@ pub unsafe fn dc_receive_imf( let mut add_delete_job: libc::c_int = 0; let mut insert_msg_id: uint32_t = 0 as uint32_t; - let mut i: size_t; - let mut icnt: size_t; /* Message-ID from the header */ let mut rfc724_mid = 0 as *mut libc::c_char; let mut sort_timestamp = 0; @@ -105,7 +103,7 @@ pub unsafe fn dc_receive_imf( dc_add_or_lookup_contacts_by_mailbox_list( context, (*fld_from).frm_mb_list, - 0x10, + Origin::IncomingUnknownFrom, from_list, &mut check_self, ); @@ -116,7 +114,8 @@ pub unsafe fn dc_receive_imf( } } else if dc_array_get_cnt(from_list) >= 1 { from_id = dc_array_get_id(from_list, 0 as size_t); - incoming_origin = dc_get_contact_origin(context, from_id, &mut from_id_blocked) + incoming_origin = + Contact::get_origin_by_id(context, from_id, &mut from_id_blocked) } dc_array_unref(from_list); } @@ -129,18 +128,18 @@ pub unsafe fn dc_receive_imf( context, (*fld_to).to_addr_list, if 0 == incoming { - 0x4000 - } else if incoming_origin >= 0x100 { - 0x400 + Origin::OutgoingTo + } else if incoming_origin.is_verified() { + Origin::IncomingTo } else { - 0x40 + Origin::IncomingUnknownTo }, to_ids, &mut to_self, ); } } - if !dc_mimeparser_get_last_nonmeta(&mime_parser).is_null() { + if dc_mimeparser_get_last_nonmeta(&mut mime_parser).is_some() { field = dc_mimeparser_lookup_field(&mime_parser, "Cc"); if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_CC as libc::c_int { let fld_cc: *mut mailimf_cc = (*field).fld_data.fld_cc; @@ -149,11 +148,11 @@ pub unsafe fn dc_receive_imf( context, (*fld_cc).cc_addr_list, if 0 == incoming { - 0x2000 - } else if incoming_origin >= 0x100 { - 0x200 + Origin::OutgoingCc + } else if incoming_origin.is_verified() { + Origin::IncomingCc } else { - 0x20 + Origin::IncomingUnknownCc }, to_ids, 0 as *mut libc::c_int, @@ -255,7 +254,7 @@ pub unsafe fn dc_receive_imf( if chat_id == 0 as libc::c_uint { let create_blocked: libc::c_int = if 0 != test_normal_chat_id && test_normal_chat_id_blocked == 0 - || incoming_origin >= 0x7fffffff + || incoming_origin.is_start_new_chat() { 0 } else { @@ -287,7 +286,7 @@ pub unsafe fn dc_receive_imf( } if chat_id == 0 as libc::c_uint { let create_blocked_0: libc::c_int = - if incoming_origin >= 0x7fffffff || from_id == to_id { + if incoming_origin.is_start_new_chat() || from_id == to_id { 0 } else { 2 @@ -311,16 +310,20 @@ pub unsafe fn dc_receive_imf( } else if 0 != dc_is_reply_to_known_message(context, &mime_parser) { - dc_scaleup_contact_origin(context, from_id, 0x100); + Contact::scaleup_origin_by_id( + context, + from_id, + Origin::IncomingReplyTo, + ); info!( context, 0, "Message is a reply to a known message, mark sender as known.", ); - incoming_origin = if incoming_origin > 0x100 { + incoming_origin = if incoming_origin.is_verified() { incoming_origin } else { - 0x100 + Origin::IncomingReplyTo } } } @@ -329,8 +332,8 @@ pub unsafe fn dc_receive_imf( chat_id = 3 as uint32_t } if 0 != chat_id_blocked && state == 10 { - if incoming_origin < 0x100 && msgrmsg == 0 { - state = 13 + if !incoming_origin.is_verified() && msgrmsg == 0 { + state = 13; } } } else { @@ -355,12 +358,13 @@ pub unsafe fn dc_receive_imf( } } if chat_id == 0 as libc::c_uint && 0 != allow_creation { - let create_blocked_1: libc::c_int = - if 0 != msgrmsg && !dc_is_contact_blocked(context, to_id) { - 0 - } else { - 2 - }; + let create_blocked_1: libc::c_int = if 0 != msgrmsg + && !Contact::is_blocked_load(context, to_id) + { + 0 + } else { + 2 + }; dc_create_or_lookup_nchat_by_contact_id( context, to_id, @@ -437,7 +441,7 @@ pub unsafe fn dc_receive_imf( ) } } - icnt = carray_count(mime_parser.parts) as size_t; + let icnt = mime_parser.parts.len(); context.sql.prepare( "INSERT INTO msgs \ @@ -452,23 +456,23 @@ pub unsafe fn dc_receive_imf( current_block = 2756754640271984560; break; } - let part = carray_get(mime_parser.parts, i as libc::c_uint) as *mut dc_mimepart_t; - if !(0 != (*part).is_meta) { + let part = &mut mime_parser.parts[i]; + if part.is_meta == 0 { if !mime_parser.location_kml.is_none() && icnt == 1 - && !(*part).msg.is_null() + && !part.msg.is_null() && (strcmp( - (*part).msg, + part.msg, b"-location-\x00" as *const u8 as *const libc::c_char, ) == 0 - || *(*part).msg.offset(0isize) as libc::c_int == 0) + || *part.msg.offset(0isize) as libc::c_int == 0) { hidden = 1; if state == 10 { state = 13 } } - if (*part).type_0 == 10 { + if part.type_0 == 10 { txt_raw = dc_mprintf( b"%s\n\n%s\x00" as *const u8 as *const libc::c_char, if !mime_parser.subject.is_null() { @@ -476,12 +480,12 @@ pub unsafe fn dc_receive_imf( } else { b"\x00" as *const u8 as *const libc::c_char }, - (*part).msg_raw, + part.msg_raw, ) } if 0 != mime_parser.is_system_message { - (*part).param.set_int( Param::Cmd, + part.param.set_int( Param::Cmd, mime_parser.is_system_message, ); } @@ -496,11 +500,11 @@ pub unsafe fn dc_receive_imf( sort_timestamp, sent_timestamp, rcvd_timestamp, - (*part).type_0, + part.type_0, state, msgrmsg, - if !(*part).msg.is_null() { - as_str((*part).msg) + if !part.msg.is_null() { + as_str(part.msg) } else { "" }, @@ -510,8 +514,8 @@ pub unsafe fn dc_receive_imf( } else { String::new() }, - (*part).param.to_string(), - (*part).bytes, + part.param.to_string(), + part.bytes, hidden, if 0 != save_mime_headers { let body_string = std::str::from_utf8(std::slice::from_raw_parts(imf_raw_not_terminated as *const u8, imf_raw_bytes)).unwrap(); @@ -588,17 +592,13 @@ pub unsafe fn dc_receive_imf( match current_block { 16282941964262048061 => {} _ => { - if carray_count(mime_parser.reports) > 0 as libc::c_uint { + if !mime_parser.reports.is_empty() { let mdns_enabled = context .sql .get_config_int(context, "mdns_enabled") .unwrap_or_else(|| 1); - icnt = carray_count(mime_parser.reports) as size_t; - i = 0 as size_t; - while i < icnt { + for report_root in mime_parser.reports { let mut mdn_consumed: libc::c_int = 0; - let report_root: *mut mailmime = - carray_get(mime_parser.reports, i as libc::c_uint) as *mut mailmime; let report_type: *mut mailmime_parameter = mailmime_find_ct_parameter( report_root, b"report-type\x00" as *const u8 as *const libc::c_char, @@ -723,8 +723,7 @@ pub unsafe fn dc_receive_imf( &mut msg_id, ) { rr_event_to_send - .push((chat_id_0, 0)); - rr_event_to_send.push((msg_id, 0)); + .push((chat_id_0, msg_id)); } mdn_consumed = (msg_id != 0 as libc::c_uint) @@ -757,7 +756,6 @@ pub unsafe fn dc_receive_imf( } } } - i = i.wrapping_add(1) } } if !mime_parser.message_kml.is_none() && chat_id > 9 as libc::c_uint { @@ -771,7 +769,7 @@ pub unsafe fn dc_receive_imf( context, chat_id, from_id, - mime_parser.message_kml.unwrap().locations, + &mime_parser.message_kml.unwrap().locations, 1, ); if 0 != newest_location_id && 0 == hidden { @@ -784,28 +782,34 @@ pub unsafe fn dc_receive_imf( if !mime_parser.location_kml.is_none() && chat_id > DC_CHAT_ID_LAST_SPECIAL as libc::c_uint { - let contact = dc_get_contact(context, from_id); - if !mime_parser.location_kml.as_ref().unwrap().addr.is_null() - && !contact.is_null() - && !(*contact).addr.is_null() - && strcasecmp( - (*contact).addr, - mime_parser.location_kml.as_ref().unwrap().addr, - ) == 0 - { - let newest_location_id = dc_save_locations( - context, - chat_id, - from_id, - mime_parser.location_kml.as_ref().unwrap().locations, - 0, - ); - if newest_location_id != 0 && hidden == 0 && !location_id_written { - dc_set_msg_location_id(context, insert_msg_id, newest_location_id); + if !mime_parser.location_kml.as_ref().unwrap().addr.is_null() { + if let Ok(contact) = Contact::get_by_id(context, from_id) { + if !contact.get_addr().is_empty() + && contact.get_addr().to_lowercase() + == as_str(mime_parser.location_kml.as_ref().unwrap().addr) + .to_lowercase() + { + let newest_location_id = dc_save_locations( + context, + chat_id, + from_id, + &mime_parser.location_kml.as_ref().unwrap().locations, + 0, + ); + if newest_location_id != 0 + && hidden == 0 + && !location_id_written + { + dc_set_msg_location_id( + context, + insert_msg_id, + newest_location_id, + ); + } + send_event = true; + } } - send_event = true; } - dc_contact_unref(contact); } if send_event { context.call_cb( @@ -955,7 +959,13 @@ unsafe fn create_or_lookup_group( if !field.is_null() && (*field).fld_type == MAILIMF_FIELD_MESSAGE_ID as libc::c_int { let fld_message_id: *mut mailimf_message_id = (*field).fld_data.fld_message_id; if !fld_message_id.is_null() { - grpid = dc_extract_grpid_from_rfc724_mid((*fld_message_id).mid_value) + if let Some(extracted_grpid) = + dc_extract_grpid_from_rfc724_mid(as_str((*fld_message_id).mid_value)) + { + grpid = extracted_grpid.strdup(); + } else { + grpid = 0 as *mut libc::c_char; + } } } if grpid.is_null() { @@ -1015,9 +1025,8 @@ unsafe fn create_or_lookup_group( if !optional_field.is_null() { X_MrRemoveFromGrp = (*optional_field).fld_value; mime_parser.is_system_message = 5; - let left_group: libc::c_int = - (dc_lookup_contact_id_by_addr(context, X_MrRemoveFromGrp) - == from_id as libc::c_uint) as libc::c_int; + let left_group = (Contact::lookup_id_by_addr(context, as_str(X_MrRemoveFromGrp)) + == from_id as u32) as libc::c_int; better_msg = context.stock_system_msg( if 0 != left_group { StockMessage::MsgGroupLeft @@ -1126,7 +1135,7 @@ unsafe fn create_or_lookup_group( && !grpname.is_null() && X_MrRemoveFromGrp.is_null() && (0 == group_explicitly_left - || !X_MrAddToGrp.is_null() && dc_addr_cmp(&self_addr, as_str(X_MrAddToGrp))) + || !X_MrAddToGrp.is_null() && addr_cmp(&self_addr, as_str(X_MrAddToGrp))) { /*otherwise, a pending "quit" message may pop up*/ /*re-create explicitly left groups only if ourself is re-added*/ @@ -1208,20 +1217,15 @@ unsafe fn create_or_lookup_group( { ok = 1 } else { - let mut i_0: libc::c_int = 0; - while (i_0 as libc::c_uint) < carray_count(mime_parser.parts) { - let part: *mut dc_mimepart_t = - carray_get(mime_parser.parts, i_0 as libc::c_uint) - as *mut dc_mimepart_t; - if (*part).type_0 == 20 { - grpimage = (*part) + for part in &mut mime_parser.parts { + if part.type_0 == 20 { + grpimage = part .param .get(Param::File) - .map(|s| to_cstring(s)) + .map(|s| s.strdup()) .unwrap_or_else(|| std::ptr::null_mut()); ok = 1 } - i_0 += 1 } } if 0 != ok { @@ -1261,17 +1265,20 @@ unsafe fn create_or_lookup_group( params![chat_id as i32], ) .ok(); - if skip.is_null() || !dc_addr_cmp(&self_addr, as_str(skip)) { + if skip.is_null() || !addr_cmp(&self_addr, as_str(skip)) { dc_add_to_chat_contacts_table(context, chat_id, 1); } if from_id > 9 { - if !dc_addr_equals_contact(context, &self_addr, from_id as u32) - && (skip.is_null() - || !dc_addr_equals_contact( - context, - to_string(skip), - from_id as u32, - )) + if !Contact::addr_equals_contact( + context, + &self_addr, + from_id as u32, + ) && (skip.is_null() + || !Contact::addr_equals_contact( + context, + to_string(skip), + from_id as u32, + )) { dc_add_to_chat_contacts_table( context, @@ -1283,9 +1290,13 @@ unsafe fn create_or_lookup_group( i = 0; while i < to_ids_cnt { let to_id = dc_array_get_id(to_ids, i as size_t); - if !dc_addr_equals_contact(context, &self_addr, to_id) + if !Contact::addr_equals_contact(context, &self_addr, to_id) && (skip.is_null() - || !dc_addr_equals_contact(context, to_string(skip), to_id)) + || !Contact::addr_equals_contact( + context, + to_string(skip), + to_id, + )) { dc_add_to_chat_contacts_table(context, chat_id, to_id); } @@ -1414,10 +1425,12 @@ unsafe fn create_or_lookup_adhoc_group( { grpname = dc_strdup(mime_parser.subject) } else { - grpname = to_cstring(context.stock_string_repl_int( - StockMessage::Member, - dc_array_get_cnt(member_ids) as libc::c_int, - )); + grpname = context + .stock_string_repl_int( + StockMessage::Member, + dc_array_get_cnt(member_ids) as libc::c_int, + ) + .strdup(); } chat_id = create_group_record(context, grpid, grpname, create_blocked, 0); @@ -1526,7 +1539,7 @@ fn hex_hash(s: impl AsRef<str>) -> *const libc::c_char { let bytes = s.as_ref().as_bytes(); let result = Sha256::digest(bytes); let result_hex = hex::encode(&result[..8]); - unsafe { to_cstring(result_hex) as *const _ } + unsafe { result_hex.strdup() as *const _ } } #[allow(non_snake_case)] @@ -1554,7 +1567,7 @@ unsafe fn search_chat_ids_by_contact_ids( i += 1 } if !(dc_array_get_cnt(contact_ids) == 0) { - dc_array_sort_ids(contact_ids); + (*contact_ids).sort_ids(); contact_ids_str = dc_array_get_string(contact_ids, b",\x00" as *const u8 as *const libc::c_char); @@ -1608,26 +1621,21 @@ unsafe fn check_verified_properties( to_ids: *const dc_array_t, failure_reason: *mut *mut libc::c_char, ) -> libc::c_int { - let contact = dc_contact_new(context); - let verify_fail = |reason: String| { - *failure_reason = to_cstring(format!("{}. See \"Info\" for details.", reason)); + *failure_reason = format!("{}. See \"Info\" for details.", reason).strdup(); warn!(context, 0, "{}", reason); }; - let cleanup = || { - dc_contact_unref(contact); + let contact = match Contact::load_from_db(context, from_id) { + Ok(contact) => contact, + Err(_err) => { + verify_fail("Internal Error; cannot load contact".into()); + return 0; + } }; - if !dc_contact_load_from_db(contact, &context.sql, from_id) { - verify_fail("Internal Error; cannot load contact".into()); - cleanup(); - return 0; - } - if 0 == mimeparser.e2ee_helper.encrypted { verify_fail("This message is not encrypted".into()); - cleanup(); return 0; } @@ -1636,18 +1644,18 @@ unsafe fn check_verified_properties( // this check is skipped for SELF as there is no proper SELF-peerstate // and results in group-splits otherwise. if from_id != 1 { - let peerstate = Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)); + let peerstate = Peerstate::from_addr(context, &context.sql, contact.get_addr()); - if peerstate.is_none() || dc_contact_is_verified_ex(contact, peerstate.as_ref()) != 2 { + if peerstate.is_none() + || contact.is_verified_ex(peerstate.as_ref()) != VerifiedStatus::BidirectVerified + { verify_fail("The sender of this message is not verified.".into()); - cleanup(); return 0; } if let Some(peerstate) = peerstate { if !peerstate.has_verified_key(&mimeparser.e2ee_helper.signatures) { verify_fail("The message was sent with non-verified encryption.".into()); - cleanup(); return 0; } } @@ -1669,7 +1677,6 @@ unsafe fn check_verified_properties( ); if rows.is_err() { - cleanup(); return 0; } for (to_addr, mut is_verified) in rows.unwrap().into_iter() { @@ -1690,7 +1697,7 @@ unsafe fn check_verified_properties( context, 0, "{} has verfied {}.", - as_str((*contact).addr), + contact.get_addr(), to_addr, ); let fp = peerstate.gossip_key_fingerprint.clone(); @@ -1706,7 +1713,6 @@ unsafe fn check_verified_properties( "{} is not a member of this verified group", to_addr )); - cleanup(); return 0; } } @@ -1714,14 +1720,13 @@ unsafe fn check_verified_properties( 1 } -unsafe fn set_better_msg<T: AsRef<str>>(mime_parser: &dc_mimeparser_t, better_msg: T) { +unsafe fn set_better_msg<T: AsRef<str>>(mime_parser: &mut dc_mimeparser_t, better_msg: T) { let msg = better_msg.as_ref(); - if !(msg.len() > 0) && carray_count((*mime_parser).parts) > 0 { - let mut part: *mut dc_mimepart_t = - carray_get(mime_parser.parts, 0 as libc::c_uint) as *mut dc_mimepart_t; + if msg.len() > 0 && !mime_parser.parts.is_empty() { + let part = &mut mime_parser.parts[0]; if (*part).type_0 == 10 { - free((*part).msg as *mut libc::c_void); - (*part).msg = to_cstring(msg); + free(part.msg as *mut libc::c_void); + part.msg = msg.strdup(); } }; } @@ -1888,7 +1893,7 @@ fn is_msgrmsg_rfc724_mid(context: &Context, rfc724_mid: *const libc::c_char) -> unsafe fn dc_add_or_lookup_contacts_by_address_list( context: &Context, adr_list: *const mailimf_address_list, - origin: libc::c_int, + origin: Origin, ids: *mut dc_array_t, check_self: *mut libc::c_int, ) { @@ -1938,7 +1943,7 @@ unsafe fn dc_add_or_lookup_contacts_by_address_list( unsafe fn dc_add_or_lookup_contacts_by_mailbox_list( context: &Context, mb_list: *const mailimf_mailbox_list, - origin: libc::c_int, + origin: Origin, ids: *mut dc_array_t, check_self: *mut libc::c_int, ) { @@ -1976,7 +1981,7 @@ unsafe fn add_or_lookup_contact_by_addr( context: &Context, display_name_enc: *const libc::c_char, addr_spec: *const libc::c_char, - origin: libc::c_int, + origin: Origin, ids: *mut dc_array_t, mut check_self: *mut libc::c_int, ) { @@ -1994,7 +1999,7 @@ unsafe fn add_or_lookup_contact_by_addr( .get_config(context, "configured_addr") .unwrap_or_default(); - if dc_addr_cmp(self_addr, as_str(addr_spec)) { + if addr_cmp(self_addr, as_str(addr_spec)) { *check_self = 1; } @@ -2002,20 +2007,15 @@ unsafe fn add_or_lookup_contact_by_addr( return; } /* add addr_spec if missing, update otherwise */ - let mut display_name_dec = 0 as *mut libc::c_char; + let mut display_name_dec = "".to_string(); if !display_name_enc.is_null() { - display_name_dec = dc_decode_header_words(display_name_enc); - dc_normalize_name(display_name_dec); + let tmp = as_str(dc_decode_header_words(display_name_enc)); + display_name_dec = normalize_name(&tmp); } /*can be NULL*/ - let row_id = dc_add_or_lookup_contact( - context, - display_name_dec, - addr_spec, - origin, - 0 as *mut libc::c_int, - ); - free(display_name_dec as *mut libc::c_void); + let row_id = Contact::add_or_lookup(context, display_name_dec, as_str(addr_spec), origin) + .map(|(id, _)| id) + .unwrap_or_default(); if 0 != row_id { if !dc_array_search_id(ids, row_id, 0 as *mut size_t) { dc_array_add_id(ids, row_id); diff --git a/src/dc_saxparser.rs b/src/dc_saxparser.rs index 57e44fde2..2c7b669d7 100644 --- a/src/dc_saxparser.rs +++ b/src/dc_saxparser.rs @@ -80,7 +80,7 @@ pub unsafe fn dc_saxparser_set_text_handler( } pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *const libc::c_char) { - let current_block: u64; + let mut is_valid = false; let mut bak: libc::c_char; let buf_start: *mut libc::c_char; let mut last_text_start: *mut libc::c_char; @@ -99,7 +99,7 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c p = buf_start; loop { if !(0 != *p) { - current_block = 13425230902034816933; + is_valid = true; break; } if *p as libc::c_int == '<' as i32 { @@ -113,7 +113,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c if strncmp(p, b"!--\x00" as *const u8 as *const libc::c_char, 3) == 0i32 { p = strstr(p, b"-->\x00" as *const u8 as *const libc::c_char); if p.is_null() { - current_block = 7627180618761592946; break; } p = p.offset(3isize) @@ -137,7 +136,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c strlen(text_beg), 'c' as i32 as libc::c_char, ); - current_block = 7627180618761592946; break; } } else if strncmp(p, b"!DOCTYPE\x00" as *const u8 as *const libc::c_char, 8) == 0i32 { @@ -149,13 +147,11 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c } if *p as libc::c_int == 0i32 { /* unclosed doctype */ - current_block = 7627180618761592946; break; } else if *p as libc::c_int == '[' as i32 { p = strstr(p, b"]>\x00" as *const u8 as *const libc::c_char); if p.is_null() { /* unclosed inline doctype */ - current_block = 7627180618761592946; break; } else { p = p.offset(2isize) @@ -167,7 +163,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c p = strstr(p, b"?>\x00" as *const u8 as *const libc::c_char); if p.is_null() { /* unclosed processing instruction */ - current_block = 7627180618761592946; break; } else { p = p.offset(2isize) @@ -328,7 +323,6 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c p = strchr(p, '>' as i32); if p.is_null() { /* unclosed start-tag or end-tag */ - current_block = 7627180618761592946; break; } else { p = p.offset(1isize) @@ -339,16 +333,13 @@ pub unsafe fn dc_saxparser_parse(saxparser: *mut dc_saxparser_t, buf_start__: *c p = p.offset(1isize) } } - match current_block { - 13425230902034816933 => { - call_text_cb( - saxparser, - last_text_start, - p.wrapping_offset_from(last_text_start) as size_t, - '&' as i32 as libc::c_char, - ); - } - _ => {} + if is_valid { + call_text_cb( + saxparser, + last_text_start, + p.wrapping_offset_from(last_text_start) as size_t, + '&' as i32 as libc::c_char, + ); } do_free_attr(attr.as_mut_ptr(), free_attr.as_mut_ptr()); free(buf_start as *mut libc::c_void); diff --git a/src/dc_securejoin.rs b/src/dc_securejoin.rs index 22ed7bcb5..45d970d96 100644 --- a/src/dc_securejoin.rs +++ b/src/dc_securejoin.rs @@ -5,11 +5,11 @@ use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; use crate::aheader::EncryptPreference; use crate::constants::*; +use crate::contact::*; use crate::context::Context; use crate::dc_array::*; use crate::dc_chat::*; use crate::dc_configure::*; -use crate::dc_contact::*; use crate::dc_e2ee::*; use crate::dc_lot::*; use crate::dc_mimeparser::*; @@ -40,17 +40,17 @@ pub unsafe fn dc_get_securejoin_qr( let mut chat = 0 as *mut Chat; let mut group_name = 0 as *mut libc::c_char; let mut group_name_urlencoded = 0 as *mut libc::c_char; - let mut qr = None; + let mut qr: Option<String> = None; dc_ensure_secret_key_exists(context); invitenumber = dc_token_lookup(context, DC_TOKEN_INVITENUMBER, group_chat_id); if invitenumber.is_null() { - invitenumber = dc_create_id(); + invitenumber = dc_create_id().strdup(); dc_token_save(context, DC_TOKEN_INVITENUMBER, group_chat_id, invitenumber); } auth = dc_token_lookup(context, DC_TOKEN_AUTH, group_chat_id); if auth.is_null() { - auth = dc_create_id(); + auth = dc_create_id().strdup(); dc_token_save(context, DC_TOKEN_AUTH, group_chat_id, auth); } let self_addr = context.sql.get_config(context, "configured_addr"); @@ -64,7 +64,7 @@ pub unsafe fn dc_get_securejoin_qr( free(group_name_urlencoded as *mut libc::c_void); if let Some(qr) = qr { - to_cstring(qr) + qr.strdup() } else { std::ptr::null_mut() } @@ -263,11 +263,8 @@ unsafe fn send_handshake_msg( grpid: *const libc::c_char, ) { let mut msg: *mut dc_msg_t = dc_msg_new_untyped(context); - (*msg).type_0 = DC_MSG_TEXT; - (*msg).text = dc_mprintf( - b"Secure-Join: %s\x00" as *const u8 as *const libc::c_char, - step, - ); + (*msg).type_0 = Viewtype::Text; + (*msg).text = Some(format!("Secure-Join: {}", to_string(step))); (*msg).hidden = 1; (*msg).param.set_int(Param::Cmd, 7); if step.is_null() { @@ -318,30 +315,23 @@ unsafe fn fingerprint_equals_sender( return 0; } let mut fingerprint_equal: libc::c_int = 0i32; - let contacts: *mut dc_array_t = dc_get_chat_contacts(context, contact_chat_id); - let contact: *mut dc_contact_t = dc_contact_new(context); + let contacts = dc_get_chat_contacts(context, contact_chat_id); if !(dc_array_get_cnt(contacts) != 1) { - if !dc_contact_load_from_db( - contact, - &context.sql, - dc_array_get_id(contacts, 0i32 as size_t), - ) { + if let Ok(contact) = Contact::load_from_db(context, dc_array_get_id(contacts, 0)) { + if let Some(peerstate) = Peerstate::from_addr(context, &context.sql, contact.get_addr()) + { + let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint)); + if peerstate.public_key_fingerprint.is_some() + && &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap() + { + fingerprint_equal = 1; + } + } + } else { return 0; } - - if let Some(peerstate) = - Peerstate::from_addr(context, &context.sql, as_str((*contact).addr)) - { - let fingerprint_normalized = dc_normalize_fingerprint(as_str(fingerprint)); - if peerstate.public_key_fingerprint.is_some() - && &fingerprint_normalized == peerstate.public_key_fingerprint.as_ref().unwrap() - { - fingerprint_equal = 1; - } - } } - dc_contact_unref(contact); dc_array_unref(contacts); fingerprint_equal @@ -363,7 +353,7 @@ pub unsafe fn dc_handle_securejoin_handshake( let mut contact_chat_id_blocked: libc::c_int = 0i32; let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; let mut ret: libc::c_int = 0i32; - let mut contact: *mut dc_contact_t = 0 as *mut dc_contact_t; + if !(contact_id <= 9i32 as libc::c_uint) { step = lookup_field(mimeparser, "Secure-Join"); if !step.is_null() { @@ -575,7 +565,11 @@ pub unsafe fn dc_handle_securejoin_handshake( ); current_block = 4378276786830486580; } else { - dc_scaleup_contact_origin(context, contact_id, 0x1000000i32); + Contact::scaleup_origin_by_id( + context, + contact_id, + Origin::SecurejoinInvited, + ); info!(context, 0, "Auth verified.",); secure_connection_established(context, contact_chat_id); context.call_cb( @@ -702,16 +696,23 @@ pub unsafe fn dc_handle_securejoin_handshake( ); current_block = 4378276786830486580; } else { - dc_scaleup_contact_origin(context, contact_id, 0x2000000i32); + Contact::scaleup_origin_by_id( + context, + contact_id, + Origin::SecurejoinJoined, + ); context.call_cb( Event::CONTACTS_CHANGED, 0i32 as uintptr_t, 0i32 as uintptr_t, ); if 0 != join_vg { - if 0 == dc_addr_equals_self( + if !addr_equals_self( context, - lookup_field(mimeparser, "Chat-Group-Member-Added"), + as_str(lookup_field( + mimeparser, + "Chat-Group-Member-Added", + )), ) { info!( context, @@ -759,22 +760,26 @@ pub unsafe fn dc_handle_securejoin_handshake( ==== Alice - the inviter side ==== ==== Step 8 in "Out-of-band verified groups" protocol ==== ============================================================ */ - contact = dc_get_contact(context, contact_id); - if contact.is_null() || 0 == dc_contact_is_verified(contact) { + if let Ok(contact) = Contact::get_by_id(context, contact_id) { + if contact.is_verified() == VerifiedStatus::Unverified { + warn!(context, 0, "vg-member-added-received invalid.",); + current_block = 4378276786830486580; + } else { + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 800i32 as uintptr_t, + ); + context.call_cb( + Event::SECUREJOIN_INVITER_PROGRESS, + contact_id as uintptr_t, + 1000i32 as uintptr_t, + ); + current_block = 10256747982273457880; + } + } else { warn!(context, 0, "vg-member-added-received invalid.",); current_block = 4378276786830486580; - } else { - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 800i32 as uintptr_t, - ); - context.call_cb( - Event::SECUREJOIN_INVITER_PROGRESS, - contact_id as uintptr_t, - 1000i32 as uintptr_t, - ); - current_block = 10256747982273457880; } } else { current_block = 10256747982273457880; @@ -789,7 +794,7 @@ pub unsafe fn dc_handle_securejoin_handshake( } } } - dc_contact_unref(contact); + free(scanned_fingerprint_of_alice as *mut libc::c_void); free(auth as *mut libc::c_void); free(own_fingerprint as *mut libc::c_void); @@ -805,23 +810,20 @@ unsafe fn end_bobs_joining(context: &Context, status: libc::c_int) { unsafe fn secure_connection_established(context: &Context, contact_chat_id: uint32_t) { let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id); - let contact: *mut dc_contact_t = dc_get_contact(context, contact_id); - let msg = CString::new(context.stock_string_repl_str( - StockMessage::ContactVerified, - if !contact.is_null() { - as_str((*contact).addr) - } else { - "?" - }, - )) - .unwrap(); + let contact = Contact::get_by_id(context, contact_id); + let addr = if let Ok(ref contact) = contact { + contact.get_addr() + } else { + "?" + }; + let msg = + CString::new(context.stock_string_repl_str(StockMessage::ContactVerified, addr)).unwrap(); dc_add_device_msg(context, contact_chat_id, msg.as_ptr()); context.call_cb( Event::CHAT_MODIFIED, contact_chat_id as uintptr_t, 0i32 as uintptr_t, ); - dc_contact_unref(contact); } unsafe fn lookup_field(mimeparser: &dc_mimeparser_t, key: &str) -> *const libc::c_char { @@ -847,11 +849,11 @@ unsafe fn could_not_establish_secure_connection( details: *const libc::c_char, ) { let contact_id: uint32_t = chat_id_2_contact_id(context, contact_chat_id); - let contact = dc_get_contact(context, contact_id); + let contact = Contact::get_by_id(context, contact_id); let msg = context.stock_string_repl_str( StockMessage::ContactNotVerified, - if !contact.is_null() { - as_str((*contact).addr) + if let Ok(ref contact) = contact { + contact.get_addr() } else { "?" }, @@ -859,7 +861,6 @@ unsafe fn could_not_establish_secure_connection( let msg_c = CString::new(msg.as_str()).unwrap(); dc_add_device_msg(context, contact_chat_id, msg_c.as_ptr()); error!(context, 0, "{} ({})", msg, as_str(details)); - dc_contact_unref(contact); } unsafe fn mark_peer_as_verified( diff --git a/src/dc_simplify.rs b/src/dc_simplify.rs index 34b567105..6cfb25fd0 100644 --- a/src/dc_simplify.rs +++ b/src/dc_simplify.rs @@ -1,6 +1,5 @@ use crate::dc_dehtml::*; use crate::dc_tools::*; -use crate::types::*; use crate::x::*; #[derive(Copy, Clone)] @@ -20,6 +19,9 @@ impl dc_simplify_t { } } + /// Simplify and normalise text: Remove quotes, signatures, unnecessary + /// lineends etc. + /// The data returned from simplify() must be free()'d when no longer used. pub unsafe fn simplify( &mut self, in_unterminated: *const libc::c_char, @@ -27,6 +29,10 @@ impl dc_simplify_t { is_html: libc::c_int, is_msgrmsg: libc::c_int, ) -> *mut libc::c_char { + if in_bytes <= 0 { + return "".strdup(); + } + /* create a copy of the given buffer */ let mut out: *mut libc::c_char; let mut temp: *mut libc::c_char; @@ -73,17 +79,13 @@ impl dc_simplify_t { these are all lines starting with the character `>` ... remove a non-empty line before the removed quote (contains sth. like "On 2.9.2016, Bjoern wrote:" in different formats and lanugages) */ /* split the given buffer into lines */ - let lines: *mut carray = dc_split_into_lines(buf_terminated); - let mut l: libc::c_int; - let mut l_first: libc::c_int = 0i32; - /* if l_last is -1, there are no lines */ - let mut l_last: libc::c_int = - carray_count(lines).wrapping_sub(1i32 as libc::c_uint) as libc::c_int; + let lines = dc_split_into_lines(buf_terminated); + let mut l_first: usize = 0; + let mut l_last = lines.len(); let mut line: *mut libc::c_char; let mut footer_mark: libc::c_int = 0i32; - l = l_first; - while l <= l_last { - line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char; + for l in l_first..l_last { + line = lines[l]; if strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32 || strcmp(line, b"-- \x00" as *const u8 as *const libc::c_char) == 0i32 { @@ -97,20 +99,15 @@ impl dc_simplify_t { self.is_cut_at_end = 1i32 } if 0 != footer_mark { - l_last = l - 1i32; + l_last = l; /* done */ break; - } else { - l += 1 } } - if l_last - l_first + 1i32 >= 3i32 { - let line0: *mut libc::c_char = - carray_get(lines, l_first as libc::c_uint) as *mut libc::c_char; - let line1: *mut libc::c_char = - carray_get(lines, (l_first + 1i32) as libc::c_uint) as *mut libc::c_char; - let line2: *mut libc::c_char = - carray_get(lines, (l_first + 2i32) as libc::c_uint) as *mut libc::c_char; + if l_last > l_first + 2 { + let line0: *mut libc::c_char = lines[l_first]; + let line1: *mut libc::c_char = lines[l_first + 1]; + let line2: *mut libc::c_char = lines[l_first + 2]; if strcmp( line0, b"---------- Forwarded message ----------\x00" as *const u8 as *const libc::c_char, @@ -119,49 +116,43 @@ impl dc_simplify_t { && *line2.offset(0isize) as libc::c_int == 0i32 { self.is_forwarded = 1i32; - l_first += 3i32 + l_first += 3 } } - l = l_first; - while l <= l_last { - line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char; + for l in l_first..l_last { + line = lines[l]; if strncmp(line, b"-----\x00" as *const u8 as *const libc::c_char, 5) == 0i32 || strncmp(line, b"_____\x00" as *const u8 as *const libc::c_char, 5) == 0i32 || strncmp(line, b"=====\x00" as *const u8 as *const libc::c_char, 5) == 0i32 || strncmp(line, b"*****\x00" as *const u8 as *const libc::c_char, 5) == 0i32 || strncmp(line, b"~~~~~\x00" as *const u8 as *const libc::c_char, 5) == 0i32 { - l_last = l - 1i32; + l_last = l; self.is_cut_at_end = 1i32; /* done */ break; - } else { - l += 1 } } if 0 == is_msgrmsg { - let mut l_lastQuotedLine: libc::c_int = -1i32; - l = l_last; - while l >= l_first { - line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char; + let mut l_lastQuotedLine = None; + for l in (l_first..l_last).rev() { + line = lines[l]; if is_plain_quote(line) { - l_lastQuotedLine = l + l_lastQuotedLine = Some(l) } else if !is_empty_line(line) { break; } - l -= 1 } - if l_lastQuotedLine != -1i32 { - l_last = l_lastQuotedLine - 1i32; + if l_lastQuotedLine.is_some() { + l_last = l_lastQuotedLine.unwrap(); self.is_cut_at_end = 1i32; - if l_last > 0i32 { - if is_empty_line(carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char) - { + if l_last > 1 { + if is_empty_line(lines[l_last - 1]) { l_last -= 1 } } - if l_last > 0i32 { - line = carray_get(lines, l_last as libc::c_uint) as *mut libc::c_char; + if l_last > 1 { + line = lines[l_last - 1]; if is_quoted_headline(line) { l_last -= 1 } @@ -169,17 +160,16 @@ impl dc_simplify_t { } } if 0 == is_msgrmsg { - let mut l_lastQuotedLine_0: libc::c_int = -1i32; - let mut hasQuotedHeadline: libc::c_int = 0i32; - l = l_first; - while l <= l_last { - line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char; + let mut l_lastQuotedLine_0 = None; + let mut hasQuotedHeadline = 0; + for l in l_first..l_last { + line = lines[l]; if is_plain_quote(line) { - l_lastQuotedLine_0 = l + l_lastQuotedLine_0 = Some(l) } else if !is_empty_line(line) { if is_quoted_headline(line) && 0 == hasQuotedHeadline - && l_lastQuotedLine_0 == -1i32 + && l_lastQuotedLine_0.is_none() { hasQuotedHeadline = 1i32 } else { @@ -187,10 +177,9 @@ impl dc_simplify_t { break; } } - l += 1 } - if l_lastQuotedLine_0 != -1i32 { - l_first = l_lastQuotedLine_0 + 1i32; + if l_lastQuotedLine_0.is_some() { + l_first = l_lastQuotedLine_0.unwrap() + 1; self.is_cut_at_begin = 1i32 } } @@ -202,9 +191,8 @@ impl dc_simplify_t { /* we write empty lines only in case and non-empty line follows */ let mut pending_linebreaks: libc::c_int = 0i32; let mut content_lines_added: libc::c_int = 0i32; - l = l_first; - while l <= l_last { - line = carray_get(lines, l as libc::c_uint) as *mut libc::c_char; + for l in l_first..l_last { + line = lines[l]; if is_empty_line(line) { pending_linebreaks += 1 } else { @@ -222,33 +210,16 @@ impl dc_simplify_t { content_lines_added += 1; pending_linebreaks = 1i32 } - l += 1 } if 0 != self.is_cut_at_end && (0 == self.is_cut_at_begin || 0 != content_lines_added) { ret += " [...]"; } dc_free_splitted_lines(lines); - to_cstring(ret) + ret.strdup() } } -/* Simplify and normalise text: Remove quotes, signatures, unnecessary -lineends etc. -The data returned from Simplify() must be free()'d when no longer used, private */ -pub unsafe fn dc_simplify_simplify( - simplify: *mut dc_simplify_t, - in_unterminated: *const libc::c_char, - in_bytes: libc::c_int, - is_html: libc::c_int, - is_msgrmsg: libc::c_int, -) -> *mut libc::c_char { - if simplify.is_null() || in_unterminated.is_null() || in_bytes <= 0i32 { - return dc_strdup(b"\x00" as *const u8 as *const libc::c_char); - } - (*simplify).simplify(in_unterminated, in_bytes, is_html, is_msgrmsg) -} - /** * Tools */ diff --git a/src/dc_strencode.rs b/src/dc_strencode.rs index 9bdb5e902..43396b6bc 100644 --- a/src/dc_strencode.rs +++ b/src/dc_strencode.rs @@ -1,4 +1,4 @@ -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use charset::Charset; use mmime::mailmime_decode::*; @@ -120,97 +120,90 @@ fn hex_2_int(ch: libc::c_char) -> libc::c_char { } pub unsafe fn dc_encode_header_words(to_encode: *const libc::c_char) -> *mut libc::c_char { - let mut current_block: u64; + let mut ok_to_continue = true; let mut ret_str: *mut libc::c_char = 0 as *mut libc::c_char; let mut cur: *const libc::c_char = to_encode; let mmapstr: *mut MMAPString = mmap_string_new(b"\x00" as *const u8 as *const libc::c_char); if to_encode.is_null() || mmapstr.is_null() { - current_block = 8550051112593613029; - } else { - current_block = 4644295000439058019; + ok_to_continue = false; } loop { - match current_block { - 8550051112593613029 => { - if !mmapstr.is_null() { - mmap_string_free(mmapstr); - } - break; + if !ok_to_continue { + if !mmapstr.is_null() { + mmap_string_free(mmapstr); } - _ => { - if *cur as libc::c_int != '\u{0}' as i32 { - let begin: *const libc::c_char; - let mut end: *const libc::c_char; - let mut do_quote: bool; - let mut quote_words: libc::c_int; - begin = cur; - end = begin; - quote_words = 0i32; - do_quote = true; - while *cur as libc::c_int != '\u{0}' as i32 { - get_word(cur, &mut cur, &mut do_quote); - if !do_quote { - break; - } - quote_words = 1i32; - end = cur; - if *cur as libc::c_int != '\u{0}' as i32 { - cur = cur.offset(1isize) - } + break; + } else { + if *cur as libc::c_int != '\u{0}' as i32 { + let begin: *const libc::c_char; + let mut end: *const libc::c_char; + let mut do_quote: bool; + let mut quote_words: libc::c_int; + begin = cur; + end = begin; + quote_words = 0i32; + do_quote = true; + while *cur as libc::c_int != '\u{0}' as i32 { + get_word(cur, &mut cur, &mut do_quote); + if !do_quote { + break; } - if 0 != quote_words { - if !quote_word( - b"utf-8\x00" as *const u8 as *const libc::c_char, - mmapstr, - begin, - end.wrapping_offset_from(begin) as size_t, - ) { - current_block = 8550051112593613029; - continue; - } - if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 { - if mmap_string_append_c(mmapstr, *end).is_null() { - current_block = 8550051112593613029; - continue; - } - end = end.offset(1isize) - } - if *end as libc::c_int != '\u{0}' as i32 { - if mmap_string_append_len( - mmapstr, - end, - cur.wrapping_offset_from(end) as size_t, - ) - .is_null() - { - current_block = 8550051112593613029; - continue; - } - } - } else if mmap_string_append_len( + quote_words = 1i32; + end = cur; + if *cur as libc::c_int != '\u{0}' as i32 { + cur = cur.offset(1isize) + } + } + if 0 != quote_words { + if !quote_word( + b"utf-8\x00" as *const u8 as *const libc::c_char, mmapstr, begin, - cur.wrapping_offset_from(begin) as size_t, - ) - .is_null() - { - current_block = 8550051112593613029; + end.wrapping_offset_from(begin) as size_t, + ) { + ok_to_continue = false; continue; } - if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) { - current_block = 4644295000439058019; - continue; + if *end as libc::c_int == ' ' as i32 || *end as libc::c_int == '\t' as i32 { + if mmap_string_append_c(mmapstr, *end).is_null() { + ok_to_continue = false; + continue; + } + end = end.offset(1isize) } - if mmap_string_append_c(mmapstr, *cur).is_null() { - current_block = 8550051112593613029; - continue; + if *end as libc::c_int != '\u{0}' as i32 { + if mmap_string_append_len( + mmapstr, + end, + cur.wrapping_offset_from(end) as size_t, + ) + .is_null() + { + ok_to_continue = false; + continue; + } } - cur = cur.offset(1isize); - current_block = 4644295000439058019; - } else { - ret_str = strdup((*mmapstr).str_0); - current_block = 8550051112593613029; + } else if mmap_string_append_len( + mmapstr, + begin, + cur.wrapping_offset_from(begin) as size_t, + ) + .is_null() + { + ok_to_continue = false; + continue; } + if !(*cur as libc::c_int == ' ' as i32 || *cur as libc::c_int == '\t' as i32) { + continue; + } + if mmap_string_append_c(mmapstr, *cur).is_null() { + ok_to_continue = false; + continue; + } + cur = cur.offset(1isize); + } else { + ret_str = strdup((*mmapstr).str_0); + ok_to_continue = false; } } } @@ -710,9 +703,8 @@ unsafe fn print_hex(target: *mut libc::c_char, cur: *const libc::c_char) { assert!(!cur.is_null()); let bytes = std::slice::from_raw_parts(cur as *const _, strlen(cur)); - let raw = to_cstring(format!("={}", &hex::encode_upper(bytes)[..2])); - libc::memcpy(target as *mut _, raw as *const _, 4); - free(raw as *mut libc::c_void); + let raw = CString::yolo(format!("={}", &hex::encode_upper(bytes)[..2])); + libc::memcpy(target as *mut _, raw.as_ptr() as *const _, 4); } #[cfg(test)] diff --git a/src/dc_token.rs b/src/dc_token.rs index 1dd4eccc1..8db5eae34 100644 --- a/src/dc_token.rs +++ b/src/dc_token.rs @@ -42,7 +42,7 @@ pub fn dc_token_lookup( params![namespc as i32, foreign_id as i32], 0, ) - .map(|s| unsafe { to_cstring(s) }) + .map(|s| unsafe { s.strdup() }) .unwrap_or_else(|| std::ptr::null_mut()) } diff --git a/src/dc_tools.rs b/src/dc_tools.rs index b9e425b2e..32f56d807 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -17,7 +17,6 @@ const ELLIPSE: &'static str = "[...]"; /* Some tools and enhancements to the used libraries, there should be no references to Context and other "larger" classes here. */ -// for carray etc. /* ** library-private **********************************************************/ /* math tools */ pub fn dc_exactly_one_bit_set(v: libc::c_int) -> bool { @@ -178,14 +177,13 @@ pub unsafe fn dc_trim(buf: *mut libc::c_char) { /* the result must be free()'d */ pub unsafe fn dc_strlower(in_0: *const libc::c_char) -> *mut libc::c_char { - to_cstring(to_string(in_0).to_lowercase()) + to_string(in_0).to_lowercase().strdup() } pub unsafe fn dc_strlower_in_place(in_0: *mut libc::c_char) { - let raw = to_cstring(to_string(in_0).to_lowercase()); - assert_eq!(strlen(in_0), strlen(raw)); - memcpy(in_0 as *mut _, raw as *const _, strlen(in_0)); - free(raw as *mut _); + let raw = CString::yolo(to_string(in_0).to_lowercase()); + assert_eq!(strlen(in_0), strlen(raw.as_ptr())); + memcpy(in_0 as *mut _, raw.as_ptr() as *const _, strlen(in_0)); } pub unsafe fn dc_str_contains( @@ -233,7 +231,7 @@ pub unsafe fn dc_binary_to_uc_hex(buf: *const uint8_t, bytes: size_t) -> *mut li let buf = std::slice::from_raw_parts(buf, bytes); let raw = hex::encode_upper(buf); - to_cstring(raw) + raw.strdup() } /* remove all \r characters from string */ @@ -263,8 +261,9 @@ pub unsafe fn dc_unify_lineends(buf: *mut libc::c_char) { } /* replace bad UTF-8 characters by sequences of `_` (to avoid problems in filenames, we do not use eg. `?`) the function is useful if strings are unexpectingly encoded eg. as ISO-8859-1 */ +#[allow(non_snake_case)] pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) { - let current_block: u64; + let mut OK_TO_CONTINUE = true; if buf.is_null() { return; } @@ -280,7 +279,6 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) { ix = p1len; 's_36: loop { if !(i < ix) { - current_block = 13550086250199790493; break; } c = *p1.offset(i as isize) as libc::c_int; @@ -293,7 +291,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) { && *p1.offset((i + 1i32) as isize) as libc::c_int & 0xa0i32 == 0xa0i32 { /* U+d800 to U+dfff */ - current_block = 2775201239069267972; + OK_TO_CONTINUE = false; break; } else if c & 0xf0i32 == 0xe0i32 { n = 2i32 @@ -302,7 +300,7 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) { } else { //else if ((c & 0xFC) == 0xF8) { n=4; } /* 111110bb - not valid in https://tools.ietf.org/html/rfc3629 */ //else if ((c & 0xFE) == 0xFC) { n=5; } /* 1111110b - not valid in https://tools.ietf.org/html/rfc3629 */ - current_block = 2775201239069267972; + OK_TO_CONTINUE = false; break; } j = 0i32; @@ -310,25 +308,22 @@ pub unsafe fn dc_replace_bad_utf8_chars(buf: *mut libc::c_char) { /* n bytes matching 10bbbbbb follow ? */ i += 1; if i == ix || *p1.offset(i as isize) as libc::c_int & 0xc0i32 != 0x80i32 { - current_block = 2775201239069267972; + OK_TO_CONTINUE = false; break 's_36; } j += 1 } i += 1 } - match current_block { - 13550086250199790493 => return, - _ => { - while 0 != *p1 { - if *p1 as libc::c_int > 0x7fi32 { - *p1 = '_' as i32 as libc::c_uchar - } - p1 = p1.offset(1isize) + if OK_TO_CONTINUE == false { + while 0 != *p1 { + if *p1 as libc::c_int > 0x7fi32 { + *p1 = '_' as i32 as libc::c_uchar } - return; + p1 = p1.offset(1isize) } - }; + return; + } } pub unsafe fn dc_utf8_strlen(s: *const libc::c_char) -> size_t { @@ -421,47 +416,30 @@ unsafe fn dc_utf8_strnlen(s: *const libc::c_char, n: size_t) -> size_t { } /* split string into lines*/ -pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> *mut carray { - let lines: *mut carray = carray_new(1024i32 as libc::c_uint); +pub unsafe fn dc_split_into_lines(buf_terminated: *const libc::c_char) -> Vec<*mut libc::c_char> { + let mut lines = Vec::new(); let mut line_chars = 0; let mut p1: *const libc::c_char = buf_terminated; let mut line_start: *const libc::c_char = p1; - let mut l_indx: libc::c_uint = 0i32 as libc::c_uint; while 0 != *p1 { if *p1 as libc::c_int == '\n' as i32 { - carray_add( - lines, - strndup(line_start, line_chars) as *mut libc::c_void, - &mut l_indx, - ); + lines.push(strndup(line_start, line_chars)); p1 = p1.offset(1isize); line_start = p1; line_chars = 0; } else { p1 = p1.offset(1isize); - line_chars = line_chars.wrapping_add(1) + line_chars += 1; } } - carray_add( - lines, - strndup(line_start, line_chars) as *mut libc::c_void, - &mut l_indx, - ); - + lines.push(strndup(line_start, line_chars)); lines } -pub unsafe fn dc_free_splitted_lines(lines: *mut carray) { - if !lines.is_null() { - let mut i: libc::c_int; - let cnt: libc::c_int = carray_count(lines) as libc::c_int; - i = 0i32; - while i < cnt { - free(carray_get(lines, i as libc::c_uint)); - i += 1 - } - carray_free(lines); - }; +pub unsafe fn dc_free_splitted_lines(lines: Vec<*mut libc::c_char>) { + for s in lines { + free(s as *mut libc::c_void); + } } /* insert a break every n characters, the return must be free()'d */ @@ -530,7 +508,7 @@ pub unsafe fn dc_str_from_clist( } } - to_cstring(res) + res.strdup() } pub unsafe fn dc_str_to_clist( @@ -561,32 +539,32 @@ pub unsafe fn dc_str_to_clist( list } +/* the colors must fulfill some criterions as: +- contrast to black and to white +- work as a text-color +- being noticeable on a typical map +- harmonize together while being different enough +(therefore, we cannot just use random rgb colors :) */ +const COLORS: [u32; 16] = [ + 0xe56555, 0xf28c48, 0x8e85ee, 0x76c84d, 0x5bb6cc, 0x549cdd, 0xd25c99, 0xb37800, 0xf23030, + 0x39b249, 0xbb243b, 0x964078, 0x66874f, 0x308ab9, 0x127ed0, 0xbe450c, +]; + +pub fn dc_str_to_color_safe(s: impl AsRef<str>) -> u32 { + let str_lower = s.as_ref().to_lowercase(); + let mut checksum = 0; + let bytes = str_lower.as_bytes(); + for i in 0..str_lower.len() { + checksum += (i + 1) * bytes[i] as usize; + checksum %= 0xffffff; + } + let color_index = checksum % COLORS.len(); + + COLORS[color_index] +} + pub unsafe fn dc_str_to_color(str: *const libc::c_char) -> libc::c_int { let str_lower: *mut libc::c_char = dc_strlower(str); - /* the colors must fulfill some criterions as: - - contrast to black and to white - - work as a text-color - - being noticeable on a typical map - - harmonize together while being different enough - (therefore, we cannot just use random rgb colors :) */ - static mut COLORS: [uint32_t; 16] = [ - 0xe56555i32 as uint32_t, - 0xf28c48i32 as uint32_t, - 0x8e85eei32 as uint32_t, - 0x76c84di32 as uint32_t, - 0x5bb6cci32 as uint32_t, - 0x549cddi32 as uint32_t, - 0xd25c99i32 as uint32_t, - 0xb37800i32 as uint32_t, - 0xf23030i32 as uint32_t, - 0x39b249i32 as uint32_t, - 0xbb243bi32 as uint32_t, - 0x964078i32 as uint32_t, - 0x66874fi32 as uint32_t, - 0x308ab9i32 as uint32_t, - 0x127ed0i32 as uint32_t, - 0xbe450ci32 as uint32_t, - ]; let mut checksum: libc::c_int = 0i32; let str_len: libc::c_int = strlen(str_lower) as libc::c_int; let mut i: libc::c_int = 0i32; @@ -669,13 +647,7 @@ pub unsafe fn dc_timestamp_from_date(date_time: *mut mailimf_date_time) -> i64 { * date/time tools ******************************************************************************/ -/* the return value must be free()'d */ -pub unsafe fn dc_timestamp_to_str(wanted: i64) -> *mut libc::c_char { - let res = dc_timestamp_to_str_safe(wanted); - to_cstring(res) -} - -pub fn dc_timestamp_to_str_safe(wanted: i64) -> String { +pub fn dc_timestamp_to_str(wanted: i64) -> String { let ts = chrono::Utc.timestamp(wanted, 0); ts.format("%Y.%m.%d %H:%M:%S").to_string() } @@ -726,7 +698,7 @@ pub unsafe fn dc_create_smeared_timestamps(context: &Context, count: libc::c_int } /* Message-ID tools */ -pub unsafe fn dc_create_id() -> *mut libc::c_char { +pub fn dc_create_id() -> String { /* generate an id. the generated ID should be as short and as unique as possible: - short, because it may also used as part of Message-ID headers or in QR codes - unique as two IDs generated on two devices should not be the same. However, collisions are not world-wide but only by the few contacts. @@ -744,39 +716,26 @@ pub unsafe fn dc_create_id() -> *mut libc::c_char { encode_66bits_as_base64(buf[0usize], buf[1usize], buf[2usize]) } -/* ****************************************************************************** - * generate Message-IDs - ******************************************************************************/ -unsafe fn encode_66bits_as_base64(v1: uint32_t, v2: uint32_t, fill: uint32_t) -> *mut libc::c_char { - /* encode 66 bits as a base64 string. This is useful for ID generating with short strings as - we save 5 character in each id compared to 64 bit hex encoding, for a typical group ID, these are 10 characters (grpid+msgid): - hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters - base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) */ - let ret: *mut libc::c_char = malloc(12) as *mut libc::c_char; - assert!(!ret.is_null()); +/// Encode 66 bits as a base64 string. +/// This is useful for ID generating with short strings as we save 5 character +/// in each id compared to 64 bit hex encoding. For a typical group ID, these +/// are 10 characters (grpid+msgid): +/// hex: 64 bit, 4 bits/character, length = 64/4 = 16 characters +/// base64: 64 bit, 6 bits/character, length = 64/6 = 11 characters (plus 2 additional bits) +/// Only the lower 2 bits of `fill` are used. +fn encode_66bits_as_base64(v1: u32, v2: u32, fill: u32) -> String { + use byteorder::{BigEndian, WriteBytesExt}; - static mut CHARS: [libc::c_char; 65] = [ - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 45, 95, 0, - ]; - *ret.offset(0isize) = CHARS[(v1 >> 26i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(1isize) = CHARS[(v1 >> 20i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(2isize) = CHARS[(v1 >> 14i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(3isize) = CHARS[(v1 >> 8i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(4isize) = CHARS[(v1 >> 2i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(5isize) = CHARS - [(v1 << 4i32 & 0x30i32 as libc::c_uint | v2 >> 28i32 & 0xfi32 as libc::c_uint) as usize]; - *ret.offset(6isize) = CHARS[(v2 >> 22i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(7isize) = CHARS[(v2 >> 16i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(8isize) = CHARS[(v2 >> 10i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(9isize) = CHARS[(v2 >> 4i32 & 0x3fi32 as libc::c_uint) as usize]; - *ret.offset(10isize) = - CHARS[(v2 << 2i32 & 0x3ci32 as libc::c_uint | fill & 0x3i32 as libc::c_uint) as usize]; - *ret.offset(11isize) = 0i32 as libc::c_char; - - ret + let mut wrapped_writer = Vec::new(); + { + let mut enc = base64::write::EncoderWriter::new(&mut wrapped_writer, base64::URL_SAFE); + enc.write_u32::<BigEndian>(v1).unwrap(); + enc.write_u32::<BigEndian>(v2).unwrap(); + enc.write_u8(((fill & 0x3) as u8) << 6).unwrap(); + enc.finish().unwrap(); + } + assert_eq!(wrapped_writer.pop(), Some('A' as u8)); // Remove last "A" + String::from_utf8(wrapped_writer).unwrap() } pub unsafe fn dc_create_incoming_rfc724_mid( @@ -816,7 +775,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid( - the message ID should be globally unique - do not add a counter or any private data as as this may give unneeded information to the receiver */ let mut rand1: *mut libc::c_char = 0 as *mut libc::c_char; - let rand2: *mut libc::c_char = dc_create_id(); + let rand2: *mut libc::c_char = dc_create_id().strdup(); let ret: *mut libc::c_char; let mut at_hostname: *const libc::c_char = strchr(from_addr, '@' as i32); if at_hostname.is_null() { @@ -830,7 +789,7 @@ pub unsafe fn dc_create_outgoing_rfc724_mid( at_hostname, ) } else { - rand1 = dc_create_id(); + rand1 = dc_create_id().strdup(); ret = dc_mprintf( b"Mr.%s.%s%s\x00" as *const u8 as *const libc::c_char, rand1, @@ -844,52 +803,49 @@ pub unsafe fn dc_create_outgoing_rfc724_mid( ret } -pub unsafe fn dc_extract_grpid_from_rfc724_mid(mid: *const libc::c_char) -> *mut libc::c_char { - /* extract our group ID from Message-IDs as `Gr.12345678901.morerandom@domain.de`; "12345678901" is the wanted ID in this example. */ - let mut success: libc::c_int = 0i32; - let mut grpid: *mut libc::c_char = 0 as *mut libc::c_char; - let p1: *mut libc::c_char; - let grpid_len: libc::c_int; - if !(mid.is_null() - || strlen(mid) < 8 - || *mid.offset(0isize) as libc::c_int != 'G' as i32 - || *mid.offset(1isize) as libc::c_int != 'r' as i32 - || *mid.offset(2isize) as libc::c_int != '.' as i32) - { - grpid = dc_strdup(&*mid.offset(3isize)); - p1 = strchr(grpid, '.' as i32); - if !p1.is_null() { - *p1 = 0i32 as libc::c_char; - grpid_len = strlen(grpid) as libc::c_int; - if !(grpid_len != 11i32 && grpid_len != 16i32) { - /* strict length comparison, the 'Gr.' magic is weak enough */ - success = 1i32 +/// Extract the group id (grpid) from a message id (mid) +/// +/// # Arguments +/// +/// * `mid` - A string that holds the message id +/// +/// # Examples +/// +/// ``` +/// use deltachat::dc_tools::dc_extract_grpid_from_rfc724_mid; +/// let mid = "Gr.12345678901.morerandom@domain.de"; +/// let grpid = dc_extract_grpid_from_rfc724_mid(mid); +/// assert_eq!(grpid, Some("12345678901")); +/// ``` +pub fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> { + if mid.len() < 9 || !mid.starts_with("Gr.") { + return None; + } + + if let Some(mid_without_offset) = mid.get(3..) { + if let Some(grpid_len) = mid_without_offset.find('.') { + /* strict length comparison, the 'Gr.' magic is weak enough */ + if grpid_len == 11 || grpid_len == 16 { + return Some(mid_without_offset.get(0..grpid_len).unwrap()); } } } - if success == 0i32 { - free(grpid as *mut libc::c_void); - grpid = 0 as *mut libc::c_char - } - return if 0 != success { - grpid - } else { - 0 as *mut libc::c_char - }; + + None } pub unsafe fn dc_extract_grpid_from_rfc724_mid_list(list: *const clist) -> *mut libc::c_char { if !list.is_null() { let mut cur: *mut clistiter = (*list).first; while !cur.is_null() { - let mid: *const libc::c_char = (if !cur.is_null() { - (*cur).data + let mid = if !cur.is_null() { + as_str((*cur).data as *const libc::c_char) } else { - 0 as *mut libc::c_void - }) as *const libc::c_char; - let grpid: *mut libc::c_char = dc_extract_grpid_from_rfc724_mid(mid); - if !grpid.is_null() { - return grpid; + "" + }; + + if let Some(grpid) = dc_extract_grpid_from_rfc724_mid(mid) { + return grpid.strdup(); } cur = if !cur.is_null() { (*cur).next @@ -1080,6 +1036,26 @@ pub unsafe fn dc_get_filemeta( 0 } +/// Expand paths relative to $BLOBDIR into absolute paths. +/// +/// If `path` starts with "$BLOBDIR", replaces it with the blobdir path. +/// Otherwise, returns path as is. +pub fn dc_get_abs_path_safe<P: AsRef<std::path::Path>>( + context: &Context, + path: P, +) -> std::path::PathBuf { + let p: &std::path::Path = path.as_ref(); + if let Ok(p) = p.strip_prefix("$BLOBDIR") { + assert!( + context.has_blobdir(), + "Expected context to have blobdir to substitute $BLOBDIR", + ); + std::path::PathBuf::from(as_str(context.get_blobdir())).join(p) + } else { + p.into() + } +} + #[allow(non_snake_case)] pub unsafe fn dc_get_abs_path( context: &Context, @@ -1110,137 +1086,79 @@ pub unsafe fn dc_get_abs_path( pathNfilename_abs } -#[allow(non_snake_case)] -pub unsafe fn dc_file_exist(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int { - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); - if pathNfilename_abs.is_null() { - return 0; - } - - let exist = { - let p = std::path::Path::new(as_str(pathNfilename_abs)); - p.exists() - }; - - free(pathNfilename_abs as *mut libc::c_void); - exist as libc::c_int +pub fn dc_file_exist(context: &Context, path: *const libc::c_char) -> libc::c_int { + dc_get_abs_path_safe(context, as_path(path)).exists() as libc::c_int } -#[allow(non_snake_case)] -pub unsafe fn dc_get_filebytes(context: &Context, pathNfilename: *const libc::c_char) -> uint64_t { - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); - if pathNfilename_abs.is_null() { - return 0; +pub fn dc_get_filebytes(context: &Context, path: *const libc::c_char) -> uint64_t { + let path_abs = dc_get_abs_path_safe(context, as_path(path)); + match fs::metadata(&path_abs) { + Ok(meta) => meta.len() as uint64_t, + Err(_err) => 0, } - - let p = std::ffi::CStr::from_ptr(pathNfilename_abs) - .to_str() - .unwrap(); - let filebytes = match fs::metadata(p) { - Ok(meta) => meta.len(), - Err(_err) => { - return 0; - } - }; - - free(pathNfilename_abs as *mut libc::c_void); - filebytes as uint64_t } -#[allow(non_snake_case)] -pub unsafe fn dc_delete_file(context: &Context, pathNfilename: *const libc::c_char) -> libc::c_int { - let mut success: libc::c_int = 0i32; - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); - if pathNfilename_abs.is_null() { - return 0; - } - let p = std::path::Path::new( - std::ffi::CStr::from_ptr(pathNfilename_abs) - .to_str() - .unwrap(), - ); - - let res = if p.is_file() { - fs::remove_file(p) +pub fn dc_delete_file(context: &Context, path: *const libc::c_char) -> libc::c_int { + let path = as_path(path); + let path_abs = dc_get_abs_path_safe(context, path); + let res = if path_abs.is_file() { + fs::remove_file(path_abs) } else { - fs::remove_dir_all(p) + fs::remove_dir_all(path_abs) }; match res { - Ok(_) => { - success = 1; - } + Ok(_) => 1, Err(_err) => { - warn!(context, 0, "Cannot delete \"{}\".", as_str(pathNfilename),); + warn!(context, 0, "Cannot delete \"{}\".", path.display()); + 0 } } - - free(pathNfilename_abs as *mut libc::c_void); - success } -#[allow(non_snake_case)] -pub unsafe fn dc_copy_file( +pub fn dc_copy_file( context: &Context, src: *const libc::c_char, dest: *const libc::c_char, ) -> libc::c_int { - let mut success = 0; - - let src_abs = dc_get_abs_path(context, src); - let dest_abs = dc_get_abs_path(context, dest); - - if src_abs.is_null() || dest_abs.is_null() { - return 0; - } - - let src_p = std::ffi::CStr::from_ptr(src_abs).to_str().unwrap(); - let dest_p = std::ffi::CStr::from_ptr(dest_abs).to_str().unwrap(); - - match fs::copy(src_p, dest_p) { - Ok(_) => { - success = 1; - } + let src = as_path(src); + let dest = as_path(dest); + let src_abs = dc_get_abs_path_safe(context, src); + let dest_abs = dc_get_abs_path_safe(context, dest); + match fs::copy(&src_abs, &dest_abs) { + Ok(_) => 1, Err(_) => { - error!(context, 0, "Cannot copy \"{}\" to \"{}\".", src_p, dest_p,); + error!( + context, + 0, + "Cannot copy \"{}\" to \"{}\".", + src.display(), + dest.display(), + ); + 0 } } - - free(src_abs as *mut libc::c_void); - free(dest_abs as *mut libc::c_void); - success } -#[allow(non_snake_case)] -pub unsafe fn dc_create_folder( - context: &Context, - pathNfilename: *const libc::c_char, -) -> libc::c_int { - let mut success = 0; - let pathNfilename_abs = dc_get_abs_path(context, pathNfilename); - { - let p = std::path::Path::new(as_str(pathNfilename_abs)); - if !p.exists() { - match fs::create_dir_all(p) { - Ok(_) => { - success = 1; - } - Err(_err) => { - warn!( - context, - 0, - "Cannot create directory \"{}\".", - as_str(pathNfilename), - ); - } +pub fn dc_create_folder(context: &Context, path: *const libc::c_char) -> libc::c_int { + let path = as_path(path); + let path_abs = dc_get_abs_path_safe(context, path); + if !path_abs.exists() { + match fs::create_dir_all(path_abs) { + Ok(_) => 1, + Err(_err) => { + warn!( + context, + 0, + "Cannot create directory \"{}\".", + path.display(), + ); + 0 } - } else { - success = 1; } + } else { + 1 } - - free(pathNfilename_abs as *mut libc::c_void); - success } #[allow(non_snake_case)] @@ -1255,35 +1173,24 @@ pub unsafe fn dc_write_file( dc_write_file_safe(context, as_str(pathNfilename), bytes) as libc::c_int } -#[allow(non_snake_case)] -pub fn dc_write_file_safe(context: &Context, pathNfilename: impl AsRef<str>, buf: &[u8]) -> bool { - let pathNfilename_abs = unsafe { - let n = to_cstring(pathNfilename.as_ref()); - let res = dc_get_abs_path(context, n); - free(n as *mut _); - res - }; - if pathNfilename_abs.is_null() { - return false; - } - - let p = as_str(pathNfilename_abs); - - let success = if let Err(_err) = fs::write(p, buf) { +pub fn dc_write_file_safe<P: AsRef<std::path::Path>>( + context: &Context, + path: P, + buf: &[u8], +) -> bool { + let path_abs = dc_get_abs_path_safe(context, &path); + if let Err(_err) = fs::write(&path_abs, buf) { warn!( context, 0, "Cannot write {} bytes to \"{}\".", buf.len(), - pathNfilename.as_ref(), + path.as_ref().display(), ); false } else { true - }; - - unsafe { free(pathNfilename_abs as *mut libc::c_void) }; - success + } } #[allow(non_snake_case)] @@ -1306,36 +1213,20 @@ pub unsafe fn dc_read_file( } } -#[allow(non_snake_case)] -pub fn dc_read_file_safe(context: &Context, pathNfilename: impl AsRef<str>) -> Option<Vec<u8>> { - let pathNfilename_abs = unsafe { - let n = to_cstring(pathNfilename.as_ref()); - let p = dc_get_abs_path(context, n); - free(n as *mut _); - p - }; - - if pathNfilename_abs.is_null() { - return None; - } - - let p = as_str(pathNfilename_abs); - let res = match fs::read(p) { +pub fn dc_read_file_safe<P: AsRef<std::path::Path>>(context: &Context, path: P) -> Option<Vec<u8>> { + let path_abs = dc_get_abs_path_safe(context, &path); + match fs::read(&path_abs) { Ok(bytes) => Some(bytes), Err(_err) => { warn!( context, 0, "Cannot read \"{}\" or file is empty.", - pathNfilename.as_ref(), + path.as_ref().display() ); None } - }; - - unsafe { free(pathNfilename_abs as *mut libc::c_void) }; - - res + } } #[allow(non_snake_case)] @@ -1545,10 +1436,49 @@ fn os_str_to_c_string_unicode( } } -/// Needs to free the result after use! -pub unsafe fn to_cstring<S: AsRef<str>>(s: S) -> *mut libc::c_char { - let cstr = CString::new(s.as_ref()).expect("invalid string converted"); - dc_strdup(cstr.as_ref().as_ptr()) +/// Convenience methods/associated functions for working with [CString] +/// +/// This is helps transitioning from unsafe code. +pub trait CStringExt { + /// Create a new [CString], yolo style + /// + /// This unwrap the result, panicking when there are embedded NULL + /// bytes. + fn yolo<T: Into<Vec<u8>>>(t: T) -> CString { + CString::new(t).expect("String contains null byte, can not be CString") + } +} + +impl CStringExt for CString {} + +/// Convenience methods to make transitioning from raw C strings easier. +/// +/// To interact with (legacy) C APIs we often need to convert from +/// Rust strings to raw C strings. This can be clumsy to do correctly +/// and the compiler sometimes allows it in an unsafe way. These +/// methods make it more succinct and help you get it right. +pub trait StrExt { + /// Allocate a new raw C `*char` version of this string. + /// + /// This allocates a new raw C string which must be freed using + /// `free`. It takes care of some common pitfalls with using + /// [CString::as_ptr]. + /// + /// [CString::as_ptr]: std::ffi::CString::as_ptr + /// + /// # Panics + /// + /// This function will panic when the original string contains an + /// interior null byte as this can not be represented in raw C + /// strings. + unsafe fn strdup(&self) -> *mut libc::c_char; +} + +impl<T: AsRef<str>> StrExt for T { + unsafe fn strdup(&self) -> *mut libc::c_char { + let tmp = CString::yolo(self.as_ref()); + dc_strdup(tmp.as_ptr()) + } } pub fn to_string(s: *const libc::c_char) -> String { @@ -1987,11 +1917,28 @@ mod tests { #[test] fn test_dc_create_id() { - unsafe { - let buf = dc_create_id(); - assert_eq!(strlen(buf), 11); - free(buf as *mut libc::c_void); - } + let buf = dc_create_id(); + assert_eq!(buf.len(), 11); + } + + #[test] + fn test_encode_66bits_as_base64() { + assert_eq!( + encode_66bits_as_base64(0x01234567, 0x89abcdef, 0), + "ASNFZ4mrze8" + ); + assert_eq!( + encode_66bits_as_base64(0x01234567, 0x89abcdef, 1), + "ASNFZ4mrze9" + ); + assert_eq!( + encode_66bits_as_base64(0x01234567, 0x89abcdef, 2), + "ASNFZ4mrze-" + ); + assert_eq!( + encode_66bits_as_base64(0x01234567, 0x89abcdef, 3), + "ASNFZ4mrze_" + ); } #[test] @@ -2090,4 +2037,53 @@ mod tests { let ptr = some_path.as_ptr(); assert_eq!(as_path_unicode(ptr), std::ffi::OsString::from("/some/path")); } + + #[test] + fn test_cstring_yolo() { + assert_eq!(CString::new("hello").unwrap(), CString::yolo("hello")); + } + + #[test] + fn test_strdup_str() { + unsafe { + let s = "hello".strdup(); + let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char); + free(s as *mut libc::c_void); + assert_eq!(cmp, 0); + } + } + + #[test] + fn test_strdup_string() { + unsafe { + let s = String::from("hello").strdup(); + let cmp = strcmp(s, b"hello\x00" as *const u8 as *const libc::c_char); + free(s as *mut libc::c_void); + assert_eq!(cmp, 0); + } + } + + #[test] + fn test_dc_extract_grpid_from_rfc724_mid() { + // Should return None if we pass invalid mid + let mid = "foobar"; + let grpid = dc_extract_grpid_from_rfc724_mid(mid); + assert_eq!(grpid, None); + + // Should return None if grpid has a length which is not 11 or 16 + let mid = "Gr.12345678.morerandom@domain.de"; + let grpid = dc_extract_grpid_from_rfc724_mid(mid); + assert_eq!(grpid, None); + + // Should return extracted grpid for grpid with length of 11 + let mid = "Gr.12345678901.morerandom@domain.de"; + let grpid = dc_extract_grpid_from_rfc724_mid(mid); + assert_eq!(grpid, Some("12345678901")); + + // Should return extracted grpid for grpid with length of 11 + let mid = "Gr.1234567890123456.morerandom@domain.de"; + let grpid = dc_extract_grpid_from_rfc724_mid(mid); + assert_eq!(grpid, Some("1234567890123456")); + } + } diff --git a/src/imap.rs b/src/imap.rs index b07767372..11a0a8bf8 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -1,3 +1,4 @@ +use std::ffi::CString; use std::net; use std::sync::{Arc, Condvar, Mutex, RwLock}; use std::time::{Duration, SystemTime}; @@ -5,10 +6,9 @@ use std::time::{Duration, SystemTime}; use crate::constants::*; use crate::context::Context; use crate::dc_loginparam::*; -use crate::dc_tools::{as_str, to_cstring}; +use crate::dc_tools::CStringExt; use crate::oauth2::dc_get_oauth2_access_token; use crate::types::*; -use crate::x::free; pub const DC_IMAP_SEEN: usize = 0x0001; pub const DC_REGENERATE: usize = 0x01; @@ -705,26 +705,16 @@ impl Imap { fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) { let key = format!("imap.mailbox.{}", folder.as_ref()); - let val1 = unsafe { - let key_c = to_cstring(key); - let val = (self.get_config)(context, key_c, 0 as *const libc::c_char); - free(key_c as *mut _); - val - }; - if val1.is_null() { - return (0, 0); + if let Some(entry) = (self.get_config)(context, &key) { + // the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>` + let mut parts = entry.split(':'); + ( + parts.next().unwrap().parse().unwrap_or_else(|_| 0), + parts.next().unwrap().parse().unwrap_or_else(|_| 0), + ) + } else { + (0, 0) } - let entry = as_str(val1); - - if entry.is_empty() { - return (0, 0); - } - // the entry has the format `imap.mailbox.<folder>=<uidvalidity>:<lastseenuid>` - let mut parts = entry.split(':'); - ( - parts.next().unwrap().parse().unwrap_or_else(|_| 0), - parts.next().unwrap().parse().unwrap_or_else(|_| 0), - ) } fn fetch_from_single_folder<S: AsRef<str>>(&self, context: &Context, folder: S) -> usize { @@ -853,10 +843,8 @@ impl Imap { .expect("missing message id"); if 0 == unsafe { - let message_id_c = to_cstring(message_id); - let res = (self.precheck_imf)(context, message_id_c, folder.as_ref(), cur_uid); - free(message_id_c as *mut _); - res + let message_id_c = CString::yolo(message_id); + (self.precheck_imf)(context, message_id_c.as_ptr(), folder.as_ref(), cur_uid) } { // check passed, go fetch the rest if self.fetch_single_msg(context, &folder, cur_uid) == 0 { @@ -924,13 +912,7 @@ impl Imap { let key = format!("imap.mailbox.{}", folder.as_ref()); let val = format!("{}:{}", uidvalidity, lastseenuid); - unsafe { - let key_c = to_cstring(key); - let val_c = to_cstring(val); - (self.set_config)(context, key_c, val_c); - free(key_c as *mut _); - free(val_c as *mut _); - }; + (self.set_config)(context, &key, Some(&val)); } fn fetch_single_msg<S: AsRef<str>>( diff --git a/src/key.rs b/src/key.rs index 5d4dd2b12..6ed4b9eab 100644 --- a/src/key.rs +++ b/src/key.rs @@ -216,22 +216,16 @@ impl Key { } } - /// Each header line must be terminated by `\r\n`, the result must be freed. - pub fn to_asc_c(&self, header: Option<(&str, &str)>) -> *mut libc::c_char { + /// Each header line must be terminated by `\r\n` + pub fn to_asc(&self, header: Option<(&str, &str)>) -> String { let headers = header.map(|(key, value)| { let mut m = BTreeMap::new(); m.insert(key.to_string(), value.to_string()); m }); - let buf = self - .to_armored_string(headers.as_ref()) - .expect("failed to serialize key"); - let buf_c = CString::new(buf).unwrap(); - - // need to use strdup to allocate the result with malloc - // so it can be `free`d later. - unsafe { strdup(buf_c.as_ptr()) } + self.to_armored_string(headers.as_ref()) + .expect("failed to serialize key") } pub fn write_asc_to_file(&self, file: *const libc::c_char, context: &Context) -> bool { @@ -239,15 +233,16 @@ impl Key { return false; } - let file_content = self.to_asc_c(None); + let file_content = self.to_asc(None); + let file_content_c = CString::new(file_content).unwrap(); let success = if 0 == unsafe { dc_write_file( context, file, - file_content as *const libc::c_void, - strlen(file_content), + file_content_c.as_ptr() as *const libc::c_void, + file_content_c.as_bytes().len(), ) } { error!(context, 0, "Cannot write key to {}", to_string(file)); @@ -256,8 +251,6 @@ impl Key { true }; - unsafe { free(file_content as *mut libc::c_void) }; - success } diff --git a/src/keyhistory.rs b/src/keyhistory.rs deleted file mode 100644 index ca4bc9507..000000000 --- a/src/keyhistory.rs +++ /dev/null @@ -1,13 +0,0 @@ -use crate::context::Context; - -/* yes: uppercase */ -/* library private: key-history */ -pub fn dc_add_to_keyhistory( - _context: &Context, - _rfc724_mid: *const libc::c_char, - _sending_time: u64, - _addr: *const libc::c_char, - _fingerprint: *const libc::c_char, -) { - -} diff --git a/src/lib.rs b/src/lib.rs index 66181db63..da16c8c4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(c_variadic, ptr_wrapping_offset_from)] +#![feature(c_variadic, ptr_wrapping_offset_from, ptr_cast)] #[macro_use] extern crate failure_derive; @@ -8,6 +8,9 @@ extern crate num_derive; extern crate smallvec; #[macro_use] extern crate rusqlite; +extern crate strum; +#[macro_use] +extern crate strum_macros; #[macro_use] mod log; @@ -18,10 +21,10 @@ pub mod aheader; pub mod chatlist; pub mod config; pub mod constants; +pub mod contact; pub mod context; pub mod imap; pub mod key; -pub mod keyhistory; pub mod keyring; pub mod oauth2; pub mod param; @@ -36,7 +39,6 @@ pub mod x; pub mod dc_array; pub mod dc_chat; pub mod dc_configure; -pub mod dc_contact; pub mod dc_dehtml; pub mod dc_e2ee; pub mod dc_imex; diff --git a/src/log.rs b/src/log.rs index 6ab8c4702..e3605d09b 100644 --- a/src/log.rs +++ b/src/log.rs @@ -7,10 +7,9 @@ macro_rules! info { #[allow(unused_unsafe)] unsafe { let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); + let formatted_c = std::ffi::CString::new(formatted).unwrap(); $ctx.call_cb($crate::constants::Event::INFO, $data1 as libc::uintptr_t, - formatted_c as libc::uintptr_t); - libc::free(formatted_c as *mut libc::c_void); + formatted_c.as_ptr() as libc::uintptr_t); }}; } @@ -23,10 +22,9 @@ macro_rules! warn { #[allow(unused_unsafe)] unsafe { let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); + let formatted_c = std::ffi::CString::new(formatted).unwrap(); $ctx.call_cb($crate::constants::Event::WARNING, $data1 as libc::uintptr_t, - formatted_c as libc::uintptr_t); - libc::free(formatted_c as *mut libc::c_void) ; + formatted_c.as_ptr() as libc::uintptr_t); }}; } @@ -39,10 +37,9 @@ macro_rules! error { #[allow(unused_unsafe)] unsafe { let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); + let formatted_c = std::ffi::CString::new(formatted).unwrap(); $ctx.call_cb($crate::constants::Event::ERROR, $data1 as libc::uintptr_t, - formatted_c as libc::uintptr_t); - libc::free(formatted_c as *mut libc::c_void); + formatted_c.as_ptr() as libc::uintptr_t); }}; } @@ -55,9 +52,8 @@ macro_rules! log_event { #[allow(unused_unsafe)] unsafe { let formatted = format!($msg, $($args),*); - let formatted_c = $crate::dc_tools::to_cstring(formatted); + let formatted_c = std::ffi::CString::new(formatted).unwrap(); $ctx.call_cb($event, $data1 as libc::uintptr_t, - formatted_c as libc::uintptr_t); - libc::free(formatted_c as *mut libc::c_void); + formatted_c.as_ptr() as libc::uintptr_t); }}; } diff --git a/src/peerstate.rs b/src/peerstate.rs index 876a300b1..de0e01abe 100644 --- a/src/peerstate.rs +++ b/src/peerstate.rs @@ -166,7 +166,6 @@ impl<'a> Peerstate<'a> { pub fn from_addr(context: &'a Context, _sql: &Sql, addr: &str) -> Option<Self> { let query = "SELECT addr, last_seen, last_seen_autocrypt, prefer_encrypted, public_key, gossip_timestamp, gossip_key, public_key_fingerprint, gossip_key_fingerprint, verified_key, verified_key_fingerprint FROM acpeerstates WHERE addr=? COLLATE NOCASE;"; - Self::from_stmt(context, query, &[addr]) } @@ -191,6 +190,11 @@ impl<'a> Peerstate<'a> { context .sql .query_row(query, params, |row| { + /* all the above queries start with this: 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 + */ let mut res = Self::new(context); res.addr = Some(row.get(0)?); @@ -198,13 +202,34 @@ impl<'a> Peerstate<'a> { 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) }; + res.public_key_fingerprint = row.get(7)?; + if res + .public_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.public_key_fingerprint = None; + } + res.gossip_key_fingerprint = row.get(8)?; + if res + .gossip_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.gossip_key_fingerprint = None; + } + res.verified_key_fingerprint = row.get(10)?; + if res + .verified_key_fingerprint + .as_ref() + .map(|s| s.is_empty()) + .unwrap_or_default() + { + res.verified_key_fingerprint = None; + } res.public_key = row .get(4) .ok() @@ -217,7 +242,8 @@ impl<'a> Peerstate<'a> { .get(9) .ok() .and_then(|blob: Vec<u8>| Key::from_slice(&blob, KeyType::Public)); - res.verified_key = if vk == res.gossip_key { + + res.verified_key = if vk == res.gossip_key && res.gossip_key.is_some() { VerifiedKey::Gossip } else if vk == res.public_key { VerifiedKey::Public @@ -422,6 +448,7 @@ impl<'a> Peerstate<'a> { &self.addr, ], ).is_ok(); + assert_eq!(success, true); } else if self.to_save == Some(ToSave::Timestamps) { success = sql::execute( self.context, @@ -462,16 +489,11 @@ mod tests { use super::*; use pretty_assertions::assert_eq; - use std::ffi::CStr; - use tempfile::{tempdir, TempDir}; - - use crate::context::*; - use crate::dc_tools::to_cstring; - use crate::x::free; + use tempfile::TempDir; #[test] fn test_peerstate_save_to_db() { - let ctx = unsafe { create_test_context() }; + let ctx = crate::test_utils::dummy_context(); let addr = "hello@mail.com"; let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap(); @@ -503,35 +525,44 @@ mod tests { assert_eq!(peerstate, peerstate_new); } + #[test] + fn test_peerstate_with_empty_gossip_key_save_to_db() { + let ctx = crate::test_utils::dummy_context(); + let addr = "hello@mail.com"; + + let pub_key = crate::key::Key::from_base64("xsBNBFztUVkBCADYaQl/UOUpRPd32nLRzx8eU0eI+jQEnG+g5anjYA+3oct1rROGl5SygjMULDKdaUy27O3o9Srsti0YjA7uxZnavIqhSopJhFidqY1M1wA9JZa/duucZdNwUGbjGIRsS/4Cjr5+3svscK24hVYub1dvDWXpwUTnj3K6xOEnJdoM+MhCqtSD5+zcJhFc9vyZm9ZTGWUxAhKh0iJTcCD8V6CQ3XZ2z9GruwzZT/FTFovWrz7m3TUI2OdSSHh0eZLRGEoxMCT/vzflAFGAr8ijCaRsEIfqP6FW8uQWnFTqkjxEUCZG6XkeFHB84aj5jqYG/1KCLjL5vEKwfl1tz/WnPhY7ABEBAAHNEDxoZWxsb0BtYWlsLmNvbT7CwIkEEAEIADMCGQEFAlztUVoCGwMECwkIBwYVCAkKCwIDFgIBFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7ijAwf+PTsuawUax9cNPn1bN90H+g9qyHZJMEwKXtUnNaXJxPW3iB7ThhpCiCzsZwP7+l7ArS8tmLeNDw2bENtcf1XCv4wovP2fdXOP3QOUUFX/GdakcTwv7DzC7CO0grB1HtaPhGw/6UX2o2cx2i9xiUf4Givq2MfCbgAW5zloH6WXGPb6yLQYJXxqDIphr4+uZDb+bMAyWHN/DUkAjHrV8nnVki7PMHqzzZpwglalxMX8RGeiGZE39ALJKL/Og87DMFah87/yoxQWGoS7Wqv0XDcCPKoTCPrpk8pOe2KEsq/lz215nefHd4aRpfUX5YCYa8HPvvfPQbGF73uvyQw5w7qjis7ATQRc7VFZAQgAt8ONdnX6KEEQ5Jw6ilJ+LBtY44SP5t0I3eK+goKepgIiKhjGDa+Mntyi4jdhH+HO6kvK5SHMh2sPp4rRO/WKHJwWFySyM1OdyiywhyH0J9R5rBY4vPHsJjf6vSKJdWLWT+ho1fNet2IIC+jVCYli91MAMbRvk6EKVj1nCc+67giOahXEkHt6xxkeCGlOvbw8hxGj1A8+AC1BLms/OR3oc4JMi9O3kq6uG0z9tlUEerac9HVwcjoO1XLe+hJhoT5H+TbnGjPuhuURP3pFiIKHpbRYgUfdSAY0dTObO7t4I5y/drPOrCTnWrBUg2wXAECUhpRKow9/ai2YemLv9KqhhwARAQABwsB2BBgBCAAgBQJc7VFaAhsMFiEEgMjHGVbvLXe6ioRROg8oKCvye7gACgkQOg8oKCvye7jmyggAhs4QzCzIbT2OsAReBxkxtm0AI+g1HZ1KFKof5NDHfgv9C/Qu1I8mKEjlZzA4qFyPmLqntgwJ0RuFy6gLbljZBNCFO7vB478AhYtnWjuKZmA40HUPwcB1hEJ31c42akzfUbioY1TLLepngdsJg7Cm8O+rhI9+1WRA66haJDgFs793SVUDyJh8f9NX50l5zR87/bsV30CFSw0q4OSSy9VI/z+2g5khn1LnuuOrCfFnYIPYtJED1BfkXkosxGlgbzy79VvGmI9d23x4atDK7oBPCzIj+lP8sytJ0u3HOguXi9OgDitKy+Pt1r8gH8frdktMJr5Ts6DW+tIn2vR23KR8aA==", KeyType::Public).unwrap(); + + let mut peerstate = Peerstate { + context: &ctx.ctx, + addr: Some(addr.into()), + last_seen: 10, + last_seen_autocrypt: 11, + prefer_encrypt: EncryptPreference::Mutual, + public_key: Some(pub_key.clone()), + public_key_fingerprint: Some(pub_key.fingerprint()), + gossip_key: None, + gossip_timestamp: 12, + gossip_key_fingerprint: None, + verified_key: VerifiedKey::None, + verified_key_fingerprint: None, + to_save: Some(ToSave::All), + degrade_event: None, + }; + + assert!(peerstate.save_to_db(&ctx.ctx.sql, true), "failed to save"); + + let peerstate_new = Peerstate::from_addr(&ctx.ctx, &ctx.ctx.sql, addr.into()) + .expect("failed to load peerstate from db"); + + // clear to_save, as that is not persissted + peerstate.to_save = None; + assert_eq!(peerstate, peerstate_new); + } + // TODO: don't copy this from stress.rs #[allow(dead_code)] struct TestContext { ctx: Context, dir: TempDir, } - - unsafe extern "C" fn cb( - _context: &Context, - _event: Event, - _data1: libc::uintptr_t, - _data2: libc::uintptr_t, - ) -> libc::uintptr_t { - 0 - } - - unsafe fn create_test_context() -> TestContext { - let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); - let dir = tempdir().unwrap(); - let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap()); - assert_eq!( - dc_open(&mut ctx, dbfile, std::ptr::null()), - 1, - "Failed to open {}", - CStr::from_ptr(dbfile as *const _).to_str().unwrap() - ); - - free(dbfile as *mut _); - - TestContext { ctx: ctx, dir: dir } - } } diff --git a/src/pgp.rs b/src/pgp.rs index 600a61fd2..bfc68f281 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -17,15 +17,14 @@ use crate::keyring::*; use crate::types::*; use crate::x::*; -// TODO should return bool /rtn pub unsafe fn dc_split_armored_data( buf: *mut libc::c_char, ret_headerline: *mut *const libc::c_char, ret_setupcodebegin: *mut *const libc::c_char, ret_preferencrypt: *mut *const libc::c_char, ret_base64: *mut *const libc::c_char, -) -> libc::c_int { - let mut success: libc::c_int = 0i32; +) -> bool { + let mut success = false; let mut line_chars: size_t = 0i32 as size_t; let mut line: *mut libc::c_char = buf; let mut p1: *mut libc::c_char = buf; @@ -128,7 +127,7 @@ pub unsafe fn dc_split_armored_data( if !ret_base64.is_null() { *ret_base64 = base64 } - success = 1i32 + success = true; } } } diff --git a/src/sql.rs b/src/sql.rs index ab3f00ac0..079130648 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -10,7 +10,6 @@ use crate::dc_tools::*; use crate::error::{Error, Result}; use crate::param::*; use crate::peerstate::*; -use crate::x::*; const DC_OPEN_READONLY: usize = 0x01; @@ -1007,35 +1006,15 @@ pub fn housekeeping(context: &Context) { } let entry = entry.unwrap(); let name_f = entry.file_name(); - let name_c = unsafe { to_cstring(name_f.to_string_lossy()) }; + let name_s = name_f.to_string_lossy(); - if unsafe { is_file_in_use(&mut files_in_use, 0 as *const libc::c_char, name_c) } - || unsafe { - is_file_in_use( - &mut files_in_use, - b".increation\x00" as *const u8 as *const libc::c_char, - name_c, - ) - } - || unsafe { - is_file_in_use( - &mut files_in_use, - b".waveform\x00" as *const u8 as *const libc::c_char, - name_c, - ) - } - || unsafe { - is_file_in_use( - &mut files_in_use, - b"-preview.jpg\x00" as *const u8 as *const libc::c_char, - name_c, - ) - } + if is_file_in_use(&mut files_in_use, None, &name_s) + || is_file_in_use(&mut files_in_use, Some(".increation"), &name_s) + || is_file_in_use(&mut files_in_use, Some(".waveform"), &name_s) + || is_file_in_use(&mut files_in_use, Some("-preview.jpg"), &name_s) { - unsafe { free(name_c as *mut _) }; continue; } - unsafe { free(name_c as *mut _) }; unreferenced_count += 1; @@ -1068,11 +1047,8 @@ pub fn housekeeping(context: &Context) { unreferenced_count, entry.file_name() ); - unsafe { - let path = to_cstring(entry.path().to_str().unwrap()); - dc_delete_file(context, path); - free(path as *mut _); - } + let path = entry.path().to_c_string().unwrap(); + dc_delete_file(context, path.as_ptr()); } } Err(err) => { @@ -1089,26 +1065,18 @@ pub fn housekeeping(context: &Context) { info!(context, 0, "Housekeeping done.",); } -unsafe fn is_file_in_use( - files_in_use: &HashSet<String>, - namespc: *const libc::c_char, - name: *const libc::c_char, -) -> bool { - let name_to_check = dc_strdup(name); - if !namespc.is_null() { - let name_len: libc::c_int = strlen(name) as libc::c_int; - let namespc_len: libc::c_int = strlen(namespc) as libc::c_int; - if name_len <= namespc_len - || strcmp(&*name.offset((name_len - namespc_len) as isize), namespc) != 0 - { +fn is_file_in_use(files_in_use: &HashSet<String>, namespc_opt: Option<&str>, name: &str) -> bool { + let name_to_check = if let Some(namespc) = namespc_opt { + let name_len = name.len(); + let namespc_len = namespc.len(); + if name_len <= namespc_len || !name.ends_with(namespc) { return false; } - *name_to_check.offset((name_len - namespc_len) as isize) = 0 as libc::c_char - } - - let contains = files_in_use.contains(as_str(name_to_check)); - free(name_to_check as *mut libc::c_void); - contains + &name[..name_len - namespc_len] + } else { + name + }; + files_in_use.contains(name_to_check) } fn maybe_add_file(files_in_use: &mut HashSet<String>, file: impl AsRef<str>) { @@ -1162,26 +1130,12 @@ mod test { maybe_add_file(&mut files, "$BLOBDIR/world.txt"); maybe_add_file(&mut files, "world2.txt"); - assert!(unsafe { - is_file_in_use( - &mut files, - std::ptr::null(), - b"hello\x00" as *const u8 as *const _, - ) - }); - assert!(!unsafe { - is_file_in_use( - &mut files, - b".txt\x00" as *const u8 as *const _, - b"hello\x00" as *const u8 as *const _, - ) - }); - assert!(unsafe { - is_file_in_use( - &mut files, - b"-suffix\x00" as *const u8 as *const _, - b"world.txt-suffix\x00" as *const u8 as *const _, - ) - }); + assert!(is_file_in_use(&mut files, None, "hello")); + assert!(!is_file_in_use(&mut files, Some(".txt"), "hello")); + assert!(is_file_in_use( + &mut files, + Some("-suffix"), + "world.txt-suffix" + )); } } diff --git a/src/stock.rs b/src/stock.rs index 3dcd66dfb..20231712b 100644 --- a/src/stock.rs +++ b/src/stock.rs @@ -1,12 +1,11 @@ use std::borrow::Cow; -use std::ffi::CString; use strum::EnumProperty; use strum_macros::EnumProperty; use crate::constants::Event; +use crate::contact::*; use crate::context::Context; -use crate::dc_contact::*; use crate::dc_tools::*; use libc::free; @@ -200,40 +199,30 @@ impl Context { from_id: u32, ) -> String { let insert1 = if id == StockMessage::MsgAddMember || id == StockMessage::MsgDelMember { - unsafe { - let param1_c = CString::new(param1.as_ref()).unwrap(); - let contact_id = dc_lookup_contact_id_by_addr(self, param1_c.as_ptr()); - if contact_id != 0 { - let contact = dc_get_contact(self, contact_id); - let displayname = dc_contact_get_name_n_addr(contact); - let ret = to_string(displayname); - free(contact as *mut libc::c_void); - free(displayname as *mut libc::c_void); - ret - } else { - param1.as_ref().to_string() - } + let contact_id = Contact::lookup_id_by_addr(self, param1.as_ref()); + if contact_id != 0 { + Contact::get_by_id(self, contact_id) + .map(|contact| contact.get_name_n_addr()) + .unwrap_or_default() + } else { + param1.as_ref().to_string() } } else { param1.as_ref().to_string() }; + let action = self.stock_string_repl_str2(id, insert1, param2.as_ref().to_string()); let action1 = action.trim_end_matches('.'); match from_id { 0 => action, 1 => self.stock_string_repl_str(StockMessage::MsgActionByMe, action1), // DC_CONTACT_ID_SELF - _ => unsafe { - let contact = dc_get_contact(self, from_id); - let displayname = dc_contact_get_display_name(contact); - let ret = self.stock_string_repl_str2( - StockMessage::MsgActionByUser, - action1, - as_str(displayname), - ); - free(contact as *mut libc::c_void); - free(displayname as *mut libc::c_void); - ret - }, + _ => { + let displayname = Contact::get_by_id(self, from_id) + .map(|contact| contact.get_name_n_addr()) + .unwrap_or_default(); + + self.stock_string_repl_str2(StockMessage::MsgActionByUser, action1, &displayname) + } } } } @@ -264,7 +253,7 @@ mod tests { #[test] fn test_stock_str() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); assert_eq!(ctx.stock_str(StockMessage::NoMessages), "No messages."); } @@ -290,7 +279,7 @@ mod tests { #[test] fn test_stock_string_repl_str() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); // uses %1$s substitution assert_eq!( ctx.stock_string_repl_str(StockMessage::Member, "42"), @@ -301,7 +290,7 @@ mod tests { #[test] fn test_stock_string_repl_int() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); assert_eq!( ctx.stock_string_repl_int(StockMessage::Member, 42), "42 member(s)" @@ -310,7 +299,7 @@ mod tests { #[test] fn test_stock_string_repl_str2() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); assert_eq!( ctx.stock_string_repl_str2(StockMessage::ServerResponse, "foo", "bar"), "Response from foo: bar" @@ -319,7 +308,7 @@ mod tests { #[test] fn test_stock_system_msg_simple() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); assert_eq!( ctx.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", 0), "Location streaming enabled." @@ -328,7 +317,7 @@ mod tests { #[test] fn test_stock_system_msg_add_member_by_me() { - let ctx = dc_context_new(None, std::ptr::null_mut(), std::ptr::null_mut()); + let ctx = dc_context_new(None, std::ptr::null_mut(), None); assert_eq!( ctx.stock_system_msg( StockMessage::MsgAddMember, @@ -343,11 +332,7 @@ mod tests { #[test] fn test_stock_system_msg_add_member_by_me_with_displayname() { let t = dummy_context(); - unsafe { - let name = CString::new("Alice").unwrap(); - let addr = CString::new("alice@example.com").unwrap(); - assert!(dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()) > 0); - } + Contact::create(&t.ctx, "Alice", "alice@example.com").expect("failed to create contact"); assert_eq!( t.ctx.stock_system_msg( StockMessage::MsgAddMember, @@ -356,23 +341,17 @@ mod tests { DC_CONTACT_ID_SELF as u32 ), "Member Alice (alice@example.com) added by me." - ) + ); } #[test] fn test_stock_system_msg_add_member_by_other_with_displayname() { let t = dummy_context(); - let contact_id = unsafe { - let name = CString::new("Alice").unwrap(); - let addr = CString::new("alice@example.com").unwrap(); - assert!( - dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()) > 0, - "Failed to create contact Alice" - ); - let name = CString::new("Bob").unwrap(); - let addr = CString::new("bob@example.com").unwrap(); - let id = dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()); - assert!(id > 0, "Failed to create contact Bob"); + let contact_id = { + Contact::create(&t.ctx, "Alice", "alice@example.com") + .expect("Failed to create contact Alice"); + let id = + Contact::create(&t.ctx, "Bob", "bob@example.com").expect("failed to create bob"); id }; assert_eq!( @@ -382,8 +361,8 @@ mod tests { "", contact_id, ), - "Member Alice (alice@example.com) added by Bob." - ) + "Member Alice (alice@example.com) added by Bob (bob@example.com)." + ); } #[test] @@ -403,21 +382,13 @@ mod tests { #[test] fn test_stock_system_msg_grp_name_other() { let t = dummy_context(); - let contact_id = unsafe { - let name = CString::new("Alice").unwrap(); - let addr = CString::new("alice@example.com").unwrap(); - let id = dc_create_contact(&t.ctx, name.as_ptr(), addr.as_ptr()); - assert!(id > 0, "Failed to create contact Alice"); - id - }; + let id = Contact::create(&t.ctx, "Alice", "alice@example.com") + .expect("failed to create contact"); + assert_eq!( - t.ctx.stock_system_msg( - StockMessage::MsgGrpName, - "Some chat", - "Other chat", - contact_id - ), - "Group name changed from \"Some chat\" to \"Other chat\" by Alice." + t.ctx + .stock_system_msg(StockMessage::MsgGrpName, "Some chat", "Other chat", id,), + "Group name changed from \"Some chat\" to \"Other chat\" by Alice (alice@example.com)." ) } } diff --git a/src/test_utils.rs b/src/test_utils.rs index 1109bd4ec..5ba5c0a37 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -7,8 +7,6 @@ use tempfile::{tempdir, TempDir}; use crate::context::{dc_context_new, dc_open, Context}; use crate::types::dc_callback_t; -use crate::dc_tools::OsStrExt; - /// A Context and temporary directory. /// /// The temporary directory can be used to store the SQLite database, @@ -26,13 +24,11 @@ pub struct TestContext { /// [Context]: crate::context::Context pub fn test_context(cb: Option<dc_callback_t>) -> TestContext { unsafe { - let mut ctx = dc_context_new(cb, std::ptr::null_mut(), std::ptr::null_mut()); + let mut ctx = dc_context_new(cb, std::ptr::null_mut(), None); let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); - let dbfile_c = dbfile.to_c_string().unwrap(); - assert_eq!( - dc_open(&mut ctx, dbfile_c.as_ptr(), std::ptr::null()), - 1, + assert!( + dc_open(&mut ctx, dbfile.to_str().unwrap(), None), "Failed to open {}", dbfile.display(), ); diff --git a/src/types.rs b/src/types.rs index 3cf1043df..4205006c9 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,6 @@ use crate::constants::Event; use crate::context::Context; -pub use mmime::carray::*; pub use mmime::clist::*; pub use rusqlite::ffi::*; @@ -34,10 +33,8 @@ the online state. */ pub type dc_precheck_imf_t = unsafe fn(_: &Context, _: *const libc::c_char, _: &str, _: u32) -> libc::c_int; -pub type dc_set_config_t = - unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> (); -pub type dc_get_config_t = - unsafe fn(_: &Context, _: *const libc::c_char, _: *const libc::c_char) -> *mut libc::c_char; +pub type dc_set_config_t = fn(_: &Context, _: &str, _: Option<&str>) -> (); +pub type dc_get_config_t = fn(_: &Context, _: &str) -> Option<String>; pub type sqlite_int64 = i64; pub type sqlite3_int64 = sqlite_int64; diff --git a/src/x.rs b/src/x.rs index aa43b3f11..f1bb01e37 100644 --- a/src/x.rs +++ b/src/x.rs @@ -1,5 +1,3 @@ -use crate::types::*; - pub use libc::{ calloc, exit, free, malloc, memcmp, memcpy, memmove, memset, realloc, strcat, strchr, strcmp, strcpy, strcspn, strlen, strncmp, strncpy, strrchr, strspn, strstr, strtol, system, @@ -37,14 +35,6 @@ pub fn strndup(s: *const libc::c_char, n: libc::c_ulong) -> *mut libc::c_char { extern "C" { pub fn clock() -> libc::clock_t; - pub fn qsort( - __base: *mut libc::c_void, - __nel: size_t, - __width: size_t, - __compar: Option< - unsafe extern "C" fn(_: *const libc::c_void, _: *const libc::c_void) -> libc::c_int, - >, - ); // -- DC Methods pub fn dc_mprintf(format: *const libc::c_char, _: ...) -> *mut libc::c_char; diff --git a/tests/stress.rs b/tests/stress.rs index c0a4245c1..46176a418 100644 --- a/tests/stress.rs +++ b/tests/stress.rs @@ -8,11 +8,11 @@ use tempfile::{tempdir, TempDir}; use deltachat::config; use deltachat::constants::*; +use deltachat::contact::*; use deltachat::context::*; use deltachat::dc_array::*; use deltachat::dc_chat::*; use deltachat::dc_configure::*; -use deltachat::dc_contact::*; use deltachat::dc_imex::*; use deltachat::dc_location::*; use deltachat::dc_lot::*; @@ -273,7 +273,6 @@ unsafe fn stress_functions(context: &Context) { assert!(res.contains(" configured_send_port ")); assert!(res.contains(" configured_server_flags ")); - let mut ok: libc::c_int; let mut buf_0: *mut libc::c_char; let mut headerline: *const libc::c_char = 0 as *const libc::c_char; let mut setupcodebegin: *const libc::c_char = 0 as *const libc::c_char; @@ -283,14 +282,14 @@ unsafe fn stress_functions(context: &Context) { b"-----BEGIN PGP MESSAGE-----\nNoVal:\n\ndata\n-----END PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, ); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, &mut setupcodebegin, 0 as *mut *const libc::c_char, &mut base64, ); - assert_eq!(ok, 1); + assert!(ok); assert!(!headerline.is_null()); assert_eq!( strcmp( @@ -308,7 +307,7 @@ unsafe fn stress_functions(context: &Context) { buf_0 = strdup(b"-----BEGIN PGP MESSAGE-----\n\ndat1\n-----END PGP MESSAGE-----\n-----BEGIN PGP MESSAGE-----\n\ndat2\n-----END PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, &mut setupcodebegin, @@ -316,7 +315,7 @@ unsafe fn stress_functions(context: &Context) { &mut base64, ); - assert_eq!(ok, 1); + assert!(ok); assert!(!headerline.is_null()); assert_eq!( strcmp( @@ -335,7 +334,7 @@ unsafe fn stress_functions(context: &Context) { b"foo \n -----BEGIN PGP MESSAGE----- \n base64-123 \n -----END PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, ); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, &mut setupcodebegin, @@ -343,7 +342,7 @@ unsafe fn stress_functions(context: &Context) { &mut base64, ); - assert_eq!(ok, 1); + assert!(ok); assert!(!headerline.is_null()); assert_eq!( strcmp( @@ -360,7 +359,7 @@ unsafe fn stress_functions(context: &Context) { free(buf_0 as *mut libc::c_void); buf_0 = strdup(b"foo-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, &mut setupcodebegin, @@ -368,19 +367,19 @@ unsafe fn stress_functions(context: &Context) { &mut base64, ); - assert_eq!(ok, 0); + assert!(!ok); free(buf_0 as *mut libc::c_void); buf_0 = strdup(b"foo \n -----BEGIN PGP MESSAGE-----\n Passphrase-BeGIN : 23 \n \n base64-567 \r\n abc \n -----END PGP MESSAGE-----\n\n\n\x00" as *const u8 as *const libc::c_char); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, &mut setupcodebegin, 0 as *mut *const libc::c_char, &mut base64, ); - assert_eq!(ok, 1); + assert!(ok); assert!(!headerline.is_null()); assert_eq!( strcmp( @@ -407,14 +406,14 @@ unsafe fn stress_functions(context: &Context) { buf_0 = strdup(b"-----BEGIN PGP PRIVATE KEY BLOCK-----\n Autocrypt-Prefer-Encrypt : mutual \n\nbase64\n-----END PGP PRIVATE KEY BLOCK-----\x00" as *const u8 as *const libc::c_char); - ok = dc_split_armored_data( + let ok = dc_split_armored_data( buf_0, &mut headerline, 0 as *mut *const libc::c_char, &mut preferencrypt, &mut base64, ); - assert_eq!(ok, 1); + assert!(ok); assert!(!headerline.is_null()); assert_eq!( strcmp( @@ -470,16 +469,13 @@ unsafe fn stress_functions(context: &Context) { let mut setupcodebegin_0: *const libc::c_char = 0 as *const libc::c_char; let mut preferencrypt_0: *const libc::c_char = 0 as *const libc::c_char; buf_1 = strdup(S_EM_SETUPFILE); - assert_ne!( - 0, - dc_split_armored_data( - buf_1, - &mut headerline_0, - &mut setupcodebegin_0, - &mut preferencrypt_0, - 0 as *mut *const libc::c_char, - ) - ); + assert!(dc_split_armored_data( + buf_1, + &mut headerline_0, + &mut setupcodebegin_0, + &mut preferencrypt_0, + 0 as *mut *const libc::c_char, + )); assert!(!headerline_0.is_null()); assert_eq!( 0, @@ -499,16 +495,13 @@ unsafe fn stress_functions(context: &Context) { free(buf_1 as *mut libc::c_void); buf_1 = dc_decrypt_setup_file(context, S_EM_SETUPCODE, S_EM_SETUPFILE); assert!(!buf_1.is_null()); - assert_ne!( - 0, - dc_split_armored_data( - buf_1, - &mut headerline_0, - &mut setupcodebegin_0, - &mut preferencrypt_0, - 0 as *mut *const libc::c_char, - ) - ); + assert!(dc_split_armored_data( + buf_1, + &mut headerline_0, + &mut setupcodebegin_0, + &mut preferencrypt_0, + 0 as *mut *const libc::c_char, + )); assert!(!headerline_0.is_null()); assert_eq!( strcmp( @@ -528,65 +521,21 @@ unsafe fn stress_functions(context: &Context) { ); free(buf_1 as *mut libc::c_void); if 0 != dc_is_configured(context) { - let setupfile: *mut libc::c_char; - let setupcode_c = - CString::new(dc_create_setup_code(context)).expect("invalid string converted"); - assert_eq!(setupcode_c.to_bytes().len(), 44); - let setupcode = setupcode_c.as_ptr(); - assert!( - 0 != !(*setupcode.offset(4isize) as libc::c_int == '-' as i32 - && *setupcode.offset(9isize) as libc::c_int == '-' as i32 - && *setupcode.offset(14isize) as libc::c_int == '-' as i32 - && *setupcode.offset(19isize) as libc::c_int == '-' as i32 - && *setupcode.offset(24isize) as libc::c_int == '-' as i32 - && *setupcode.offset(29isize) as libc::c_int == '-' as i32 - && *setupcode.offset(34isize) as libc::c_int == '-' as i32 - && *setupcode.offset(39isize) as libc::c_int == '-' as i32) - as usize - ); - setupfile = dc_render_setup_file(context, setupcode); - assert!(!setupfile.is_null()); - let buf_2: *mut libc::c_char = dc_strdup(setupfile); - let mut headerline_1: *const libc::c_char = 0 as *const libc::c_char; - let mut setupcodebegin_1: *const libc::c_char = 0 as *const libc::c_char; - assert_eq!( - 0, - dc_split_armored_data( - buf_2, - &mut headerline_1, - &mut setupcodebegin_1, - 0 as *mut *const libc::c_char, - 0 as *mut *const libc::c_char, - ) - ); - assert!(!headerline_1.is_null()); - assert_eq!( - strcmp( - headerline_1, - b"-----BEGIN PGP MESSAGE-----\x00" as *const u8 as *const libc::c_char, - ), - 0 - ); - assert!( - !(!setupcodebegin_1.is_null() - && strlen(setupcodebegin_1) == 2 - && strncmp(setupcodebegin_1, setupcode, 2) == 0i32) - ); - free(buf_2 as *mut libc::c_void); + let setupcode = dc_create_setup_code(context); + let setupcode_c = CString::yolo(setupcode.clone()); + let setupfile = dc_render_setup_file(context, &setupcode).unwrap(); + let setupfile_c = CString::yolo(setupfile); let payload: *mut libc::c_char; let mut headerline_2: *const libc::c_char = 0 as *const libc::c_char; - payload = dc_decrypt_setup_file(context, setupcode, setupfile); + payload = dc_decrypt_setup_file(context, setupcode_c.as_ptr(), setupfile_c.as_ptr()); assert!(payload.is_null()); - assert_eq!( - 0, - dc_split_armored_data( - payload, - &mut headerline_2, - 0 as *mut *const libc::c_char, - 0 as *mut *const libc::c_char, - 0 as *mut *const libc::c_char, - ) - ); + assert!(!dc_split_armored_data( + payload, + &mut headerline_2, + 0 as *mut *const libc::c_char, + 0 as *mut *const libc::c_char, + 0 as *mut *const libc::c_char, + )); assert!(!headerline_2.is_null()); assert_eq!( strcmp( @@ -596,7 +545,6 @@ unsafe fn stress_functions(context: &Context) { 0 ); free(payload as *mut libc::c_void); - free(setupfile as *mut libc::c_void); } if 0 != dc_is_configured(context) { @@ -684,7 +632,7 @@ fn test_encryption_decryption() { assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----")); let ctext_signed_bytes = ctext.len(); - let ctext_signed = to_cstring(ctext); + let ctext_signed = CString::yolo(ctext); let ctext = dc_pgp_pk_encrypt( original_text as *const libc::c_void, @@ -697,7 +645,7 @@ fn test_encryption_decryption() { assert!(ctext.starts_with("-----BEGIN PGP MESSAGE-----")); let ctext_unsigned_bytes = ctext.len(); - let ctext_unsigned = to_cstring(ctext); + let ctext_unsigned = CString::yolo(ctext); let mut keyring = Keyring::default(); keyring.add_owned(private_key); @@ -711,7 +659,7 @@ fn test_encryption_decryption() { let mut valid_signatures: HashSet<String> = Default::default(); let plain = dc_pgp_pk_decrypt( - ctext_signed as *const _, + ctext_signed.as_ptr() as *const _, ctext_signed_bytes, &keyring, &public_keyring, @@ -726,7 +674,7 @@ fn test_encryption_decryption() { let empty_keyring = Keyring::default(); let plain = dc_pgp_pk_decrypt( - ctext_signed as *const _, + ctext_signed.as_ptr() as *const _, ctext_signed_bytes, &keyring, &empty_keyring, @@ -739,7 +687,7 @@ fn test_encryption_decryption() { valid_signatures.clear(); let plain = dc_pgp_pk_decrypt( - ctext_signed as *const _, + ctext_signed.as_ptr() as *const _, ctext_signed_bytes, &keyring, &public_keyring2, @@ -754,7 +702,7 @@ fn test_encryption_decryption() { public_keyring2.add_ref(&public_key); let plain = dc_pgp_pk_decrypt( - ctext_signed as *const _, + ctext_signed.as_ptr() as *const _, ctext_signed_bytes, &keyring, &public_keyring2, @@ -767,7 +715,7 @@ fn test_encryption_decryption() { valid_signatures.clear(); let plain = dc_pgp_pk_decrypt( - ctext_unsigned as *const _, + ctext_unsigned.as_ptr() as *const _, ctext_unsigned_bytes, &keyring, &public_keyring, @@ -775,7 +723,6 @@ fn test_encryption_decryption() { ) .unwrap(); - free(ctext_unsigned as *mut _); assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),); valid_signatures.clear(); @@ -786,7 +733,7 @@ fn test_encryption_decryption() { public_keyring.add_ref(&public_key); let plain = dc_pgp_pk_decrypt( - ctext_signed as *const _, + ctext_signed.as_ptr() as *const _, ctext_signed_bytes, &keyring, &public_keyring, @@ -794,7 +741,6 @@ fn test_encryption_decryption() { ) .unwrap(); - free(ctext_signed as *mut _); assert_eq!(std::str::from_utf8(&plain).unwrap(), as_str(original_text),); } } @@ -815,16 +761,14 @@ struct TestContext { } unsafe fn create_test_context() -> TestContext { - let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); + let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None); let dir = tempdir().unwrap(); - let dbfile = to_cstring(dir.path().join("db.sqlite").to_str().unwrap()); - assert_eq!( - dc_open(&mut ctx, dbfile, std::ptr::null()), - 1, + let dbfile = dir.path().join("db.sqlite"); + assert!( + dc_open(&mut ctx, dbfile.to_str().unwrap(), None), "Failed to open {}", - as_str(dbfile as *const libc::c_char) + dbfile.display() ); - free(dbfile as *mut _); TestContext { ctx: ctx, dir: dir } } @@ -842,24 +786,24 @@ fn test_dc_kml_parse() { assert!(!kml.addr.is_null()); assert_eq!(as_str(kml.addr as *const libc::c_char), "user@example.org",); - assert_eq!(dc_array_get_cnt(kml.locations), 2); + let locations_ref = &kml.locations.as_ref().unwrap(); + assert_eq!(locations_ref.len(), 2); - assert!(dc_array_get_latitude(kml.locations, 0) > 53.6f64); - assert!(dc_array_get_latitude(kml.locations, 0) < 53.8f64); - assert!(dc_array_get_longitude(kml.locations, 0) > 9.3f64); - assert!(dc_array_get_longitude(kml.locations, 0) < 9.5f64); - assert!(dc_array_get_accuracy(kml.locations, 0) > 31.9f64); - assert!(dc_array_get_accuracy(kml.locations, 0) < 32.1f64); - assert_eq!(dc_array_get_timestamp(kml.locations, 0), 1551906597); + assert!(locations_ref[0].latitude > 53.6f64); + assert!(locations_ref[0].latitude < 53.8f64); + assert!(locations_ref[0].longitude > 9.3f64); + assert!(locations_ref[0].longitude < 9.5f64); + assert!(locations_ref[0].accuracy > 31.9f64); + assert!(locations_ref[0].accuracy < 32.1f64); + assert_eq!(locations_ref[0].timestamp, 1551906597); - assert!(dc_array_get_latitude(kml.locations, 1) > 63.6f64); - assert!(dc_array_get_latitude(kml.locations, 1) < 63.8f64); - assert!(dc_array_get_longitude(kml.locations, 1) > 19.3f64); - assert!(dc_array_get_longitude(kml.locations, 1) < 19.5f64); - assert!(dc_array_get_accuracy(kml.locations, 1) > 2.4f64); - assert!(dc_array_get_accuracy(kml.locations, 1) < 2.6f64); - - assert_eq!(dc_array_get_timestamp(kml.locations, 1), 1544739072); + assert!(locations_ref[1].latitude > 63.6f64); + assert!(locations_ref[1].latitude < 63.8f64); + assert!(locations_ref[1].longitude > 19.3f64); + assert!(locations_ref[1].longitude < 19.5f64); + assert!(locations_ref[1].accuracy > 2.4f64); + assert!(locations_ref[1].accuracy < 2.6f64); + assert_eq!(locations_ref[1].timestamp, 1544739072); dc_kml_unref(&mut kml); } @@ -898,7 +842,7 @@ fn test_dc_mimeparser_with_context() { b"Chat-Version\x00" as *const u8 as *const libc::c_char, ); assert_eq!(as_str((*of).fld_value as *const libc::c_char), "1.0",); - assert_eq!(carray_count(mimeparser.parts), 1); + assert_eq!(mimeparser.parts.len(), 1); dc_mimeparser_unref(&mut mimeparser); } @@ -946,29 +890,20 @@ fn test_stress_tests() { fn test_get_contacts() { unsafe { let context = create_test_context(); - let name = to_cstring("some2"); - let contacts = dc_get_contacts(&context.ctx, 0, name); + let contacts = Contact::get_all(&context.ctx, 0, Some("some2")).unwrap(); assert_eq!(dc_array_get_cnt(contacts), 0); dc_array_unref(contacts); - free(name as *mut _); - let name = to_cstring("bob"); - let email = to_cstring("bob@mail.de"); - let id = dc_create_contact(&context.ctx, name, email); + let id = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap(); assert_ne!(id, 0); - let contacts = dc_get_contacts(&context.ctx, 0, name); + let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).unwrap(); assert_eq!(dc_array_get_cnt(contacts), 1); dc_array_unref(contacts); - let name2 = to_cstring("alice"); - let contacts = dc_get_contacts(&context.ctx, 0, name2); + let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).unwrap(); assert_eq!(dc_array_get_cnt(contacts), 0); dc_array_unref(contacts); - - free(name as *mut _); - free(name2 as *mut _); - free(email as *mut _); } } @@ -976,12 +911,7 @@ fn test_get_contacts() { fn test_chat() { unsafe { let context = create_test_context(); - let name = to_cstring("bob"); - let email = to_cstring("bob@mail.de"); - - let contact1 = dc_create_contact(&context.ctx, name, email); - free(name as *mut _); - free(email as *mut _); + let contact1 = Contact::create(&context.ctx, "bob", "bob@mail.de").unwrap(); assert_ne!(contact1, 0); let chat_id = dc_create_chat_by_contact_id(&context.ctx, contact1); @@ -1001,14 +931,12 @@ fn test_chat() { #[test] fn test_wrong_db() { unsafe { - let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), std::ptr::null_mut()); + let mut ctx = dc_context_new(Some(cb), std::ptr::null_mut(), None); let dir = tempdir().unwrap(); let dbfile = dir.path().join("db.sqlite"); std::fs::write(&dbfile, b"123").unwrap(); - let dbfile_c = to_cstring(dbfile.to_str().unwrap()); - let res = dc_open(&mut ctx, dbfile_c, std::ptr::null()); - free(dbfile_c as *mut _); - assert_eq!(res, 0); + let res = dc_open(&mut ctx, dbfile.to_str().unwrap(), None); + assert!(!res); } }