diff --git a/.circleci/config.yml b/.circleci/config.yml index 2c40632fb..cd3d70517 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,6 +7,10 @@ executors: doxygen: docker: - image: hrektts/doxygen + python: + docker: + - image: 3.7.7-stretch + restore-workspace: &restore-workspace attach_workspace: diff --git a/CHANGELOG.md b/CHANGELOG.md index cecfd6723..80560f305 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,73 @@ # Changelog +## 1.32.0 + +- fix endless loop when trying to download messages with bad RFC Message-ID, + also be more reliable on similar errors #1463 #1466 #1462 + +- fix bug with comma in contact request #1438 + +- do not refer to hidden messages on replies #1459 + +- improve error handling #1468 #1465 #1464 + + +## 1.31.0 + +- always describe the context of the displayed error #1451 + +- do not emit `DC_EVENT_ERROR` when message sending fails; + `dc_msg_get_state()` and `dc_get_msg_info()` are sufficient #1451 + +- new config-option `media_quality` #1449 + +- try over if writing message to database fails #1447 + + +## 1.30.0 + +- expunge deleted messages #1440 + +- do not send `DC_EVENT_MSGS_CHANGED|INCOMING_MSG` on hidden messages #1439 + + +## 1.29.0 + +- new config options `delete_device_after` and `delete_server_after`, + each taking an amount of seconds after which messages + are deleted from the device and/or the server #1310 #1335 #1411 #1417 #1423 + +- new api `dc_estimate_deletion_cnt()` to estimate the effect + of `delete_device_after` and `delete_server_after` + +- use Ed25519 keys by default, these keys are much shorter + than RSA keys, which results in saving traffic and speed improvements #1362 + +- improve message ellipsizing #1397 #1430 + +- emit `DC_EVENT_ERROR_NETWORK` also on smtp-errors #1378 + +- do not show badly formatted non-delta-messages as empty #1384 + +- try over SMTP on potentially recoverable error 5.5.0 #1379 + +- remove device-chat from forward-to-chat-list #1367 + +- improve group-handling #1368 + +- `dc_get_info()` returns uptime (how long the context is in use) + +- python improvements and adaptions #1408 #1415 + +- log to the stdout and stderr in tests #1416 + +- refactoring, code improvements #1363 #1365 #1366 #1370 #1375 #1389 #1390 #1418 #1419 + +- removed api: `dc_chat_get_subtitle()`, `dc_get_version_str()`, `dc_array_add_id()` + +- removed events: `DC_EVENT_MEMBER_ADDED`, `DC_EVENT_MEMBER_REMOVED` + + ## 1.28.0 - new flag DC_GCL_FOR_FORWARDING for dc_get_chatlist() diff --git a/Cargo.lock b/Cargo.lock index f12ffb684..ff9ad0b17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,13 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "addr2line" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "adler32" version = "1.0.4" @@ -58,6 +66,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "anyhow" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arrayref" version = "0.3.6" @@ -86,8 +99,8 @@ name = "async-attributes" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -100,15 +113,15 @@ dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-pool 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "imap-proto 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rental 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "stop-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -118,7 +131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -129,25 +142,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-trait 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", + "async-trait 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "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.8 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-std" version = "1.5.0" -source = "git+https://github.com/async-rs/async-std#2dbebe54ede4d2c0a18380f51e785d5306022ac5" +source = "git+https://github.com/async-rs/async-std?rev=3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58#3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58" dependencies = [ "async-attributes 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "async-task 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -155,18 +168,18 @@ dependencies = [ "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kv-log-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -180,18 +193,18 @@ dependencies = [ "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "kv-log-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -200,18 +213,18 @@ name = "async-task" version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "async-trait" -version = "0.1.24" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -219,8 +232,8 @@ name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -236,24 +249,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", + "addr2line 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "backtrace-sys" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "base64" version = "0.10.1" @@ -269,15 +274,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bit-set" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bit-vec" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -351,11 +356,11 @@ name = "broadcaster" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -375,7 +380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "bumpalo" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -414,9 +419,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -431,7 +436,7 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.50" +version = "1.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -454,7 +459,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -464,7 +469,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -477,7 +482,7 @@ name = "clear_on_drop" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -504,7 +509,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -554,7 +559,7 @@ dependencies = [ "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -579,11 +584,11 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -614,10 +619,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -626,8 +631,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -650,14 +655,16 @@ dependencies = [ [[package]] name = "deltachat" -version = "1.28.0" +version = "1.32.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "anyhow 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)", "async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "async-smtp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "async-std 1.5.0 (git+https://github.com/async-rs/async-std)", - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", + "async-std 1.5.0 (git+https://github.com/async-rs/async-std?rev=3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58)", + "async-trait 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -669,44 +676,43 @@ dependencies = [ "email 0.0.21 (git+https://github.com/deltachat/rust-email)", "encoded-words 0.1.0 (git+https://github.com/async-email/encoded-words)", "escaper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)", - "image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "image-meta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre_email 0.9.2 (git+https://github.com/deltachat/lettre)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mailparse 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pgp 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pgp 0.5.2 (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.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proptest 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)", "quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)", "r2d2_sqlite 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "sanitize-filename 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "stop-token 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "strum 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -714,21 +720,22 @@ dependencies = [ name = "deltachat_derive" version = "2.0.0" dependencies = [ - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "deltachat_ffi" -version = "1.28.0" +version = "1.32.0" dependencies = [ + "anyhow 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "deltachat 1.28.0", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "deltachat 1.32.0", + "human-panic 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -738,9 +745,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", "derive_builder_core 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -749,9 +756,9 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -782,14 +789,14 @@ name = "dirs" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "doc-comment" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -824,7 +831,7 @@ dependencies = [ "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -835,11 +842,11 @@ source = "git+https://github.com/async-email/encoded-words#2631c258183620f6d976a dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -901,7 +908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "encoding_rs" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -920,7 +927,7 @@ dependencies = [ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -929,7 +936,7 @@ name = "error-chain" version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -943,21 +950,21 @@ dependencies = [ [[package]] name = "failure" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -986,12 +993,12 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1034,67 +1041,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-channel" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-core" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-executor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-io" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-macro" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "futures-sink" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-task" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "futures-timer" @@ -1103,19 +1113,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures-util" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1124,7 +1135,7 @@ name = "generic-array" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1133,7 +1144,7 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1146,6 +1157,11 @@ dependencies = [ "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "glob" version = "0.2.11" @@ -1153,20 +1169,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "h2" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.3.2 (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)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1179,10 +1195,10 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1195,13 +1211,13 @@ name = "hostname" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winutil 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "http" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1215,7 +1231,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1225,18 +1241,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "human-panic" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.47 (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.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.104 (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)", - "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1249,23 +1263,23 @@ dependencies = [ [[package]] name = "hyper" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "h2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (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)", - "pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1276,10 +1290,10 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1304,21 +1318,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", - "jpeg-decoder 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", + "jpeg-decoder 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "image-meta" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "skeptic 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1350,7 +1364,7 @@ name = "iovec" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1368,7 +1382,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jpeg-decoder" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1376,10 +1390,10 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.36" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1398,7 +1412,7 @@ dependencies = [ [[package]] name = "kv-log-macro" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1431,8 +1445,8 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.2 (git+https://github.com/deltachat/lettre)", "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1444,13 +1458,13 @@ dependencies = [ "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" -version = "0.2.67" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1460,22 +1474,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libsqlite3-sys" -version = "0.17.1" +version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1494,7 +1508,7 @@ name = "lru-cache" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1539,10 +1553,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memoffset" -version = "0.5.3" +version = "0.5.4" 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)", + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1569,7 +1583,7 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1577,22 +1591,22 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (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)", + "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "mio-uds" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1601,7 +1615,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1612,24 +1626,24 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (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.28 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.29 (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.54 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "net2" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1639,9 +1653,9 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1682,8 +1696,8 @@ dependencies = [ "num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "zeroize 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1692,9 +1706,9 @@ name = "num-derive" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1718,7 +1732,7 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1736,13 +1750,18 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "object" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "once_cell" version = "1.3.1" @@ -1755,15 +1774,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.10.28" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1773,21 +1792,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-src" -version = "111.6.1+1.1.1d" +version = "111.9.0+1.1.1g" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.56" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-src 111.6.1+1.1.1d (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-src 111.9.0+1.1.1g (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1797,7 +1816,7 @@ name = "os_type" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1818,23 +1837,23 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "parking_lot_core" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1845,7 +1864,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pgp" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1866,7 +1885,7 @@ dependencies = [ "des 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)", "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1882,8 +1901,8 @@ dependencies = [ "sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", "try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "x25519-dalek 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1892,30 +1911,30 @@ dependencies = [ [[package]] name = "pin-project" -version = "0.4.8" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-internal 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-internal" -version = "0.4.8" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pin-project-lite" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pin-utils" -version = "0.1.0-alpha.4" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1945,7 +1964,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "ctor 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1962,22 +1981,17 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.11" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "proc-macro-nested" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1985,10 +1999,10 @@ dependencies = [ [[package]] name = "proptest" -version = "0.9.5" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2030,10 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2047,8 +2061,8 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "scheduled-thread-pool 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2066,7 +2080,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2078,7 +2092,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2096,7 +2110,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "packed_simd 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2171,7 +2185,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2183,7 +2197,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2231,7 +2245,7 @@ dependencies = [ [[package]] name = "regex" -version = "1.3.5" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2267,9 +2281,9 @@ name = "rental-impl" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2279,31 +2293,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encoding_rs 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (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.16 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2323,7 +2337,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint-dig 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2342,10 +2356,10 @@ dependencies = [ "bitflags 1.2.1 (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.17.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libsqlite3-sys 0.17.3 (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.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2389,7 +2403,7 @@ version = "4.1.0" 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.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2401,7 +2415,7 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2414,7 +2428,7 @@ name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2423,12 +2437,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "schannel" -version = "0.1.17" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2437,10 +2451,10 @@ dependencies = [ [[package]] name = "scheduled-thread-pool" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2450,22 +2464,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "security-framework" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", + "security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "security-framework-sys" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2474,7 +2489,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2484,30 +2499,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2517,7 +2532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2565,7 +2580,7 @@ dependencies = [ "error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2577,26 +2592,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "smallvec" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "snafu" -version = "0.6.2" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "doc-comment 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "snafu-derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "snafu-derive 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "snafu-derive" -version = "0.6.2" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2620,7 +2635,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2647,9 +2662,9 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2669,11 +2684,11 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.16" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2690,9 +2705,9 @@ name = "synstructure" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2711,45 +2726,37 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "termcolor" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "termcolor" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror" -version = "1.0.11" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror-impl 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "thiserror-impl" -version = "1.0.11" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2770,58 +2777,58 @@ dependencies = [ [[package]] name = "time" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio" -version = "0.2.13" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tokio-util" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "toml" -version = "0.4.10" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2859,7 +2866,7 @@ dependencies = [ [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2883,7 +2890,7 @@ name = "unicode-normalization" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2929,15 +2936,6 @@ name = "utf8parse" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "uuid" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "uuid" version = "0.8.1" @@ -2971,7 +2969,7 @@ name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2981,7 +2979,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3000,73 +2998,73 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.59" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "web-sys" -version = "0.3.36" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3095,7 +3093,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3106,14 +3104,6 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "wincolor" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "winreg" version = "0.6.2" @@ -3162,13 +3152,14 @@ name = "zeroize_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] +"checksum addr2line 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "456d75cbb82da1ad150c8a9d97285ffcd21c9931dcb11e995903e7d75141b38b" "checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9" "checksum aes-soft 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" @@ -3176,6 +3167,7 @@ dependencies = [ "checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +"checksum anyhow 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "dc98824304f5513bb8f862f9e5985219003de4d730689e59d8f28818283a6fe4" "checksum arrayref 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" @@ -3184,19 +3176,18 @@ dependencies = [ "checksum async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)" = "" "checksum async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33" "checksum async-smtp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3652e5c6072c0694a2bcdb7e8409980d2676bd4f024adf4aab10c68fd2b48f5" -"checksum async-std 1.5.0 (git+https://github.com/async-rs/async-std)" = "" +"checksum async-std 1.5.0 (git+https://github.com/async-rs/async-std?rev=3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58)" = "" "checksum async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" "checksum async-task 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0ac2c016b079e771204030951c366db398864f5026f84a44dafb0ff20f02085d" -"checksum async-trait 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "750b1c38a1dfadd108da0f01c08f4cdc7ff1bb39b325f9c82cc972361780a6e1" +"checksum async-trait 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b" "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" "checksum autocfg 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -"checksum backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" -"checksum backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" +"checksum backtrace 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)" = "a5393cb2f40a6fae0014c9af00018e95846f3b241b331a6b7733c326d3e58108" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" -"checksum bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e84c238982c4b1e1ee668d136c510c67a13465279c0cb367ea6baf6310620a80" -"checksum bit-vec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f59bbe95d4e52a6398ec21238d31577f2b28a9d86807f06ca59d191d8440d0bb" +"checksum bit-set 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" +"checksum bit-vec 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f0dc55f2d8a1a85650ac47858bb001b4c0dd73d79e3c455a842925e68d29cd3" "checksum bitfield 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum blake2b_simd 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d8fb2d74254a3a0b5cac33ac9f8ed0e44aa50378d9dbb2e5d83bd21ed1dc2c8a" @@ -3208,7 +3199,7 @@ dependencies = [ "checksum broadcaster 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9c972e21e0d055a36cf73e4daae870941fe7a8abcd5ac3396aab9e4c126bd87" "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 bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +"checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" "checksum byte-pool 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9342e102eac8b1879fbedf9a7e0572c40b0cc5805b663c4d4ca791cae0bae221" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum bytecount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b92204551573580e078dc80017f36a213eb77a0450e4ddd8cfa0f3f2d1f0178f" @@ -3216,7 +3207,7 @@ dependencies = [ "checksum bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" "checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe" "checksum cast5 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ce5759b4c52ca74f9a98421817c882f1fd9b0071ae41cd61ab9f9d059c04fd6" -"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" +"checksum cc 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)" = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" "checksum cfb-mode 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "190e7b55d3a27cf8879becf61035a141cbc783f3258a41d16d1706719f991345" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4f426e64df1c3de26cbf44593c6ffff5dbfd43bbf9de0d075058558126b3fc73" @@ -3235,7 +3226,7 @@ dependencies = [ "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" "checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" -"checksum ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1" +"checksum ctor 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "cf6b25ee9ac1995c54d7adb2eff8cfffb7260bc774fb63c601ec65467f43cd9d" "checksum curve25519-dalek 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839" "checksum darling 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" "checksum darling_core 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" @@ -3248,7 +3239,7 @@ dependencies = [ "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -"checksum doc-comment 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "807e5847c39ad6a11eac66de492ed1406f76a260eb8656e8740cad9eabc69c27" +"checksum doc-comment 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" "checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3" "checksum ed25519-dalek 1.0.0-pre.3 (registry+https://github.com/rust-lang/crates.io-index)" = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" "checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" @@ -3261,74 +3252,75 @@ dependencies = [ "checksum encoding-index-singlebyte 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" "checksum encoding-index-tradchinese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" "checksum encoding_index_tests 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" -"checksum encoding_rs 0.8.22 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8d03faa7fe0c1431609dfad7bbe827af30f82e1e2ae6f7ee4fca6bd764bc28" +"checksum encoding_rs 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171" "checksum entities 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum error-chain 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" "checksum escaper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39da344028c2227132b2dfa7c186e2104ecc153467583d00ed9c398f9ff693b0" -"checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" -"checksum failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" +"checksum failure 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +"checksum failure_derive 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" "checksum fast_chemail 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "495a39d30d624c2caabe6312bfead73e7717692b44e0b32df168c275a2e8e9e4" -"checksum flate2 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" -"checksum futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c329ae8753502fb44ae4fc2b622fa2a94652c41e795143765ba0927f92ab780" -"checksum futures-channel 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c77d04ce8edd9cb903932b608268b3fffec4163dc053b3b402bf47eac1f1a8" -"checksum futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" -"checksum futures-executor 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f674f3e1bcb15b37284a90cedf55afdba482ab061c407a9c0ebbd0f3109741ba" -"checksum futures-io 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a638959aa96152c7a4cddf50fcb1e3fede0583b27157c26e67d6f99904090dc6" -"checksum futures-macro 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9a5081aa3de1f7542a794a397cde100ed903b0630152d0973479018fd85423a7" -"checksum futures-sink 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3466821b4bc114d95b087b850a724c6f83115e929bc88f1fa98a3304a944c8a6" -"checksum futures-task 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27" +"checksum futures 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1e05b85ec287aac0dc34db7d4a569323df697f9c55b99b15d6b4ef8cde49f613" +"checksum futures-channel 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f366ad74c28cca6ba456d95e6422883cfb4b252a83bed929c83abfdbbf2967d5" +"checksum futures-core 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" +"checksum futures-executor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "10d6bb888be1153d3abeb9006b11b02cf5e9b209fda28693c31ae1e4e012e314" +"checksum futures-io 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" +"checksum futures-macro 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39" +"checksum futures-sink 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f2032893cb734c7a05d85ce0cc8b8c4075278e93b24b66f9de99d6eb0fa8acc" +"checksum futures-task 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bdb66b5f09e22019b1ab0830f7785bcea8e7a42148683f99214f73f8ec21a626" "checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6" -"checksum futures-util 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5" +"checksum futures-util 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8764574ff08b701a084482c3c7031349104b07ac897393010494beaa18ce32c6" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" "checksum gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" +"checksum gimli 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum h2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9d5c295d1c0c68e4e42003d75f908f5e16a1edd1cbe0b0d02e4dc2006a384f47" +"checksum h2 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "79b7246d7e4b979c03fa093da39cfb3617a96bbeee6310af63991668d7e843ff" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +"checksum hermit-abi 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" "checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" "checksum hostname 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "21ceb46a83a85e824ef93669c8b390009623863b5c195d1ba747292c0c72f94e" -"checksum http 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b708cc7f06493459026f53b9a61a7a121a5d1ec6238dee58ea4941132b30156b" +"checksum http 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9" "checksum http-body 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" "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" +"checksum human-panic 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "39f357a500abcbd7c5f967c1d45c8838585b36743823b9d43488f24850534e36" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -"checksum hyper 0.13.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e7b15203263d1faa615f9337d79c1d37959439dc46c2b4faab33286fadc2a1c5" +"checksum hyper 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96816e1d921eca64d208a85aab4f7798455a8e34229ee5a88c935bdee1b78b14" "checksum hyper-tls 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3adcd308402b9553630734e9c36b77a7e48b3821251ca2493e8cd596763aafaa" "checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum image 0.22.5 (registry+https://github.com/rust-lang/crates.io-index)" = "08ed2ada878397b045454ac7cfb011d73132c59f31a955d230bd1f1c2e68eb4a" -"checksum image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b00861cbbb254a627d8acc0cec786b484297d896ab8f20fdc8e28536a3e918ef" +"checksum image-meta 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a2da7b4225d0954c9b8ba1a0dcec85be29f496cba4d85f9390426f810e3ab0d" "checksum imap-proto 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "16a6def1d5ac8975d70b3fd101d57953fe3278ef2ee5d7816cba54b1d1dfc22f" "checksum indexmap 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" -"checksum jpeg-decoder 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "0256f0aec7352539102a9efbcb75543227b7ab1117e0f95450023af730128451" -"checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +"checksum jpeg-decoder 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5b47b4c4e017b01abdc5bcc126d2d1002e5a75bbe3ce73f9f4f311a916363704" +"checksum js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7" "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 kv-log-macro 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c54d9f465d530a752e6ebdc217e081a7a614b48cb200f6f0aee21ba6bc9aabb" +"checksum kv-log-macro 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2a2d3beed37e5483887d81eb39de6de03a8346531410e1306ca48a9a89bd3a51" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lettre 0.9.2 (git+https://github.com/deltachat/lettre)" = "" "checksum lettre_email 0.9.2 (git+https://github.com/deltachat/lettre)" = "" "checksum lexical-core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d7043aa5c05dd34fb73b47acb8c3708eac428de4545ea3682ed2f11293ebd890" -"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" +"checksum libc 0.2.70 (registry+https://github.com/rust-lang/crates.io-index)" = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" "checksum libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" -"checksum libsqlite3-sys 0.17.1 (registry+https://github.com/rust-lang/crates.io-index)" = "266eb8c361198e8d1f682bc974e5d9e2ae90049fb1943890904d11dad7d4a77d" -"checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum libsqlite3-sys 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56d90181c2904c287e5390186be820e5ef311a3c62edebb7d6ca3d6a48ce041d" +"checksum linked-hash-map 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +"checksum lock_api 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" "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 lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" @@ -3337,15 +3329,15 @@ dependencies = [ "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" "checksum md-5 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a18af3dcaf2b0219366cdb4e2af65a6101457b415c3d1a5c71dd9c2b7c77b9c8" "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" -"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum memoffset 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b4fc2c02a7e374099d4ee95a193111f72d2110197fe200272371758f6c3643d8" "checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" "checksum mime_guess 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" "checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" -"checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" +"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430" +"checksum mio-uds 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum native-tls 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d" -"checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum net2 0.2.34 (registry+https://github.com/rust-lang/crates.io-index)" = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7" "checksum nix 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4dbdc256eaac2e3bd236d93ad999d3479ef775c863dbda3068c4006a92eec51b" "checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" @@ -3354,40 +3346,41 @@ dependencies = [ "checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746" "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" "checksum num-iter 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" -"checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" +"checksum num-rational 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +"checksum object 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9cbca9424c482ee628fa549d9c812e2cd22f1180b9222c9200fdfa6eb31aecb2" "checksum once_cell 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -"checksum openssl 0.10.28 (registry+https://github.com/rust-lang/crates.io-index)" = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" +"checksum openssl 0.10.29 (registry+https://github.com/rust-lang/crates.io-index)" = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-src 111.6.1+1.1.1d (registry+https://github.com/rust-lang/crates.io-index)" = "c91b04cb43c1a8a90e934e0cd612e2a5715d976d2d6cff4490278a0cddf35005" -"checksum openssl-sys 0.9.54 (registry+https://github.com/rust-lang/crates.io-index)" = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +"checksum openssl-src 111.9.0+1.1.1g (registry+https://github.com/rust-lang/crates.io-index)" = "a2dbe10ddd1eb335aba3780eb2eaa13e1b7b441d2562fd962398740927f39ec4" +"checksum openssl-sys 0.9.56 (registry+https://github.com/rust-lang/crates.io-index)" = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e" "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" "checksum packed_simd 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220" -"checksum parking_lot 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92e98c49ab0b7ce5b222f2cc9193fc4efe11c6d0bd4f648e374684a6857b1cfc" -"checksum parking_lot_core 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7582838484df45743c8434fbff785e8edf260c28748353d44bc0da32e0ceabf1" +"checksum parking_lot 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +"checksum parking_lot_core 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -"checksum pgp 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "96c659fb6141cb4b6bd2c50af03869b82789291770f0b035f36ab92eba5d8663" -"checksum pin-project 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7804a463a8d9572f13453c516a5faea534a2403d7ced2f0c7e100eeff072772c" -"checksum pin-project-internal 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "385322a45f2ecf3410c68d2a549a4a2685e8051d0f278e39743ff4e451cb9b3f" -"checksum pin-project-lite 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" -"checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" +"checksum pgp 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8172973101790c866e66966002bf1028d0df27bf6b3b29be86a6fd440d8a4285" +"checksum pin-project 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "81d480cb4e89522ccda96d0eed9af94180b7a5f93fb28f66e1fd7d68431663d1" +"checksum pin-project-internal 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "a82996f11efccb19b685b14b5df818de31c1edcee3daa256ab5775dd98e72feb" +"checksum pin-project-lite 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f7505eeebd78492e0f6108f7171c4948dbb120ee8119d9d77d0afa5469bef67f" +"checksum pin-utils 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" "checksum pkg-config 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" "checksum png 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef859a23054bbfee7811284275ae522f0434a3c8e7f4b74bd4a35ae7e1c4a283" "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074" -"checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" -"checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" -"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" -"checksum proptest 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "bf6147d103a7c9d7598f4105cf049b15c99e2ecd93179bf024f0fd349be5ada4" +"checksum proc-macro-hack 0.5.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63" +"checksum proc-macro-nested 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e946095f9d3ed29ec38de908c22f95d9ac008e424c7bcae54c75a79c527c694" +"checksum proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +"checksum proptest 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "01c477819b845fe023d33583ebf10c9f62518c8d79a0960ba5c36d6ac8a55a5b" "checksum pulldown-cmark 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eef52fac62d0ea7b9b4dc7da092aa64ea7ec3d90af6679422d3d7e0e14b6ee15" "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" "checksum quick-xml 0.17.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe1e430bdcf30c9fdc25053b9c459bb1a4672af4617b6c783d7d91dc17c6bbb0" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum quote 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "42934bc9c8ab0d3b273a16d8551c8f0fcff46be73276ca083ec2414c15c4ba5e" "checksum quoted_printable 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "47b080c5db639b292ac79cbd34be0cfc5d36694768d8341109634d90b86930e2" "checksum r2d2 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1497e40855348e4a8a40767d8e55174bce1e445a3ac9254ad44ad468ee0485af" "checksum r2d2_sqlite 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b061f5b16692bbe81eeb260f92e6fc7d13aea455c4cbe67f5c4aa20aa92d1d9e" @@ -3409,7 +3402,7 @@ dependencies = [ "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" -"checksum regex 1.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8900ebc1363efa7ea1c399ccc32daed870b4002651e0bed86e72d501ebbe0048" +"checksum regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692" "checksum regex-syntax 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rental 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" @@ -3423,29 +3416,29 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusty-fork 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3dd93264e10c577503e926bd1430193eeb5d21b059148910082245309b424fae" "checksum rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0f47ea1ceb347d2deae482d655dc8eef4bd82363d3329baffa3818bd76fea48b" -"checksum ryu 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" +"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" "checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" "checksum sanitize-filename 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23fd0fec94ec480abfd86bb8f4f6c57e0efb36dac5c852add176ea7b04c74801" -"checksum schannel 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" -"checksum scheduled-thread-pool 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f5de7bc31f28f8e6c28df5e1bf3d10610f5fdc14cc95f272853512c70a2bd779" +"checksum schannel 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)" = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +"checksum scheduled-thread-pool 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0988d7fdf88d5e5fcf5923a0f1e8ab345f3e98ab4bc6bc45a2d5ff7f7458fbf6" "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" -"checksum security-framework 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" -"checksum security-framework-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +"checksum security-framework 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +"checksum security-framework-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" "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 serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -"checksum serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)" = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c" +"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984" +"checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" "checksum sha-1 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" "checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" "checksum skeptic 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fb8ed853fdc19ce09752d63f3a2e5b5158aeb261520cd75eb618bd60305165" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -"checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" -"checksum snafu 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "546db9181bce2aa22ed883c33d65603b76335b4c2533a98289f54265043de7a1" -"checksum snafu-derive 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc75da2e0323f297402fd9c8fdba709bb04e4c627cbe31d19a2c91fc8d9f0e2" +"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4" +"checksum snafu 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7f5aed652511f5c9123cf2afbe9c244c29db6effa2abb05c866e965c82405ce" +"checksum snafu-derive 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ebf8f7d5720104a9df0f7076a8682024e958bba0fe9848767bb44f251f3648e9" "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" @@ -3456,28 +3449,27 @@ dependencies = [ "checksum strum_macros 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0054a7df764039a6cd8592b9de84be4bec368ff081d203a7d5371cbfa8e65c81" "checksum subtle 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum syn 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "4696caa4048ac7ce2bcd2e484b3cef88c1004e41b8e945a277e2c25dc0b72060" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "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" -"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -"checksum thiserror 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee14bf8e6767ab4c687c9e8bc003879e042a96fd67a3ba5934eadb6536bef4db" -"checksum thiserror-impl 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "a7b51e1fbc44b5a0840be594fbc0f960be09050f2617e61e6aa43bef97cd3ef4" +"checksum thiserror 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "467e5ff447618a916519a4e0d62772ab14f434897f3d63f05d8700ef1e9b22c1" +"checksum thiserror-impl 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "e63c1091225b9834089b429bc4a2e01223470e3183e891582909e9d1c4cb55d9" "checksum thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7da3caa820d0308c84c8654f6cafd81cc3195d45433311cbe22fcf44fc8be071" "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum tokio 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "0fa5e81d6bc4e67fe889d5783bd2a128ab2e0cfa487e0be16b6a8d177b101616" -"checksum tokio-tls 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7bde02a3a5291395f59b06ec6945a3077602fac2b07eeeaf0dee2122f3619828" -"checksum tokio-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" -"checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" +"checksum time 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +"checksum tokio 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "05c1d570eb1a36f0345a5ce9c6c6e665b70b73d11236912c0b477616aeec47b1" +"checksum tokio-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +"checksum tokio-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum tower-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum try_from 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "283d3b89e1368717881a9d51dad843cc435380d8109c9e47d38780a324698d8b" "checksum twofish 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum typenum 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" "checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" @@ -3488,7 +3480,6 @@ dependencies = [ "checksum unsafe-any 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" "checksum url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" -"checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" "checksum uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" @@ -3498,20 +3489,19 @@ dependencies = [ "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" "checksum want 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -"checksum wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" -"checksum wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" -"checksum wasm-bindgen-futures 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "457414a91863c0ec00090dba537f88ab955d93ca6555862c29b6d860990b8a8a" -"checksum wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" -"checksum wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" -"checksum wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" -"checksum web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +"checksum wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551" +"checksum wasm-bindgen-backend 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94" +"checksum wasm-bindgen-futures 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "8a369c5e1dfb7569e14d62af4da642a3cbc2f9a3652fe586e26ac22222aa4b04" +"checksum wasm-bindgen-macro 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776" +"checksum wasm-bindgen-macro-support 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a" +"checksum wasm-bindgen-shared 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad" +"checksum web-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)" = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80" +"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" "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 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" diff --git a/Cargo.toml b/Cargo.toml index a40e166cb..9d9e5577c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat" -version = "1.28.0" +version = "1.32.0" authors = ["Delta Chat Developers (ML) "] edition = "2018" license = "MPL-2.0" @@ -25,15 +25,13 @@ email = { git = "https://github.com/deltachat/rust-email", branch = "master" } lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" } async-imap = { git = "https://github.com/async-email/async-imap", branch = "feat/send" } async-native-tls = "0.3.1" -async-std = { git = "https://github.com/async-rs/async-std", version = "1.5", features = ["unstable"] } +async-std = { git = "https://github.com/async-rs/async-std", rev = "3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58", features = ["unstable"] } base64 = "0.11" charset = "0.1" percent-encoding = "2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" chrono = "0.4.6" -failure = "0.1.5" -failure_derive = "0.1.5" indexmap = "1.3.0" lazy_static = "1.4.0" regex = "1.1.6" @@ -59,19 +57,21 @@ native-tls = "0.2.3" image = { version = "0.22.4", default-features=false, features = ["gif_codec", "jpeg", "ico", "png_codec", "pnm", "webp", "bmp"] } futures = "0.3.4" crossbeam-queue = "0.2.1" - +thiserror = "1.0.14" +anyhow = "1.0.28" pretty_env_logger = { version = "0.3.1", optional = true } log = {version = "0.4.8", optional = true } rustyline = { version = "4.1.0", optional = true } ansi_term = { version = "0.12.1", optional = true } +async-trait = "0.1.31" [dev-dependencies] tempfile = "3.0" pretty_assertions = "0.6.1" pretty_env_logger = "0.3.0" proptest = "0.9.4" -async-std = { git = "https://github.com/async-rs/async-std", version = "1.5", features = ["unstable", "attributes"] } +async-std = { git = "https://github.com/async-rs/async-std", rev = "3ff9e98f20a193eb63e43fb9d71f9d60c33f6d58", features = ["unstable", "attributes"] } [workspace] members = [ diff --git a/README.md b/README.md index 50c244b14..5129154db 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,9 @@ $ curl https://sh.rustup.rs -sSf | sh Compile and run Delta Chat Core command line utility, using `cargo`: ``` -$ RUST_LOG=info cargo run --example repl --features repl -- /path/to/db +$ RUST_LOG=info cargo run --example repl --features repl -- ~/deltachat-db ``` +where ~/deltachat-db is the database file. Delta Chat will create it if it does not exist. Configure your account (if not already configured): diff --git a/ci_scripts/ci_upload.sh b/ci_scripts/ci_upload.sh index bb610aa53..e357b9617 100755 --- a/ci_scripts/ci_upload.sh +++ b/ci_scripts/ci_upload.sh @@ -37,7 +37,7 @@ echo ----------------------- # Bundle external shared libraries into the wheels pushd $WHEELHOUSEDIR -pip3 install -U pip +pip3 install -U pip setuptools pip3 install devpi-client devpi use https://m.devpi.net devpi login dc --password $DEVPI_LOGIN diff --git a/ci_scripts/cleanup_devpi_indices.py b/ci_scripts/cleanup_devpi_indices.py index 628c47d84..8e1eaa9ee 100644 --- a/ci_scripts/cleanup_devpi_indices.py +++ b/ci_scripts/cleanup_devpi_indices.py @@ -48,7 +48,7 @@ def run(): projectnames = get_projectnames(baseurl, username, indexname) if indexname == "master" or not indexname: continue - assert projectnames == ["deltachat"] + clear_index = not projectnames for projectname in projectnames: dates = get_release_dates(baseurl, username, indexname, projectname) if not dates: @@ -60,8 +60,11 @@ def run(): date = datetime.datetime(*max(dates)) if (datetime.datetime.now() - date) > datetime.timedelta(days=MAXDAYS): assert username and indexname - url = baseurl + username + "/" + indexname - subprocess.check_call(["devpi", "index", "-y", "--delete", url]) + clear_index = True + break + if clear_index: + url = baseurl + username + "/" + indexname + subprocess.check_call(["devpi", "index", "-y", "--delete", url]) diff --git a/deltachat-ffi/Cargo.toml b/deltachat-ffi/Cargo.toml index 63c5bbd4c..9aa19c8a6 100644 --- a/deltachat-ffi/Cargo.toml +++ b/deltachat-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deltachat_ffi" -version = "1.28.0" +version = "1.32.0" description = "Deltachat FFI" authors = ["Delta Chat Developers (ML) "] edition = "2018" @@ -19,9 +19,10 @@ deltachat = { path = "../", default-features = false } libc = "0.2" human-panic = "1.0.1" num-traits = "0.2.6" -failure = "0.1.6" serde_json = "1.0" async-std = "1.5.0" +anyhow = "1.0.28" +thiserror = "1.0.14" [features] default = ["vendored", "nightly"] diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 8adcf3a8c..e8a231386 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -42,7 +42,7 @@ typedef struct _dc_event dc_event_t; * uintptr_t event_handler_func(dc_context_t* context, int event, * uintptr_t data1, uintptr_t data2) * { - * return 0; // for unhandled events, it is always safe to return 0 + * return 0; * } * * dc_context_t* context = dc_context_new(event_handler_func, NULL, NULL); @@ -236,7 +236,7 @@ void dc_event_unref (dc_event_t* event); * otherwise! * - The callback SHOULD return _fast_, for GUI updates etc. you should * post yourself an asynchronous message to your GUI thread, if needed. - * - If not mentioned otherweise, the callback should return 0. + * - events do not expect a return value, just always return 0. * @param userdata can be used by the client for any purpuse. He finds it * later in dc_get_userdata(). * @param os_name is only for decorative use @@ -349,7 +349,8 @@ char* dc_get_blobdir (const dc_context_t* context); * - `smtp_certificate_checks` = how to check SMTP certificates, one of the @ref DC_CERTCK flags, defaults to #DC_CERTCK_AUTO (0) * - `displayname` = Own name to use when sending messages. MUAs are allowed to spread this way eg. using CC, defaults to empty * - `selfstatus` = Own status to display eg. in email footers, defaults to a standard text - * - `selfavatar` = File containing avatar. Will be copied to blob directory. + * - `selfavatar` = File containing avatar. Will immediately be copied to the + * `blobdir`; the original image will not be needed anymore. * NULL to remove the avatar. * It is planned for future versions * to send this image together with the next messages. @@ -384,10 +385,20 @@ char* dc_get_blobdir (const dc_context_t* context); * >=1=seconds, after which messages are deleted automatically from the device. * Messages in the "saved messages" chat (see dc_chat_is_self_talk()) are skipped. * Messages are deleted whether they were seen or not, the UI should clearly point that out. + * See also dc_estimate_deletion_cnt(). * - `delete_server_after` = 0=do not delete messages from server automatically (default), * >=1=seconds, after which messages are deleted automatically from the server. * "Saved messages" are deleted from the server as well as * emails matching the `show_emails` settings above, the UI should clearly point that out. + * See also dc_estimate_deletion_cnt(). + * - `media_quality` = DC_MEDIA_QUALITY_BALANCED (0) = + * good outgoing images/videos/voice quality at reasonable sizes (default) + * DC_MEDIA_QUALITY_WORSE (1) + * allow worse images/videos/voice quality to gain smaller sizes, + * suitable for providers or areas known to have a bad connection. + * In contrast to other options, the implementation of this option is currently up to the UIs; + * this may change in future, however, + * having the option in the core allows provider-specific-defaults already today. * * If you want to retrieve a value, use dc_get_config(). * @@ -665,8 +676,10 @@ int dc_preconfigure_keypair (dc_context_t* context, const cha * if DC_GCL_ARCHIVED_ONLY is not set, only unarchived chats are returned and * the pseudo-chat DC_CHAT_ID_ARCHIVED_LINK is added if there are _any_ archived * chats - * - the flag DC_GCL_FOR_FORWARDING sorts "Saved messages" to the top of the chatlist, + * - the flag DC_GCL_FOR_FORWARDING sorts "Saved messages" to the top of the chatlist + * and hides the "Device chat" and the deaddrop. * typically used on forwarding, may be combined with DC_GCL_NO_SPECIALS + * to also hide the archive link. * - if the flag DC_GCL_NO_SPECIALS is set, deaddrop and archive link are not added * to the list (may be used eg. for selecting chats on forwarding, the flag is * not needed when DC_GCL_ARCHIVED_ONLY is already set) @@ -1026,6 +1039,7 @@ int dc_get_fresh_msg_cnt (dc_context_t* context, uint32_t ch * by the dc_set_config()-options `delete_device_after` or `delete_server_after`. * This is typically used to show the estimated impact to the user before actually enabling ephemeral messages. * + * @memberof dc_context_t * @param context The context object as returned from dc_context_new(). * @param from_server 1=Estimate deletion count for server, 0=Estimate deletion count for device * @param seconds Count messages older than the given number of seconds. @@ -1329,8 +1343,10 @@ int dc_set_chat_name (dc_context_t* context, uint32_t ch * @memberof dc_context_t * @param context The context as created by dc_context_new(). * @param chat_id The chat ID to set the image for. - * @param image Full path of the image to use as the group image. If you pass NULL here, - * the group image is deleted (for promoted groups, all members are informed about this change anyway). + * @param image Full path of the image to use as the group image. The image will immediately be copied to the + * `blobdir`; the original image will not be needed anymore. + * If you pass NULL here, the group image is deleted (for promoted groups, all members are informed about + * this change anyway). * @return 1=success, 0=error */ int dc_set_chat_profile_image (dc_context_t* context, uint32_t chat_id, const char* image); @@ -1503,7 +1519,7 @@ int dc_may_be_valid_addr (const char* addr); /** * 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 get a list of all known and unblocked contacts, use dc_get_contacts(). * * To validate an e-mail address independently of the contact database * use dc_may_be_valid_addr(). @@ -1511,7 +1527,8 @@ int dc_may_be_valid_addr (const char* addr); * @memberof dc_context_t * @param context The context object as created by dc_context_new(). * @param addr The e-mail-address to check. - * @return 1=address is a contact in use, 0=address is not a contact in use. + * @return Contact ID of the contact belonging to the e-mail-address + * or 0 if there is no contact that is or was introduced by an accepted contact. */ uint32_t dc_lookup_contact_id_by_addr (dc_context_t* context, const char* addr); @@ -2543,19 +2560,6 @@ int dc_chat_get_type (const dc_chat_t* chat); char* dc_chat_get_name (const dc_chat_t* chat); -/* - * Get a subtitle for a chat. The subtitle is eg. the email-address or the - * number of group members. - * - * Deprecated function. Subtitles should be created in the ui - * where plural forms and other specials can be handled more gracefully. - * - * @param chat The chat object to calulate the subtitle for. - * @return Subtitle as a string. Must be released using dc_str_unref() after usage. Never NULL. - */ -char* dc_chat_get_subtitle (const dc_chat_t* chat); - - /** * Get the chat's profile image. * For groups, this is the image set by any group member @@ -3876,8 +3880,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * 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, - * so there is no need to add a "case" for every event. * * @addtogroup DC_EVENT * @{ @@ -3892,7 +3894,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_INFO 100 @@ -3903,7 +3904,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_SMTP_CONNECTED 101 @@ -3914,7 +3914,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_IMAP_CONNECTED 102 @@ -3924,7 +3923,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_SMTP_MESSAGE_SENT 103 @@ -3934,7 +3932,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_IMAP_MESSAGE_DELETED 104 @@ -3944,7 +3941,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_IMAP_MESSAGE_MOVED 105 @@ -3954,7 +3950,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) folder name. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_IMAP_FOLDER_EMPTIED 106 @@ -3964,7 +3959,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) path name * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_NEW_BLOB_FILE 150 @@ -3974,7 +3968,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) path name * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_DELETED_BLOB_FILE 151 @@ -3987,7 +3980,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 0 * @param data2 (const char*) Warning string in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_WARNING 300 @@ -4010,7 +4002,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * Some error strings are taken from dc_set_stock_translation(), * however, most error strings will be in english language. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_ERROR 400 @@ -4034,7 +4025,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * 0=subsequent network error, should be logged only * @param data2 (const char*) Error string, always set, never NULL. * Must not be unref'd or modified and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_ERROR_NETWORK 401 @@ -4050,7 +4040,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data2 (const char*) Info string in english language. * Must not be unref'd or modified * and is valid only until the callback returns. - * @return 0 */ #define DC_EVENT_ERROR_SELF_NOT_IN_GROUP 410 @@ -4064,7 +4053,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id for single added messages * @param data2 (int) msg_id for single added messages - * @return 0 */ #define DC_EVENT_MSGS_CHANGED 2000 @@ -4077,7 +4065,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id * @param data2 (int) msg_id - * @return 0 */ #define DC_EVENT_INCOMING_MSG 2005 @@ -4088,7 +4075,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id * @param data2 (int) msg_id - * @return 0 */ #define DC_EVENT_MSG_DELIVERED 2010 @@ -4099,7 +4085,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id * @param data2 (int) msg_id - * @return 0 */ #define DC_EVENT_MSG_FAILED 2012 @@ -4110,7 +4095,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id * @param data2 (int) msg_id - * @return 0 */ #define DC_EVENT_MSG_READ 2015 @@ -4123,7 +4107,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) chat_id * @param data2 0 - * @return 0 */ #define DC_EVENT_CHAT_MODIFIED 2020 @@ -4133,7 +4116,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) If not 0, this is the contact_id of an added contact that should be selected. * @param data2 0 - * @return 0 */ #define DC_EVENT_CONTACTS_CHANGED 2030 @@ -4146,7 +4128,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * If the locations of several contacts have been changed, * eg. after calling dc_delete_all_locations(), this parameter is set to 0. * @param data2 0 - * @return 0 */ #define DC_EVENT_LOCATION_CHANGED 2035 @@ -4156,7 +4137,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done * @param data2 0 - * @return 0 */ #define DC_EVENT_CONFIGURE_PROGRESS 2041 @@ -4166,7 +4146,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * * @param data1 (int) 0=error, 1-999=progress in permille, 1000=success and done * @param data2 0 - * @return 0 */ #define DC_EVENT_IMEX_PROGRESS 2051 @@ -4181,7 +4160,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data1 (const char*) Path and file name. * Must not be unref'd or modified and is valid only until the callback returns. * @param data2 0 - * @return 0 */ #define DC_EVENT_IMEX_FILE_WRITTEN 2052 @@ -4199,7 +4177,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * 600=vg-/vc-request-with-auth received, vg-member-added/vc-contact-confirm sent, typically shown as "bob@addr verified". * 800=vg-member-added-received received, shown as "bob@addr securely joined GROUP", only sent for the verified-group-protocol. * 1000=Protocol finished for this contact. - * @return 0 */ #define DC_EVENT_SECUREJOIN_INVITER_PROGRESS 2060 @@ -4215,21 +4192,9 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); * @param data2 (int) Progress as: * 400=vg-/vc-request-with-auth sent, typically shown as "alice@addr verified, introducing myself." * (Bob has verified alice and waits until Alice does the same for him) - * @return 0 */ #define DC_EVENT_SECUREJOIN_JOINER_PROGRESS 2061 - -/** - * This event is sent out to the inviter when a joiner successfully joined a group. - * - * @param data1 (int) chat_id - * @param data2 (int) contact_id - * @return 0 - */ -#define DC_EVENT_SECUREJOIN_MEMBER_ADDED 2062 - - /** * @} */ @@ -4245,8 +4210,6 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); #define DC_EVENT_DATA2_IS_STRING(e) ((e)>=100 && (e)<=499) #define DC_EVENT_RETURNS_INT(e) ((e)==DC_EVENT_IS_OFFLINE) // not used anymore #define DC_EVENT_RETURNS_STRING(e) ((e)==DC_EVENT_GET_STRING) // not used anymore -char* dc_get_version_str (void); // deprecated -void dc_array_add_id (dc_array_t*, uint32_t); // deprecated #define dc_archive_chat(a,b,c) dc_set_chat_visibility((a), (b), (c)? 1 : 0) // not used anymore #define dc_chat_get_archived(a) (dc_chat_get_visibility((a))==1? 1 : 0) // not used anymore @@ -4258,6 +4221,14 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca #define DC_SHOW_EMAILS_ACCEPTED_CONTACTS 1 #define DC_SHOW_EMAILS_ALL 2 + +/* + * Values for dc_get|set_config("media_quality") + */ +#define DC_MEDIA_QUALITY_BALANCED 0 +#define DC_MEDIA_QUALITY_WORSE 1 + + /* * Values for dc_get|set_config("key_gen_type") */ @@ -4376,8 +4347,6 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca #define DC_STR_NOMESSAGES 1 #define DC_STR_SELF 2 #define DC_STR_DRAFT 3 -#define DC_STR_MEMBER 4 -#define DC_STR_CONTACT 6 #define DC_STR_VOICEMESSAGE 7 #define DC_STR_DEADDROP 8 #define DC_STR_IMAGE 9 @@ -4409,7 +4378,6 @@ void dc_array_add_id (dc_array_t*, uint32_t); // depreca #define DC_STR_STARREDMSGS 41 #define DC_STR_AC_SETUP_MSG_SUBJECT 42 #define DC_STR_AC_SETUP_MSG_BODY 43 -#define DC_STR_SELFTALK_SUBTITLE 50 #define DC_STR_CANNOT_LOGIN 60 #define DC_STR_SERVER_RESPONSE 61 #define DC_STR_MSGACTIONBYUSER 62 diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 5b65267aa..c5171c764 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -13,7 +13,7 @@ extern crate human_panic; extern crate num_traits; extern crate serde_json; -use std::collections::HashMap; +use std::collections::BTreeMap; use std::convert::TryInto; use std::ffi::CString; use std::fmt::Write; @@ -22,13 +22,14 @@ use std::str::FromStr; use std::sync::{Arc, RwLock}; use std::time::{Duration, SystemTime}; +use anyhow::anyhow; use async_std::task::block_on; use libc::uintptr_t; use num_traits::{FromPrimitive, ToPrimitive}; use deltachat::chat::{ChatId, ChatVisibility, MuteDuration}; use deltachat::constants::DC_MSG_ID_LAST_SPECIAL; -use deltachat::contact::Contact; +use deltachat::contact::{Contact, Origin}; use deltachat::context::Context; use deltachat::key::DcKey; use deltachat::message::MsgId; @@ -177,7 +178,7 @@ macro_rules! try_inner_async { let $name = ctx; $block.await } - None => Err(failure::err_msg("context not open")), + None => Err(anyhow!("context not open")), } }) }}; @@ -416,7 +417,7 @@ pub unsafe extern "C" fn dc_get_info(context: *mut dc_context_t) -> *mut libc::c } fn render_info( - info: HashMap<&'static str, String>, + info: BTreeMap<&'static str, String>, ) -> std::result::Result { let mut res = String::new(); for (key, value) in &info { @@ -449,11 +450,6 @@ pub unsafe extern "C" fn dc_get_oauth2_url( .unwrap_or_else(|_| ptr::null_mut()) } -#[no_mangle] -pub unsafe extern "C" fn dc_get_version_str() -> *mut libc::c_char { - context::get_version_str().strdup() -} - #[no_mangle] pub unsafe extern "C" fn dc_configure(context: *mut dc_context_t) { if context.is_null() { @@ -656,14 +652,6 @@ unsafe fn translate_event(event: Event) -> *mut dc_event_t { data1: contact_id as uintptr_t, data2: progress as uintptr_t, }, - Event::SecurejoinMemberAdded { - chat_id, - contact_id, - } => EventWrapper { - event_id, - data1: chat_id.to_u32() as uintptr_t, - data2: contact_id as uintptr_t, - }, }; Box::into_raw(Box::new(wrapper)) @@ -1072,12 +1060,12 @@ pub unsafe extern "C" fn dc_estimate_deletion_cnt( return 0; } let ffi_context = &*context; - ffi_context - .with_inner(|ctx| { - message::estimate_deletion_cnt(ctx, from_server != 0, seconds).unwrap_or(0) - as libc::c_int - }) - .unwrap_or(0) + with_inner_async!(ffi_context, ctx, async move { + message::estimate_deletion_cnt(ctx, from_server != 0, seconds) + .await + .unwrap_or(0) as libc::c_int + }) + .unwrap_or(0) } #[no_mangle] @@ -1700,7 +1688,7 @@ pub unsafe extern "C" fn dc_lookup_contact_id_by_addr( with_inner_async!( ffi_context, ctx, - Contact::lookup_id_by_addr(&ctx, to_string_lossy(addr)) + Contact::lookup_id_by_addr(&ctx, to_string_lossy(addr), Origin::IncomingReplyTo) ) .unwrap_or(0) } @@ -2171,16 +2159,6 @@ pub unsafe extern "C" fn dc_array_unref(a: *mut dc_array::dc_array_t) { Box::from_raw(a); } -#[no_mangle] -pub unsafe extern "C" fn dc_array_add_id(array: *mut dc_array_t, item: libc::c_uint) { - if array.is_null() { - eprintln!("ignoring careless call to dc_array_add_id()"); - return; - } - - (*array).add_id(item); -} - #[no_mangle] pub unsafe extern "C" fn dc_array_get_cnt(array: *const dc_array_t) -> libc::size_t { if array.is_null() { @@ -2511,20 +2489,6 @@ pub unsafe extern "C" fn dc_chat_get_name(chat: *mut dc_chat_t) -> *mut libc::c_ ffi_chat.chat.get_name().strdup() } -#[no_mangle] -pub unsafe extern "C" fn dc_chat_get_subtitle(chat: *mut dc_chat_t) -> *mut libc::c_char { - if chat.is_null() { - eprintln!("ignoring careless call to dc_chat_get_subtitle()"); - return "".strdup(); - } - let ffi_chat = &*chat; - let ffi_context: &ContextWrapper = &*ffi_chat.context; - - with_inner_async!(ffi_context, ctx, ffi_chat.chat.get_subtitle(&ctx)) - .map(|s| s.strdup()) - .unwrap_or_else(|_| "".strdup()) -} - #[no_mangle] pub unsafe extern "C" fn dc_chat_get_profile_image(chat: *mut dc_chat_t) -> *mut libc::c_char { if chat.is_null() { @@ -2848,7 +2812,7 @@ pub unsafe extern "C" fn dc_msg_get_file(msg: *mut dc_msg_t) -> *mut libc::c_cha ffi_msg .message .get_file(ctx) - .map(|p| p.strdup()) + .map(|p| p.to_string_lossy().strdup()) .unwrap_or_else(|| "".strdup()) }) .unwrap_or_else(|_| "".strdup()) diff --git a/deltachat-ffi/src/string.rs b/deltachat-ffi/src/string.rs index 256b110b0..db9baa7bb 100644 --- a/deltachat-ffi/src/string.rs +++ b/deltachat-ffi/src/string.rs @@ -1,4 +1,3 @@ -use failure::Fail; use std::ffi::{CStr, CString}; use std::ptr; @@ -31,13 +30,13 @@ unsafe fn dc_strdup(s: *const libc::c_char) -> *mut libc::c_char { } /// Error type for the [OsStrExt] trait -#[derive(Debug, Fail, PartialEq)] +#[derive(Debug, PartialEq, thiserror::Error)] pub(crate) enum CStringError { /// The string contains an interior null byte - #[fail(display = "String contains an interior null byte")] + #[error("String contains an interior null byte")] InteriorNullByte, /// The string is not valid Unicode - #[fail(display = "String is not valid unicode")] + #[error("String is not valid unicode")] NotUnicode, } diff --git a/deltachat_derive/src/lib.rs b/deltachat_derive/src/lib.rs index f911b9fa9..664581464 100644 --- a/deltachat_derive/src/lib.rs +++ b/deltachat_derive/src/lib.rs @@ -3,7 +3,6 @@ extern crate proc_macro; use crate::proc_macro::TokenStream; use quote::quote; -use syn; // For now, assume (not check) that these macroses are applied to enum without // data. If this assumption is violated, compiler error will point to diff --git a/examples/repl/cmdline.rs b/examples/repl/cmdline.rs index 8bdffd0b6..ca0a74a7e 100644 --- a/examples/repl/cmdline.rs +++ b/examples/repl/cmdline.rs @@ -1,7 +1,7 @@ use std::str::FromStr; +use anyhow::{bail, ensure}; use async_std::path::Path; - use deltachat::chat::{self, Chat, ChatId, ChatVisibility}; use deltachat::chatlist::*; use deltachat::constants::*; @@ -92,7 +92,7 @@ async fn reset_tables(context: &Context, bits: i32) { }); } -async fn poke_eml_file(context: &Context, filename: impl AsRef) -> Result<(), Error> { +async fn poke_eml_file(context: &Context, filename: impl AsRef) -> Result<(), anyhow::Error> { let data = dc_read_file(context, filename).await?; if let Err(err) = dc_receive_imf(context, &data, "import", 0, false).await { @@ -292,11 +292,7 @@ fn chat_prefix(chat: &Chat) -> &'static str { chat.typ.into() } -pub async fn cmdline( - context: Context, - line: &str, - chat_id: &mut ChatId, -) -> Result<(), failure::Error> { +pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Result<(), Error> { let mut sel_chat = if !chat_id.is_unset() { Chat::load_from_db(&context, *chat_id).await.ok() } else { diff --git a/examples/repl/main.rs b/examples/repl/main.rs index 96a39cec3..9df98fdc4 100644 --- a/examples/repl/main.rs +++ b/examples/repl/main.rs @@ -7,13 +7,16 @@ #[macro_use] extern crate deltachat; #[macro_use] -extern crate failure; +extern crate lazy_static; +#[macro_use] +extern crate rusqlite; use std::borrow::Cow::{self, Borrowed, Owned}; use std::io::{self, Write}; use std::process::Command; use ansi_term::Color; +use anyhow::{bail, Error}; use async_std::path::Path; use deltachat::chat::ChatId; use deltachat::config; @@ -269,10 +272,10 @@ impl Highlighter for DcHelper { impl Helper for DcHelper {} -async fn start(args: Vec) -> Result<(), failure::Error> { +async fn start(args: Vec) -> Result<(), Error> { if args.len() < 2 { println!("Error: Bad arguments, expected [db-name]."); - return Err(format_err!("No db-name specified")); + bail!("No db-name specified"); } let context = Context::new("CLI".into(), Path::new(&args[1]).to_path_buf()).await?; @@ -353,7 +356,7 @@ async fn handle_cmd( line: &str, ctx: Context, selected_chat: &mut ChatId, -) -> Result { +) -> Result { let mut args = line.splitn(2, ' '); let arg0 = args.next().unwrap_or_default(); let arg1 = args.next().unwrap_or_default(); @@ -417,7 +420,7 @@ async fn handle_cmd( Ok(ExitResult::Continue) } -fn main() -> Result<(), failure::Error> { +fn main() -> Result<(), Error> { let _ = pretty_env_logger::try_init(); let args = std::env::args().collect(); diff --git a/python/CHANGELOG b/python/CHANGELOG index 7e6038376..a32c2def9 100644 --- a/python/CHANGELOG +++ b/python/CHANGELOG @@ -1,3 +1,15 @@ +0.900.0 (DRAFT) +--------------- + +- refactored internals to use plugin-approach + +- introduced PerAccount and Global hooks that plugins can implement + +- introduced `ac_member_added()` and `ac_member_removed()` plugin events. + +- introduced two documented examples for an echo and a group-membership + tracking plugin. + 0.800.0 ------- diff --git a/python/doc/api.rst b/python/doc/api.rst index d82364b75..049bc2928 100644 --- a/python/doc/api.rst +++ b/python/doc/api.rst @@ -2,10 +2,6 @@ high level API reference ======================== -.. note:: - - This API is work in progress and may change in versions prior to 1.0. - - :class:`deltachat.account.Account` (your main entry point, creates the other classes) - :class:`deltachat.contact.Contact` diff --git a/python/doc/capi.rst b/python/doc/capi.rst deleted file mode 100644 index 67cbf0884..000000000 --- a/python/doc/capi.rst +++ /dev/null @@ -1,7 +0,0 @@ - -C deltachat interface -===================== - -See :doc:`lapi` for accessing many of the below functions -through the ``deltachat.capi.lib`` namespace. - diff --git a/python/doc/examples.rst b/python/doc/examples.rst index 94e15e754..dd8f2a023 100644 --- a/python/doc/examples.rst +++ b/python/doc/examples.rst @@ -1,37 +1,60 @@ - examples ======== - -Playing around on the commandline ----------------------------------- - Once you have :doc:`installed deltachat bindings ` -you can start playing from the python interpreter commandline. -For example you can type ``python`` and then:: +you need email/password credentials for an IMAP/SMTP account. +Delta Chat developers and the CI system use a special URL to create +temporary e-mail accounts on [testrun.org](https://testrun.org) for testing. - # instantiate and configure deltachat account - import deltachat - ac = deltachat.Account("/tmp/db") +Receiving a Chat message from the command line +---------------------------------------------- - # start configuration activity and smtp/imap threads - ac.start_threads() - ac.configure(addr="test2@hq5.merlinux.eu", mail_pw="********") +Here is a simple bot that: - # create a contact and send a message - contact = ac.create_contact("someother@email.address") - chat = ac.create_chat_by_contact(contact) - chat.send_text("hi from the python interpreter command line") +- receives a message and sends back ("echoes") a message -Checkout our :doc:`api` for the various high-level things you can do -to send/receive messages, create contacts and chats. +- terminates the bot if the message `/quit` is sent +.. include:: ../examples/echo_and_quit.py + :literal: -Looking at a real example +With this file in your working directory you can run the bot +by specifying a database path, an e-mail address and password of +a SMTP-IMAP account:: + + $ cd examples + $ python echo_and_quit.py /tmp/db --email ADDRESS --password PASSWORD + +While this process is running you can start sending chat messages +to `ADDRESS`. + +Track member additions and removals in a group +---------------------------------------------- + +Here is a simple bot that: + +- echoes messages sent to it + +- tracks if configuration completed + +- tracks member additions and removals for all chat groups + +.. include:: ../examples/group_tracking.py + :literal: + +With this file in your working directory you can run the bot +by specifying a database path, an e-mail address and password of +a SMTP-IMAP account:: + + python group_tracking.py --email ADDRESS --password PASSWORD /tmp/db + +When this process is running you can start sending chat messages +to `ADDRESS`. + +Writing bots for real ------------------------- The `deltabot repository `_ -contains a real-life example of Python bindings usage. - +contains a little framework for writing deltachat bots in Python. diff --git a/python/doc/index.rst b/python/doc/index.rst index 1c27b9317..b5643eee4 100644 --- a/python/doc/index.rst +++ b/python/doc/index.rst @@ -4,8 +4,9 @@ deltachat python bindings The ``deltachat`` Python package provides two layers of bindings for the core Rust-library of the https://delta.chat messaging ecosystem: -- :doc:`api` is a high level interface to deltachat-core which aims - to be memory safe and thoroughly tested through continous tox/pytest runs. +- :doc:`api` is a high level interface to deltachat-core. + +- :doc:`plugins` is a brief introduction into implementing plugin hooks. - :doc:`lapi` is a lowlevel CFFI-binding to the `Rust Core `_. @@ -28,6 +29,7 @@ getting started changelog api lapi + plugins .. Indices and tables diff --git a/python/doc/plugins.rst b/python/doc/plugins.rst new file mode 100644 index 000000000..f7bd45322 --- /dev/null +++ b/python/doc/plugins.rst @@ -0,0 +1,38 @@ + +Implementing Plugin Hooks +========================== + +The Delta Chat Python bindings use `pluggy `_ +for managing global and per-account plugin registration, and performing +hook calls. There are two kinds of plugins: + +- Global plugins that are active for all accounts; they can implement + hooks at account-creation and account-shutdown time. + +- Account plugins that are only active during the lifetime of a + single Account instance. + + +Registering a plugin +-------------------- + +.. autofunction:: deltachat.register_global_plugin + :noindex: + +.. automethod:: deltachat.account.Account.add_account_plugin + :noindex: + + +Per-Account Hook specifications +------------------------------- + +.. autoclass:: deltachat.hookspec.PerAccount + :members: + + +Global Hook specifications +-------------------------- + +.. autoclass:: deltachat.hookspec.Global + :members: + diff --git a/python/examples/echo_and_quit.py b/python/examples/echo_and_quit.py new file mode 100644 index 000000000..367d1fd58 --- /dev/null +++ b/python/examples/echo_and_quit.py @@ -0,0 +1,30 @@ + +# content of echo_and_quit.py + +from deltachat import account_hookimpl, run_cmdline + + +class EchoPlugin: + @account_hookimpl + def ac_incoming_message(self, message): + print("process_incoming message", message) + if message.text.strip() == "/quit": + message.account.shutdown() + else: + # unconditionally accept the chat + message.accept_sender_contact() + addr = message.get_sender_contact().addr + text = message.text + message.chat.send_text("echoing from {}:\n{}".format(addr, text)) + + @account_hookimpl + def ac_message_delivered(self, message): + print("ac_message_delivered", message) + + +def main(argv=None): + run_cmdline(argv=argv, account_plugins=[EchoPlugin()]) + + +if __name__ == "__main__": + main() diff --git a/python/examples/group_tracking.py b/python/examples/group_tracking.py new file mode 100644 index 000000000..d7f180985 --- /dev/null +++ b/python/examples/group_tracking.py @@ -0,0 +1,52 @@ + +# content of group_tracking.py + +from deltachat import account_hookimpl, run_cmdline + + +class GroupTrackingPlugin: + @account_hookimpl + def ac_incoming_message(self, message): + print("process_incoming message", message) + if message.text.strip() == "/quit": + message.account.shutdown() + else: + # unconditionally accept the chat + message.accept_sender_contact() + addr = message.get_sender_contact().addr + text = message.text + message.chat.send_text("echoing from {}:\n{}".format(addr, text)) + + @account_hookimpl + def ac_outgoing_message(self, message): + print("ac_outgoing_message:", message) + + @account_hookimpl + def ac_configure_completed(self, success): + print("ac_configure_completed:", success) + + @account_hookimpl + def ac_chat_modified(self, chat): + print("ac_chat_modified:", chat.id, chat.get_name()) + for member in chat.get_contacts(): + print("chat member: {}".format(member.addr)) + + @account_hookimpl + def ac_member_added(self, chat, contact, message): + print("ac_member_added {} to chat {} from {}".format( + contact.addr, chat.id, message.get_sender_contact().addr)) + for member in chat.get_contacts(): + print("chat member: {}".format(member.addr)) + + @account_hookimpl + def ac_member_removed(self, chat, contact, message): + print("ac_member_removed {} from chat {} by {}".format( + contact.addr, chat.id, message.get_sender_contact().addr)) + + +def main(argv=None): + run_cmdline(argv=argv, account_plugins=[GroupTrackingPlugin()]) + + +if __name__ == "__main__": + main() diff --git a/python/examples/test_examples.py b/python/examples/test_examples.py new file mode 100644 index 000000000..da32b127d --- /dev/null +++ b/python/examples/test_examples.py @@ -0,0 +1,72 @@ + +import pytest +import py +import echo_and_quit +import group_tracking +from deltachat.eventlogger import FFIEventLogger + + +@pytest.fixture(scope='session') +def datadir(): + """The py.path.local object of the test-data/ directory.""" + for path in reversed(py.path.local(__file__).parts()): + datadir = path.join('test-data') + if datadir.isdir(): + return datadir + else: + pytest.skip('test-data directory not found') + + +def test_echo_quit_plugin(acfactory): + botproc = acfactory.run_bot_process(echo_and_quit) + + ac1 = acfactory.get_one_online_account() + bot_contact = ac1.create_contact(botproc.addr) + ch1 = ac1.create_chat_by_contact(bot_contact) + ch1.send_text("hello") + reply = ac1._evtracker.wait_next_incoming_message() + assert "hello" in reply.text + assert reply.chat == ch1 + ch1.send_text("/quit") + botproc.wait() + + +def test_group_tracking_plugin(acfactory, lp): + lp.sec("creating one group-tracking bot and two temp accounts") + botproc = acfactory.run_bot_process(group_tracking, ffi=False) + + ac1, ac2 = acfactory.get_two_online_accounts(quiet=True) + + botproc.fnmatch_lines(""" + *ac_configure_completed* + """) + ac1.add_account_plugin(FFIEventLogger(ac1, "ac1")) + ac2.add_account_plugin(FFIEventLogger(ac2, "ac2")) + + lp.sec("creating bot test group with bot") + bot_contact = ac1.create_contact(botproc.addr) + ch = ac1.create_group_chat("bot test group") + ch.add_contact(bot_contact) + ch.send_text("hello") + + botproc.fnmatch_lines(""" + *ac_chat_modified*bot test group* + """) + + lp.sec("adding third member {}".format(ac2.get_config("addr"))) + contact3 = ac1.create_contact(ac2.get_config("addr")) + ch.add_contact(contact3) + + reply = ac1._evtracker.wait_next_incoming_message() + assert "hello" in reply.text + + lp.sec("now looking at what the bot received") + botproc.fnmatch_lines(""" + *ac_member_added {}* + """.format(contact3.addr)) + + lp.sec("contact successfully added, now removing") + ch.remove_contact(contact3) + botproc.fnmatch_lines(""" + *ac_member_removed {}* + """.format(contact3.addr)) diff --git a/python/setup.py b/python/setup.py index 8c8e63ecc..e2a79e4b1 100644 --- a/python/setup.py +++ b/python/setup.py @@ -22,6 +22,11 @@ def main(): packages=setuptools.find_packages('src'), package_dir={'': 'src'}, cffi_modules=['src/deltachat/_build.py:ffibuilder'], + entry_points = { + 'pytest11': [ + 'deltachat.testplugin = deltachat.testplugin', + ], + }, classifiers=[ 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', diff --git a/python/src/deltachat/__init__.py b/python/src/deltachat/__init__.py index d18003480..3d7e43e61 100644 --- a/python/src/deltachat/__init__.py +++ b/python/src/deltachat/__init__.py @@ -1,6 +1,13 @@ -from deltachat import capi, const -from deltachat.capi import ffi -from deltachat.account import Account # noqa +import sys + +from . import capi, const, hookspec +from .capi import ffi +from .account import Account # noqa +from .message import Message # noqa +from .contact import Contact # noqa +from .chat import Chat # noqa +from .hookspec import account_hookimpl, global_hookimpl # noqa +from . import eventlogger from pkg_resources import get_distribution, DistributionNotFound try: @@ -64,3 +71,62 @@ def get_dc_event_name(integer, _DC_EVENTNAME_MAP={}): if name.startswith("DC_EVENT_"): _DC_EVENTNAME_MAP[val] = name return _DC_EVENTNAME_MAP[integer] + + +def register_global_plugin(plugin): + """ Register a global plugin which implements one or more + of the :class:`deltachat.hookspec.Global` hooks. + """ + gm = hookspec.Global._get_plugin_manager() + gm.register(plugin) + gm.check_pending() + + +def unregister_global_plugin(plugin): + gm = hookspec.Global._get_plugin_manager() + gm.unregister(plugin) + + +register_global_plugin(eventlogger) + + +def run_cmdline(argv=None, account_plugins=None): + """ Run a simple default command line app, registering the specified + account plugins. """ + import argparse + if argv is None: + argv = sys.argv + + parser = argparse.ArgumentParser(prog=argv[0] if argv else None) + parser.add_argument("db", action="store", help="database file") + parser.add_argument("--show-ffi", action="store_true", help="show low level ffi events") + parser.add_argument("--email", action="store", help="email address") + parser.add_argument("--password", action="store", help="password") + + args = parser.parse_args(argv[1:]) + + ac = Account(args.db) + + if args.show_ffi: + log = eventlogger.FFIEventLogger(ac, "bot") + ac.add_account_plugin(log) + + if not ac.is_configured(): + assert args.email and args.password, ( + "you must specify --email and --password once to configure this database/account" + ) + ac.set_config("addr", args.email) + ac.set_config("mail_pw", args.password) + ac.set_config("mvbox_move", "0") + ac.set_config("mvbox_watch", "0") + ac.set_config("sentbox_watch", "0") + + for plugin in account_plugins or []: + ac.add_account_plugin(plugin) + + # start IO threads and configure if neccessary + ac.start() + + print("{}: waiting for message".format(ac.get_config("addr"))) + + ac.wait_shutdown() diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 7e41fcc11..b24de8de6 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -2,21 +2,25 @@ from __future__ import print_function import atexit -import threading +from contextlib import contextmanager +from email.utils import parseaddr +import queue +from threading import Event import os -import time from array import array -from queue import Queue - import deltachat from . import const from .capi import ffi, lib from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array, DCLot from .chat import Chat -from .message import Message +from .message import Message, map_system_message from .contact import Contact -from .eventlogger import EventLogger -from .hookspec import get_plugin_manager, hookimpl +from .tracker import ImexTracker +from . import hookspec, iothreads + + +class MissingCredentials(ValueError): + """ Account is missing `addr` and `mail_pw` config values. """ class Account(object): @@ -24,45 +28,53 @@ class Account(object): by the underlying deltachat core library. All public Account methods are meant to be memory-safe and return memory-safe objects. """ - def __init__(self, db_path, logid=None, os_name=None, debug=True): + MissingCredentials = MissingCredentials + + def __init__(self, db_path, os_name=None): """ initialize account object. :param db_path: a path to the account database. The database will be created if it doesn't exist. - :param logid: an optional logging prefix that should be used with - the default internal logging. :param os_name: this will be put to the X-Mailer header in outgoing messages - :param debug: turn on debug logging for events. """ + # initialize per-account plugin system + self._pm = hookspec.PerAccount._make_plugin_manager() + self.add_account_plugin(self) + self._dc_context = ffi.gc( lib.dc_context_new(ffi.NULL, as_dc_charpointer(os_name)), _destroy_dc_context, ) - self._evlogger = EventLogger(self, logid, debug) - self._threads = IOThreads(self._dc_context, self._evlogger._log_event) - # register event call back and initialize plugin system - def _ll_event(ctx, evt_name, data1, data2): - assert ctx == self._dc_context - self.pluggy.hook.process_low_level_event( - account=self, event_name=evt_name, data1=data1, data2=data2 - ) + hook = hookspec.Global._get_plugin_manager().hook - self.pluggy = get_plugin_manager() - self.pluggy.register(self._evlogger) - deltachat.set_context_callback(self._dc_context, _ll_event) + self._threads = iothreads.IOThreads(self) + self._hook_event_queue = queue.Queue() + self._in_use_iter_events = False + self._shutdown_event = Event() # open database + self.db_path = db_path if hasattr(db_path, "encode"): db_path = db_path.encode("utf8") if not lib.dc_open(self._dc_context, db_path, ffi.NULL): raise ValueError("Could not dc_open: {}".format(db_path)) self._configkeys = self.get_config("sys.config_keys").split() atexit.register(self.shutdown) + hook.dc_account_init(account=self) + + @hookspec.account_hookimpl + def ac_process_ffi_event(self, ffi_event): + for name, kwargs in self._map_ffi_event(ffi_event): + ev = HookEvent(self, name=name, kwargs=kwargs) + self._hook_event_queue.put(ev) # def __del__(self): # self.shutdown() + def ac_log_line(self, msg): + self._pm.hook.ac_log_line(message=msg) + def _check_config_key(self, name): if name not in self._configkeys: raise KeyError("{!r} not a valid config key, existing keys: {!r}".format( @@ -134,16 +146,15 @@ class Account(object): if res == 0: raise Exception("Failed to set key") - def configure(self, **kwargs): - """ set config values and configure this account. + def update_config(self, kwargs): + """ update config values. :param kwargs: name=value config settings for this account. values need to be unicode. :returns: None """ - for name, value in kwargs.items(): - self.set_config(name, value) - lib.dc_configure(self._dc_context) + for key, value in kwargs.items(): + self.set_config(key, str(value)) def is_configured(self): """ determine if the account is configured already; an initial connection @@ -151,7 +162,7 @@ class Account(object): :returns: True if account is configured. """ - return lib.dc_is_configured(self._dc_context) + return bool(lib.dc_is_configured(self._dc_context)) def set_avatar(self, img_path): """Set self avatar. @@ -202,8 +213,7 @@ class Account(object): :returns: :class:`deltachat.contact.Contact` """ - self.check_is_configured() - return Contact(self._dc_context, const.DC_CONTACT_ID_SELF) + return Contact(self, const.DC_CONTACT_ID_SELF) def create_contact(self, email, name=None): """ create a (new) Contact. If there already is a Contact @@ -214,11 +224,14 @@ class Account(object): :param name: display name for this contact (optional) :returns: :class:`deltachat.contact.Contact` instance. """ - name = as_dc_charpointer(name) - email = as_dc_charpointer(email) - contact_id = lib.dc_create_contact(self._dc_context, name, email) + realname, addr = parseaddr(email) + if name: + realname = name + realname = as_dc_charpointer(realname) + addr = as_dc_charpointer(addr) + contact_id = lib.dc_create_contact(self._dc_context, realname, addr) assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL - return Contact(self._dc_context, contact_id) + return Contact(self, contact_id) def delete_contact(self, contact): """ delete a Contact. @@ -231,6 +244,14 @@ class Account(object): assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL return bool(lib.dc_delete_contact(self._dc_context, contact_id)) + def get_contact_by_addr(self, email): + """ get a contact for the email address or None if it's blocked or doesn't exist. """ + _, addr = parseaddr(email) + addr = as_dc_charpointer(addr) + contact_id = lib.dc_lookup_contact_id_by_addr(self._dc_context, addr) + if contact_id: + return self.get_contact_by_id(contact_id) + def get_contacts(self, query=None, with_self=False, only_verified=False): """ get a (filtered) list of contacts. @@ -250,7 +271,15 @@ class Account(object): lib.dc_get_contacts(self._dc_context, flags, query), lib.dc_array_unref ) - return list(iter_array(dc_array, lambda x: Contact(self._dc_context, x))) + return list(iter_array(dc_array, lambda x: Contact(self, x))) + + def get_fresh_messages(self): + """ yield all fresh messages from all chats. """ + dc_array = ffi.gc( + lib.dc_get_fresh_msgs(self._dc_context), + lib.dc_array_unref + ) + yield from iter_array(dc_array, lambda x: Message.from_db(self, x)) def create_chat_by_contact(self, contact): """ create or get an existing 1:1 chat object for the specified contact or contact id. @@ -272,6 +301,9 @@ class Account(object): """ create or get an existing chat object for the the specified message. + If this message is in the deaddrop chat then + the sender will become an accepted contact. + :param message: messsage id or message instance. :returns: a :class:`deltachat.chat.Chat` object. """ @@ -324,6 +356,13 @@ class Account(object): """ return Message.from_db(self, msg_id) + def get_contact_by_id(self, contact_id): + """ return Contact instance or None. + :param contact_id: integer id of this contact. + :returns: None or :class:`deltachat.contact.Contact` instance. + """ + return Contact(self, contact_id) + def get_chat_by_id(self, chat_id): """ return Chat instance. :param chat_id: integer id of this chat. @@ -368,13 +407,18 @@ class Account(object): lib.dc_delete_msgs(self._dc_context, msg_ids, len(msg_ids)) def export_self_keys(self, path): - """ export public and private keys to the specified directory. """ + """ export public and private keys to the specified directory. + + Note that the account does not have to be started. + """ return self._export(path, imex_cmd=1) def export_all(self, path): """return new file containing a backup of all database state (chats, contacts, keys, media, ...). The file is created in the the `path` directory. + + Note that the account does not have to be started. """ export_files = self._export(path, 11) if len(export_files) != 1: @@ -382,7 +426,7 @@ class Account(object): return export_files[0] def _export(self, path, imex_cmd): - with ImexTracker(self) as imex_tracker: + with self.temp_plugin(ImexTracker()) as imex_tracker: lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL) return imex_tracker.wait_finish() @@ -390,6 +434,8 @@ class Account(object): """ Import private keys found in the `path` directory. The last imported key is made the default keys unless its name contains the string legacy. Public keys are not imported. + + Note that the account does not have to be started. """ self._import(path, imex_cmd=2) @@ -402,7 +448,7 @@ class Account(object): self._import(path, imex_cmd=12) def _import(self, path, imex_cmd): - with ImexTracker(self) as imex_tracker: + with self.temp_plugin(ImexTracker()) as imex_tracker: lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL) imex_tracker.wait_finish() @@ -469,46 +515,6 @@ class Account(object): raise ValueError("could not join group") return Chat(self, chat_id) - def stop_ongoing(self): - lib.dc_stop_ongoing_process(self._dc_context) - - # - # meta API for start/stop and event based processing - # - - def wait_next_incoming_message(self): - """ wait for and return next incoming message. """ - ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - return self.get_message_by_id(ev[2]) - - def start_threads(self): - """ start IMAP/SMTP threads (and configure account if it hasn't happened). - - :raises: ValueError if 'addr' or 'mail_pw' are not configured. - :returns: None - """ - if not self.is_configured(): - self.configure() - self._threads.start() - - def stop_threads(self, wait=True): - """ stop IMAP/SMTP threads. """ - if self._threads.is_started(): - self.stop_ongoing() - self._threads.stop(wait=wait) - - def shutdown(self, wait=True): - """ stop threads and close and remove underlying dc_context and callbacks. """ - if hasattr(self, "_dc_context") and hasattr(self, "_threads"): - # print("SHUTDOWN", self) - self.stop_threads(wait=False) - lib.dc_close(self._dc_context) - self.stop_threads(wait=wait) # to wait for threads - deltachat.clear_context_callback(self._dc_context) - del self._dc_context - atexit.unregister(self.shutdown) - self.pluggy.unregister(self._evlogger) - def set_location(self, latitude=0.0, longitude=0.0, accuracy=0.0): """set a new location. It effects all chats where we currently have enabled location streaming. @@ -523,88 +529,122 @@ class Account(object): if dc_res == 0: raise ValueError("no chat is streaming locations") + # + # meta API for start/stop and event based processing + # -class ImexTracker: - def __init__(self, account): - self._imex_events = Queue() - self.account = account + def add_account_plugin(self, plugin, name=None): + """ add an account plugin which implements one or more of + the :class:`deltachat.hookspec.PerAccount` hooks. + """ + self._pm.register(plugin, name=name) + self._pm.check_pending() + return plugin - def __enter__(self): - self.account.pluggy.register(self) - return self + @contextmanager + def temp_plugin(self, plugin): + """ run a with-block with the given plugin temporarily registered. """ + self._pm.register(plugin) + yield plugin + self._pm.unregister(plugin) - def __exit__(self, *args): - self.account.pluggy.unregister(self) + def stop_ongoing(self): + """ Stop ongoing securejoin, configuration or other core jobs. """ + lib.dc_stop_ongoing_process(self._dc_context) - @hookimpl - def process_low_level_event(self, account, event_name, data1, data2): - # there could be multiple accounts instantiated - if self.account is not account: + def start(self, callback_thread=True): + """ start this account (activate imap/smtp threads etc.) + and return immediately. + + If this account is not configured, an internal configuration + job will be scheduled if config values are sufficiently specified. + + You may call `wait_shutdown` or `shutdown` after the + account is in started mode. + + :raises MissingCredentials: if `addr` and `mail_pw` values are not set. + + :returns: None + """ + if not self.is_configured(): + if not self.get_config("addr") or not self.get_config("mail_pw"): + raise MissingCredentials("addr or mail_pwd not set in config") + lib.dc_configure(self._dc_context) + self._threads.start(callback_thread=callback_thread) + + def wait_shutdown(self): + """ wait until shutdown of this account has completed. """ + self._shutdown_event.wait() + + def shutdown(self, wait=True): + """ shutdown account, stop threads and close and remove + underlying dc_context and callbacks. """ + dc_context = self._dc_context + if dc_context is None: return - if event_name == "DC_EVENT_IMEX_PROGRESS": - self._imex_events.put(data1) - elif event_name == "DC_EVENT_IMEX_FILE_WRITTEN": - self._imex_events.put(data1) - def wait_finish(self, progress_timeout=60): - """ Return list of written files, raise ValueError if ExportFailed. """ - files_written = [] - while True: - ev = self._imex_events.get(timeout=progress_timeout) - if isinstance(ev, str): - files_written.append(ev) - elif ev == 0: - raise ValueError("export failed, exp-files: {}".format(files_written)) - elif ev == 1000: - return files_written + if self._threads.is_started(): + self.stop_ongoing() + self._threads.stop(wait=False) + lib.dc_close(dc_context) + self._hook_event_queue.put(None) + self._threads.stop(wait=wait) # to wait for threads + self._dc_context = None + atexit.unregister(self.shutdown) + self._shutdown_event.set() + hook = hookspec.Global._get_plugin_manager().hook + hook.dc_account_after_shutdown(account=self, dc_context=dc_context) + def _handle_current_events(self): + """ handle all currently queued events and then return. """ + while 1: + try: + event = self._hook_event_queue.get(block=False) + except queue.Empty: + break + else: + event.call_hook() -class IOThreads: - def __init__(self, dc_context, log_event=lambda *args: None): - self._dc_context = dc_context - self._thread_quitflag = False - self._name2thread = {} - self._log_event = log_event - self._log_running = True + def iter_events(self, timeout=None): + """ yield hook events until shutdown. - # Make sure the current - self._start_one_thread("deltachat-log", self.dc_thread_run) + It is not allowed to call iter_events() from multiple threads. + """ + if self._in_use_iter_events: + raise RuntimeError("can only call iter_events() from one thread") + self._in_use_iter_events = True + while 1: + event = self._hook_event_queue.get(timeout=timeout) + if event is None: + break + yield event - def is_started(self): - return lib.dc_is_open(self._dc_context) and lib.dc_is_running(self._dc_context) - - def start(self, imap=True, smtp=True, mvbox=False, sentbox=False): - assert not self.is_started() - - lib.dc_context_run(self._dc_context) - - def _start_one_thread(self, name, func): - self._name2thread[name] = t = threading.Thread(target=func, name=name) - t.setDaemon(1) - t.start() - - def stop(self, wait=False): - if self.is_started(): - lib.dc_context_shutdown(self._dc_context) - - def dc_thread_run(self): - self._log_event("py-bindings-info", 0, "DC LOG THREAD START") - - while self._log_running: - if lib.dc_is_open(self._dc_context) and lib.dc_has_next_event(self._dc_context): - event = lib.dc_get_next_event(self._dc_context) - if event != ffi.NULL: - deltachat.py_dc_callback( - self._dc_context, - lib.dc_event_get_id(event), - lib.dc_event_get_data1(event), - lib.dc_event_get_data2(event) - ) - lib.dc_event_unref(event) - else: - time.sleep(0.05) - - self._log_event("py-bindings-info", 0, "DC LOG THREAD FINISHED") + def _map_ffi_event(self, ffi_event): + name = ffi_event.name + if name == "DC_EVENT_CONFIGURE_PROGRESS": + data1 = ffi_event.data1 + if data1 == 0 or data1 == 1000: + success = data1 == 1000 + yield "ac_configure_completed", dict(success=success) + elif name == "DC_EVENT_INCOMING_MSG": + msg = self.get_message_by_id(ffi_event.data2) + yield map_system_message(msg) or ("ac_incoming_message", dict(message=msg)) + elif name == "DC_EVENT_MSGS_CHANGED": + if ffi_event.data2 != 0: + msg = self.get_message_by_id(ffi_event.data2) + if msg.is_outgoing(): + res = map_system_message(msg) + if res and res[0].startswith("ac_member"): + yield res + yield "ac_outgoing_message", dict(message=msg) + elif msg.is_in_fresh(): + yield map_system_message(msg) or ("ac_incoming_message", dict(message=msg)) + elif name == "DC_EVENT_MSG_DELIVERED": + msg = self.get_message_by_id(ffi_event.data2) + yield "ac_message_delivered", dict(message=msg) + elif name == "DC_EVENT_CHAT_MODIFIED": + chat = self.get_chat_by_id(ffi_event.data1) + yield "ac_chat_modified", dict(chat=chat) def _destroy_dc_context(dc_context, dc_context_unref=lib.dc_context_unref): @@ -631,3 +671,17 @@ class ScannedQRCode: @property def contact_id(self): return self._dc_lot.id() + + +class HookEvent: + def __init__(self, account, name, kwargs): + assert hasattr(account._pm.hook, name), name + self.account = account + self.name = name + self.kwargs = kwargs + + def call_hook(self): + hook = getattr(self.account._pm.hook, self.name, None) + if hook is None: + raise ValueError("event_name {} unknown".format(self.name)) + return hook(**self.kwargs) diff --git a/python/src/deltachat/chat.py b/python/src/deltachat/chat.py index 09883f452..f2d6cd09a 100644 --- a/python/src/deltachat/chat.py +++ b/python/src/deltachat/chat.py @@ -30,7 +30,7 @@ class Chat(object): return not (self == other) def __repr__(self): - return "".format(self.id, self.get_name(), self._dc_context) + return "".format(self.id, self.get_name()) @property def _dc_chat(self): @@ -51,6 +51,16 @@ class Chat(object): # ------ chat status/metadata API ------------------------------ + def is_group(self): + """ return true if this chat is a group chat. + + :returns: True if chat is a group-chat, false if it's a contact 1:1 chat. + """ + return lib.dc_chat_get_type(self._dc_chat) in ( + const.DC_CHAT_TYPE_GROUP, + const.DC_CHAT_TYPE_VERIFIED_GROUP + ) + def is_deaddrop(self): """ return true if this chat is a deaddrop chat. @@ -129,7 +139,7 @@ class Chat(object): return bool(lib.dc_chat_get_remaining_mute_duration(self.id)) def get_type(self): - """ return type of this chat. + """ (deprecated) return type of this chat. :returns: one of const.DC_CHAT_TYPE_* """ @@ -353,7 +363,7 @@ class Chat(object): lib.dc_array_unref ) return list(iter_array( - dc_array, lambda id: Contact(self._dc_context, id)) + dc_array, lambda id: Contact(self.account, id)) ) def set_profile_image(self, img_path): @@ -405,12 +415,6 @@ class Chat(object): """ return lib.dc_chat_get_color(self._dc_chat) - def get_subtitle(self): - """return the subtitle of the chat - :returns: the subtitle - """ - return from_dc_charpointer(lib.dc_chat_get_subtitle(self._dc_chat)) - # ------ location streaming API ------------------------------ def is_sending_locations(self): diff --git a/python/src/deltachat/const.py b/python/src/deltachat/const.py index 7135b4905..20139ef44 100644 --- a/python/src/deltachat/const.py +++ b/python/src/deltachat/const.py @@ -11,6 +11,7 @@ from os.path import join as joinpath DC_GCL_ARCHIVED_ONLY = 0x01 DC_GCL_NO_SPECIALS = 0x02 DC_GCL_ADD_ALLDONE_HINT = 0x04 +DC_GCL_FOR_FORWARDING = 0x08 DC_GCL_VERIFIED_ONLY = 0x01 DC_GCL_ADD_SELF = 0x02 DC_QR_ASK_VERIFYCONTACT = 200 @@ -98,7 +99,6 @@ DC_EVENT_IMEX_PROGRESS = 2051 DC_EVENT_IMEX_FILE_WRITTEN = 2052 DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060 DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061 -DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062 DC_EVENT_FILE_COPIED = 2055 DC_EVENT_IS_OFFLINE = 2081 DC_EVENT_GET_STRING = 2091 @@ -115,8 +115,6 @@ DC_CHAT_VISIBILITY_PINNED = 2 DC_STR_NOMESSAGES = 1 DC_STR_SELF = 2 DC_STR_DRAFT = 3 -DC_STR_MEMBER = 4 -DC_STR_CONTACT = 6 DC_STR_VOICEMESSAGE = 7 DC_STR_DEADDROP = 8 DC_STR_IMAGE = 9 @@ -148,7 +146,6 @@ DC_STR_ARCHIVEDCHATS = 40 DC_STR_STARREDMSGS = 41 DC_STR_AC_SETUP_MSG_SUBJECT = 42 DC_STR_AC_SETUP_MSG_BODY = 43 -DC_STR_SELFTALK_SUBTITLE = 50 DC_STR_CANNOT_LOGIN = 60 DC_STR_SERVER_RESPONSE = 61 DC_STR_MSGACTIONBYUSER = 62 diff --git a/python/src/deltachat/contact.py b/python/src/deltachat/contact.py index 9cebc829a..6e2cf1ae5 100644 --- a/python/src/deltachat/contact.py +++ b/python/src/deltachat/contact.py @@ -10,8 +10,9 @@ class Contact(object): You obtain instances of it through :class:`deltachat.account.Account`. """ - def __init__(self, dc_context, id): - self._dc_context = dc_context + def __init__(self, account, id): + self.account = account + self._dc_context = account._dc_context self.id = id def __eq__(self, other): @@ -57,3 +58,7 @@ class Contact(object): if dc_res == ffi.NULL: return None return from_dc_charpointer(dc_res) + + def get_chat(self): + """return 1:1 chat for this contact. """ + return self.account.create_chat_by_contact(self) diff --git a/python/src/deltachat/eventlogger.py b/python/src/deltachat/eventlogger.py index e1dfffda6..5490e7e72 100644 --- a/python/src/deltachat/eventlogger.py +++ b/python/src/deltachat/eventlogger.py @@ -1,28 +1,88 @@ +import deltachat import threading -import re import time +import re from queue import Queue, Empty -from .hookspec import hookimpl +from .hookspec import account_hookimpl, global_hookimpl -class EventLogger: +@global_hookimpl +def dc_account_init(account): + # send all FFI events for this account to a plugin hook + def _ll_event(ctx, evt_name, data1, data2): + assert ctx == account._dc_context + ffi_event = FFIEvent(name=evt_name, data1=data1, data2=data2) + account._pm.hook.ac_process_ffi_event( + account=account, ffi_event=ffi_event + ) + deltachat.set_context_callback(account._dc_context, _ll_event) + + +@global_hookimpl +def dc_account_after_shutdown(dc_context): + deltachat.clear_context_callback(dc_context) + + +class FFIEvent: + def __init__(self, name, data1, data2): + self.name = name + self.data1 = data1 + self.data2 = data2 + + def __str__(self): + return "{name} data1={data1} data2={data2}".format(**self.__dict__) + + +class FFIEventLogger: + """ If you register an instance of this logger with an Account + you'll get all ffi-events printed. + """ + # to prevent garbled logging _loglock = threading.RLock() - def __init__(self, account, logid=None, debug=True): + def __init__(self, account, logid): + """ + :param logid: an optional logging prefix that should be used with + the default internal logging. + """ self.account = account - self._event_queue = Queue() - self._debug = debug - if logid is None: - logid = str(self.account._dc_context).strip(">").split()[-1] self.logid = logid - self._timeout = None self.init_time = time.time() - @hookimpl - def process_low_level_event(self, account, event_name, data1, data2): - if self.account == account: - self._log_event(event_name, data1, data2) - self._event_queue.put((event_name, data1, data2)) + @account_hookimpl + def ac_process_ffi_event(self, ffi_event): + self._log_event(ffi_event) + + def _log_event(self, ffi_event): + # don't show events that are anyway empty impls now + if ffi_event.name == "DC_EVENT_GET_STRING": + return + self.account.ac_log_line(str(ffi_event)) + + @account_hookimpl + def ac_log_line(self, message): + t = threading.currentThread() + tname = getattr(t, "name", t) + if tname == "MainThread": + tname = "MAIN" + elapsed = time.time() - self.init_time + locname = tname + if self.logid: + locname += "-" + self.logid + s = "{:2.2f} [{}] {}".format(elapsed, locname, message) + with self._loglock: + print(s, flush=True) + + +class FFIEventTracker: + def __init__(self, account, timeout=None): + self.account = account + self._timeout = timeout + self._event_queue = Queue() + + @account_hookimpl + def ac_process_ffi_event(self, ffi_event): + self._event_queue.put(ffi_event) def set_timeout(self, timeout): self._timeout = timeout @@ -32,10 +92,10 @@ class EventLogger: self.get(check_error=check_error) def get(self, timeout=None, check_error=True): - timeout = timeout or self._timeout + timeout = timeout if timeout is not None else self._timeout ev = self._event_queue.get(timeout=timeout) - if check_error and ev[0] == "DC_EVENT_ERROR": - raise ValueError("{}({!r},{!r})".format(*ev)) + if check_error and ev.name == "DC_EVENT_ERROR": + raise ValueError(str(ev)) return ev def ensure_event_not_queued(self, event_name_regex): @@ -47,35 +107,31 @@ class EventLogger: except Empty: break else: - assert not rex.match(ev[0]), "event found {}".format(ev) + assert not rex.match(ev.name), "event found {}".format(ev) def get_matching(self, event_name_regex, check_error=True, timeout=None): - self._log("-- waiting for event with regex: {} --".format(event_name_regex)) + self.account.ac_log_line("-- waiting for event with regex: {} --".format(event_name_regex)) rex = re.compile("(?:{}).*".format(event_name_regex)) while 1: ev = self.get(timeout=timeout, check_error=check_error) - if rex.match(ev[0]): + if rex.match(ev.name): return ev def get_info_matching(self, regex): rex = re.compile("(?:{}).*".format(regex)) while 1: ev = self.get_matching("DC_EVENT_INFO") - if rex.match(ev[2]): + if rex.match(ev.data2): return ev - def _log_event(self, evt_name, data1, data2): - # don't show events that are anyway empty impls now - if evt_name == "DC_EVENT_GET_STRING": - return - if self._debug: - evpart = "{}({!r},{!r})".format(evt_name, data1, data2) - self._log(evpart) + def wait_next_incoming_message(self): + """ wait for and return next incoming message. """ + ev = self.get_matching("DC_EVENT_INCOMING_MSG") + return self.account.get_message_by_id(ev.data2) - def _log(self, msg): - t = threading.currentThread() - tname = getattr(t, "name", t) - if tname == "MainThread": - tname = "MAIN" - with self._loglock: - print("{:2.2f} [{}-{}] {}".format(time.time() - self.init_time, tname, self.logid, msg)) + def wait_next_messages_changed(self): + """ wait for and return next message-changed message or None + if the event contains no msgid""" + ev = self.get_matching("DC_EVENT_MSGS_CHANGED") + if ev.data2 > 0: + return self.account.get_message_by_id(ev.data2) diff --git a/python/src/deltachat/hookspec.py b/python/src/deltachat/hookspec.py index b79b5ffef..00ec36d98 100644 --- a/python/src/deltachat/hookspec.py +++ b/python/src/deltachat/hookspec.py @@ -1,25 +1,92 @@ -""" Hooks for python bindings """ +""" Hooks for Python bindings to Delta Chat Core Rust CFFI""" import pluggy -name = "deltachat" -hookspec = pluggy.HookspecMarker(name) -hookimpl = pluggy.HookimplMarker(name) -_plugin_manager = None +account_spec_name = "deltachat-account" +account_hookspec = pluggy.HookspecMarker(account_spec_name) +account_hookimpl = pluggy.HookimplMarker(account_spec_name) + +global_spec_name = "deltachat-global" +global_hookspec = pluggy.HookspecMarker(global_spec_name) +global_hookimpl = pluggy.HookimplMarker(global_spec_name) -def get_plugin_manager(): - global _plugin_manager - if _plugin_manager is None: - _plugin_manager = pluggy.PluginManager(name) - _plugin_manager.add_hookspecs(DeltaChatHookSpecs) - return _plugin_manager +class PerAccount: + """ per-Account-instance hook specifications. + + Except for ac_process_ffi_event all hooks are executed + in the thread which calls Account.wait_shutdown(). + """ + @classmethod + def _make_plugin_manager(cls): + pm = pluggy.PluginManager(account_spec_name) + pm.add_hookspecs(cls) + return pm + + @account_hookspec + def ac_process_ffi_event(self, ffi_event): + """ process a CFFI low level events for a given account. + + ffi_event has "name", "data1", "data2" values as specified + with `DC_EVENT_* `_. + + DANGER: this hook is executed from the callback invoked by core. + Hook implementations need to be short running and can typically + not call back into core because this would easily cause recursion issues. + """ + + @account_hookspec + def ac_log_line(self, message): + """ log a message related to the account. """ + + @account_hookspec + def ac_configure_completed(self, success): + """ Called when a configure process completed. """ + + @account_hookspec + def ac_incoming_message(self, message): + """ Called on any incoming message (to deaddrop or chat). """ + + @account_hookspec + def ac_outgoing_message(self, message): + """ Called on each outgoing message (both system and "normal").""" + + @account_hookspec + def ac_message_delivered(self, message): + """ Called when an outgoing message has been delivered to SMTP. """ + + @account_hookspec + def ac_chat_modified(self, chat): + """ Chat was created or modified regarding membership, avatar, title. """ + + @account_hookspec + def ac_member_added(self, chat, contact, message): + """ Called for each contact added to an accepted chat. """ + + @account_hookspec + def ac_member_removed(self, chat, contact, message): + """ Called for each contact removed from a chat. """ -class DeltaChatHookSpecs: - """ Plugin Hook specifications for Python bindings to Delta Chat CFFI. """ +class Global: + """ global hook specifications using a per-process singleton + plugin manager instance. - @hookspec - def process_low_level_event(self, account, event_name, data1, data2): - """ process a CFFI low level events for a given account. """ + """ + _plugin_manager = None + + @classmethod + def _get_plugin_manager(cls): + if cls._plugin_manager is None: + cls._plugin_manager = pm = pluggy.PluginManager(global_spec_name) + pm.add_hookspecs(cls) + return cls._plugin_manager + + @global_hookspec + def dc_account_init(self, account): + """ called when `Account::__init__()` function starts executing. """ + + @global_hookspec + def dc_account_after_shutdown(self, account, dc_context): + """ Called after the account has been shutdown. """ diff --git a/python/src/deltachat/iothreads.py b/python/src/deltachat/iothreads.py new file mode 100644 index 000000000..1e9a864f1 --- /dev/null +++ b/python/src/deltachat/iothreads.py @@ -0,0 +1,106 @@ + +import threading +import time + +from contextlib import contextmanager + +from .capi import lib + + +class IOThreads: + def __init__(self, account): + self.account = account + self._dc_context = account._dc_context + self._thread_quitflag = False + self._name2thread = {} + + def is_started(self): + return len(self._name2thread) > 0 + + def start(self, callback_thread): + assert not self.is_started() + self._start_one_thread("inbox", self.imap_thread_run) + self._start_one_thread("smtp", self.smtp_thread_run) + + if callback_thread: + self._start_one_thread("cb", self.cb_thread_run) + + if int(self.account.get_config("mvbox_watch")): + self._start_one_thread("mvbox", self.mvbox_thread_run) + + if int(self.account.get_config("sentbox_watch")): + self._start_one_thread("sentbox", self.sentbox_thread_run) + + def _start_one_thread(self, name, func): + self._name2thread[name] = t = threading.Thread(target=func, name=name) + t.setDaemon(1) + t.start() + + @contextmanager + def log_execution(self, message): + self.account.ac_log_line(message + " START") + yield + self.account.ac_log_line(message + " FINISHED") + + def stop(self, wait=False): + self._thread_quitflag = True + + # Workaround for a race condition. Make sure that thread is + # not in between checking for quitflag and entering idle. + time.sleep(0.5) + + lib.dc_interrupt_imap_idle(self._dc_context) + lib.dc_interrupt_smtp_idle(self._dc_context) + if "mvbox" in self._name2thread: + lib.dc_interrupt_mvbox_idle(self._dc_context) + if "sentbox" in self._name2thread: + lib.dc_interrupt_sentbox_idle(self._dc_context) + if wait: + for name, thread in self._name2thread.items(): + if thread != threading.currentThread(): + thread.join() + + def cb_thread_run(self): + with self.log_execution("CALLBACK THREAD START"): + it = self.account.iter_events() + while not self._thread_quitflag: + try: + ev = next(it) + except StopIteration: + break + self.account.ac_log_line("calling hook name={} kwargs={}".format(ev.name, ev.kwargs)) + ev.call_hook() + + def imap_thread_run(self): + with self.log_execution("INBOX THREAD START"): + while not self._thread_quitflag: + lib.dc_perform_imap_jobs(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_imap_fetch(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_imap_idle(self._dc_context) + + def mvbox_thread_run(self): + with self.log_execution("MVBOX THREAD"): + while not self._thread_quitflag: + lib.dc_perform_mvbox_jobs(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_mvbox_fetch(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_mvbox_idle(self._dc_context) + + def sentbox_thread_run(self): + with self.log_execution("SENTBOX THREAD"): + while not self._thread_quitflag: + lib.dc_perform_sentbox_jobs(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_sentbox_fetch(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_sentbox_idle(self._dc_context) + + def smtp_thread_run(self): + with self.log_execution("SMTP THREAD"): + while not self._thread_quitflag: + lib.dc_perform_smtp_jobs(self._dc_context) + if not self._thread_quitflag: + lib.dc_perform_smtp_idle(self._dc_context) diff --git a/python/src/deltachat/message.py b/python/src/deltachat/message.py index d8128bfb0..91cc738c0 100644 --- a/python/src/deltachat/message.py +++ b/python/src/deltachat/message.py @@ -28,7 +28,9 @@ class Message(object): return self.account == other.account and self.id == other.id def __repr__(self): - return "".format(self.id, self._dc_context) + c = self.get_sender_contact() + return "".format( + self.id, c.id, c.addr, self.is_outgoing(), self.chat.id, self.chat.get_name()) @classmethod def from_db(cls, account, id): @@ -50,6 +52,16 @@ class Message(object): lib.dc_msg_unref )) + def accept_sender_contact(self): + """ ensure that the sender is an accepted contact + and that the message has a non-deaddrop chat object. + """ + self.account.create_chat_by_message(self) + self._dc_msg = ffi.gc( + lib.dc_get_msg(self._dc_context, self.id), + lib.dc_msg_unref + ) + @props.with_doc def text(self): """unicode text of this messages (might be empty if not a text message). """ @@ -81,6 +93,10 @@ class Message(object): """mime type of the file (if it exists)""" return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg)) + def is_system_message(self): + """ return True if this message is a system/info message. """ + return lib.dc_msg_is_info(self._dc_msg) + def is_setup_message(self): """ return True if this message is a setup message. """ return lib.dc_msg_is_setupmessage(self._dc_msg) @@ -159,6 +175,13 @@ class Message(object): chat_id = lib.dc_msg_get_chat_id(self._dc_msg) return Chat(self.account, chat_id) + def get_sender_chat(self): + """return the 1:1 chat with the sender of this message. + + :returns: :class:`deltachat.chat.Chat` instance + """ + return self.get_sender_contact().get_chat() + def get_sender_contact(self): """return the contact of who wrote the message. @@ -166,7 +189,7 @@ class Message(object): """ from .contact import Contact contact_id = lib.dc_msg_get_from_id(self._dc_msg) - return Contact(self._dc_context, contact_id) + return Contact(self.account, contact_id) # # Message State query methods @@ -207,6 +230,13 @@ class Message(object): """ return self._msgstate == const.DC_STATE_IN_SEEN + def is_outgoing(self): + """Return True if Message is outgoing. """ + return self._msgstate in ( + const.DC_STATE_OUT_PREPARING, const.DC_STATE_OUT_PENDING, + const.DC_STATE_OUT_FAILED, const.DC_STATE_OUT_MDN_RCVD, + const.DC_STATE_OUT_DELIVERED) + def is_out_preparing(self): """Return True if Message is outgoing, but its file is being prepared. """ @@ -269,6 +299,10 @@ class Message(object): """ return True if it's a file message. """ return self._view_type == const.DC_MSG_FILE + def mark_seen(self): + """ mark this message as seen. """ + self.account.mark_seen_messages([self.id]) + # some code for handling DC_MSG_* view types @@ -288,3 +322,29 @@ def get_viewtype_code_from_name(view_type_name): return code raise ValueError("message typecode not found for {!r}, " "available {!r}".format(view_type_name, list(_view_type_mapping.values()))) + + +# +# some helper code for turning system messages into hook events +# + +def map_system_message(msg): + if msg.is_system_message(): + res = parse_system_add_remove(msg.text) + if res: + contact = msg.account.get_contact_by_addr(res[1]) + if contact: + d = dict(chat=msg.chat, contact=contact, message=msg) + return "ac_member_" + res[0], d + + +def parse_system_add_remove(text): + # Member Me (x@y) removed by a@b. + # Member x@y removed by a@b + text = text.lower() + parts = text.split() + if parts[0] == "member": + if parts[2] in ("removed", "added"): + return parts[2], parts[1] + if parts[3] in ("removed", "added"): + return parts[3], parts[2].strip("()") diff --git a/python/src/deltachat/testplugin.py b/python/src/deltachat/testplugin.py new file mode 100644 index 000000000..f336ac058 --- /dev/null +++ b/python/src/deltachat/testplugin.py @@ -0,0 +1,381 @@ +from __future__ import print_function +import os +import sys +import subprocess +import queue +import threading +import fnmatch +import pytest +import requests +import time +from . import Account, const +from .tracker import ConfigureTracker +from .capi import lib +from .eventlogger import FFIEventLogger, FFIEventTracker +from _pytest.monkeypatch import MonkeyPatch +from _pytest._code import Source + +import tempfile + + +def pytest_addoption(parser): + parser.addoption( + "--liveconfig", action="store", default=None, + help="a file with >=2 lines where each line " + "contains NAME=VALUE config settings for one account" + ) + parser.addoption( + "--ignored", action="store_true", + help="Also run tests marked with the ignored marker", + ) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "ignored: Mark test as bing slow, skipped unless --ignored is used." + ) + cfg = config.getoption('--liveconfig') + if not cfg: + cfg = os.getenv('DCC_NEW_TMP_EMAIL') + if cfg: + config.option.liveconfig = cfg + + +def pytest_runtest_setup(item): + if (list(item.iter_markers(name="ignored")) + and not item.config.getoption("ignored")): + pytest.skip("Ignored tests not requested, use --ignored") + + +def pytest_report_header(config, startdir): + summary = [] + + t = tempfile.mktemp() + m = MonkeyPatch() + try: + m.setattr(sys.stdout, "write", lambda x: len(x)) + ac = Account(t) + info = ac.get_info() + ac.shutdown() + finally: + m.undo() + os.remove(t) + summary.extend(['Deltachat core={} sqlite={}'.format( + info['deltachat_core_version'], + info['sqlite_version'], + )]) + + cfg = config.option.liveconfig + if cfg: + if "?" in cfg: + url, token = cfg.split("?", 1) + summary.append('Liveconfig provider: {}?'.format(url)) + else: + summary.append('Liveconfig file: {}'.format(cfg)) + return summary + + +class SessionLiveConfigFromFile: + def __init__(self, fn): + self.fn = fn + self.configlist = [] + for line in open(fn): + if line.strip() and not line.strip().startswith('#'): + d = {} + for part in line.split(): + name, value = part.split("=") + d[name] = value + self.configlist.append(d) + + def get(self, index): + return self.configlist[index] + + def exists(self): + return bool(self.configlist) + + +class SessionLiveConfigFromURL: + def __init__(self, url): + self.configlist = [] + self.url = url + + def get(self, index): + try: + return self.configlist[index] + except IndexError: + assert index == len(self.configlist), index + res = requests.post(self.url) + if res.status_code != 200: + pytest.skip("creating newtmpuser failed {!r}".format(res)) + d = res.json() + config = dict(addr=d["email"], mail_pw=d["password"]) + self.configlist.append(config) + return config + + def exists(self): + return bool(self.configlist) + + +@pytest.fixture(scope="session") +def session_liveconfig(request): + liveconfig_opt = request.config.option.liveconfig + if liveconfig_opt: + if liveconfig_opt.startswith("http"): + return SessionLiveConfigFromURL(liveconfig_opt) + else: + return SessionLiveConfigFromFile(liveconfig_opt) + + +@pytest.fixture +def data(request): + class Data: + def __init__(self): + # trying to find test data heuristically + # because we are run from a dev-setup with pytest direct, + # through tox, and then maybe also from deltachat-binding + # users like "deltabot". + self.paths = [os.path.normpath(x) for x in [ + os.path.join(os.path.dirname(request.fspath.strpath), "data"), + os.path.join(os.path.dirname(__file__), "..", "..", "..", "test-data") + ]] + + def get_path(self, bn): + """ return path of file or None if it doesn't exist. """ + for path in self.paths: + fn = os.path.join(path, *bn.split("/")) + if os.path.exists(fn): + return fn + print("WARNING: path does not exist: {!r}".format(fn)) + + def read_path(self, bn, mode="r"): + fn = self.get_path(bn) + if fn is not None: + with open(fn, mode) as f: + return f.read() + + return Data() + + +@pytest.fixture +def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data): + + class AccountMaker: + def __init__(self): + self.live_count = 0 + self.offline_count = 0 + self._finalizers = [] + self.init_time = time.time() + self._generated_keys = ["alice", "bob", "charlie", + "dom", "elena", "fiona"] + + def finalize(self): + while self._finalizers: + fin = self._finalizers.pop() + fin() + + def make_account(self, path, logid, quiet=False): + ac = Account(path) + ac._evtracker = ac.add_account_plugin(FFIEventTracker(ac)) + ac._configtracker = ac.add_account_plugin(ConfigureTracker()) + if not quiet: + ac.add_account_plugin(FFIEventLogger(ac, logid=logid)) + self._finalizers.append(ac.shutdown) + return ac + + def get_unconfigured_account(self): + self.offline_count += 1 + tmpdb = tmpdir.join("offlinedb%d" % self.offline_count) + ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.offline_count)) + ac._evtracker.init_time = self.init_time + ac._evtracker.set_timeout(2) + return ac + + def _preconfigure_key(self, account, addr): + # Only set a key if we haven't used it yet for another account. + if self._generated_keys: + keyname = self._generated_keys.pop(0) + fname_pub = data.read_path("key/{name}-public.asc".format(name=keyname)) + fname_sec = data.read_path("key/{name}-secret.asc".format(name=keyname)) + if fname_pub and fname_sec: + account._preconfigure_keypair(addr, fname_pub, fname_sec) + return True + else: + print("WARN: could not use preconfigured keys for {!r}".format(addr)) + + def get_configured_offline_account(self): + ac = self.get_unconfigured_account() + + # do a pseudo-configured account + addr = "addr{}@offline.org".format(self.offline_count) + ac.set_config("addr", addr) + self._preconfigure_key(ac, addr) + lib.dc_set_config(ac._dc_context, b"configured_addr", addr.encode("ascii")) + ac.set_config("mail_pw", "123") + lib.dc_set_config(ac._dc_context, b"configured_mail_pw", b"123") + lib.dc_set_config(ac._dc_context, b"configured", b"1") + return ac + + def get_online_config(self, pre_generated_key=True, quiet=False): + if not session_liveconfig: + pytest.skip("specify DCC_NEW_TMP_EMAIL or --liveconfig") + configdict = session_liveconfig.get(self.live_count) + self.live_count += 1 + if "e2ee_enabled" not in configdict: + configdict["e2ee_enabled"] = "1" + + # Enable strict certificate checks for online accounts + configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT) + configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT) + + tmpdb = tmpdir.join("livedb%d" % self.live_count) + ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count), quiet=quiet) + if pre_generated_key: + self._preconfigure_key(ac, configdict['addr']) + ac._evtracker.init_time = self.init_time + ac._evtracker.set_timeout(30) + return ac, dict(configdict) + + def get_online_configuring_account(self, mvbox=False, sentbox=False, move=False, + pre_generated_key=True, quiet=False, config={}): + ac, configdict = self.get_online_config( + pre_generated_key=pre_generated_key, quiet=quiet) + configdict.update(config) + configdict["mvbox_watch"] = str(int(mvbox)) + configdict["mvbox_move"] = str(int(move)) + configdict["sentbox_watch"] = str(int(sentbox)) + ac.update_config(configdict) + ac.start() + return ac + + def get_one_online_account(self, pre_generated_key=True, mvbox=False, move=False): + ac1 = self.get_online_configuring_account( + pre_generated_key=pre_generated_key, mvbox=mvbox, move=move) + ac1._configtracker.wait_imap_connected() + ac1._configtracker.wait_smtp_connected() + ac1._configtracker.wait_finish() + return ac1 + + def get_two_online_accounts(self, move=False, quiet=False): + ac1 = self.get_online_configuring_account(move=True, quiet=quiet) + ac2 = self.get_online_configuring_account(quiet=quiet) + ac1._configtracker.wait_finish() + ac2._configtracker.wait_finish() + return ac1, ac2 + + def clone_online_account(self, account, pre_generated_key=True): + self.live_count += 1 + tmpdb = tmpdir.join("livedb%d" % self.live_count) + ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count)) + if pre_generated_key: + self._preconfigure_key(ac, account.get_config("addr")) + ac._evtracker.init_time = self.init_time + ac._evtracker.set_timeout(30) + ac.update_config(dict( + addr=account.get_config("addr"), + mail_pw=account.get_config("mail_pw"), + mvbox_watch=account.get_config("mvbox_watch"), + mvbox_move=account.get_config("mvbox_move"), + sentbox_watch=account.get_config("sentbox_watch"), + )) + ac.start() + return ac + + def run_bot_process(self, module, ffi=True): + fn = module.__file__ + + bot_ac, bot_cfg = self.get_online_config() + + args = [ + sys.executable, + "-u", + fn, + "--email", bot_cfg["addr"], + "--password", bot_cfg["mail_pw"], + bot_ac.db_path, + ] + if ffi: + args.insert(-1, "--show-ffi") + print("$", " ".join(args)) + popen = subprocess.Popen( + args=args, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, # combine stdout/stderr in one stream + bufsize=0, # line buffering + close_fds=True, # close all FDs other than 0/1/2 + universal_newlines=True # give back text + ) + bot = BotProcess(popen, bot_cfg) + self._finalizers.append(bot.kill) + return bot + + am = AccountMaker() + request.addfinalizer(am.finalize) + return am + + +class BotProcess: + def __init__(self, popen, bot_cfg): + self.popen = popen + self.addr = bot_cfg["addr"] + + # we read stdout as quickly as we can in a thread and make + # the (unicode) lines available for readers through a queue. + self.stdout_queue = queue.Queue() + self.stdout_thread = t = threading.Thread(target=self._run_stdout_thread, name="bot-stdout-thread") + t.setDaemon(1) + t.start() + + def _run_stdout_thread(self): + try: + while 1: + line = self.popen.stdout.readline() + if not line: + break + line = line.strip() + self.stdout_queue.put(line) + finally: + self.stdout_queue.put(None) + + def kill(self): + self.popen.kill() + + def wait(self, timeout=30): + self.popen.wait(timeout=timeout) + + def fnmatch_lines(self, pattern_lines): + patterns = [x.strip() for x in Source(pattern_lines.rstrip()).lines if x.strip()] + for next_pattern in patterns: + print("+++FNMATCH:", next_pattern) + ignored = [] + while 1: + line = self.stdout_queue.get(timeout=15) + if line is None: + if ignored: + print("BOT stdout terminated after these lines") + for line in ignored: + print(line) + raise IOError("BOT stdout-thread terminated") + if fnmatch.fnmatch(line, next_pattern): + print("+++MATCHED:", line) + break + else: + print("+++IGN:", line) + ignored.append(line) + + +@pytest.fixture +def tmp_db_path(tmpdir): + return tmpdir.join("test.db").strpath + + +@pytest.fixture +def lp(): + class Printer: + def sec(self, msg): + print() + print("=" * 10, msg, "=" * 10) + + def step(self, msg): + print("-" * 5, "step " + msg, "-" * 5) + return Printer() diff --git a/python/src/deltachat/tracker.py b/python/src/deltachat/tracker.py new file mode 100644 index 000000000..4570f3d4c --- /dev/null +++ b/python/src/deltachat/tracker.py @@ -0,0 +1,76 @@ + +from queue import Queue +from threading import Event + +from .hookspec import account_hookimpl + + +class ImexFailed(RuntimeError): + """ Exception for signalling that import/export operations failed.""" + + +class ImexTracker: + def __init__(self): + self._imex_events = Queue() + + @account_hookimpl + def ac_process_ffi_event(self, ffi_event): + if ffi_event.name == "DC_EVENT_IMEX_PROGRESS": + self._imex_events.put(ffi_event.data1) + elif ffi_event.name == "DC_EVENT_IMEX_FILE_WRITTEN": + self._imex_events.put(ffi_event.data1) + + def wait_finish(self, progress_timeout=60): + """ Return list of written files, raise ValueError if ExportFailed. """ + files_written = [] + while True: + ev = self._imex_events.get(timeout=progress_timeout) + if isinstance(ev, str): + files_written.append(ev) + elif ev == 0: + raise ImexFailed("export failed, exp-files: {}".format(files_written)) + elif ev == 1000: + return files_written + + +class ConfigureFailed(RuntimeError): + """ Exception for signalling that configuration failed.""" + + +class ConfigureTracker: + ConfigureFailed = ConfigureFailed + + def __init__(self): + self._configure_events = Queue() + self._smtp_finished = Event() + self._imap_finished = Event() + self._ffi_events = [] + + @account_hookimpl + def ac_process_ffi_event(self, ffi_event): + self._ffi_events.append(ffi_event) + if ffi_event.name == "DC_EVENT_SMTP_CONNECTED": + self._smtp_finished.set() + elif ffi_event.name == "DC_EVENT_IMAP_CONNECTED": + self._imap_finished.set() + + @account_hookimpl + def ac_configure_completed(self, success): + self._configure_events.put(success) + + def wait_smtp_connected(self): + """ wait until smtp is configured. """ + self._smtp_finished.wait() + + def wait_imap_connected(self): + """ wait until smtp is configured. """ + self._imap_finished.wait() + + def wait_finish(self): + """ wait until configure is completed. + + Raise Exception if Configure failed + """ + if not self._configure_events.get(): + content = "\n".join(map(str, self._ffi_events)) + raise ConfigureFailed(content) diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 425a7f680..109085739 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -1,320 +1,17 @@ from __future__ import print_function -import os -import sys -import py -import pytest -import requests -import time -from deltachat import Account -from deltachat import const -from deltachat.capi import lib -from _pytest.monkeypatch import MonkeyPatch -import tempfile - - -def pytest_addoption(parser): - parser.addoption( - "--liveconfig", action="store", default=None, - help="a file with >=2 lines where each line " - "contains NAME=VALUE config settings for one account" - ) - parser.addoption( - "--ignored", action="store_true", - help="Also run tests marked with the ignored marker", - ) - - -def pytest_configure(config): - config.addinivalue_line( - "markers", "ignored: Mark test as bing slow, skipped unless --ignored is used." - ) - cfg = config.getoption('--liveconfig') - if not cfg: - cfg = os.getenv('DCC_NEW_TMP_EMAIL') - if cfg: - config.option.liveconfig = cfg - - -def pytest_runtest_setup(item): - if (list(item.iter_markers(name="ignored")) - and not item.config.getoption("ignored")): - pytest.skip("Ignored tests not requested, use --ignored") - - -def pytest_report_header(config, startdir): - summary = [] - - t = tempfile.mktemp() - m = MonkeyPatch() - try: - m.setattr(sys.stdout, "write", lambda x: len(x)) - ac = Account(t) - info = ac.get_info() - ac.shutdown() - finally: - m.undo() - os.remove(t) - summary.extend(['Deltachat core={} sqlite={}'.format( - info['deltachat_core_version'], - info['sqlite_version'], - )]) - - cfg = config.option.liveconfig - if cfg: - if "#" in cfg: - url, token = cfg.split("#", 1) - summary.append('Liveconfig provider: {}#'.format(url)) - else: - summary.append('Liveconfig file: {}'.format(cfg)) - return summary - - -@pytest.fixture(scope="session") -def data(): - class Data: - def __init__(self): - self.path = os.path.join(os.path.dirname(__file__), "data") - - def get_path(self, bn): - fn = os.path.join(self.path, bn) - assert os.path.exists(fn) - return fn - return Data() - - -class SessionLiveConfigFromFile: - def __init__(self, fn): - self.fn = fn - self.configlist = [] - for line in open(fn): - if line.strip() and not line.strip().startswith('#'): - d = {} - for part in line.split(): - name, value = part.split("=") - d[name] = value - self.configlist.append(d) - - def get(self, index): - return self.configlist[index] - - def exists(self): - return bool(self.configlist) - - -class SessionLiveConfigFromURL: - def __init__(self, url): - self.configlist = [] - self.url = url - - def get(self, index): - try: - return self.configlist[index] - except IndexError: - assert index == len(self.configlist), index - res = requests.post(self.url) - if res.status_code != 200: - pytest.skip("creating newtmpuser failed {!r}".format(res)) - d = res.json() - config = dict(addr=d["email"], mail_pw=d["password"]) - self.configlist.append(config) - return config - - def exists(self): - return bool(self.configlist) - - -@pytest.fixture(scope="session") -def session_liveconfig(request): - liveconfig_opt = request.config.option.liveconfig - if liveconfig_opt: - if liveconfig_opt.startswith("http"): - return SessionLiveConfigFromURL(liveconfig_opt) - else: - return SessionLiveConfigFromFile(liveconfig_opt) - - -@pytest.fixture(scope='session') -def datadir(): - """The py.path.local object of the test-data/ directory.""" - for path in reversed(py.path.local(__file__).parts()): - datadir = path.join('test-data') - if datadir.isdir(): - return datadir - else: - pytest.skip('test-data directory not found') - - -@pytest.fixture -def acfactory(pytestconfig, tmpdir, request, session_liveconfig, datadir): - - class AccountMaker: - def __init__(self): - self.live_count = 0 - self.offline_count = 0 - self._finalizers = [] - self.init_time = time.time() - self._generated_keys = ["alice", "bob", "charlie", - "dom", "elena", "fiona"] - - def finalize(self): - while self._finalizers: - fin = self._finalizers.pop() - fin() - - def make_account(self, path, logid): - ac = Account(path, logid=logid) - self._finalizers.append(ac.shutdown) - return ac - - def get_unconfigured_account(self): - self.offline_count += 1 - tmpdb = tmpdir.join("offlinedb%d" % self.offline_count) - ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.offline_count)) - ac._evlogger.init_time = self.init_time - ac._evlogger.set_timeout(2) - return ac - - def _preconfigure_key(self, account, addr): - # Only set a key if we haven't used it yet for another account. - if self._generated_keys: - keyname = self._generated_keys.pop(0) - fname_pub = "key/{name}-public.asc".format(name=keyname) - fname_sec = "key/{name}-secret.asc".format(name=keyname) - account._preconfigure_keypair(addr, - datadir.join(fname_pub).read(), - datadir.join(fname_sec).read()) - - def get_configured_offline_account(self): - ac = self.get_unconfigured_account() - - # do a pseudo-configured account - addr = "addr{}@offline.org".format(self.offline_count) - ac.set_config("addr", addr) - self._preconfigure_key(ac, addr) - lib.dc_set_config(ac._dc_context, b"configured_addr", addr.encode("ascii")) - ac.set_config("mail_pw", "123") - lib.dc_set_config(ac._dc_context, b"configured_mail_pw", b"123") - lib.dc_set_config(ac._dc_context, b"configured", b"1") - return ac - - def get_online_config(self, pre_generated_key=True): - if not session_liveconfig: - pytest.skip("specify DCC_NEW_TMP_EMAIL or --liveconfig") - configdict = session_liveconfig.get(self.live_count) - self.live_count += 1 - if "e2ee_enabled" not in configdict: - configdict["e2ee_enabled"] = "1" - - # Enable strict certificate checks for online accounts - configdict["imap_certificate_checks"] = str(const.DC_CERTCK_STRICT) - configdict["smtp_certificate_checks"] = str(const.DC_CERTCK_STRICT) - - tmpdb = tmpdir.join("livedb%d" % self.live_count) - ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count)) - if pre_generated_key: - self._preconfigure_key(ac, configdict['addr']) - ac._evlogger.init_time = self.init_time - ac._evlogger.set_timeout(30) - return ac, dict(configdict) - - def get_online_configuring_account(self, mvbox=False, sentbox=False, - pre_generated_key=True, config={}): - ac, configdict = self.get_online_config( - pre_generated_key=pre_generated_key) - configdict.update(config) - ac.configure(**configdict) - return ac - - def get_one_online_account(self, pre_generated_key=True): - ac1 = self.get_online_configuring_account( - pre_generated_key=pre_generated_key) - wait_successful_IMAP_SMTP_connection(ac1) - wait_configuration_progress(ac1, 1000) - ac1.start_threads() - return ac1 - - def get_two_online_accounts(self): - ac1 = self.get_online_configuring_account() - ac2 = self.get_online_configuring_account() - wait_successful_IMAP_SMTP_connection(ac1) - wait_configuration_progress(ac1, 1000) - ac1.start_threads() - wait_successful_IMAP_SMTP_connection(ac2) - wait_configuration_progress(ac2, 1000) - ac2.start_threads() - - return ac1, ac2 - - def clone_online_account(self, account, pre_generated_key=True): - self.live_count += 1 - tmpdb = tmpdir.join("livedb%d" % self.live_count) - ac = self.make_account(tmpdb.strpath, logid="ac{}".format(self.live_count)) - if pre_generated_key: - self._preconfigure_key(ac, account.get_config("addr")) - ac._evlogger.init_time = self.init_time - ac._evlogger.set_timeout(30) - ac.configure(addr=account.get_config("addr"), mail_pw=account.get_config("mail_pw")) - - return ac - - am = AccountMaker() - request.addfinalizer(am.finalize) - return am - - -@pytest.fixture -def tmp_db_path(tmpdir): - return tmpdir.join("test.db").strpath - - -@pytest.fixture -def lp(): - class Printer: - def sec(self, msg): - print() - print("=" * 10, msg, "=" * 10) - - def step(self, msg): - print("-" * 5, "step " + msg, "-" * 5) - return Printer() - def wait_configuration_progress(account, min_target, max_target=1001, check_error=True): min_target = min(min_target, max_target) while 1: - evt_name, data1, data2 = \ - account._evlogger.get_matching("DC_EVENT_CONFIGURE_PROGRESS", check_error=check_error) - if data1 >= min_target and data1 <= max_target: + event = account._evtracker.get_matching("DC_EVENT_CONFIGURE_PROGRESS") + if event.data1 >= min_target and event.data1 <= max_target: print("** CONFIG PROGRESS {}".format(min_target), account) break def wait_securejoin_inviter_progress(account, target): while 1: - evt_name, data1, data2 = \ - account._evlogger.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS") - if data2 >= target: + event = account._evtracker.get_matching("DC_EVENT_SECUREJOIN_INVITER_PROGRESS") + if event.data2 >= target: print("** SECUREJOINT-INVITER PROGRESS {}".format(target), account) break - - -def wait_successful_IMAP_SMTP_connection(account): - imap_ok = smtp_ok = False - while not imap_ok or not smtp_ok: - evt_name, data1, data2 = \ - account._evlogger.get_matching("DC_EVENT_(IMAP|SMTP)_CONNECTED") - if evt_name == "DC_EVENT_IMAP_CONNECTED": - imap_ok = True - print("** IMAP OK", account) - if evt_name == "DC_EVENT_SMTP_CONNECTED": - smtp_ok = True - print("** SMTP OK", account) - print("** IMAP and SMTP logins successful", account) - - -def wait_msgs_changed(account, chat_id, msg_id=None): - ev = account._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev[1] == chat_id - if msg_id is not None: - assert ev[2] == msg_id - return ev[2] diff --git a/python/tests/data/key b/python/tests/data/key new file mode 120000 index 000000000..0351f725d --- /dev/null +++ b/python/tests/data/key @@ -0,0 +1 @@ +../../../test-data/key \ No newline at end of file diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 4108a5a32..17e5b725c 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -5,11 +5,23 @@ import queue import time from deltachat import const, Account from deltachat.message import Message +from deltachat.hookspec import account_hookimpl from datetime import datetime, timedelta from conftest import (wait_configuration_progress, wait_securejoin_inviter_progress) +@pytest.mark.parametrize("msgtext,res", [ + ("Member Me (tmp1@x.org) removed by tmp2@x.org.", ("removed", "tmp1@x.org")), + ("Member tmp1@x.org added by tmp2@x.org.", ("added", "tmp1@x.org")), +]) +def test_parse_system_add_remove(msgtext, res): + from deltachat.message import parse_system_add_remove + + out = parse_system_add_remove(msgtext) + assert out == res + + class TestOfflineAccountBasic: def test_wrong_db(self, tmpdir): p = tmpdir.join("hello.db") @@ -25,11 +37,12 @@ class TestOfflineAccountBasic: ac1 = Account(p.strpath, os_name="solarpunk") ac1.get_info() - def test_preconfigure_keypair(self, acfactory, datadir): + def test_preconfigure_keypair(self, acfactory, data): ac = acfactory.get_unconfigured_account() - ac._preconfigure_keypair("alice@example.com", - datadir.join("key/alice-public.asc").read(), - datadir.join("key/alice-secret.asc").read()) + alice_public = data.read_path("key/alice-public.asc") + alice_secret = data.read_path("key/alice-secret.asc") + assert alice_public and alice_secret + ac._preconfigure_keypair("alice@example.com", alice_public, alice_secret) def test_getinfo(self, acfactory): ac1 = acfactory.get_unconfigured_account() @@ -62,8 +75,7 @@ class TestOfflineAccountBasic: def test_selfcontact_if_unconfigured(self, acfactory): ac1 = acfactory.get_unconfigured_account() - with pytest.raises(ValueError): - ac1.get_self_contact() + assert not ac1.get_self_contact().addr def test_selfcontact_configured(self, acfactory): ac1 = acfactory.get_configured_offline_account() @@ -146,6 +158,9 @@ class TestOfflineChat: str(chat1) repr(chat1) + def test_is_group(self, chat1): + assert not chat1.is_group() + def test_chat_by_id(self, chat1): chat2 = chat1.account.get_chat_by_id(chat1.id) assert chat2 == chat1 @@ -188,18 +203,17 @@ class TestOfflineChat: # assert d["param"] == chat.param assert d["color"] == chat.get_color() assert d["profile_image"] == "" if chat.get_profile_image() is None else chat.get_profile_image() - assert d["subtitle"] == chat.get_subtitle() assert d["draft"] == "" if chat.get_draft() is None else chat.get_draft() def test_group_chat_creation_with_translation(self, ac1): ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %1$s") - ac1._evlogger.consume_events() + ac1._evtracker.consume_events() with pytest.raises(ValueError): ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %2$s") - ac1._evlogger.get_matching("DC_EVENT_WARNING") + ac1._evtracker.get_matching("DC_EVENT_WARNING") with pytest.raises(ValueError): ac1.set_stock_translation(500, "xyz %1$s") - ac1._evlogger.get_matching("DC_EVENT_WARNING") + ac1._evtracker.get_matching("DC_EVENT_WARNING") contact1 = ac1.create_contact("some1@hello.com", name="some1") contact2 = ac1.create_contact("some2@hello.com", name="some2") chat = ac1.create_group_chat(name="title1") @@ -216,6 +230,7 @@ class TestOfflineChat: def test_group_chat_qr(self, acfactory, ac1, verified): ac2 = acfactory.get_configured_offline_account() chat = ac1.create_group_chat(name="title1", verified=verified) + assert chat.is_group() qr = chat.get_join_qr() assert ac2.check_qr(qr).is_ask_verifygroup @@ -242,7 +257,7 @@ class TestOfflineChat: def test_delete_and_send_fails(self, ac1, chat1): chat1.delete() - ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + ac1._evtracker.wait_next_messages_changed() with pytest.raises(ValueError): chat1.send_text("msg1") @@ -305,9 +320,9 @@ class TestOfflineChat: chat1.send_image(path="notexists") fn = data.get_path("d.png") lp.sec("sending image") - chat1.account._evlogger.consume_events() + chat1.account._evtracker.consume_events() msg = chat1.send_image(fn) - chat1.account._evlogger.get_matching("DC_EVENT_NEW_BLOB_FILE") + chat1.account._evtracker.get_matching("DC_EVENT_NEW_BLOB_FILE") assert msg.is_image() assert msg assert msg.id > 0 @@ -333,6 +348,17 @@ class TestOfflineChat: assert msg2 != msg assert msg2.filename != msg.filename + def test_create_contact(self, acfactory): + ac1 = acfactory.get_configured_offline_account() + email = "hello " + contact1 = ac1.create_contact(email) + assert contact1.addr == "hello@example.org" + assert contact1.display_name == "hello" + contact1 = ac1.create_contact(email, name="world") + assert contact1.display_name == "world" + contact2 = ac1.create_contact("display1 ", "real") + assert contact2.display_name == "real" + def test_create_chat_mismatch(self, acfactory): ac1 = acfactory.get_configured_offline_account() ac2 = acfactory.get_configured_offline_account() @@ -354,13 +380,11 @@ class TestOfflineChat: contact = msg.get_sender_contact() assert contact == ac1.get_self_contact() - def test_basic_configure_ok_addr_setting_forbidden(self, ac1): + def test_set_config_after_configure_is_forbidden(self, ac1): assert ac1.get_config("mail_pw") assert ac1.is_configured() with pytest.raises(ValueError): ac1.set_config("addr", "123@example.org") - with pytest.raises(ValueError): - ac1.configure(addr="123@example.org") def test_import_export_one_contact(self, acfactory, tmpdir): backupdir = tmpdir.mkdir("backup") @@ -419,20 +443,65 @@ class TestOfflineChat: def test_group_chat_many_members_add_remove(self, ac1, lp): lp.sec("ac1: creating group chat with 10 other members") chat = ac1.create_group_chat(name="title1") + # promote chat + chat.send_text("hello") + assert chat.is_promoted() + + # activate local plugin + in_list = [] + + class InPlugin: + @account_hookimpl + def ac_member_added(self, chat, contact): + in_list.append(("added", chat, contact)) + + @account_hookimpl + def ac_member_removed(self, chat, contact): + in_list.append(("removed", chat, contact)) + + ac1.add_account_plugin(InPlugin()) + + # perform add contact many times contacts = [] for i in range(10): + lp.sec("create contact") contact = ac1.create_contact("some{}@example.org".format(i)) contacts.append(contact) + lp.sec("add contact") chat.add_contact(contact) num_contacts = len(chat.get_contacts()) assert num_contacts == 11 + # perform plugin hooks + ac1._handle_current_events() + + assert len(in_list) == 10 + chat_contacts = chat.get_contacts() + for in_cmd, in_chat, in_contact in in_list: + assert in_cmd == "added" + assert in_chat == chat + assert in_contact in chat_contacts + chat_contacts.remove(in_contact) + + assert chat_contacts[0].id == 1 # self contact + + in_list[:] = [] + lp.sec("ac1: removing two contacts and checking things are right") chat.remove_contact(contacts[9]) chat.remove_contact(contacts[3]) assert len(chat.get_contacts()) == 9 + ac1._handle_current_events() + assert len(in_list) == 2 + assert in_list[0][0] == "removed" + assert in_list[0][1] == chat + assert in_list[0][2] == contacts[9] + assert in_list[1][0] == "removed" + assert in_list[1][1] == chat + assert in_list[1][2] == contacts[3] + class TestOnlineAccount: def get_chat(self, ac1, ac2, both_created=False): @@ -443,6 +512,11 @@ class TestOnlineAccount: ac2.create_chat_by_contact(ac2.create_contact(email=ac1.get_config("addr"))) return chat + def test_double_iter_events(self, acfactory): + ac1 = acfactory.get_one_online_account() + with pytest.raises(RuntimeError): + next(ac1.iter_events()) + @pytest.mark.ignored def test_configure_generate_key(self, acfactory, lp): # A slow test which will generate new keys. @@ -454,6 +528,8 @@ class TestOnlineAccount: pre_generated_key=False, config={"key_gen_type": str(const.DC_KEY_GEN_ED25519)} ) + # rsa key gen can be slow especially on CI, adjust timeout + ac1._evtracker.set_timeout(120) wait_configuration_progress(ac1, 1000) ac1.start_threads() wait_configuration_progress(ac2, 1000) @@ -463,24 +539,24 @@ class TestOnlineAccount: lp.sec("ac1: send unencrypted message to ac2") chat.send_text("message1") lp.sec("ac2: waiting for message from ac1") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg_in = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev.data2) assert msg_in.text == "message1" assert not msg_in.is_encrypted() lp.sec("ac2: send encrypted message to ac1") msg_in.chat.send_text("message2") lp.sec("ac1: waiting for message from ac2") - ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg2_in = ac1.get_message_by_id(ev[2]) + ev = ac1._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg2_in = ac1.get_message_by_id(ev.data2) assert msg2_in.text == "message2" assert msg2_in.is_encrypted() lp.sec("ac1: send encrypted message to ac2") msg2_in.chat.send_text("message3") lp.sec("ac2: waiting for message from ac1") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg3_in = ac1.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg3_in = ac1.get_message_by_id(ev.data2) assert msg3_in.text == "message3" assert msg3_in.is_encrypted() @@ -500,8 +576,8 @@ class TestOnlineAccount: assert len(export_files) == 2 for x in export_files: assert x.startswith(dir.strpath) - ac1._evlogger.consume_events() - ac1.import_self_keys(dir.strpath) + ac1._evtracker.consume_events() + ac2.import_self_keys(dir.strpath) def test_one_account_send_bcc_setting(self, acfactory, lp): ac1 = acfactory.get_online_configuring_account() @@ -529,13 +605,13 @@ class TestOnlineAccount: assert not msg_out.is_forwarded() # wait for send out (no BCC) - ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ev = ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") assert ac1.get_config("bcc_self") == "0" # make sure we are not sending message to ourselves - assert self_addr not in ev[2] - assert other_addr in ev[2] - ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE") + assert self_addr not in ev.data2 + assert other_addr in ev.data2 + ev = ac1._evtracker.get_matching("DC_EVENT_DELETED_BLOB_FILE") lp.sec("ac1: setting bcc_self=1") ac1.set_config("bcc_self", "1") @@ -544,17 +620,17 @@ class TestOnlineAccount: msg_out = chat.send_text("message2") # wait for send out (BCC) - ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ev = ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") assert ac1.get_config("bcc_self") == "1" # now make sure we are sending message to ourselves too - assert self_addr in ev[2] - assert other_addr in ev[2] - ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE") + assert self_addr in ev.data2 + assert other_addr in ev.data2 + ev = ac1._evtracker.get_matching("DC_EVENT_DELETED_BLOB_FILE") # Second client receives only second message, but not the first - ev = ac1_clone._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ac1_clone.get_message_by_id(ev[2]).text == msg_out.text + ev_msg = ac1_clone._evtracker.wait_next_messages_changed() + assert ev_msg.text == msg_out.text def test_send_file_twice_unicode_filename_mangling(self, tmpdir, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -573,9 +649,9 @@ class TestOnlineAccount: chat.send_msg(msg1) lp.sec("ac2: receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL - return ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev.data2 > const.DC_CHAT_ID_LAST_SPECIAL + return ac2.get_message_by_id(ev.data2) msg = send_and_receive_message() assert msg.text == "withfile" @@ -605,16 +681,16 @@ class TestOnlineAccount: chat.send_file(p, mime_type="text/html") lp.sec("ac2: receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL - msg = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev.data2 > const.DC_CHAT_ID_LAST_SPECIAL + msg = ac2.get_message_by_id(ev.data2) assert open(msg.filename).read() == content assert msg.filename.endswith(basename) def test_mvbox_sentbox_threads(self, acfactory, lp): lp.sec("ac1: start with mvbox thread") - ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True) + ac1 = acfactory.get_online_configuring_account(mvbox=True, move=True, sentbox=True) lp.sec("ac2: start without mvbox/sentbox threads") ac2 = acfactory.get_online_configuring_account() @@ -630,25 +706,25 @@ class TestOnlineAccount: lp.sec("ac1: send message and wait for ac2 to receive it") chat = self.get_chat(ac1, ac2) chat.send_text("message1") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev.data2 > const.DC_CHAT_ID_LAST_SPECIAL lp.sec("test finished") def test_move_works(self, acfactory): ac1 = acfactory.get_online_configuring_account() - ac2 = acfactory.get_online_configuring_account(mvbox=True) + ac2 = acfactory.get_online_configuring_account(mvbox=True, move=True) wait_configuration_progress(ac2, 1000) ac2.start_threads() wait_configuration_progress(ac1, 1000) ac1.start_threads() chat = self.get_chat(ac1, ac2) chat.send_text("message1") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL - ev = ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev.data2 > const.DC_CHAT_ID_LAST_SPECIAL + ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") def test_move_works_on_self_sent(self, acfactory): - ac1 = acfactory.get_online_configuring_account(mvbox=True) + ac1 = acfactory.get_online_configuring_account(mvbox=True, move=True) ac1.set_config("bcc_self", "1") ac2 = acfactory.get_online_configuring_account() wait_configuration_progress(ac2, 1000) @@ -659,9 +735,9 @@ class TestOnlineAccount: chat.send_text("message1") chat.send_text("message2") chat.send_text("message3") - ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") - ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") - ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") + ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") + ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") + ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") def test_forward_messages(self, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -671,8 +747,8 @@ class TestOnlineAccount: msg_out = chat.send_text("message2") lp.sec("ac2: wait for receive") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - assert ev[2] == msg_out.id + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev.data2 == msg_out.id msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message2" @@ -704,8 +780,8 @@ class TestOnlineAccount: msg_out = chat.send_text("message2") lp.sec("receiving message") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg_in = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev.data2) assert msg_in.text == "message2" assert not msg_in.is_forwarded() @@ -715,22 +791,22 @@ class TestOnlineAccount: ac1.forward_messages([msg_out], group) # wait for other account to receive - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg_in = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev.data2) assert msg_in.text == "message2" assert msg_in.is_forwarded() def test_send_self_message_and_empty_folder(self, acfactory, lp): - ac1 = acfactory.get_one_online_account() + ac1 = acfactory.get_one_online_account(mvbox=True, move=True) lp.sec("ac1: create self chat") chat = ac1.create_chat_by_contact(ac1.get_self_contact()) chat.send_text("hello") - ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") ac1.empty_server_folders(inbox=True, mvbox=True) - ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED") - assert ev[2] == "DeltaChat" - ev = ac1._evlogger.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED") - assert ev[2] == "INBOX" + ev1 = ac1._evtracker.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED") + ev2 = ac1._evtracker.get_matching("DC_EVENT_IMAP_FOLDER_EMPTIED") + boxes = sorted([ev1.data2, ev2.data2]) + assert boxes == ["DeltaChat", "INBOX"] def test_send_and_receive_message_markseen(self, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -743,15 +819,14 @@ class TestOnlineAccount: lp.sec("sending text message from ac1 to ac2") msg_out = chat.send_text("message1") - ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED") - evt_name, data1, data2 = ev - assert data1 == chat.id - assert data2 == msg_out.id + ev = ac1._evtracker.get_matching("DC_EVENT_MSG_DELIVERED") + assert ev.data1 == chat.id + assert ev.data2 == msg_out.id assert msg_out.is_out_delivered() lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev[2] == msg_out.id + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev.data2 == msg_out.id msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message1" assert not msg_in.is_forwarded() @@ -762,7 +837,7 @@ class TestOnlineAccount: assert msg_in in chat2.get_messages() assert chat2.is_deaddrop() assert chat2.count_fresh_messages() == 0 - assert msg_in.time_received > msg_in.time_sent + assert msg_in.time_received >= msg_out.time_sent lp.sec("create new chat with contact and verify it's proper") chat2b = ac2.create_chat_by_message(msg_in) @@ -773,37 +848,37 @@ class TestOnlineAccount: chat2b.mark_noticed() assert chat2b.count_fresh_messages() == 0 - ac2._evlogger.consume_events() + ac2._evtracker.consume_events() lp.sec("sending a second message from ac1 to ac2") msg_out2 = chat.send_text("message2") lp.sec("wait for ac2 to receive second message") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - assert ev[2] == msg_out2.id + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + assert ev.data2 == msg_out2.id msg_in2 = ac2.get_message_by_id(msg_out2.id) lp.sec("mark messages as seen on ac2, wait for changes on ac1") ac2.mark_seen_messages([msg_in, msg_in2]) lp.step("1") for i in range(2): - 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 + ev = ac1._evtracker.get_matching("DC_EVENT_MSG_READ") + assert ev.data1 > const.DC_CHAT_ID_LAST_SPECIAL + assert ev.data2 > const.DC_MSG_ID_LAST_SPECIAL lp.step("2") assert msg_out.is_out_mdn_received() assert msg_out2.is_out_mdn_received() lp.sec("check that a second call to mark_seen does not create change or smtp job") - ac2._evlogger.consume_events() - ac2.mark_seen_messages([msg_in]) + ac2._evtracker.consume_events() + msg_in.mark_seen() try: - ac2._evlogger.get_matching("DC_EVENT_MSG_READ", timeout=0.01) + ac2._evtracker.get_matching("DC_EVENT_MSG_READ", timeout=0.01) except queue.Empty: pass # mark_seen_messages() has generated events before it returns def test_mdn_asymetric(self, acfactory, lp): - ac1, ac2 = acfactory.get_two_online_accounts() + ac1, ac2 = acfactory.get_two_online_accounts(move=True) lp.sec("ac1: create chat with ac2") chat = self.get_chat(ac1, ac2, both_created=True) @@ -821,7 +896,7 @@ class TestOnlineAccount: ac1.set_config("mdns_enabled", "0") lp.sec("wait for ac2 to receive message") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert len(msg.chat.get_messages()) == 1 @@ -830,7 +905,7 @@ class TestOnlineAccount: lp.sec("ac1: waiting for incoming activity") # MDN should be moved even though MDNs are already disabled - ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") + ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED") assert len(chat.get_messages()) == 1 @@ -848,8 +923,8 @@ class TestOnlineAccount: assert not msg_out.is_encrypted() lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev[2] == msg_out.id + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev.data2 == msg_out.id msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message1" @@ -858,13 +933,20 @@ class TestOnlineAccount: 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]) + ev = ac1._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + assert ev.data1 == chat.id + assert ev.data2 > msg_out.id + msg_back = ac1.get_message_by_id(ev.data2) assert msg_back.text == "message-back" assert msg_back.is_encrypted() + # test get_fresh_messages + fresh_msgs = list(ac1.get_fresh_messages()) + assert len(fresh_msgs) == 1 + assert fresh_msgs[0] == msg_back + msg_back.mark_seen() + assert not list(ac1.get_fresh_messages()) + # Test that we do not gossip peer keys in 1-to-1 chat, # as it makes no sense to gossip to peers their own keys. # Gossip is only sent in encrypted messages, @@ -876,7 +958,7 @@ class TestOnlineAccount: chat.add_contact(ac1.create_contact(ac2.get_config("addr"))) chat.add_contact(ac1.create_contact("notexisting@testrun.org")) msg = chat.send_text("test not encrypt") - ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ev = ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") assert not msg.is_encrypted() def test_send_first_message_as_long_unicode_with_cr(self, acfactory, lp): @@ -897,11 +979,11 @@ class TestOnlineAccount: assert not msg_out.is_encrypted() lp.sec("wait for ac2 to receive multi-line non-unicode message") - msg_in = ac2.wait_next_incoming_message() + msg_in = ac2._evtracker.wait_next_incoming_message() assert msg_in.text == text1 lp.sec("wait for ac2 to receive multi-line unicode message") - msg_in = ac2.wait_next_incoming_message() + msg_in = ac2._evtracker.wait_next_incoming_message() assert msg_in.text == text2 assert ac1.get_config("addr") in msg_in.chat.get_name() @@ -916,7 +998,7 @@ class TestOnlineAccount: assert not msg_out.is_encrypted() lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message1" assert not msg_in.is_encrypted() @@ -926,9 +1008,9 @@ class TestOnlineAccount: 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 - msg_back = ac1.get_message_by_id(ev[2]) + ev = ac1._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + assert ev.data1 == chat.id + msg_back = ac1.get_message_by_id(ev.data2) assert msg_back.text == "message-back" assert msg_back.is_encrypted() @@ -953,8 +1035,8 @@ class TestOnlineAccount: assert chat.get_draft() is None lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg_in = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev.data2) assert msg_in.text == "message2 -- should be encrypted" assert msg_in.is_encrypted() @@ -967,12 +1049,12 @@ class TestOnlineAccount: lp.sec("sending text message from ac1 to ac2") msg_out = chat.send_text("message1") - ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED") + ac1._evtracker.get_matching("DC_EVENT_MSG_DELIVERED") assert msg_out.get_mime_headers() is None lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - in_id = ev[2] + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + in_id = ev.data2 mime = ac2.get_message_by_id(in_id).get_mime_headers() assert mime.get_all("From") assert mime.get_all("Received") @@ -981,22 +1063,49 @@ class TestOnlineAccount: ac1, ac2 = acfactory.get_two_online_accounts() chat = self.get_chat(ac1, ac2) + message_queue = queue.Queue() + + class InPlugin: + @account_hookimpl + def ac_incoming_message(self, message): + message_queue.put(message) + + delivered = queue.Queue() + out = queue.Queue() + + class OutPlugin: + @account_hookimpl + def ac_message_delivered(self, message): + delivered.put(message) + + @account_hookimpl + def ac_outgoing_message(self, message): + out.put(message) + + ac1.add_account_plugin(OutPlugin()) + ac2.add_account_plugin(InPlugin()) + lp.sec("sending image message from ac1 to ac2") path = data.get_path("d.png") msg_out = chat.send_image(path) - ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED") - evt_name, data1, data2 = ev - assert data1 == chat.id - assert data2 == msg_out.id + ev = ac1._evtracker.get_matching("DC_EVENT_MSG_DELIVERED") + assert ev.data1 == chat.id + assert ev.data2 == msg_out.id assert msg_out.is_out_delivered() + m = out.get() + assert m == msg_out + m = delivered.get() + assert m == msg_out lp.sec("wait for ac2 to receive message") - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev[2] == msg_out.id + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev.data2 == msg_out.id msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.is_image() assert os.path.exists(msg_in.filename) assert os.stat(msg_in.filename).st_size == os.stat(path).st_size + m = message_queue.get() + assert m == msg_in def test_import_export_online_all(self, acfactory, tmpdir, lp): ac1 = acfactory.get_online_configuring_account() @@ -1056,9 +1165,9 @@ class TestOnlineAccount: lp.sec("trigger ac setup message and return setupcode") assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"] setup_code = ac1.initiate_key_transfer() - ac2._evlogger.set_timeout(30) - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - msg = ac2.get_message_by_id(ev[2]) + ac2._evtracker.set_timeout(30) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + msg = ac2.get_message_by_id(ev.data2) assert msg.is_setup_message() assert msg.get_setupcodebegin() == setup_code[:2] lp.sec("try a bad setup code") @@ -1073,7 +1182,7 @@ class TestOnlineAccount: def test_ac_setup_message_twice(self, acfactory, lp): ac1 = acfactory.get_online_configuring_account() ac2 = acfactory.clone_online_account(ac1) - ac2._evlogger.set_timeout(30) + ac2._evtracker.set_timeout(30) wait_configuration_progress(ac2, 1000) ac2.start_threads() wait_configuration_progress(ac1, 1000) @@ -1082,12 +1191,12 @@ class TestOnlineAccount: lp.sec("trigger ac setup message but ignore") assert ac1.get_info()["fingerprint"] != ac2.get_info()["fingerprint"] ac1.initiate_key_transfer() - ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") lp.sec("trigger second ac setup message, wait for receive ") setup_code2 = ac1.initiate_key_transfer() - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") - msg = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + msg = ac2.get_message_by_id(ev.data2) assert msg.is_setup_message() assert msg.get_setupcodebegin() == setup_code2[:2] @@ -1099,6 +1208,7 @@ class TestOnlineAccount: ac1, ac2 = acfactory.get_two_online_accounts() lp.sec("ac1: create QR code and let ac2 scan it, starting the securejoin") qr = ac1.get_setup_contact_qr() + lp.sec("ac2: start QR-code based setup contact protocol") ch = ac2.qr_setup_contact(qr) assert ch.id >= 10 @@ -1113,10 +1223,9 @@ class TestOnlineAccount: ch = ac2.qr_join_chat(qr) assert ch.id >= 10 # check that at least some of the handshake messages are deleted - ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") - ac2._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") + ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") + ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") wait_securejoin_inviter_progress(ac1, 1000) - ac1._evlogger.get_matching("DC_EVENT_SECUREJOIN_MEMBER_ADDED") def test_qr_verified_group_and_chatting(self, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -1128,10 +1237,9 @@ class TestOnlineAccount: chat2 = ac2.qr_join_chat(qr) assert chat2.id >= 10 wait_securejoin_inviter_progress(ac1, 1000) - ac1._evlogger.get_matching("DC_EVENT_SECUREJOIN_MEMBER_ADDED") lp.sec("ac2: read member added message") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert msg.is_encrypted() assert "added" in msg.text.lower() @@ -1140,14 +1248,14 @@ class TestOnlineAccount: assert msg_out.is_encrypted() lp.sec("ac2: read message and check it's verified chat") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert msg.text == "hello" assert msg.chat.is_verified() assert msg.is_encrypted() lp.sec("ac2: send message and let ac1 read it") chat2.send_text("world") - msg = ac1.wait_next_incoming_message() + msg = ac1._evtracker.wait_next_incoming_message() assert msg.text == "world" assert msg.is_encrypted() @@ -1166,7 +1274,7 @@ class TestOnlineAccount: assert not msg.is_encrypted() lp.sec("ac2: wait for receiving message and avatar from ac1") - msg1 = ac2.wait_next_incoming_message() + msg1 = ac2._evtracker.wait_next_incoming_message() assert not msg1.chat.is_deaddrop() received_path = msg1.get_sender_contact().get_profile_image() assert open(received_path, "rb").read() == open(p, "rb").read() @@ -1180,13 +1288,13 @@ class TestOnlineAccount: assert m.is_encrypted() lp.sec("ac1: wait for receiving message and avatar from ac2") - msg2 = ac1.wait_next_incoming_message() + msg2 = ac1._evtracker.wait_next_incoming_message() received_path = msg2.get_sender_contact().get_profile_image() assert received_path is not None, "did not get avatar through encrypted message" assert open(received_path, "rb").read() == open(p, "rb").read() - ac2._evlogger.consume_events() - ac1._evlogger.consume_events() + ac2._evtracker.consume_events() + ac1._evtracker.consume_events() # XXX not sure if the following is correct / possible. you may remove it lp.sec("ac1: delete profile image from chat, and send message to ac2") @@ -1195,9 +1303,83 @@ class TestOnlineAccount: assert m.is_encrypted() lp.sec("ac2: wait for message along with avatar deletion of ac1") - msg3 = ac2.wait_next_incoming_message() + msg3 = ac2._evtracker.wait_next_incoming_message() assert msg3.get_sender_contact().get_profile_image() is None + def test_add_remove_member_remote_events(self, acfactory, lp): + ac1, ac2 = acfactory.get_two_online_accounts() + ac1_addr = ac1.get_config("addr") + ac2_addr = ac2.get_config("addr") + # activate local plugin for ac2 + in_list = queue.Queue() + + class EventHolder: + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + class InPlugin: + @account_hookimpl + def ac_incoming_message(self, message): + # we immediately accept the sender because + # otherwise we won't see member_added contacts + message.accept_sender_contact() + + @account_hookimpl + def ac_chat_modified(self, chat): + in_list.put(EventHolder(action="chat-modified", chat=chat)) + + @account_hookimpl + def ac_member_added(self, chat, contact, message): + in_list.put(EventHolder(action="added", chat=chat, contact=contact, message=message)) + + @account_hookimpl + def ac_member_removed(self, chat, contact, message): + in_list.put(EventHolder(action="removed", chat=chat, contact=contact, message=message)) + + ac2.add_account_plugin(InPlugin()) + + lp.sec("ac1: create group chat with ac2") + chat = ac1.create_group_chat("hello") + contact = ac1.create_contact(email=ac2_addr) + chat.add_contact(contact) + + lp.sec("ac1: send a message to group chat to promote the group") + chat.send_text("afterwards promoted") + ev = in_list.get(timeout=10) + assert ev.action == "chat-modified" + assert chat.is_promoted() + assert sorted(x.addr for x in chat.get_contacts()) == \ + sorted(x.addr for x in ev.chat.get_contacts()) + + lp.sec("ac1: add address2") + # note that if the above accept_sender_contact() would not + # happen we would not receive a proper member_added event + contact2 = ac1.create_contact(email="notexistingaccountihope@testrun.org") + chat.add_contact(contact2) + ev = in_list.get(timeout=10) + assert ev.action == "chat-modified" + ev = in_list.get(timeout=10) + assert ev.action == "added" + assert ev.message.get_sender_contact().addr == ac1_addr + assert ev.contact.addr == "notexistingaccountihope@testrun.org" + + lp.sec("ac1: remove address2") + chat.remove_contact(contact2) + ev = in_list.get(timeout=10) + assert ev.action == "chat-modified" + ev = in_list.get(timeout=10) + assert ev.action == "removed" + assert ev.contact.addr == contact2.addr + assert ev.message.get_sender_contact().addr == ac1_addr + + lp.sec("ac1: remove ac2 contact from chat") + chat.remove_contact(contact) + ev = in_list.get(timeout=10) + assert ev.action == "chat-modified" + ev = in_list.get(timeout=10) + assert ev.action == "removed" + assert ev.message.get_sender_contact().addr == ac1_addr + def test_set_get_group_image(self, acfactory, data, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -1207,7 +1389,7 @@ class TestOnlineAccount: lp.sec("ac1: set profile image on unpromoted chat") chat.set_profile_image(p) - ac1._evlogger.get_matching("DC_EVENT_CHAT_MODIFIED") + ac1._evtracker.get_matching("DC_EVENT_CHAT_MODIFIED") assert not chat.is_promoted() lp.sec("ac1: send text to promote chat (XXX without contact added)") @@ -1220,7 +1402,7 @@ class TestOnlineAccount: lp.sec("ac2: add ac1 to a chat so the message does not land in DEADDROP") c1 = ac2.create_contact(email=ac1.get_config("addr")) ac2.create_chat_by_contact(c1) - ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + ev = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") lp.sec("ac1: add ac2 to promoted group chat") c2 = ac1.create_contact(email=ac2.get_config("addr")) @@ -1231,8 +1413,8 @@ class TestOnlineAccount: assert chat.is_promoted() lp.sec("ac2: wait for receiving message from ac1") - ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - msg_in = ac2.get_message_by_id(ev[2]) + ev = ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + msg_in = ac2.get_message_by_id(ev.data2) assert not msg_in.chat.is_deaddrop() lp.sec("ac2: create chat and read profile image") @@ -1241,16 +1423,25 @@ class TestOnlineAccount: assert p2 is not None assert open(p2, "rb").read() == open(p, "rb").read() - ac2._evlogger.consume_events() - ac1._evlogger.consume_events() + ac2._evtracker.consume_events() + ac1._evtracker.consume_events() lp.sec("ac2: delete profile image from chat") chat2.remove_profile_image() - ev = ac1._evlogger.get_matching("DC_EVENT_INCOMING_MSG") - assert ev[1] == chat.id - chat1b = ac1.create_chat_by_message(ev[2]) + ev = ac1._evtracker.get_matching("DC_EVENT_INCOMING_MSG") + assert ev.data1 == chat.id + chat1b = ac1.create_chat_by_message(ev.data2) assert chat1b.get_profile_image() is None assert chat.get_profile_image() is None + def test_accept_sender_contact(self, acfactory, lp): + ac1, ac2 = acfactory.get_two_online_accounts() + ch = ac1.create_chat_by_contact(ac1.create_contact(ac2.get_config("addr"))) + ch.send_text("hello") + msg = ac2._evtracker.wait_next_messages_changed() + assert msg.chat.is_deaddrop() + msg.accept_sender_contact() + assert not msg.chat.is_deaddrop() + def test_send_receive_locations(self, acfactory, lp): now = datetime.utcnow() ac1, ac2 = acfactory.get_two_online_accounts() @@ -1263,25 +1454,25 @@ class TestOnlineAccount: with pytest.raises(ValueError): ac1.set_location(latitude=0.0, longitude=10.0) - ac1._evlogger.consume_events() - ac2._evlogger.consume_events() + ac1._evtracker.consume_events() + ac2._evtracker.consume_events() lp.sec("ac1: enable location sending in chat") chat1.enable_sending_locations(seconds=100) assert chat1.is_sending_locations() - ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") ac1.set_location(latitude=2.0, longitude=3.0, accuracy=0.5) - ac1._evlogger.get_matching("DC_EVENT_LOCATION_CHANGED") + ac1._evtracker.get_matching("DC_EVENT_LOCATION_CHANGED") chat1.send_text("hello") - ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") + ac1._evtracker.get_matching("DC_EVENT_SMTP_MESSAGE_SENT") lp.sec("ac2: wait for incoming location message") - ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") # "enabled-location streaming" + ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") # "enabled-location streaming" # currently core emits location changed before event_incoming message - ac2._evlogger.get_matching("DC_EVENT_LOCATION_CHANGED") - ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG") # text message with location + ac2._evtracker.get_matching("DC_EVENT_LOCATION_CHANGED") + ac2._evtracker.get_matching("DC_EVENT_INCOMING_MSG") # text message with location locations = chat2.get_locations() assert len(locations) == 1 @@ -1324,7 +1515,7 @@ class TestGroupStressTests: # send a message to get the contact key via autocrypt header chat1.send_text("hi") - msg = ac1.wait_next_incoming_message() + msg = ac1._evtracker.wait_next_incoming_message() assert msg.text == "hi" # Save fifth account for later @@ -1350,14 +1541,14 @@ class TestGroupStressTests: lp.sec("ac2: checking that the chat arrived correctly") ac2 = accounts[0] - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert msg.text == "hello" print("chat is", msg.chat) assert len(msg.chat.get_contacts()) == 4 lp.sec("ac3: checking that 'ac4' is a known contact") ac3 = accounts[1] - msg3 = ac3.wait_next_incoming_message() + msg3 = ac3._evtracker.wait_next_incoming_message() assert msg3.text == "hello" ac3_contacts = ac3.get_contacts() assert len(ac3_contacts) == 3 @@ -1366,10 +1557,11 @@ class TestGroupStressTests: lp.sec("ac2: removing one contact") to_remove = contacts[-1] + msg.chat.remove_contact(to_remove) lp.sec("ac1: receiving system message about contact removal") - sysmsg = ac1.wait_next_incoming_message() + sysmsg = ac1._evtracker.wait_next_incoming_message() assert to_remove.addr in sysmsg.text assert len(sysmsg.chat.get_contacts()) == 3 @@ -1378,7 +1570,7 @@ class TestGroupStressTests: lp.sec("ac1: sending another message to the chat") chat.send_text("hello2") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert msg.text == "hello2" assert chat.get_summary()["gossiped_timestamp"] == gossiped_timestamp @@ -1388,12 +1580,12 @@ class TestGroupStressTests: assert chat.get_summary()["gossiped_timestamp"] >= gossiped_timestamp lp.sec("ac2: receiving system message about contact addition") - sysmsg = ac2.wait_next_incoming_message() + sysmsg = ac2._evtracker.wait_next_incoming_message() assert contact5.addr in sysmsg.text assert len(sysmsg.chat.get_contacts()) == 4 lp.sec("ac5: waiting for message about addition to the chat") - sysmsg = ac5.wait_next_incoming_message() + sysmsg = ac5._evtracker.wait_next_incoming_message() msg = sysmsg.chat.send_text("hello!") # Message should be encrypted because keys of other members are gossiped assert msg.is_encrypted() @@ -1428,7 +1620,7 @@ class TestGroupStressTests: # send a message to get the contact key via autocrypt header chat1.send_text("hi") - msg = ac1.wait_next_incoming_message() + msg = ac1._evtracker.wait_next_incoming_message() assert msg.text == "hi" ac2, ac3 = accounts @@ -1449,7 +1641,7 @@ class TestGroupStressTests: lp.sec("checking that the chat arrived correctly") for ac in accounts: - msg = ac.wait_next_incoming_message() + msg = ac._evtracker.wait_next_incoming_message() assert msg.text == "hello" print("chat is", msg.chat) assert len(msg.chat.get_contacts()) == 3 @@ -1458,7 +1650,7 @@ class TestGroupStressTests: chat.remove_contact(contacts[0]) lp.sec("ac2: wait for a message about removal from the chat") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() lp.sec("ac1: removing ac3") chat.remove_contact(contacts[1]) @@ -1469,7 +1661,7 @@ class TestGroupStressTests: chat.add_contact(contacts[0]) lp.sec("ac2: check that ac3 is removed") - msg = ac2.wait_next_incoming_message() + msg = ac2._evtracker.wait_next_incoming_message() assert len(msg.chat.get_contacts()) == len(chat.get_contacts()) @@ -1477,24 +1669,29 @@ class TestGroupStressTests: class TestOnlineConfigureFails: def test_invalid_password(self, acfactory): ac1, configdict = acfactory.get_online_config() - ac1.configure(addr=configdict["addr"], mail_pw="123") + + ac1.update_config(dict(addr=configdict["addr"], mail_pw="123")) + ac1.start() wait_configuration_progress(ac1, 500) - ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK", check_error=False) - assert "cannot login" in ev1[2].lower() - wait_configuration_progress(ac1, 0, 0, check_error=False) + ev = ac1._evtracker.get_matching("DC_EVENT_ERROR_NETWORK") + assert "cannot login" in ev.data2.lower() + wait_configuration_progress(ac1, 0, 0) def test_invalid_user(self, acfactory): ac1, configdict = acfactory.get_online_config() - ac1.configure(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"]) + ac1.update_config(dict(addr="x" + configdict["addr"], mail_pw=configdict["mail_pw"])) + ac1.start() wait_configuration_progress(ac1, 500) - ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK", check_error=False) - assert "cannot login" in ev1[2].lower() - wait_configuration_progress(ac1, 0, 0, check_error=False) + ev = ac1._evtracker.get_matching("DC_EVENT_ERROR_NETWORK") + assert "cannot login" in ev.data2.lower() + wait_configuration_progress(ac1, 0, 0) def test_invalid_domain(self, acfactory): ac1, configdict = acfactory.get_online_config() - ac1.configure(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"]) - wait_configuration_progress(ac1, 500, check_error=False) - ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK") - assert "could not connect" in ev1[2].lower() - wait_configuration_progress(ac1, 0, 0, check_error=False) + ac1.update_config((dict(addr=configdict["addr"] + "x", mail_pw=configdict["mail_pw"]))) + ac1.start() + wait_configuration_progress(ac1, 500) + ev = ac1._evtracker.get_matching("DC_EVENT_ERROR_NETWORK") + assert "could not connect" in ev.data2.lower() + wait_configuration_progress(ac1, 0, 0) + diff --git a/python/tests/test_increation.py b/python/tests/test_increation.py index 4c31dcaa0..f480ea289 100644 --- a/python/tests/test_increation.py +++ b/python/tests/test_increation.py @@ -6,10 +6,18 @@ import shutil import pytest from filecmp import cmp -from conftest import wait_configuration_progress, wait_msgs_changed +from conftest import wait_configuration_progress from deltachat import const +def wait_msgs_changed(account, chat_id, msg_id=None): + ev = account._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev.data1 == chat_id + if msg_id is not None: + assert ev.data2 == msg_id + return ev.data2 + + class TestOnlineInCreation: def test_increation_not_blobdir(self, tmpdir, acfactory, lp): ac1 = acfactory.get_online_configuring_account() @@ -97,22 +105,22 @@ class TestOnlineInCreation: assert fwd_msg.is_out_pending() or fwd_msg.is_out_delivered() lp.sec("wait for the messages to be delivered to SMTP") - ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED") - assert ev[1] == chat.id - assert ev[2] == prepared_original.id - ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED") - assert ev[1] == chat2.id - assert ev[2] == forwarded_id + ev = ac1._evtracker.get_matching("DC_EVENT_MSG_DELIVERED") + assert ev.data1 == chat.id + assert ev.data2 == prepared_original.id + ev = ac1._evtracker.get_matching("DC_EVENT_MSG_DELIVERED") + assert ev.data1 == chat2.id + assert ev.data2 == forwarded_id lp.sec("wait1 for original or forwarded messages to arrive") - ev1 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev1[1] > const.DC_CHAT_ID_LAST_SPECIAL - received_original = ac2.get_message_by_id(ev1[2]) + ev1 = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev1.data1 > const.DC_CHAT_ID_LAST_SPECIAL + received_original = ac2.get_message_by_id(ev1.data2) assert cmp(received_original.filename, orig, shallow=False) lp.sec("wait2 for original or forwarded messages to arrive") - ev2 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") - assert ev2[1] > const.DC_CHAT_ID_LAST_SPECIAL - assert ev2[1] != ev1[1] - received_copy = ac2.get_message_by_id(ev2[2]) + ev2 = ac2._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") + assert ev2.data1 > const.DC_CHAT_ID_LAST_SPECIAL + assert ev2.data1 != ev1.data1 + received_copy = ac2.get_message_by_id(ev2.data2) assert cmp(received_copy.filename, orig, shallow=False) diff --git a/python/tests/test_lowlevel.py b/python/tests/test_lowlevel.py index 8186071d3..6c62afb0f 100644 --- a/python/tests/test_lowlevel.py +++ b/python/tests/test_lowlevel.py @@ -1,7 +1,10 @@ from __future__ import print_function + import threading import time -from deltachat import capi, cutil, const, set_context_callback, clear_context_callback, py_dc_callback +from deltachat import capi, cutil, const, set_context_callback, clear_context_callback +from deltachat import register_global_plugin +from deltachat.hookspec import global_hookimpl from deltachat.capi import ffi from deltachat.capi import lib # from deltachat.account import EventLogger @@ -47,51 +50,37 @@ def test_callback_None2int(): clear_context_callback(ctx) -def test_start_stop_event_thread_basic(): - print("1") - ctx = capi.lib.dc_context_new(ffi.NULL, ffi.NULL) - print("2") - ev_thread = EventThread(ctx) - print("3 -- starting event thread") - ev_thread.start() - print("4 -- stopping event thread") - ev_thread.stop() +def test_dc_close_events(tmpdir, acfactory): + ac1 = acfactory.get_unconfigured_account() + # register after_shutdown function + shutdowns = [] -# FIXME: EventLogger doesn't work without an account anymore -# def test_dc_close_events(tmpdir): -# ctx = ffi.gc( -# capi.lib.dc_context_new(ffi.NULL, ffi.NULL), -# lib.dc_context_unref, -# ) -# evlog = EventLogger(ctx) -# evlog.set_timeout(5) -# set_context_callback( -# ctx, -# lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2) -# ) -# ev_thread = EventThread(ctx) -# ev_thread.start() + class ShutdownPlugin: + @global_hookimpl + def dc_account_after_shutdown(self, account): + assert account._dc_context is None + shutdowns.append(account) + register_global_plugin(ShutdownPlugin()) + assert hasattr(ac1, "_dc_context") + ac1.shutdown() + assert shutdowns == [ac1] -# p = tmpdir.join("hello.db") -# lib.dc_open(ctx, p.strpath.encode("ascii"), ffi.NULL) -# capi.lib.dc_close(ctx) + def find(info_string): + evlog = ac1._evtracker + while 1: + ev = evlog.get_matching("DC_EVENT_INFO", check_error=False) + data2 = ev.data2 + if info_string in data2: + return + else: + print("skipping event", ev) -# def find(info_string): -# while 1: -# ev = evlog.get_matching("DC_EVENT_INFO", check_error=False) -# data2 = ev[2] -# if info_string in data2: -# return -# else: -# print("skipping event", *ev) - -# find("disconnecting inbox-thread") -# find("disconnecting sentbox-thread") -# find("disconnecting mvbox-thread") -# find("disconnecting SMTP") -# find("Database closed") -# ev_thread.stop() + find("disconnecting inbox-thread") + find("disconnecting sentbox-thread") + find("disconnecting mvbox-thread") + find("disconnecting SMTP") + find("Database closed") def test_wrong_db(tmpdir): @@ -136,10 +125,10 @@ def test_markseen_invalid_message_ids(acfactory): contact1 = ac1.create_contact(email="some1@example.com", name="some1") chat = ac1.create_chat_by_contact(contact1) chat.send_text("one messae") - ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") + ac1._evtracker.get_matching("DC_EVENT_MSGS_CHANGED") msg_ids = [9] lib.dc_markseen_msgs(ac1._dc_context, msg_ids, len(msg_ids)) - ac1._evlogger.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR") + ac1._evtracker.ensure_event_not_queued("DC_EVENT_WARNING|DC_EVENT_ERROR") def test_get_special_message_id_returns_empty_message(acfactory): diff --git a/python/tox.ini b/python/tox.ini index 965607770..3309b08a3 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -7,14 +7,14 @@ envlist = [testenv] commands = - pytest -n6 --reruns 2 --reruns-delay 5 -v -rsXx --ignored {posargs:tests} + pytest -n6 --reruns 2 --reruns-delay 5 -v -rsXx --ignored {posargs: tests examples} python tests/package_wheels.py {toxworkdir}/wheelhouse passenv = TRAVIS DCC_RS_DEV DCC_RS_TARGET DCC_PY_LIVECONFIG - DCC_NEW_TMP_EMAIL + DCC_NEW_TMP_EMAIL CARGO_TARGET_DIR RUSTC_WRAPPER deps = @@ -41,13 +41,13 @@ deps = restructuredtext_lint commands = flake8 src/deltachat - flake8 tests/ + flake8 tests/ examples/ rst-lint --encoding 'utf-8' README.rst [testenv:doc] changedir=doc deps = - sphinx==2.2.0 + sphinx breathe commands = sphinx-build -Q -w toxdoc-warnings.log -b html . _build/html @@ -67,7 +67,6 @@ commands = [pytest] addopts = -v -ra --strict-markers -python_files = tests/test_*.py norecursedirs = .tox xfail_strict=true timeout = 90 diff --git a/src/aheader.rs b/src/aheader.rs index 628de89f7..6dee60eeb 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -127,14 +127,10 @@ impl str::FromStr for Aheader { .split(';') .filter_map(|a| { let attribute: Vec<&str> = a.trim().splitn(2, '=').collect(); - if attribute.len() < 2 { - return None; + match &attribute[..] { + [key, value] => Some((key.trim().to_string(), value.trim().to_string())), + _ => None, } - - Some(( - attribute[0].trim().to_string(), - attribute[1].trim().to_string(), - )) }) .collect(); diff --git a/src/blob.rs b/src/blob.rs index 438fea6dd..919c42f9b 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -7,13 +7,13 @@ use async_std::path::{Path, PathBuf}; use async_std::prelude::*; use async_std::{fs, io}; -use self::image::GenericImageView; +use image::GenericImageView; +use thiserror::Error; + use crate::constants::AVATAR_SIZE; use crate::context::Context; use crate::events::Event; -extern crate image; - /// Represents a file in the blob directory. /// /// The object has a name, which will always be valid UTF-8. Having a @@ -58,7 +58,6 @@ impl<'a> BlobObject<'a> { blobdir: blobdir.to_path_buf(), blobname: name.clone(), cause: err, - backtrace: failure::Backtrace::new(), })?; let blob = BlobObject { blobdir, @@ -91,7 +90,6 @@ impl<'a> BlobObject<'a> { blobdir: dir.to_path_buf(), blobname: name, cause: err, - backtrace: failure::Backtrace::new(), }); } else { name = format!("{}-{}{}", stem, rand::random::(), ext); @@ -104,7 +102,6 @@ impl<'a> BlobObject<'a> { blobdir: dir.to_path_buf(), blobname: name, cause: std::io::Error::new(std::io::ErrorKind::Other, "supposedly unreachable"), - backtrace: failure::Backtrace::new(), }) } @@ -132,7 +129,6 @@ impl<'a> BlobObject<'a> { blobname: String::from(""), src: src.as_ref().to_path_buf(), cause: err, - backtrace: failure::Backtrace::new(), })?; let (stem, ext) = BlobObject::sanitise_name(&src.as_ref().to_string_lossy()); let (name, mut dst_file) = @@ -149,7 +145,6 @@ impl<'a> BlobObject<'a> { blobname: name_for_err, src: src.as_ref().to_path_buf(), cause: err, - backtrace: failure::Backtrace::new(), }); } let blob = BlobObject { @@ -209,17 +204,14 @@ impl<'a> BlobObject<'a> { .map_err(|_| BlobError::WrongBlobdir { blobdir: context.get_blobdir().to_path_buf(), src: path.as_ref().to_path_buf(), - backtrace: failure::Backtrace::new(), })?; if !BlobObject::is_acceptible_blob_name(&rel_path) { return Err(BlobError::WrongName { blobname: path.as_ref().to_path_buf(), - backtrace: failure::Backtrace::new(), }); } let name = rel_path.to_str().ok_or_else(|| BlobError::WrongName { blobname: path.as_ref().to_path_buf(), - backtrace: failure::Backtrace::new(), })?; BlobObject::from_name(context, name.to_string()) } @@ -247,7 +239,6 @@ impl<'a> BlobObject<'a> { if !BlobObject::is_acceptible_blob_name(&name) { return Err(BlobError::WrongName { blobname: PathBuf::from(name), - backtrace: failure::Backtrace::new(), }); } Ok(BlobObject { @@ -370,7 +361,6 @@ impl<'a> BlobObject<'a> { blobdir: context.get_blobdir().to_path_buf(), blobname: blob_abs.to_str().unwrap_or_default().to_string(), cause: err, - backtrace: failure::Backtrace::new(), })?; if img.width() <= AVATAR_SIZE && img.height() <= AVATAR_SIZE { @@ -383,7 +373,6 @@ impl<'a> BlobObject<'a> { blobdir: context.get_blobdir().to_path_buf(), blobname: blob_abs.to_str().unwrap_or_default().to_string(), cause: err, - backtrace: failure::Backtrace::new(), })?; Ok(()) @@ -397,98 +386,41 @@ impl<'a> fmt::Display for BlobObject<'a> { } /// Errors for the [BlobObject]. -#[derive(Fail, Debug)] +#[derive(Debug, Error)] pub enum BlobError { + #[error("Failed to create blob {blobname} in {}", .blobdir.display())] CreateFailure { blobdir: PathBuf, blobname: String, - #[cause] + #[source] cause: std::io::Error, - backtrace: failure::Backtrace, }, + #[error("Failed to write data to blob {blobname} in {}", .blobdir.display())] WriteFailure { blobdir: PathBuf, blobname: String, - #[cause] + #[source] cause: std::io::Error, - backtrace: failure::Backtrace, }, + #[error("Failed to copy data from {} to blob {blobname} in {}", .src.display(), .blobdir.display())] CopyFailure { blobdir: PathBuf, blobname: String, src: PathBuf, - #[cause] + #[source] cause: std::io::Error, - backtrace: failure::Backtrace, }, + #[error("Failed to recode to blob {blobname} in {}", .blobdir.display())] RecodeFailure { blobdir: PathBuf, blobname: String, - #[cause] + #[source] cause: image::ImageError, - backtrace: failure::Backtrace, }, - WrongBlobdir { - blobdir: PathBuf, - src: PathBuf, - backtrace: failure::Backtrace, - }, - WrongName { - blobname: PathBuf, - backtrace: failure::Backtrace, - }, -} - -// Implementing Display is done by hand because the failure -// #[fail(display = "...")] syntax does not allow using -// `blobdir.display()`. -impl fmt::Display for BlobError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // Match on the data rather than kind, they are equivalent for - // identifying purposes but contain the actual data we need. - match &self { - BlobError::CreateFailure { - blobdir, blobname, .. - } => write!( - f, - "Failed to create blob {} in {}", - blobname, - blobdir.display() - ), - BlobError::WriteFailure { - blobdir, blobname, .. - } => write!( - f, - "Failed to write data to blob {} in {}", - blobname, - blobdir.display() - ), - BlobError::CopyFailure { - blobdir, - blobname, - src, - .. - } => write!( - f, - "Failed to copy data from {} to blob {} in {}", - src.display(), - blobname, - blobdir.display(), - ), - BlobError::RecodeFailure { - blobdir, blobname, .. - } => write!(f, "Failed to recode {} in {}", blobname, blobdir.display(),), - BlobError::WrongBlobdir { blobdir, src, .. } => write!( - f, - "File path {} is not in blobdir {}", - src.display(), - blobdir.display(), - ), - BlobError::WrongName { blobname, .. } => { - write!(f, "Blob has a bad name: {}", blobname.display(),) - } - } - } + #[error("File path {} is not in {}", .src.display(), .blobdir.display())] + WrongBlobdir { blobdir: PathBuf, src: PathBuf }, + #[error("Blob has a badname {}", .blobname.display())] + WrongName { blobname: PathBuf }, } #[cfg(test)] diff --git a/src/chat.rs b/src/chat.rs index 12c8eccf6..f756a8134 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -15,7 +15,7 @@ use crate::constants::*; use crate::contact::*; use crate::context::Context; use crate::dc_tools::*; -use crate::error::Error; +use crate::error::{bail, ensure, format_err, Error}; use crate::events::Event; use crate::job::{self, Action}; use crate::message::{self, InvalidMsgId, Message, MessageState, MsgId}; @@ -340,13 +340,12 @@ impl ChatId { time(), msg.viewtype, MessageState::OutDraft, - msg.text.as_ref().cloned().unwrap_or_default(), + msg.text.as_deref().unwrap_or(""), msg.param.to_string(), 1, ], ) .await?; - Ok(()) } @@ -399,6 +398,62 @@ impl ChatId { Ok(self.get_param(context).await?.exists(Param::Devicetalk)) } + async fn parent_query( + self, + context: &Context, + fields: &str, + f: F, + ) -> sql::Result> + where + F: FnOnce(&rusqlite::Row) -> rusqlite::Result, + { + let sql = &context.sql; + let query = format!( + "SELECT {} \ + FROM msgs WHERE chat_id=? AND state NOT IN (?, ?, ?, ?) AND NOT hidden \ + ORDER BY timestamp DESC, id DESC \ + LIMIT 1;", + fields + ); + sql.query_row_optional( + query, + paramsv![ + self, + MessageState::OutPreparing, + MessageState::OutDraft, + MessageState::OutPending, + MessageState::OutFailed + ], + f, + ) + .await + } + + async fn get_parent_mime_headers(self, context: &Context) -> Option<(String, String, String)> { + let collect = |row: &rusqlite::Row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)); + self.parent_query( + context, + "rfc724_mid, mime_in_reply_to, mime_references", + collect, + ) + .await + .ok() + .flatten() + } + + async fn parent_is_encrypted(self, context: &Context) -> Result { + let collect = |row: &rusqlite::Row| Ok(row.get(0)?); + let packed: Option = self.parent_query(context, "param", collect).await?; + + if let Some(ref packed) = packed { + let param = packed.parse::()?; + Ok(param.exists(Param::GuaranteeE2ee)) + } else { + // No messages + Ok(false) + } + } + /// Bad evil escape hatch. /// /// Avoid using this, eventually types should be cleaned up enough @@ -580,44 +635,6 @@ impl Chat { &self.name } - pub async fn get_subtitle(&self, context: &Context) -> String { - // returns either the address or the number of chat members - - if self.typ == Chattype::Single && self.param.exists(Param::Selftalk) { - return context - .stock_str(StockMessage::SelfTalkSubTitle) - .await - .into(); - } - - if self.typ == Chattype::Single { - return context - .sql - .query_get_value( - context, - "SELECT c.addr - FROM chats_contacts cc - LEFT JOIN contacts c ON c.id=cc.contact_id - WHERE cc.chat_id=?;", - paramsv![self.id], - ) - .await - .unwrap_or_else(|| "Err".into()); - } - - if self.typ == Chattype::Group || self.typ == Chattype::VerifiedGroup { - if self.id.is_deaddrop() { - return context.stock_str(StockMessage::DeadDrop).await.into(); - } - let cnt = get_chat_contact_cnt(context, self.id).await; - return context - .stock_string_repl_int(StockMessage::Member, cnt as i32) - .await; - } - - "Err".to_string() - } - fn parent_query(fields: &str) -> String { // Check for server_uid guarantees that we don't // select a draft or undelivered message. @@ -717,7 +734,6 @@ impl Chat { .await .map(Into::into) .unwrap_or_else(std::path::PathBuf::new), - subtitle: self.get_subtitle(context).await, draft, is_muted: self.is_muted(), }) @@ -873,7 +889,7 @@ impl Chat { } } - if can_encrypt && (all_mutual || self.parent_is_encrypted(context).await?) { + if can_encrypt && (all_mutual || self.id.parent_is_encrypted(context).await?) { msg.param.set_int(Param::GuaranteeE2ee, 1); } } @@ -888,7 +904,7 @@ impl Chat { // we do not set In-Reply-To/References in this case. if !self.is_self_talk() { if let Some((parent_rfc724_mid, parent_in_reply_to, parent_references)) = - self.get_parent_mime_headers(context).await + self.id.get_parent_mime_headers(context).await { if !parent_rfc724_mid.is_empty() { new_in_reply_to = parent_rfc724_mid.clone(); @@ -1067,9 +1083,6 @@ pub struct ChatInfo { /// currently. pub profile_image: std::path::PathBuf, - /// Subtitle for the chat. - pub subtitle: String, - /// The draft message text. /// /// If the chat has not draft this is an empty string. @@ -1535,7 +1548,7 @@ pub async fn get_chat_msgs( flags: u32, marker1before: Option, ) -> Vec { - match hide_device_expired_messages(context).await { + match delete_device_expired_messages(context).await { Err(err) => warn!(context, "Failed to delete expired messages: {}", err), Ok(messages_deleted) => { if messages_deleted { @@ -1700,11 +1713,11 @@ pub async fn marknoticed_all_chats(context: &Context) -> Result<(), Error> { Ok(()) } -/// Hides messages which are expired according to "delete_device_after" setting. +/// Deletes messages which are expired according to "delete_device_after" setting. /// -/// Returns true if any message is hidden, so event can be emitted. If nothing -/// has been hidden, returns false. -pub async fn hide_device_expired_messages(context: &Context) -> Result { +/// Returns true if any message is deleted, so event can be emitted. If nothing +/// has been deleted, returns false. +pub async fn delete_device_expired_messages(context: &Context) -> Result { if let Some(delete_device_after) = context.get_config_delete_device_after().await { let threshold_timestamp = time() - delete_device_after; @@ -1717,7 +1730,7 @@ pub async fn hide_device_expired_messages(context: &Context) -> Result Result