mirror of
https://github.com/chatmail/core.git
synced 2026-07-12 14:03:10 +03:00
Compare commits
29 Commits
1.0.0-beta
...
feat/minim
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97afe582e1 | ||
|
|
4498954918 | ||
|
|
61ff2c4bee | ||
|
|
14902ecf15 | ||
|
|
289d8fdabf | ||
|
|
26c39f876b | ||
|
|
1f53d489cb | ||
|
|
35d055ea62 | ||
|
|
a2c94fc715 | ||
|
|
e93911effb | ||
|
|
41a7e06332 | ||
|
|
29bb2ec58b | ||
|
|
efcad4126d | ||
|
|
b4a5767347 | ||
|
|
4dbcab9e6d | ||
|
|
a80632ab36 | ||
|
|
893eb8b73b | ||
|
|
07a5ee7d2c | ||
|
|
c7a300be2f | ||
|
|
ef842fca89 | ||
|
|
bdbe9e1ca5 | ||
|
|
0c7f65222c | ||
|
|
a8fa644d25 | ||
|
|
cf7ccb5b8c | ||
|
|
49fdd6fc5b | ||
|
|
5e91c74304 | ||
|
|
b83e6f6e7c | ||
|
|
2687777a82 | ||
|
|
918b8036ea |
101
Cargo.lock
generated
101
Cargo.lock
generated
@@ -89,41 +89,42 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-imap"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/async-email/async-imap?branch=dcc-stable#50e843113e3a67e924a8a14c477833da3ebc1b44"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"async-attributes 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bytes 0.4.12 (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.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures_codec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"imap-proto 0.9.1 (git+https://github.com/djc/tokio-imap)",
|
||||
"imap-proto 0.10.0 (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)",
|
||||
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.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)",
|
||||
"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.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-native-tls"
|
||||
version = "0.1.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-smtp"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/async-email/async-smtp#6a4830032953f06020edc09db8daa06193e2b93f"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"async-native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-trait 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -131,7 +132,6 @@ dependencies = [
|
||||
"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)",
|
||||
"native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project 0.4.6 (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)",
|
||||
@@ -343,6 +343,15 @@ name = "bumpalo"
|
||||
version = "3.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byte-pool"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byte-tools"
|
||||
version = "0.3.1"
|
||||
@@ -358,15 +367,6 @@ name = "byteorder"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.5.3"
|
||||
@@ -498,6 +498,19 @@ dependencies = [
|
||||
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.7.3"
|
||||
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)",
|
||||
"crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.0"
|
||||
@@ -528,6 +541,15 @@ dependencies = [
|
||||
"scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.1"
|
||||
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)",
|
||||
"crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.6.6"
|
||||
@@ -622,9 +644,9 @@ dependencies = [
|
||||
name = "deltachat"
|
||||
version = "1.0.0-beta.24"
|
||||
dependencies = [
|
||||
"async-imap 0.1.1 (git+https://github.com/async-email/async-imap?branch=dcc-stable)",
|
||||
"async-native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-smtp 0.1.0 (git+https://github.com/async-email/async-smtp)",
|
||||
"async-imap 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"async-native-tls 0.3.2 (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.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -632,6 +654,7 @@ dependencies = [
|
||||
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"deltachat_derive 2.0.0",
|
||||
"email 0.0.21 (git+https://github.com/deltachat/rust-email)",
|
||||
@@ -1126,17 +1149,6 @@ dependencies = [
|
||||
"slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures_codec"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pin-project 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.12.3"
|
||||
@@ -1345,8 +1357,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "imap-proto"
|
||||
version = "0.9.1"
|
||||
source = "git+https://github.com/djc/tokio-imap#de420791b4e240242592616938407b742454c46c"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@@ -3300,9 +3312,9 @@ dependencies = [
|
||||
"checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8"
|
||||
"checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a"
|
||||
"checksum async-attributes 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423"
|
||||
"checksum async-imap 0.1.1 (git+https://github.com/async-email/async-imap?branch=dcc-stable)" = "<none>"
|
||||
"checksum async-native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a29e9e4ed87f4070dd6099d0bd5edfc7692c94442c80d656b50a802c6fde23b7"
|
||||
"checksum async-smtp 0.1.0 (git+https://github.com/async-email/async-smtp)" = "<none>"
|
||||
"checksum async-imap 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46ff8df29e2a90154d85d3c21e843d1f6d9337dcdcf23b3b5a87228d18122c84"
|
||||
"checksum async-native-tls 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d40a615e861c981117e15c28c577daf9918cabd2e2d588a5e06811ae79c9da1a"
|
||||
"checksum async-smtp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3652e5c6072c0694a2bcdb7e8409980d2676bd4f024adf4aab10c68fd2b48f5"
|
||||
"checksum async-std 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bf6039b315300e057d198b9d3ab92ee029e31c759b7f1afae538145e6f18a3e"
|
||||
"checksum async-task 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de6bd58f7b9cc49032559422595c81cbfcf04db2f2133592f70af19e258a1ced"
|
||||
"checksum async-trait 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "8b6dd385bb33043b833ba049048d57bdbb4d654a121ed68c71871ca51ff67070"
|
||||
@@ -3326,10 +3338,10 @@ dependencies = [
|
||||
"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.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5fb8038c1ddc0a5f73787b130f4cc75151e96ed33e417fde765eb5a81e3532f4"
|
||||
"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"
|
||||
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
|
||||
"checksum bytes 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10004c15deb332055f7a4a208190aed362cf9a7c2f6ab70a305fba50e1105f38"
|
||||
"checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb"
|
||||
"checksum cargo_metadata 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e5d1b4d380e1bab994591a24c2bdd1b054f64b60bef483a8c598c7c345bc3bbe"
|
||||
@@ -3348,9 +3360,11 @@ dependencies = [
|
||||
"checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
|
||||
"checksum crc24 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fd121741cf3eb82c08dd3023eb55bf2665e5f60ec20f89760cf836ae4562e6a0"
|
||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
"checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
|
||||
"checksum crossbeam-channel 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec9a3b0b3559f15aee4f90746c4e5e293b701c0f7d3925d24e01645267b68c"
|
||||
"checksum crossbeam-deque 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca"
|
||||
"checksum crossbeam-epoch 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac"
|
||||
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
|
||||
"checksum crossbeam-utils 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4"
|
||||
"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc"
|
||||
@@ -3412,7 +3426,6 @@ dependencies = [
|
||||
"checksum futures-timer 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a1de7508b218029b0f01662ed8f61b1c964b3ae99d6f25462d0f55a595109df6"
|
||||
"checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76"
|
||||
"checksum futures-util-preview 0.3.0-alpha.19 (registry+https://github.com/rust-lang/crates.io-index)" = "5ce968633c17e5f97936bd2797b6e38fb56cf16a7422319f7ec2e30d3c470e8d"
|
||||
"checksum futures_codec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "793d2283ff61ffff52d51cc631be0c8e75370d96056a38e09f124a67263913da"
|
||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||
"checksum getrandom 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "e7db7ca94ed4cd01190ceee0d8a8052f08a247aa1b469a7f68c6a3b71afcf407"
|
||||
"checksum gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af"
|
||||
@@ -3434,7 +3447,7 @@ dependencies = [
|
||||
"checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
|
||||
"checksum image 0.22.4 (registry+https://github.com/rust-lang/crates.io-index)" = "53cb19c4e35102e5c6fb9ade5e0e236c5588424dc171a849af3141bf0b47768a"
|
||||
"checksum image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b00861cbbb254a627d8acc0cec786b484297d896ab8f20fdc8e28536a3e918ef"
|
||||
"checksum imap-proto 0.9.1 (git+https://github.com/djc/tokio-imap)" = "<none>"
|
||||
"checksum imap-proto 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "11ba57a7d18ba7bca5a50d4723c16a3e532d2b3014d6fd3123910c266214d4f2"
|
||||
"checksum indexmap 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2"
|
||||
"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"
|
||||
|
||||
21
Cargo.toml
21
Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
# lto = true
|
||||
|
||||
[dependencies]
|
||||
deltachat_derive = { path = "./deltachat_derive" }
|
||||
@@ -20,15 +20,12 @@ smallvec = "1.0.0"
|
||||
reqwest = { version = "0.10.0", features = ["blocking", "json"] }
|
||||
num-derive = "0.3.0"
|
||||
num-traits = "0.2.6"
|
||||
async-smtp = { git = "https://github.com/async-email/async-smtp" }
|
||||
async-smtp = "0.2"
|
||||
email = { git = "https://github.com/deltachat/rust-email", branch = "master" }
|
||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" }
|
||||
|
||||
# XXX newer commits of async-imap lead to import-export tests hanging
|
||||
async-imap = { git = "https://github.com/async-email/async-imap", branch = "dcc-stable" }
|
||||
|
||||
async-native-tls = "0.1.1"
|
||||
async-std = { version = "1.0", features = ["unstable"] }
|
||||
async-imap = "0.2"
|
||||
async-native-tls = "0.3.1"
|
||||
async-std = { version = "1.4", features = ["unstable"] }
|
||||
base64 = "0.11"
|
||||
charset = "0.1"
|
||||
percent-encoding = "2.0"
|
||||
@@ -38,8 +35,6 @@ chrono = "0.4.6"
|
||||
failure = "0.1.5"
|
||||
failure_derive = "0.1.5"
|
||||
indexmap = "1.3.0"
|
||||
# TODO: make optional
|
||||
rustyline = "4.1.0"
|
||||
lazy_static = "1.4.0"
|
||||
regex = "1.1.6"
|
||||
rusqlite = { version = "0.21", features = ["bundled"] }
|
||||
@@ -62,6 +57,9 @@ mailparse = "0.10.2"
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
||||
native-tls = "0.2.3"
|
||||
image = { version = "0.22.4", default-features=false, features = ["gif_codec", "jpeg", "ico", "png_codec", "pnm", "webp", "bmp"] }
|
||||
pretty_env_logger = "0.3.1"
|
||||
rustyline = { version = "4.1.0", optional = true }
|
||||
crossbeam = "0.7.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
@@ -82,10 +80,11 @@ path = "examples/simple.rs"
|
||||
[[example]]
|
||||
name = "repl"
|
||||
path = "examples/repl/main.rs"
|
||||
required-features = ["rustyline"]
|
||||
|
||||
|
||||
[features]
|
||||
default = ["nightly", "ringbuf"]
|
||||
vendored = ["native-tls/vendored", "reqwest/native-tls-vendored"]
|
||||
vendored = ["async-native-tls/vendored", "reqwest/native-tls-vendored", "async-smtp/native-tls-vendored"]
|
||||
nightly = ["pgp/nightly"]
|
||||
ringbuf = ["pgp/ringbuf"]
|
||||
|
||||
@@ -243,7 +243,7 @@ typedef uintptr_t (*dc_callback_t) (dc_context_t* context, int event, uintptr_t
|
||||
* The object must be passed to the other context functions
|
||||
* and must be freed using dc_context_unref() after usage.
|
||||
*/
|
||||
dc_context_t* dc_context_new (dc_callback_t cb, void* userdata, const char* os_name);
|
||||
dc_context_t* dc_context_new (void* userdata, const char* os_name);
|
||||
|
||||
|
||||
/**
|
||||
@@ -538,307 +538,14 @@ int dc_is_configured (const dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Execute pending imap-jobs.
|
||||
* This function and dc_perform_imap_fetch() and dc_perform_imap_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void* imap_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_imap_jobs(context);
|
||||
* dc_perform_imap_fetch(context);
|
||||
* dc_perform_imap_idle(context);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // start imap-thread that runs forever
|
||||
* pthread_t imap_thread;
|
||||
* pthread_create(&imap_thread, NULL, imap_thread_func, context);
|
||||
*
|
||||
* ... program runs ...
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_imap_idle() in the thread above
|
||||
* // to return so that jobs are executed and messages are fetched.
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
* TODO: Document
|
||||
*/
|
||||
void dc_perform_imap_jobs (dc_context_t* context);
|
||||
|
||||
void dc_context_run (dc_context_t* context, dc_callback_t cb);
|
||||
|
||||
/**
|
||||
* Fetch new messages, if any.
|
||||
* This function and dc_perform_imap_jobs() and dc_perform_imap_idle() must be called from the same thread,
|
||||
* typically in a loop.
|
||||
*
|
||||
* See dc_perform_imap_jobs() for an example.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
* TODO: Document
|
||||
*/
|
||||
void dc_perform_imap_fetch (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Wait for messages or jobs.
|
||||
* This function and dc_perform_imap_jobs() and dc_perform_imap_fetch() must be called from the same thread,
|
||||
* typically in a loop.
|
||||
*
|
||||
* You should call this function directly after calling dc_perform_imap_fetch().
|
||||
*
|
||||
* See dc_perform_imap_jobs() for an example.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_imap_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Interrupt waiting for imap-jobs.
|
||||
* If dc_perform_imap_jobs(), dc_perform_imap_fetch() and dc_perform_imap_idle() are called in a loop,
|
||||
* calling this function causes imap-jobs to be executed and messages to be fetched.
|
||||
*
|
||||
* dc_interrupt_imap_idle() does _not_ interrupt dc_perform_imap_jobs() or dc_perform_imap_fetch().
|
||||
* If the imap-thread is inside one of these functions when dc_interrupt_imap_idle() is called, however,
|
||||
* the next call of the imap-thread to dc_perform_imap_idle() is interrupted immediately.
|
||||
*
|
||||
* Internally, this function is called whenever a imap-jobs should be processed
|
||||
* (delete message, markseen etc.).
|
||||
*
|
||||
* When you need to call this function just because to get jobs done after network changes,
|
||||
* use dc_maybe_network() instead.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_interrupt_imap_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Execute pending mvbox-jobs.
|
||||
* This function and dc_perform_mvbox_fetch() and dc_perform_mvbox_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void* mvbox_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_mvbox_jobs(context);
|
||||
* dc_perform_mvbox_fetch(context);
|
||||
* dc_perform_mvbox_idle(context);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // start mvbox-thread that runs forever
|
||||
* pthread_t mvbox_thread;
|
||||
* pthread_create(&mvbox_thread, NULL, mvbox_thread_func, context);
|
||||
*
|
||||
* ... program runs ...
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_mvbox_idle() in the thread above
|
||||
* // to return so that jobs are executed and messages are fetched.
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_mvbox_jobs (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch new messages from the MVBOX, if any.
|
||||
* The MVBOX is a folder on the account where chat messages are moved to.
|
||||
* The moving is done to not disturb shared accounts that are used by both,
|
||||
* Delta Chat and a classical MUA.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_mvbox_fetch (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Wait for messages or jobs in the MVBOX-thread.
|
||||
* This function and dc_perform_mvbox_fetch().
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* You should call this function directly after calling dc_perform_mvbox_fetch().
|
||||
*
|
||||
* See dc_perform_mvbox_fetch() for an example.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_mvbox_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Interrupt waiting for MVBOX-fetch.
|
||||
* dc_interrupt_mvbox_idle() does _not_ interrupt dc_perform_mvbox_fetch().
|
||||
* If the MVBOX-thread is inside this function when dc_interrupt_mvbox_idle() is called, however,
|
||||
* the next call of the MVBOX-thread to dc_perform_mvbox_idle() is interrupted immediately.
|
||||
*
|
||||
* Internally, this function is called whenever a imap-jobs should be processed.
|
||||
*
|
||||
* When you need to call this function just because to get jobs done after network changes,
|
||||
* use dc_maybe_network() instead.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_interrupt_mvbox_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Execute pending sentbox-jobs.
|
||||
* This function and dc_perform_sentbox_fetch() and dc_perform_sentbox_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void* sentbox_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_sentbox_jobs(context);
|
||||
* dc_perform_sentbox_fetch(context);
|
||||
* dc_perform_sentbox_idle(context);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // start sentbox-thread that runs forever
|
||||
* pthread_t sentbox_thread;
|
||||
* pthread_create(&sentbox_thread, NULL, sentbox_thread_func, context);
|
||||
*
|
||||
* ... program runs ...
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_sentbox_idle() in the thread above
|
||||
* // to return so that jobs are executed and messages are fetched.
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_sentbox_jobs (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Fetch new messages from the Sent folder, if any.
|
||||
* This function and dc_perform_sentbox_idle()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_sentbox_fetch (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Wait for messages or jobs in the SENTBOX-thread.
|
||||
* This function and dc_perform_sentbox_fetch()
|
||||
* must be called from the same thread, typically in a loop.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_sentbox_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Interrupt waiting for messages or jobs in the SENTBOX-thread.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_interrupt_sentbox_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Execute pending smtp-jobs.
|
||||
* This function and dc_perform_smtp_idle() must be called from the same thread,
|
||||
* typically in a loop.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* void* smtp_thread_func(void* context)
|
||||
* {
|
||||
* while (true) {
|
||||
* dc_perform_smtp_jobs(context);
|
||||
* dc_perform_smtp_idle(context);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // start smtp-thread that runs forever
|
||||
* pthread_t smtp_thread;
|
||||
* pthread_create(&smtp_thread, NULL, smtp_thread_func, context);
|
||||
*
|
||||
* ... program runs ...
|
||||
*
|
||||
* // network becomes available again -
|
||||
* // the interrupt causes dc_perform_smtp_idle() in the thread above
|
||||
* // to return so that jobs are executed
|
||||
* dc_maybe_network(context);
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_smtp_jobs (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Wait for smtp-jobs.
|
||||
* This function and dc_perform_smtp_jobs() must be called from the same thread,
|
||||
* typically in a loop.
|
||||
*
|
||||
* See dc_interrupt_smtp_idle() for an example.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_perform_smtp_idle (dc_context_t* context);
|
||||
|
||||
|
||||
/**
|
||||
* Interrupt waiting for smtp-jobs.
|
||||
* If dc_perform_smtp_jobs() and dc_perform_smtp_idle() are called in a loop,
|
||||
* calling this function causes jobs to be executed.
|
||||
*
|
||||
* dc_interrupt_smtp_idle() does _not_ interrupt dc_perform_smtp_jobs().
|
||||
* If the smtp-thread is inside this function when dc_interrupt_smtp_idle() is called, however,
|
||||
* the next call of the smtp-thread to dc_perform_smtp_idle() is interrupted immediately.
|
||||
*
|
||||
* Internally, this function is called whenever a message is to be sent.
|
||||
*
|
||||
* When you need to call this function just because to get jobs done after network changes,
|
||||
* use dc_maybe_network() instead.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context as created by dc_context_new().
|
||||
* @return None.
|
||||
*/
|
||||
void dc_interrupt_smtp_idle (dc_context_t* context);
|
||||
|
||||
void dc_context_shutdown (dc_context_t* context);
|
||||
|
||||
/**
|
||||
* This function can be called whenever there is a hint
|
||||
@@ -1756,7 +1463,7 @@ uint32_t dc_create_contact (dc_context_t* context, const char*
|
||||
* Trying to add email-addresses that are already in the contact list,
|
||||
* results in updating the name unless the name was changed manually by the user.
|
||||
* If any email-address or any name is really updated,
|
||||
* the event DC_EVENT_CONTACTS_CHANGED is sent.
|
||||
* the event #DC_EVENT_CONTACTS_CHANGED is sent.
|
||||
*
|
||||
* To add a single contact entered by the user, you should prefer dc_create_contact(),
|
||||
* however, for adding a bunch of addresses, this function is _much_ faster.
|
||||
@@ -2107,20 +1814,22 @@ dc_lot_t* dc_check_qr (dc_context_t* context, const char*
|
||||
|
||||
|
||||
/**
|
||||
* Get QR code text that will offer an secure-join verification.
|
||||
* Get QR code text that will offer an Setup-Contact or Verified-Group invitation.
|
||||
* The QR code is compatible to the OPENPGP4FPR format
|
||||
* so that a basic fingerprint comparison also works eg. with OpenKeychain.
|
||||
*
|
||||
* The scanning device will pass the scanned content to dc_check_qr() then;
|
||||
* if this function returns DC_QR_ASK_VERIFYCONTACT or DC_QR_ASK_VERIFYGROUP
|
||||
* if dc_check_qr() returns DC_QR_ASK_VERIFYCONTACT or DC_QR_ASK_VERIFYGROUP
|
||||
* an out-of-band-verification can be joined using dc_join_securejoin()
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object.
|
||||
* @param chat_id If set to a group-chat-id,
|
||||
* the group-join-protocol is offered in the QR code;
|
||||
* the Verified-Group-Invite protocol is offered in the QR code;
|
||||
* works for verified groups as well as for normal groups.
|
||||
* If set to 0, the setup-Verified-contact-protocol is offered in the QR code.
|
||||
* If set to 0, the Setup-Contact protocol is offered in the QR code.
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
* for details about both protocols.
|
||||
* @return Text that should go to the QR code,
|
||||
* On errors, an empty QR code is returned, NULL is never returned.
|
||||
* The returned string must be released using dc_str_unref() after usage.
|
||||
@@ -2129,13 +1838,29 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
||||
|
||||
|
||||
/**
|
||||
* Join an out-of-band-verification initiated on another device with dc_get_securejoin_qr().
|
||||
* Continue a Setup-Contact or Verified-Group-Invite protocol
|
||||
* started on another device with dc_get_securejoin_qr().
|
||||
* This function is typically called when dc_check_qr() returns
|
||||
* lot.state=DC_QR_ASK_VERIFYCONTACT or lot.state=DC_QR_ASK_VERIFYGROUP.
|
||||
*
|
||||
* This function takes some time and sends and receives several messages.
|
||||
* You should call it in a separate thread; if you want to abort it, you should
|
||||
* call dc_stop_ongoing_process().
|
||||
* Depending on the given QR code,
|
||||
* this function may takes some time and sends and receives several messages.
|
||||
* Therefore, you should call it always in a separate thread;
|
||||
* if you want to abort it, you should call dc_stop_ongoing_process().
|
||||
*
|
||||
* - If the given QR code starts the Setup-Contact protocol,
|
||||
* the function typically returns immediately
|
||||
* and the handshake runs in background.
|
||||
* Subsequent calls of dc_join_securejoin() will abort unfinished tasks.
|
||||
* The returned chat is the one-to-one opportunistic chat.
|
||||
* When the protocol has finished, an info-message is added to that chat.
|
||||
* - If the given QR code starts the Verified-Group-Invite protocol,
|
||||
* the function waits until the protocol has finished.
|
||||
* This is because the verified group is not opportunistic
|
||||
* and can be created only when the contacts have verified each other.
|
||||
*
|
||||
* See https://countermitm.readthedocs.io/en/latest/new.html
|
||||
* for details about both protocols.
|
||||
*
|
||||
* @memberof dc_context_t
|
||||
* @param context The context object
|
||||
@@ -2143,6 +1868,9 @@ char* dc_get_securejoin_qr (dc_context_t* context, uint32_t ch
|
||||
* to dc_check_qr().
|
||||
* @return Chat-id of the joined chat, the UI may redirect to the this chat.
|
||||
* If the out-of-band verification failed or was aborted, 0 is returned.
|
||||
* A returned chat-id does not guarantee that the chat or the belonging contact is verified.
|
||||
* If needed, this be checked with dc_chat_is_verified() and dc_contact_is_verified(),
|
||||
* however, in practise, the UI will just listen to #DC_EVENT_CONTACTS_CHANGED unconditionally.
|
||||
*/
|
||||
uint32_t dc_join_securejoin (dc_context_t* context, const char* qr);
|
||||
|
||||
@@ -4347,7 +4075,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
|
||||
|
||||
|
||||
/**
|
||||
* Contact(s) created, renamed, blocked or deleted.
|
||||
* Contact(s) created, renamed, verified, blocked or deleted.
|
||||
*
|
||||
* @param data1 (int) If not 0, this is the contact_id of an added contact that should be selected.
|
||||
* @param data2 0
|
||||
|
||||
@@ -57,7 +57,6 @@ use self::string::*;
|
||||
/// and protected by an [RwLock]. Other than that it needs to store
|
||||
/// the data which is passed into [dc_context_new].
|
||||
pub struct ContextWrapper {
|
||||
cb: Option<dc_callback_t>,
|
||||
userdata: *mut libc::c_void,
|
||||
os_name: String,
|
||||
inner: RwLock<Option<context::Context>>,
|
||||
@@ -91,7 +90,10 @@ impl ContextWrapper {
|
||||
///
|
||||
/// This function makes it easy to log an error.
|
||||
unsafe fn error(&self, msg: &str) {
|
||||
self.translate_cb(Event::Error(msg.to_string()));
|
||||
self.with_inner(|ctx| {
|
||||
ctx.call_cb(Event::Error(msg.to_string()));
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Unlock the context and execute a closure with it.
|
||||
@@ -117,93 +119,19 @@ impl ContextWrapper {
|
||||
match guard.as_ref() {
|
||||
Some(ref ctx) => Ok(ctxfn(ctx)),
|
||||
None => {
|
||||
unsafe { self.error("context not open") };
|
||||
eprintln!("context not open");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Translates the callback from the rust style to the C-style version.
|
||||
unsafe fn translate_cb(&self, event: Event) {
|
||||
if let Some(ffi_cb) = self.cb {
|
||||
let event_id = event.as_id();
|
||||
match event {
|
||||
Event::Info(msg)
|
||||
| Event::SmtpConnected(msg)
|
||||
| Event::ImapConnected(msg)
|
||||
| Event::SmtpMessageSent(msg)
|
||||
| Event::ImapMessageDeleted(msg)
|
||||
| Event::ImapMessageMoved(msg)
|
||||
| Event::ImapFolderEmptied(msg)
|
||||
| Event::NewBlobFile(msg)
|
||||
| Event::DeletedBlobFile(msg)
|
||||
| Event::Warning(msg)
|
||||
| Event::Error(msg)
|
||||
| Event::ErrorNetwork(msg)
|
||||
| Event::ErrorSelfNotInGroup(msg) => {
|
||||
let data2 = CString::new(msg).unwrap_or_default();
|
||||
ffi_cb(self, event_id, 0, data2.as_ptr() as uintptr_t);
|
||||
}
|
||||
Event::MsgsChanged { chat_id, msg_id }
|
||||
| Event::IncomingMsg { chat_id, msg_id }
|
||||
| Event::MsgDelivered { chat_id, msg_id }
|
||||
| Event::MsgFailed { chat_id, msg_id }
|
||||
| Event::MsgRead { chat_id, msg_id } => {
|
||||
ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
msg_id.to_u32() as uintptr_t,
|
||||
);
|
||||
}
|
||||
Event::ChatModified(chat_id) => {
|
||||
ffi_cb(self, event_id, chat_id.to_u32() as uintptr_t, 0);
|
||||
}
|
||||
Event::ContactsChanged(id) | Event::LocationChanged(id) => {
|
||||
let id = id.unwrap_or_default();
|
||||
ffi_cb(self, event_id, id as uintptr_t, 0);
|
||||
}
|
||||
Event::ConfigureProgress(progress) | Event::ImexProgress(progress) => {
|
||||
ffi_cb(self, event_id, progress as uintptr_t, 0);
|
||||
}
|
||||
Event::ImexFileWritten(file) => {
|
||||
let data1 = file.to_c_string().unwrap_or_default();
|
||||
ffi_cb(self, event_id, data1.as_ptr() as uintptr_t, 0);
|
||||
}
|
||||
Event::SecurejoinInviterProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
}
|
||||
| Event::SecurejoinJoinerProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
} => {
|
||||
ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
contact_id as uintptr_t,
|
||||
progress as uintptr_t,
|
||||
);
|
||||
}
|
||||
Event::SecurejoinMemberAdded {
|
||||
chat_id,
|
||||
contact_id,
|
||||
} => {
|
||||
ffi_cb(
|
||||
self,
|
||||
event_id,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
contact_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn is_open(&self) -> bool {
|
||||
self.inner.read().unwrap().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_context_new(
|
||||
cb: Option<dc_callback_t>,
|
||||
userdata: *mut libc::c_void,
|
||||
os_name: *const libc::c_char,
|
||||
) -> *mut dc_context_t {
|
||||
@@ -215,11 +143,11 @@ pub unsafe extern "C" fn dc_context_new(
|
||||
to_string_lossy(os_name)
|
||||
};
|
||||
let ffi_ctx = ContextWrapper {
|
||||
cb,
|
||||
userdata,
|
||||
os_name,
|
||||
inner: RwLock::new(None),
|
||||
};
|
||||
|
||||
Box::into_raw(Box::new(ffi_ctx))
|
||||
}
|
||||
|
||||
@@ -257,17 +185,11 @@ pub unsafe extern "C" fn dc_open(
|
||||
return 0;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
let rust_cb = move |_ctx: &Context, evt: Event| ffi_context.translate_cb(evt);
|
||||
|
||||
let ctx = if blobdir.is_null() || *blobdir == 0 {
|
||||
Context::new(
|
||||
Box::new(rust_cb),
|
||||
ffi_context.os_name.clone(),
|
||||
as_path(dbfile).to_path_buf(),
|
||||
)
|
||||
Context::new(ffi_context.os_name.clone(), as_path(dbfile).to_path_buf())
|
||||
} else {
|
||||
Context::with_blobdir(
|
||||
Box::new(rust_cb),
|
||||
ffi_context.os_name.clone(),
|
||||
as_path(dbfile).to_path_buf(),
|
||||
as_path(blobdir).to_path_buf(),
|
||||
@@ -300,11 +222,7 @@ pub unsafe extern "C" fn dc_is_open(context: *mut dc_context_t) -> libc::c_int {
|
||||
return 0;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
let inner_guard = ffi_context.inner.read().unwrap();
|
||||
match *inner_guard {
|
||||
Some(_) => 1,
|
||||
None => 0,
|
||||
}
|
||||
ffi_context.is_open() as libc::c_int
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -455,9 +373,7 @@ pub unsafe extern "C" fn dc_configure(context: *mut dc_context_t) {
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| configure::configure(ctx))
|
||||
.unwrap_or(())
|
||||
ffi_context.with_inner(|ctx| ctx.configure()).unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@@ -468,188 +384,120 @@ pub unsafe extern "C" fn dc_is_configured(context: *mut dc_context_t) -> libc::c
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| configure::dc_is_configured(ctx) as libc::c_int)
|
||||
.with_inner(|ctx| ctx.is_configured() as libc::c_int)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_imap_jobs(context: *mut dc_context_t) {
|
||||
pub unsafe extern "C" fn dc_context_run(context: *mut dc_context_t, cb: Option<dc_callback_t>) {
|
||||
eprintln!("dc_context_run");
|
||||
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_imap_jobs()");
|
||||
eprintln!("ignoring careless call to dc_run()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_inbox_jobs(ctx))
|
||||
.with_inner(|ctx| {
|
||||
eprintln!("RUN");
|
||||
ctx.run(|_ctx, event| {
|
||||
translate_cb(ffi_context, cb, event);
|
||||
});
|
||||
eprintln!("RUN DONE");
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_imap_fetch(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_imap_fetch()");
|
||||
return;
|
||||
/// Translates the callback from the rust style to the C-style version.
|
||||
unsafe fn translate_cb(ctx: &ContextWrapper, ffi_cb: Option<dc_callback_t>, event: Event) {
|
||||
if let Some(ffi_cb) = ffi_cb {
|
||||
let event_id = event.as_id();
|
||||
match event {
|
||||
Event::Info(msg)
|
||||
| Event::SmtpConnected(msg)
|
||||
| Event::ImapConnected(msg)
|
||||
| Event::SmtpMessageSent(msg)
|
||||
| Event::ImapMessageDeleted(msg)
|
||||
| Event::ImapMessageMoved(msg)
|
||||
| Event::ImapFolderEmptied(msg)
|
||||
| Event::NewBlobFile(msg)
|
||||
| Event::DeletedBlobFile(msg)
|
||||
| Event::Warning(msg)
|
||||
| Event::Error(msg)
|
||||
| Event::ErrorNetwork(msg)
|
||||
| Event::ErrorSelfNotInGroup(msg) => {
|
||||
let data2 = CString::new(msg).unwrap_or_default();
|
||||
ffi_cb(ctx, event_id, 0, data2.as_ptr() as uintptr_t);
|
||||
}
|
||||
Event::MsgsChanged { chat_id, msg_id }
|
||||
| Event::IncomingMsg { chat_id, msg_id }
|
||||
| Event::MsgDelivered { chat_id, msg_id }
|
||||
| Event::MsgFailed { chat_id, msg_id }
|
||||
| Event::MsgRead { chat_id, msg_id } => {
|
||||
ffi_cb(
|
||||
ctx,
|
||||
event_id,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
msg_id.to_u32() as uintptr_t,
|
||||
);
|
||||
}
|
||||
Event::ChatModified(chat_id) => {
|
||||
ffi_cb(ctx, event_id, chat_id.to_u32() as uintptr_t, 0);
|
||||
}
|
||||
Event::ContactsChanged(id) | Event::LocationChanged(id) => {
|
||||
let id = id.unwrap_or_default();
|
||||
ffi_cb(ctx, event_id, id as uintptr_t, 0);
|
||||
}
|
||||
Event::ConfigureProgress(progress) | Event::ImexProgress(progress) => {
|
||||
ffi_cb(ctx, event_id, progress as uintptr_t, 0);
|
||||
}
|
||||
Event::ImexFileWritten(file) => {
|
||||
let data1 = file.to_c_string().unwrap_or_default();
|
||||
ffi_cb(ctx, event_id, data1.as_ptr() as uintptr_t, 0);
|
||||
}
|
||||
Event::SecurejoinInviterProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
}
|
||||
| Event::SecurejoinJoinerProgress {
|
||||
contact_id,
|
||||
progress,
|
||||
} => {
|
||||
ffi_cb(
|
||||
ctx,
|
||||
event_id,
|
||||
contact_id as uintptr_t,
|
||||
progress as uintptr_t,
|
||||
);
|
||||
}
|
||||
Event::SecurejoinMemberAdded {
|
||||
chat_id,
|
||||
contact_id,
|
||||
} => {
|
||||
ffi_cb(
|
||||
ctx,
|
||||
event_id,
|
||||
chat_id.to_u32() as uintptr_t,
|
||||
contact_id as uintptr_t,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_inbox_fetch(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_imap_idle(context: *mut dc_context_t) {
|
||||
// TODO rename function in co-ordination with UIs
|
||||
pub unsafe extern "C" fn dc_context_shutdown(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_imap_idle()");
|
||||
eprintln!("ignoring careless call to dc_shutdown()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_inbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_interrupt_imap_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_interrupt_imap_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::interrupt_inbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_mvbox_fetch(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_mvbox_fetch()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_mvbox_fetch(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_mvbox_jobs(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_mvbox_jobs()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_mvbox_jobs(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_mvbox_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_mvbox_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_mvbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_interrupt_mvbox_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_interrupt_mvbox_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::interrupt_mvbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_sentbox_fetch(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_sentbox_fetch()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_sentbox_fetch(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_sentbox_jobs(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_sentbox_jobs()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_sentbox_jobs(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_sentbox_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_sentbox_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_sentbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_interrupt_sentbox_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_interrupt_sentbox_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::interrupt_sentbox_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_smtp_jobs(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_smtp_jobs()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_smtp_jobs(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_perform_smtp_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_perform_smtp_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::perform_smtp_idle(ctx))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn dc_interrupt_smtp_idle(context: *mut dc_context_t) {
|
||||
if context.is_null() {
|
||||
eprintln!("ignoring careless call to dc_interrupt_smtp_idle()");
|
||||
return;
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| job::interrupt_smtp_idle(ctx))
|
||||
.with_inner(|ctx| {
|
||||
eprintln!("SHUTDOWN");
|
||||
ctx.shutdown();
|
||||
eprintln!("SHUTDOWN:DONE")
|
||||
})
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
|
||||
@@ -9,22 +9,17 @@ extern crate deltachat;
|
||||
#[macro_use]
|
||||
extern crate failure;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate rusqlite;
|
||||
|
||||
use std::borrow::Cow::{self, Borrowed, Owned};
|
||||
use std::io::{self, Write};
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use deltachat::chat::ChatId;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::job::*;
|
||||
use deltachat::oauth2::*;
|
||||
use deltachat::securejoin::*;
|
||||
use deltachat::Event;
|
||||
@@ -114,107 +109,6 @@ fn receive_event(_context: &Context, event: Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// Threads for waiting for messages and for jobs
|
||||
|
||||
lazy_static! {
|
||||
static ref HANDLE: Arc<Mutex<Option<Handle>>> = Arc::new(Mutex::new(None));
|
||||
static ref IS_RUNNING: AtomicBool = AtomicBool::new(true);
|
||||
}
|
||||
|
||||
struct Handle {
|
||||
handle_imap: Option<std::thread::JoinHandle<()>>,
|
||||
handle_mvbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_sentbox: Option<std::thread::JoinHandle<()>>,
|
||||
handle_smtp: Option<std::thread::JoinHandle<()>>,
|
||||
}
|
||||
|
||||
macro_rules! while_running {
|
||||
($code:block) => {
|
||||
if IS_RUNNING.load(Ordering::Relaxed) {
|
||||
$code
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn start_threads(c: Arc<RwLock<Context>>) {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
println!("Starting threads");
|
||||
IS_RUNNING.store(true, Ordering::Relaxed);
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_imap = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_inbox_jobs(&ctx.read().unwrap());
|
||||
perform_inbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
let context = ctx.read().unwrap();
|
||||
perform_inbox_idle(&context);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_mvbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_mvbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_mvbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c.clone();
|
||||
let handle_sentbox = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_sentbox_fetch(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_sentbox_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let ctx = c;
|
||||
let handle_smtp = std::thread::spawn(move || loop {
|
||||
while_running!({
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
while_running!({
|
||||
perform_smtp_idle(&ctx.read().unwrap());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
*HANDLE.clone().lock().unwrap() = Some(Handle {
|
||||
handle_imap: Some(handle_imap),
|
||||
handle_mvbox: Some(handle_mvbox),
|
||||
handle_sentbox: Some(handle_sentbox),
|
||||
handle_smtp: Some(handle_smtp),
|
||||
});
|
||||
}
|
||||
|
||||
fn stop_threads(context: &Context) {
|
||||
if let Some(ref mut handle) = *HANDLE.clone().lock().unwrap() {
|
||||
println!("Stopping threads");
|
||||
IS_RUNNING.store(false, Ordering::Relaxed);
|
||||
|
||||
interrupt_inbox_idle(context);
|
||||
interrupt_mvbox_idle(context);
|
||||
interrupt_sentbox_idle(context);
|
||||
interrupt_smtp_idle(context);
|
||||
|
||||
handle.handle_imap.take().unwrap().join().unwrap();
|
||||
handle.handle_mvbox.take().unwrap().join().unwrap();
|
||||
handle.handle_sentbox.take().unwrap().join().unwrap();
|
||||
handle.handle_smtp.take().unwrap().join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// === The main loop
|
||||
|
||||
struct DcHelper {
|
||||
completer: FilenameCompleter,
|
||||
highlighter: MatchingBracketHighlighter,
|
||||
@@ -420,9 +314,7 @@ fn main_0(args: Vec<String>) -> Result<(), failure::Error> {
|
||||
}
|
||||
rl.save_history(".dc-history.txt")?;
|
||||
println!("history saved");
|
||||
{
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
}
|
||||
ctx.read().unwrap().shutdown();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -440,28 +332,20 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failu
|
||||
|
||||
match arg0 {
|
||||
"connect" => {
|
||||
start_threads(ctx);
|
||||
crossbeam::scope(|s| {
|
||||
s.spawn(|_| ctx.read().unwrap().run());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
"disconnect" => {
|
||||
stop_threads(&ctx.read().unwrap());
|
||||
}
|
||||
"smtp-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("smtp-jobs are already running in a thread.",);
|
||||
} else {
|
||||
perform_smtp_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
}
|
||||
"imap-jobs" => {
|
||||
if HANDLE.clone().lock().unwrap().is_some() {
|
||||
println!("inbox-jobs are already running in a thread.");
|
||||
} else {
|
||||
perform_inbox_jobs(&ctx.read().unwrap());
|
||||
}
|
||||
ctx.read().unwrap().shutdown();
|
||||
}
|
||||
"configure" => {
|
||||
start_threads(ctx.clone());
|
||||
configure(&ctx.read().unwrap());
|
||||
crossbeam::scope(|s| {
|
||||
s.spawn(|_| ctx.read().unwrap().run());
|
||||
})
|
||||
.unwrap();
|
||||
ctx.read().unwrap().configure();
|
||||
}
|
||||
"oauth2" => {
|
||||
if let Some(addr) = ctx.read().unwrap().get_config(config::Config::Addr) {
|
||||
@@ -484,7 +368,11 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failu
|
||||
print!("\x1b[1;1H\x1b[2J");
|
||||
}
|
||||
"getqr" | "getbadqr" => {
|
||||
start_threads(ctx.clone());
|
||||
crossbeam::scope(|s| {
|
||||
s.spawn(|_| ctx.read().unwrap().run());
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
if let Some(mut qr) = dc_get_securejoin_qr(
|
||||
&ctx.read().unwrap(),
|
||||
ChatId::new(arg1.parse().unwrap_or_default()),
|
||||
@@ -504,8 +392,12 @@ fn handle_cmd(line: &str, ctx: Arc<RwLock<Context>>) -> Result<ExitResult, failu
|
||||
}
|
||||
}
|
||||
"joinqr" => {
|
||||
start_threads(ctx.clone());
|
||||
if !arg0.is_empty() {
|
||||
crossbeam::scope(|s| {
|
||||
s.spawn(|_| ctx.read().unwrap().run());
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
dc_join_securejoin(&ctx.read().unwrap(), arg1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
extern crate deltachat;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{thread, time};
|
||||
use tempfile::tempdir;
|
||||
|
||||
use deltachat::chat;
|
||||
use deltachat::chatlist::*;
|
||||
use deltachat::config;
|
||||
use deltachat::configure::*;
|
||||
use deltachat::contact::*;
|
||||
use deltachat::context::*;
|
||||
use deltachat::job::{
|
||||
perform_inbox_fetch, perform_inbox_idle, perform_inbox_jobs, perform_smtp_idle,
|
||||
perform_smtp_jobs,
|
||||
};
|
||||
use deltachat::Event;
|
||||
|
||||
fn cb(_ctx: &Context, event: Event) {
|
||||
@@ -36,77 +30,52 @@ fn main() {
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
println!("creating database {:?}", dbfile);
|
||||
let ctx =
|
||||
Context::new(Box::new(cb), "FakeOs".into(), dbfile).expect("Failed to create context");
|
||||
let running = Arc::new(RwLock::new(true));
|
||||
let ctx = Context::new("FakeOs".into(), dbfile).expect("Failed to create context");
|
||||
let info = ctx.get_info();
|
||||
let duration = time::Duration::from_millis(4000);
|
||||
let duration = time::Duration::from_millis(8000);
|
||||
println!("info: {:#?}", info);
|
||||
|
||||
let ctx = Arc::new(ctx);
|
||||
let ctx1 = ctx.clone();
|
||||
let r1 = running.clone();
|
||||
let t1 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_inbox_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_inbox_fetch(&ctx1);
|
||||
crossbeam::scope(|s| {
|
||||
let t1 = s.spawn(|_| {
|
||||
ctx.run(cb);
|
||||
});
|
||||
|
||||
if *r1.read().unwrap() {
|
||||
perform_inbox_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
println!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 2, "missing password");
|
||||
let pw = args[1].clone();
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
ctx.configure();
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
println!("sending a message");
|
||||
let contact_id =
|
||||
Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
|
||||
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(&ctx, 0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
}
|
||||
});
|
||||
|
||||
let ctx1 = ctx.clone();
|
||||
let r1 = running.clone();
|
||||
let t2 = thread::spawn(move || {
|
||||
while *r1.read().unwrap() {
|
||||
perform_smtp_jobs(&ctx1);
|
||||
if *r1.read().unwrap() {
|
||||
perform_smtp_idle(&ctx1);
|
||||
}
|
||||
}
|
||||
});
|
||||
thread::sleep(duration);
|
||||
|
||||
println!("configuring");
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
assert_eq!(args.len(), 2, "missing password");
|
||||
let pw = args[1].clone();
|
||||
ctx.set_config(config::Config::Addr, Some("d@testrun.org"))
|
||||
.unwrap();
|
||||
ctx.set_config(config::Config::MailPw, Some(&pw)).unwrap();
|
||||
configure(&ctx);
|
||||
println!("stopping threads");
|
||||
ctx.shutdown();
|
||||
|
||||
thread::sleep(duration);
|
||||
println!("joining");
|
||||
t1.join().unwrap();
|
||||
|
||||
println!("sending a message");
|
||||
let contact_id = Contact::create(&ctx, "dignifiedquire", "dignifiedquire@gmail.com").unwrap();
|
||||
let chat_id = chat::create_by_contact_id(&ctx, contact_id).unwrap();
|
||||
chat::send_text_msg(&ctx, chat_id, "Hi, here is my first message!".into()).unwrap();
|
||||
|
||||
println!("fetching chats..");
|
||||
let chats = Chatlist::try_load(&ctx, 0, None, None).unwrap();
|
||||
|
||||
for i in 0..chats.len() {
|
||||
let summary = chats.get_summary(&ctx, 0, None);
|
||||
let text1 = summary.get_text1();
|
||||
let text2 = summary.get_text2();
|
||||
println!("chat: {} - {:?} - {:?}", i, text1, text2,);
|
||||
}
|
||||
|
||||
thread::sleep(duration);
|
||||
|
||||
println!("stopping threads");
|
||||
|
||||
*running.write().unwrap() = false;
|
||||
deltachat::job::interrupt_inbox_idle(&ctx);
|
||||
deltachat::job::interrupt_smtp_idle(&ctx);
|
||||
|
||||
println!("joining");
|
||||
t1.join().unwrap();
|
||||
t2.join().unwrap();
|
||||
|
||||
println!("closing");
|
||||
println!("closing");
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
7
python/fail_test.py
Normal file
7
python/fail_test.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from __future__ import print_function
|
||||
from deltachat import capi
|
||||
from deltachat.capi import ffi, lib
|
||||
|
||||
if __name__ == "__main__":
|
||||
ctx = capi.lib.dc_context_new(ffi.NULL, ffi.NULL)
|
||||
lib.dc_context_shutdown(ctx)
|
||||
@@ -38,7 +38,7 @@ class Account(object):
|
||||
:param debug: turn on debug logging for events.
|
||||
"""
|
||||
self._dc_context = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, as_dc_charpointer(os_name)),
|
||||
lib.dc_context_new(ffi.NULL, as_dc_charpointer(os_name)),
|
||||
_destroy_dc_context,
|
||||
)
|
||||
if eventlogging:
|
||||
@@ -380,8 +380,6 @@ class Account(object):
|
||||
def _export(self, path, imex_cmd):
|
||||
self._imex_events_clear()
|
||||
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
files_written = []
|
||||
while True:
|
||||
ev = self._imex_events.get()
|
||||
@@ -410,8 +408,6 @@ class Account(object):
|
||||
def _import(self, path, imex_cmd):
|
||||
self._imex_events_clear()
|
||||
lib.dc_imex(self._dc_context, imex_cmd, as_dc_charpointer(path), ffi.NULL)
|
||||
if not self._threads.is_started():
|
||||
lib.dc_perform_imap_jobs(self._dc_context)
|
||||
if not self._imex_events.get():
|
||||
raise ValueError("import from path '{}' failed".format(path))
|
||||
|
||||
@@ -490,7 +486,7 @@ class Account(object):
|
||||
ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG")
|
||||
return self.get_message_by_id(ev[2])
|
||||
|
||||
def start_threads(self, mvbox=False, sentbox=False):
|
||||
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.
|
||||
@@ -498,7 +494,7 @@ class Account(object):
|
||||
"""
|
||||
if not self.is_configured():
|
||||
self.configure()
|
||||
self._threads.start(mvbox=mvbox, sentbox=sentbox)
|
||||
self._threads.start()
|
||||
|
||||
def stop_threads(self, wait=True):
|
||||
""" stop IMAP/SMTP threads. """
|
||||
@@ -560,16 +556,9 @@ class IOThreads:
|
||||
def is_started(self):
|
||||
return len(self._name2thread) > 0
|
||||
|
||||
def start(self, imap=True, smtp=True, mvbox=False, sentbox=False):
|
||||
def start(self):
|
||||
assert not self.is_started()
|
||||
if imap:
|
||||
self._start_one_thread("inbox", self.imap_thread_run)
|
||||
if mvbox:
|
||||
self._start_one_thread("mvbox", self.mvbox_thread_run)
|
||||
if sentbox:
|
||||
self._start_one_thread("sentbox", self.sentbox_thread_run)
|
||||
if smtp:
|
||||
self._start_one_thread("smtp", self.smtp_thread_run)
|
||||
self._start_one_thread("deltachat", self.dc_thread_run)
|
||||
|
||||
def _start_one_thread(self, name, func):
|
||||
self._name2thread[name] = t = threading.Thread(target=func, name=name)
|
||||
@@ -577,48 +566,18 @@ class IOThreads:
|
||||
t.start()
|
||||
|
||||
def stop(self, wait=False):
|
||||
self._thread_quitflag = True
|
||||
lib.dc_interrupt_imap_idle(self._dc_context)
|
||||
lib.dc_interrupt_smtp_idle(self._dc_context)
|
||||
lib.dc_interrupt_mvbox_idle(self._dc_context)
|
||||
lib.dc_interrupt_sentbox_idle(self._dc_context)
|
||||
lib.dc_context_shutdown(self._dc_context)
|
||||
if wait:
|
||||
for name, thread in self._name2thread.items():
|
||||
thread.join()
|
||||
|
||||
def imap_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "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 dc_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "DC THREAD START")
|
||||
|
||||
lib.dc_context_run(self._dc_context, lib.py_dc_callback)
|
||||
|
||||
self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED")
|
||||
|
||||
def mvbox_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "MVBOX THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_mvbox_jobs(self._dc_context)
|
||||
lib.dc_perform_mvbox_fetch(self._dc_context)
|
||||
lib.dc_perform_mvbox_idle(self._dc_context)
|
||||
self._log_event("py-bindings-info", 0, "MVBOX THREAD FINISHED")
|
||||
|
||||
def sentbox_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "SENTBOX THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_sentbox_jobs(self._dc_context)
|
||||
lib.dc_perform_sentbox_fetch(self._dc_context)
|
||||
lib.dc_perform_sentbox_idle(self._dc_context)
|
||||
self._log_event("py-bindings-info", 0, "SENTBOX THREAD FINISHED")
|
||||
|
||||
def smtp_thread_run(self):
|
||||
self._log_event("py-bindings-info", 0, "SMTP THREAD START")
|
||||
while not self._thread_quitflag:
|
||||
lib.dc_perform_smtp_jobs(self._dc_context)
|
||||
lib.dc_perform_smtp_idle(self._dc_context)
|
||||
self._log_event("py-bindings-info", 0, "SMTP THREAD FINISHED")
|
||||
|
||||
|
||||
class EventLogger:
|
||||
_loglock = threading.RLock()
|
||||
|
||||
@@ -182,7 +182,7 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig):
|
||||
def get_online_configuring_account(self, mvbox=False, sentbox=False):
|
||||
ac, configdict = self.get_online_config()
|
||||
ac.configure(**configdict)
|
||||
ac.start_threads(mvbox=mvbox, sentbox=sentbox)
|
||||
ac.start_threads()
|
||||
return ac
|
||||
|
||||
def get_one_online_account(self):
|
||||
|
||||
@@ -176,6 +176,7 @@ class TestOfflineChat:
|
||||
assert d["draft"] == "" if chat.get_draft() is None else chat.get_draft()
|
||||
|
||||
def test_group_chat_creation_with_translation(self, ac1):
|
||||
ac1.start_threads()
|
||||
ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %1$s")
|
||||
ac1._evlogger.consume_events()
|
||||
with pytest.raises(ValueError):
|
||||
@@ -195,6 +196,7 @@ class TestOfflineChat:
|
||||
assert not chat.is_promoted()
|
||||
msg = chat.get_draft()
|
||||
assert msg.text == "xyz title1"
|
||||
ac1.stop_threads()
|
||||
|
||||
@pytest.mark.parametrize("verified", [True, False])
|
||||
def test_group_chat_qr(self, acfactory, ac1, verified):
|
||||
|
||||
@@ -1,25 +1,48 @@
|
||||
from __future__ import print_function
|
||||
import threading
|
||||
from deltachat import capi, cutil, const, set_context_callback, clear_context_callback
|
||||
from deltachat.capi import ffi
|
||||
from deltachat.capi import lib
|
||||
from deltachat.account import EventLogger
|
||||
|
||||
|
||||
class EventThread(threading.Thread):
|
||||
def __init__(self, dc_context):
|
||||
self.dc_context = dc_context
|
||||
super(EventThread, self).__init__()
|
||||
self.setDaemon(1)
|
||||
|
||||
def run(self):
|
||||
lib.dc_context_run(self.dc_context, lib.py_dc_callback)
|
||||
|
||||
def stop(self):
|
||||
lib.dc_context_shutdown(self.dc_context)
|
||||
|
||||
|
||||
def test_empty_context():
|
||||
ctx = capi.lib.dc_context_new(capi.ffi.NULL, capi.ffi.NULL, capi.ffi.NULL)
|
||||
ctx = capi.lib.dc_context_new(capi.ffi.NULL, capi.ffi.NULL)
|
||||
capi.lib.dc_close(ctx)
|
||||
|
||||
|
||||
def test_callback_None2int():
|
||||
ctx = capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL)
|
||||
ctx = capi.lib.dc_context_new(ffi.NULL, ffi.NULL)
|
||||
set_context_callback(ctx, lambda *args: None)
|
||||
capi.lib.dc_close(ctx)
|
||||
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):
|
||||
ctx = ffi.gc(
|
||||
capi.lib.dc_context_new(capi.lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
capi.lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
evlog = EventLogger(ctx)
|
||||
@@ -28,6 +51,9 @@ def test_dc_close_events(tmpdir):
|
||||
ctx,
|
||||
lambda ctx, evt_name, data1, data2: evlog(evt_name, data1, data2)
|
||||
)
|
||||
ev_thread = EventThread(ctx)
|
||||
ev_thread.start()
|
||||
|
||||
p = tmpdir.join("hello.db")
|
||||
lib.dc_open(ctx, p.strpath.encode("ascii"), ffi.NULL)
|
||||
capi.lib.dc_close(ctx)
|
||||
@@ -46,11 +72,12 @@ def test_dc_close_events(tmpdir):
|
||||
find("disconnecting mvbox-thread")
|
||||
find("disconnecting SMTP")
|
||||
find("Database closed")
|
||||
ev_thread.stop()
|
||||
|
||||
|
||||
def test_wrong_db(tmpdir):
|
||||
dc_context = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
p = tmpdir.join("hello.db")
|
||||
@@ -62,7 +89,7 @@ def test_wrong_db(tmpdir):
|
||||
def test_empty_blobdir(tmpdir):
|
||||
# Apparently some client code expects this to be the same as passing NULL.
|
||||
ctx = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
db_fname = tmpdir.join("hello.db")
|
||||
@@ -119,7 +146,7 @@ def test_provider_info_none():
|
||||
|
||||
def test_get_info_closed():
|
||||
ctx = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
info = cutil.from_dc_charpointer(lib.dc_get_info(ctx))
|
||||
@@ -129,7 +156,7 @@ def test_get_info_closed():
|
||||
|
||||
def test_get_info_open(tmpdir):
|
||||
ctx = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
db_fname = tmpdir.join("test.db")
|
||||
@@ -141,7 +168,7 @@ def test_get_info_open(tmpdir):
|
||||
|
||||
def test_is_open_closed():
|
||||
ctx = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
assert lib.dc_is_open(ctx) == 0
|
||||
@@ -149,7 +176,7 @@ def test_is_open_closed():
|
||||
|
||||
def test_is_open_actually_open(tmpdir):
|
||||
ctx = ffi.gc(
|
||||
lib.dc_context_new(lib.py_dc_callback, ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_new(ffi.NULL, ffi.NULL),
|
||||
lib.dc_context_unref,
|
||||
)
|
||||
db_fname = tmpdir.join("test.db")
|
||||
|
||||
@@ -69,7 +69,7 @@ addopts = -v -ra
|
||||
python_files = tests/test_*.py
|
||||
norecursedirs = .tox
|
||||
xfail_strict=true
|
||||
timeout = 60
|
||||
timeout = 90
|
||||
timeout_method = thread
|
||||
|
||||
[flake8]
|
||||
|
||||
@@ -1 +1 @@
|
||||
nightly-2019-11-06
|
||||
nightly-2020-01-26
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::constants::DC_VERSION_STR;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::job::*;
|
||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||
use crate::stock::StockMessage;
|
||||
use rusqlite::NO_PARAMS;
|
||||
|
||||
@@ -98,7 +99,7 @@ impl Context {
|
||||
rel_path.map(|p| dc_get_abs_path(self, &p).to_string_lossy().into_owned())
|
||||
}
|
||||
Config::SysVersion => Some((&*DC_VERSION_STR).clone()),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", 24 * 1024 * 1024 / 4 * 3)),
|
||||
Config::SysMsgsizeMaxRecommended => Some(format!("{}", RECOMMENDED_FILE_SIZE)),
|
||||
Config::SysConfigKeys => Some(get_config_keys_string()),
|
||||
_ => self.sql.get_raw_config(self, key),
|
||||
};
|
||||
|
||||
@@ -13,10 +13,9 @@ use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee;
|
||||
use crate::job::{self, job_add, job_kill_action};
|
||||
use crate::job;
|
||||
use crate::login_param::{CertificateChecks, LoginParam};
|
||||
use crate::oauth2::*;
|
||||
use crate::param::Params;
|
||||
|
||||
use auto_mozilla::moz_autoconfigure;
|
||||
use auto_outlook::outlk_autodiscover;
|
||||
@@ -31,21 +30,6 @@ macro_rules! progress {
|
||||
};
|
||||
}
|
||||
|
||||
// connect
|
||||
pub fn configure(context: &Context) {
|
||||
if context.has_ongoing() {
|
||||
warn!(context, "There is already another ongoing process running.",);
|
||||
return;
|
||||
}
|
||||
job_kill_action(context, job::Action::ConfigureImap);
|
||||
job_add(context, job::Action::ConfigureImap, 0, Params::new(), 0);
|
||||
}
|
||||
|
||||
/// Check if the context is already configured.
|
||||
pub fn dc_is_configured(context: &Context) -> bool {
|
||||
context.sql.get_raw_config_bool(context, "configured")
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Configure JOB
|
||||
******************************************************************************/
|
||||
@@ -200,10 +184,7 @@ pub fn JobConfigureImap(context: &Context) -> job::Status {
|
||||
7 => {
|
||||
progress!(context, 310);
|
||||
if param_autoconfig.is_none() {
|
||||
let url = format!(
|
||||
"https://{}{}/autodiscover/autodiscover.xml",
|
||||
"", param_domain
|
||||
);
|
||||
let url = format!("https://{}/autodiscover/autodiscover.xml", param_domain);
|
||||
param_autoconfig = outlk_autodiscover(context, &url, ¶m).ok();
|
||||
}
|
||||
true
|
||||
|
||||
161
src/context.rs
161
src/context.rs
@@ -3,7 +3,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsString;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, Condvar, Mutex, RwLock};
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Condvar, Mutex, RwLock,
|
||||
};
|
||||
|
||||
use crossbeam::channel::select;
|
||||
use crossbeam::channel::{bounded, unbounded, Receiver, Sender};
|
||||
|
||||
use crate::chat::*;
|
||||
use crate::config::Config;
|
||||
@@ -12,7 +18,7 @@ use crate::contact::*;
|
||||
use crate::error::*;
|
||||
use crate::events::Event;
|
||||
use crate::imap::*;
|
||||
use crate::job::*;
|
||||
use crate::job;
|
||||
use crate::job_thread::JobThread;
|
||||
use crate::key::*;
|
||||
use crate::login_param::LoginParam;
|
||||
@@ -32,7 +38,7 @@ use crate::sql::Sql;
|
||||
/// * `data2` - Depends on the event parameter, see [Event].
|
||||
pub type ContextCallback = dyn Fn(&Context, Event) -> () + Send + Sync;
|
||||
|
||||
#[derive(DebugStub)]
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
/// Database file path
|
||||
dbfile: PathBuf,
|
||||
@@ -47,8 +53,6 @@ pub struct Context {
|
||||
pub smtp: Arc<Mutex<Smtp>>,
|
||||
pub smtp_state: Arc<(Mutex<SmtpState>, Condvar)>,
|
||||
pub oauth2_critical: Arc<Mutex<()>>,
|
||||
#[debug_stub = "Callback"]
|
||||
cb: Box<ContextCallback>,
|
||||
pub os_name: Option<String>,
|
||||
pub cmdline_sel_chat_id: Arc<RwLock<ChatId>>,
|
||||
pub bob: Arc<RwLock<BobStatus>>,
|
||||
@@ -57,6 +61,13 @@ pub struct Context {
|
||||
/// Mutex to avoid generating the key for the user more than once.
|
||||
pub generating_key_mutex: Mutex<()>,
|
||||
pub translated_stockstrings: RwLock<HashMap<usize, String>>,
|
||||
|
||||
event_sender: Sender<Event>,
|
||||
event_receiver: Receiver<Event>,
|
||||
shutdown_sender: Sender<()>,
|
||||
shutdown_receiver: Receiver<()>,
|
||||
|
||||
is_running: AtomicBool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -80,9 +91,19 @@ pub fn get_info() -> HashMap<&'static str, String> {
|
||||
res
|
||||
}
|
||||
|
||||
macro_rules! while_running {
|
||||
($self:expr, $code:block) => {
|
||||
if $self.is_running.load(Ordering::Relaxed) {
|
||||
$code
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Context {
|
||||
/// Creates new context.
|
||||
pub fn new(cb: Box<ContextCallback>, os_name: String, dbfile: PathBuf) -> Result<Context> {
|
||||
pub fn new(os_name: String, dbfile: PathBuf) -> Result<Context> {
|
||||
let mut blob_fname = OsString::new();
|
||||
blob_fname.push(dbfile.file_name().unwrap_or_default());
|
||||
blob_fname.push("-blobs");
|
||||
@@ -90,24 +111,22 @@ impl Context {
|
||||
if !blobdir.exists() {
|
||||
std::fs::create_dir_all(&blobdir)?;
|
||||
}
|
||||
Context::with_blobdir(cb, os_name, dbfile, blobdir)
|
||||
Context::with_blobdir(os_name, dbfile, blobdir)
|
||||
}
|
||||
|
||||
pub fn with_blobdir(
|
||||
cb: Box<ContextCallback>,
|
||||
os_name: String,
|
||||
dbfile: PathBuf,
|
||||
blobdir: PathBuf,
|
||||
) -> Result<Context> {
|
||||
pub fn with_blobdir(os_name: String, dbfile: PathBuf, blobdir: PathBuf) -> Result<Context> {
|
||||
ensure!(
|
||||
blobdir.is_dir(),
|
||||
"Blobdir does not exist: {}",
|
||||
blobdir.display()
|
||||
);
|
||||
|
||||
let (event_sender, event_receiver) = unbounded();
|
||||
let (shutdown_sender, shutdown_receiver) = bounded(0);
|
||||
|
||||
let ctx = Context {
|
||||
blobdir,
|
||||
dbfile,
|
||||
cb,
|
||||
os_name: Some(os_name),
|
||||
running_state: Arc::new(RwLock::new(Default::default())),
|
||||
sql: Sql::new(),
|
||||
@@ -136,6 +155,11 @@ impl Context {
|
||||
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
|
||||
generating_key_mutex: Mutex::new(()),
|
||||
translated_stockstrings: RwLock::new(HashMap::new()),
|
||||
event_sender,
|
||||
event_receiver,
|
||||
shutdown_sender,
|
||||
shutdown_receiver,
|
||||
is_running: Default::default(),
|
||||
};
|
||||
|
||||
ensure!(
|
||||
@@ -157,7 +181,98 @@ impl Context {
|
||||
}
|
||||
|
||||
pub fn call_cb(&self, event: Event) {
|
||||
(*self.cb)(self, event);
|
||||
self.event_sender.send(event).unwrap();
|
||||
}
|
||||
|
||||
/// Start the run loop.
|
||||
pub fn run<F>(&self, cb: F)
|
||||
where
|
||||
F: Fn(&Context, Event) -> () + Send + Sync,
|
||||
{
|
||||
if self.is_running.swap(true, Ordering::Relaxed) {
|
||||
panic!("Run can only be called once");
|
||||
}
|
||||
|
||||
crossbeam::scope(|s| {
|
||||
let imap_handle = s.spawn(|_| loop {
|
||||
while_running!(self, {
|
||||
job::perform_inbox_jobs(self);
|
||||
while_running!(self, {
|
||||
job::perform_inbox_fetch(self);
|
||||
while_running!(self, { job::perform_inbox_idle(self) });
|
||||
});
|
||||
});
|
||||
});
|
||||
let mvbox_handle = s.spawn(|_| loop {
|
||||
while_running!(self, {
|
||||
job::perform_mvbox_fetch(self);
|
||||
while_running!(self, {
|
||||
job::perform_mvbox_idle(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
let sentbox_handle = s.spawn(|_| loop {
|
||||
while_running!(self, {
|
||||
job::perform_sentbox_fetch(self);
|
||||
while_running!(self, {
|
||||
job::perform_sentbox_idle(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
let smtp_handle = s.spawn(|_| loop {
|
||||
while_running!(self, {
|
||||
job::perform_smtp_jobs(self);
|
||||
while_running!(self, {
|
||||
job::perform_smtp_idle(self);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
loop {
|
||||
select! {
|
||||
recv(self.event_receiver) -> event => {
|
||||
// This gurantees that the callback is always called from the thread
|
||||
// that called `run`.
|
||||
cb(self, event.unwrap())
|
||||
},
|
||||
recv(self.shutdown_receiver) -> _ => break,
|
||||
}
|
||||
}
|
||||
|
||||
imap_handle.join().unwrap();
|
||||
mvbox_handle.join().unwrap();
|
||||
sentbox_handle.join().unwrap();
|
||||
smtp_handle.join().unwrap();
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Stop the run loop. Blocks until all threads have shutdown.
|
||||
pub fn shutdown(&self) {
|
||||
if !self.is_running.swap(false, Ordering::Relaxed) {
|
||||
// already shutdown
|
||||
return;
|
||||
}
|
||||
self.shutdown_sender.send(()).unwrap();
|
||||
|
||||
job::interrupt_inbox_idle(self);
|
||||
job::interrupt_mvbox_idle(self);
|
||||
job::interrupt_sentbox_idle(self);
|
||||
job::interrupt_smtp_idle(self);
|
||||
}
|
||||
|
||||
pub fn configure(&self) {
|
||||
if self.has_ongoing() {
|
||||
warn!(self, "There is already another ongoing process running.");
|
||||
return;
|
||||
}
|
||||
job::job_kill_action(self, job::Action::ConfigureImap);
|
||||
job::job_add(self, job::Action::ConfigureImap, 0, Params::new(), 0);
|
||||
}
|
||||
|
||||
/// Check if the context is already configured.
|
||||
pub fn is_configured(&self) -> bool {
|
||||
self.sql.get_raw_config_bool(self, "configured")
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -439,9 +554,9 @@ impl Context {
|
||||
match msg.is_dc_message {
|
||||
MessengerMessage::No => {}
|
||||
MessengerMessage::Yes | MessengerMessage::Reply => {
|
||||
job_add(
|
||||
job::job_add(
|
||||
self,
|
||||
Action::MoveMsg,
|
||||
job::Action::MoveMsg,
|
||||
msg.id.to_u32() as i32,
|
||||
Params::new(),
|
||||
0,
|
||||
@@ -519,7 +634,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
std::fs::write(&dbfile, b"123").unwrap();
|
||||
let res = Context::new(Box::new(|_, _| ()), "FakeOs".into(), dbfile);
|
||||
let res = Context::new("FakeOs".into(), dbfile);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -534,7 +649,7 @@ mod tests {
|
||||
fn test_blobdir_exists() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap();
|
||||
Context::new("FakeOS".into(), dbfile).unwrap();
|
||||
let blobdir = tmp.path().join("db.sqlite-blobs");
|
||||
assert!(blobdir.is_dir());
|
||||
}
|
||||
@@ -545,7 +660,7 @@ mod tests {
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
let blobdir = tmp.path().join("db.sqlite-blobs");
|
||||
std::fs::write(&blobdir, b"123").unwrap();
|
||||
let res = Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile);
|
||||
let res = Context::new("FakeOS".into(), dbfile);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -555,7 +670,7 @@ mod tests {
|
||||
let subdir = tmp.path().join("subdir");
|
||||
let dbfile = subdir.join("db.sqlite");
|
||||
let dbfile2 = dbfile.clone();
|
||||
Context::new(Box::new(|_, _| ()), "FakeOS".into(), dbfile).unwrap();
|
||||
Context::new("FakeOS".into(), dbfile).unwrap();
|
||||
assert!(subdir.is_dir());
|
||||
assert!(dbfile2.is_file());
|
||||
}
|
||||
@@ -565,7 +680,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
let blobdir = PathBuf::new();
|
||||
let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir);
|
||||
let res = Context::with_blobdir("FakeOS".into(), dbfile, blobdir);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
@@ -574,7 +689,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let dbfile = tmp.path().join("db.sqlite");
|
||||
let blobdir = tmp.path().join("blobs");
|
||||
let res = Context::with_blobdir(Box::new(|_, _| ()), "FakeOS".into(), dbfile, blobdir);
|
||||
let res = Context::with_blobdir("FakeOS".into(), dbfile, blobdir);
|
||||
assert!(res.is_err());
|
||||
}
|
||||
|
||||
|
||||
@@ -460,7 +460,7 @@ pub(crate) fn time() -> i64 {
|
||||
///
|
||||
/// ```
|
||||
/// use deltachat::dc_tools::EmailAddress;
|
||||
/// let email = match EmailAddress::new("someone@example.com") {
|
||||
/// let email = match "someone@example.com".parse::<EmailAddress>() {
|
||||
/// Ok(addr) => addr,
|
||||
/// Err(e) => panic!("Error parsing address, error was {}", e),
|
||||
/// };
|
||||
@@ -474,12 +474,6 @@ pub struct EmailAddress {
|
||||
pub domain: String,
|
||||
}
|
||||
|
||||
impl EmailAddress {
|
||||
pub fn new(input: &str) -> Result<Self, Error> {
|
||||
input.parse::<EmailAddress>()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EmailAddress {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}@{}", self.local, self.domain)
|
||||
@@ -670,29 +664,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_emailaddress_parse() {
|
||||
assert_eq!(EmailAddress::new("").is_ok(), false);
|
||||
assert_eq!("".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!(
|
||||
EmailAddress::new("user@domain.tld").unwrap(),
|
||||
"user@domain.tld".parse::<EmailAddress>().unwrap(),
|
||||
EmailAddress {
|
||||
local: "user".into(),
|
||||
domain: "domain.tld".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(EmailAddress::new("uuu").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("dd.tt").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("tt.dd@uu").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("u@d").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("u@d.").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("u@d.t").is_ok(), false);
|
||||
assert_eq!("uuu".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("dd.tt".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("tt.dd@uu".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("u@d".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("u@d.".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("u@d.t".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!(
|
||||
EmailAddress::new("u@d.tt").unwrap(),
|
||||
"u@d.tt".parse::<EmailAddress>().unwrap(),
|
||||
EmailAddress {
|
||||
local: "u".into(),
|
||||
domain: "d.tt".into(),
|
||||
}
|
||||
);
|
||||
assert_eq!(EmailAddress::new("u@.tt").is_ok(), false);
|
||||
assert_eq!(EmailAddress::new("@d.tt").is_ok(), false);
|
||||
assert_eq!("u@tt".parse::<EmailAddress>().is_ok(), false);
|
||||
assert_eq!("@d.tt".parse::<EmailAddress>().is_ok(), false);
|
||||
}
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
@@ -80,18 +80,21 @@ impl Imap {
|
||||
} else {
|
||||
info!(context, "Idle entering wait-on-remote state");
|
||||
match idle_wait.await {
|
||||
IdleResponse::NewData(_) => {
|
||||
Ok(IdleResponse::NewData(_)) => {
|
||||
info!(context, "Idle has NewData");
|
||||
}
|
||||
// TODO: idle_wait does not distinguish manual interrupts
|
||||
// from Timeouts if we would know it's a Timeout we could bail
|
||||
// directly and reconnect .
|
||||
IdleResponse::Timeout => {
|
||||
Ok(IdleResponse::Timeout) => {
|
||||
info!(context, "Idle-wait timeout or interruption");
|
||||
}
|
||||
IdleResponse::ManualInterrupt => {
|
||||
Ok(IdleResponse::ManualInterrupt) => {
|
||||
info!(context, "Idle wait was interrupted");
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Idle wait errored: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we can't properly terminate the idle
|
||||
@@ -134,18 +137,21 @@ impl Imap {
|
||||
} else {
|
||||
info!(context, "Idle entering wait-on-remote state");
|
||||
match idle_wait.await {
|
||||
IdleResponse::NewData(_) => {
|
||||
Ok(IdleResponse::NewData(_)) => {
|
||||
info!(context, "Idle has NewData");
|
||||
}
|
||||
// TODO: idle_wait does not distinguish manual interrupts
|
||||
// from Timeouts if we would know it's a Timeout we could bail
|
||||
// directly and reconnect .
|
||||
IdleResponse::Timeout => {
|
||||
Ok(IdleResponse::Timeout) => {
|
||||
info!(context, "Idle-wait timeout or interruption");
|
||||
}
|
||||
IdleResponse::ManualInterrupt => {
|
||||
Ok(IdleResponse::ManualInterrupt) => {
|
||||
info!(context, "Idle wait was interrupted");
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "Idle wait errored: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we can't properly terminate the idle
|
||||
|
||||
@@ -35,9 +35,8 @@ impl Client {
|
||||
certificate_checks: CertificateChecks,
|
||||
) -> ImapResult<Self> {
|
||||
let stream = TcpStream::connect(addr).await?;
|
||||
let tls = dc_build_tls(certificate_checks)?;
|
||||
let tls_connector: async_native_tls::TlsConnector = tls.into();
|
||||
let tls_stream = tls_connector.connect(domain.as_ref(), stream).await?;
|
||||
let tls = dc_build_tls(certificate_checks);
|
||||
let tls_stream = tls.connect(domain.as_ref(), stream).await?;
|
||||
let mut client = ImapClient::new(tls_stream);
|
||||
if std::env::var(crate::DCC_IMAP_DEBUG).is_ok() {
|
||||
client.debug = true;
|
||||
@@ -73,10 +72,8 @@ impl Client {
|
||||
) -> ImapResult<Client> {
|
||||
match self {
|
||||
Client::Insecure(client) => {
|
||||
let tls = dc_build_tls(certificate_checks)?;
|
||||
let tls_stream = tls.into();
|
||||
|
||||
let client_sec = client.secure(domain, &tls_stream).await?;
|
||||
let tls = dc_build_tls(certificate_checks);
|
||||
let client_sec = client.secure(domain, tls).await?;
|
||||
|
||||
Ok(Client::Secure(client_sec))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ use crate::blob::BlobObject;
|
||||
use crate::chat;
|
||||
use crate::chat::delete_and_reset_all_device_msgs;
|
||||
use crate::config::Config;
|
||||
use crate::configure::*;
|
||||
use crate::constants::*;
|
||||
use crate::context::Context;
|
||||
use crate::dc_tools::*;
|
||||
@@ -423,7 +422,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef<Path>) -> Resul
|
||||
);
|
||||
|
||||
ensure!(
|
||||
!dc_is_configured(context),
|
||||
!context.is_configured(),
|
||||
"Cannot import backups to accounts in use."
|
||||
);
|
||||
context.sql.close(&context);
|
||||
|
||||
34
src/job.rs
34
src/job.rs
@@ -553,7 +553,7 @@ pub fn job_kill_ids(context: &Context, job_ids: &[u32]) -> sql::Result<()> {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn perform_inbox_fetch(context: &Context) {
|
||||
pub(crate) fn perform_inbox_fetch(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::InboxWatch);
|
||||
|
||||
task::block_on(
|
||||
@@ -565,7 +565,7 @@ pub fn perform_inbox_fetch(context: &Context) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_fetch(context: &Context) {
|
||||
pub(crate) fn perform_mvbox_fetch(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::MvboxWatch);
|
||||
|
||||
task::block_on(
|
||||
@@ -577,7 +577,7 @@ pub fn perform_mvbox_fetch(context: &Context) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn perform_sentbox_fetch(context: &Context) {
|
||||
pub(crate) fn perform_sentbox_fetch(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::SentboxWatch);
|
||||
|
||||
task::block_on(
|
||||
@@ -589,7 +589,7 @@ pub fn perform_sentbox_fetch(context: &Context) {
|
||||
);
|
||||
}
|
||||
|
||||
pub fn perform_inbox_idle(context: &Context) {
|
||||
pub(crate) fn perform_inbox_idle(context: &Context) {
|
||||
if *context.perform_inbox_jobs_needed.clone().read().unwrap() {
|
||||
info!(
|
||||
context,
|
||||
@@ -606,7 +606,7 @@ pub fn perform_inbox_idle(context: &Context) {
|
||||
.idle(context, use_network);
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_idle(context: &Context) {
|
||||
pub(crate) fn perform_mvbox_idle(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::MvboxWatch);
|
||||
|
||||
context
|
||||
@@ -616,7 +616,7 @@ pub fn perform_mvbox_idle(context: &Context) {
|
||||
.idle(context, use_network);
|
||||
}
|
||||
|
||||
pub fn perform_sentbox_idle(context: &Context) {
|
||||
pub(crate) fn perform_sentbox_idle(context: &Context) {
|
||||
let use_network = context.get_config_bool(Config::SentboxWatch);
|
||||
|
||||
context
|
||||
@@ -626,7 +626,7 @@ pub fn perform_sentbox_idle(context: &Context) {
|
||||
.idle(context, use_network);
|
||||
}
|
||||
|
||||
pub fn interrupt_inbox_idle(context: &Context) {
|
||||
pub(crate) fn interrupt_inbox_idle(context: &Context) {
|
||||
info!(context, "interrupt_inbox_idle called");
|
||||
// we do not block on trying to obtain the thread lock
|
||||
// because we don't know in which state the thread is.
|
||||
@@ -643,11 +643,11 @@ pub fn interrupt_inbox_idle(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_mvbox_idle(context: &Context) {
|
||||
pub(crate) fn interrupt_mvbox_idle(context: &Context) {
|
||||
context.mvbox_thread.read().unwrap().interrupt_idle(context);
|
||||
}
|
||||
|
||||
pub fn interrupt_sentbox_idle(context: &Context) {
|
||||
pub(crate) fn interrupt_sentbox_idle(context: &Context) {
|
||||
context
|
||||
.sentbox_thread
|
||||
.read()
|
||||
@@ -655,7 +655,7 @@ pub fn interrupt_sentbox_idle(context: &Context) {
|
||||
.interrupt_idle(context);
|
||||
}
|
||||
|
||||
pub fn perform_smtp_jobs(context: &Context) {
|
||||
pub(crate) fn perform_smtp_jobs(context: &Context) {
|
||||
let probe_smtp_network = {
|
||||
let &(ref lock, _) = &*context.smtp_state.clone();
|
||||
let mut state = lock.lock().unwrap();
|
||||
@@ -684,7 +684,7 @@ pub fn perform_smtp_jobs(context: &Context) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn perform_smtp_idle(context: &Context) {
|
||||
pub(crate) fn perform_smtp_idle(context: &Context) {
|
||||
info!(context, "SMTP-idle started...",);
|
||||
{
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
@@ -864,7 +864,7 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn perform_inbox_jobs(context: &Context) {
|
||||
pub(crate) fn perform_inbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_inbox_jobs starting.",);
|
||||
|
||||
let probe_imap_network = *context.probe_imap_network.clone().read().unwrap();
|
||||
@@ -875,14 +875,6 @@ pub fn perform_inbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_inbox_jobs ended.",);
|
||||
}
|
||||
|
||||
pub fn perform_mvbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_mbox_jobs EMPTY (for now).",);
|
||||
}
|
||||
|
||||
pub fn perform_sentbox_jobs(context: &Context) {
|
||||
info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",);
|
||||
}
|
||||
|
||||
fn job_perform(context: &Context, thread: Thread, probe_network: bool) {
|
||||
while let Some(mut job) = load_next_job(context, thread, probe_network) {
|
||||
info!(context, "{}-job {} started...", thread, job);
|
||||
@@ -1137,7 +1129,7 @@ pub fn job_add(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt_smtp_idle(context: &Context) {
|
||||
pub(crate) fn interrupt_smtp_idle(context: &Context) {
|
||||
info!(context, "Interrupting SMTP-idle...",);
|
||||
|
||||
let &(ref lock, ref cvar) = &*context.smtp_state.clone();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![forbid(unsafe_code)]
|
||||
#![deny(clippy::correctness, missing_debug_implementations, clippy::all)]
|
||||
#![allow(clippy::match_bool)]
|
||||
|
||||
|
||||
@@ -258,10 +258,8 @@ fn get_readable_flags(flags: i32) -> String {
|
||||
res
|
||||
}
|
||||
|
||||
pub fn dc_build_tls(
|
||||
certificate_checks: CertificateChecks,
|
||||
) -> Result<native_tls::TlsConnector, native_tls::Error> {
|
||||
let mut tls_builder = native_tls::TlsConnector::builder();
|
||||
pub fn dc_build_tls(certificate_checks: CertificateChecks) -> async_native_tls::TlsConnector {
|
||||
let tls_builder = async_native_tls::TlsConnector::new();
|
||||
match certificate_checks {
|
||||
CertificateChecks::Automatic => {
|
||||
// Same as AcceptInvalidCertificates for now.
|
||||
@@ -270,13 +268,12 @@ pub fn dc_build_tls(
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.danger_accept_invalid_certs(true)
|
||||
}
|
||||
CertificateChecks::Strict => &mut tls_builder,
|
||||
CertificateChecks::Strict => tls_builder,
|
||||
CertificateChecks::AcceptInvalidCertificates
|
||||
| CertificateChecks::AcceptInvalidCertificates2 => tls_builder
|
||||
.danger_accept_invalid_hostnames(true)
|
||||
.danger_accept_invalid_certs(true),
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -47,10 +47,7 @@ impl MsgId {
|
||||
///
|
||||
/// This kind of message ID can not be used for real messages.
|
||||
pub fn is_special(self) -> bool {
|
||||
match self.0 {
|
||||
0..=DC_MSG_ID_LAST_SPECIAL => true,
|
||||
_ => false,
|
||||
}
|
||||
self.0 <= DC_MSG_ID_LAST_SPECIAL
|
||||
}
|
||||
|
||||
/// Whether the message ID is unset.
|
||||
@@ -206,7 +203,8 @@ impl Message {
|
||||
pub fn load_from_db(context: &Context, id: MsgId) -> Result<Message, Error> {
|
||||
ensure!(
|
||||
!id.is_special(),
|
||||
"Can not load special message IDs from DB."
|
||||
"Can not load special message IDs from DB. ({:?})",
|
||||
id
|
||||
);
|
||||
context
|
||||
.sql
|
||||
|
||||
@@ -17,6 +17,13 @@ use crate::param::*;
|
||||
use crate::peerstate::{Peerstate, PeerstateVerifiedStatus};
|
||||
use crate::stock::StockMessage;
|
||||
|
||||
// attachments of 25 mb brutto should work on the majority of providers
|
||||
// (brutto examples: web.de=50, 1&1=40, t-online.de=32, gmail=25, posteo=50, yahoo=25, all-inkl=100).
|
||||
// as an upper limit, we double the size; the core won't send messages larger than this
|
||||
// to get the netto sizes, we subtract 1 mb header-overhead and the base64-overhead.
|
||||
pub const RECOMMENDED_FILE_SIZE: u64 = 24 * 1024 * 1024 / 4 * 3;
|
||||
const UPPER_LIMIT_FILE_SIZE: u64 = 49 * 1024 * 1024 / 4 * 3;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Loaded {
|
||||
Message { chat: Chat },
|
||||
@@ -846,7 +853,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
if !is_file_size_okay(context, &self.msg) {
|
||||
bail!(
|
||||
"Message exceeds the recommended {} MB.",
|
||||
24 * 1024 * 1024 / 4 * 3 / 1000 / 1000,
|
||||
RECOMMENDED_FILE_SIZE / 1_000_000,
|
||||
);
|
||||
} else {
|
||||
let (file_part, _) = build_body_file(context, &self.msg, "")?;
|
||||
@@ -1104,7 +1111,7 @@ fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||
match msg.param.get_path(Param::File, context).unwrap_or(None) {
|
||||
Some(path) => {
|
||||
let bytes = dc_get_filebytes(context, &path);
|
||||
bytes <= (49 * 1024 * 1024 / 4 * 3)
|
||||
bytes <= UPPER_LIMIT_FILE_SIZE
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
@@ -1113,7 +1120,7 @@ fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||
fn render_rfc724_mid(rfc724_mid: &str) -> String {
|
||||
let rfc724_mid = rfc724_mid.trim().to_string();
|
||||
|
||||
if rfc724_mid.chars().nth(0).unwrap_or_default() == '<' {
|
||||
if rfc724_mid.chars().next().unwrap_or_default() == '<' {
|
||||
rfc724_mid
|
||||
} else {
|
||||
format!("<{}>", rfc724_mid)
|
||||
|
||||
@@ -95,42 +95,25 @@ impl<'a> MimeMessage<'a> {
|
||||
pub fn from_bytes(context: &'a Context, body: &[u8]) -> Result<Self> {
|
||||
let mail = mailparse::parse_mail(body)?;
|
||||
|
||||
let mut parser = MimeMessage {
|
||||
parts: Vec::new(),
|
||||
header: Default::default(),
|
||||
decrypting_failed: false,
|
||||
|
||||
// only non-empty if it was a valid autocrypt message
|
||||
signatures: Default::default(),
|
||||
gossipped_addr: Default::default(),
|
||||
is_forwarded: false,
|
||||
context,
|
||||
reports: Vec::new(),
|
||||
is_system_message: SystemMessage::Unknown,
|
||||
location_kml: None,
|
||||
message_kml: None,
|
||||
user_avatar: AvatarAction::None,
|
||||
group_avatar: AvatarAction::None,
|
||||
};
|
||||
|
||||
let message_time = mail
|
||||
.headers
|
||||
.get_first_value("Date")?
|
||||
.and_then(|v| mailparse::dateparse(&v).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut headers = Default::default();
|
||||
|
||||
// init known headers with what mailparse provided us
|
||||
parser.merge_headers(&mail.headers);
|
||||
MimeMessage::merge_headers(&mut headers, &mail.headers);
|
||||
|
||||
// Memory location for a possible decrypted message.
|
||||
let mail_raw;
|
||||
let mut gossipped_addr = Default::default();
|
||||
|
||||
let mail = match e2ee::try_decrypt(parser.context, &mail, message_time) {
|
||||
let (mail, signatures) = match e2ee::try_decrypt(context, &mail, message_time) {
|
||||
Ok((raw, signatures)) => {
|
||||
// Valid autocrypt message, encrypted
|
||||
parser.signatures = signatures;
|
||||
|
||||
if let Some(raw) = raw {
|
||||
// Valid autocrypt message, encrypted
|
||||
mail_raw = raw;
|
||||
let decrypted_mail = mailparse::parse_mail(&mail_raw)?;
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
@@ -142,17 +125,17 @@ impl<'a> MimeMessage<'a> {
|
||||
// "3.6 Key Gossip" of https://autocrypt.org/autocrypt-spec-1.1.0.pdf
|
||||
let gossip_headers =
|
||||
decrypted_mail.headers.get_all_values("Autocrypt-Gossip")?;
|
||||
parser.gossipped_addr =
|
||||
gossipped_addr =
|
||||
update_gossip_peerstates(context, message_time, &mail, gossip_headers)?;
|
||||
|
||||
// let known protected headers from the decrypted
|
||||
// part override the unencrypted top-level
|
||||
parser.merge_headers(&decrypted_mail.headers);
|
||||
MimeMessage::merge_headers(&mut headers, &decrypted_mail.headers);
|
||||
|
||||
decrypted_mail
|
||||
(decrypted_mail, signatures)
|
||||
} else {
|
||||
// Message was not encrypted
|
||||
mail
|
||||
(mail, signatures)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
@@ -163,20 +146,36 @@ impl<'a> MimeMessage<'a> {
|
||||
// if we just return here, the header is missing
|
||||
// and the caller cannot display the message
|
||||
// and try to assign the message to a chat
|
||||
warn!(parser.context, "decryption failed: {}", err);
|
||||
mail
|
||||
warn!(context, "decryption failed: {}", err);
|
||||
(mail, Default::default())
|
||||
}
|
||||
};
|
||||
|
||||
parser.parse_mime_recursive(&mail)?;
|
||||
let mut parser = MimeMessage {
|
||||
parts: Vec::new(),
|
||||
header: headers,
|
||||
decrypting_failed: false,
|
||||
|
||||
// only non-empty if it was a valid autocrypt message
|
||||
signatures,
|
||||
gossipped_addr,
|
||||
is_forwarded: false,
|
||||
context,
|
||||
reports: Vec::new(),
|
||||
is_system_message: SystemMessage::Unknown,
|
||||
location_kml: None,
|
||||
message_kml: None,
|
||||
user_avatar: AvatarAction::None,
|
||||
group_avatar: AvatarAction::None,
|
||||
};
|
||||
parser.parse_mime_recursive(&mail)?;
|
||||
parser.parse_headers()?;
|
||||
|
||||
Ok(parser)
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn parse_headers(&mut self) -> Result<()> {
|
||||
/// Parses system messages.
|
||||
fn parse_system_message_headers(&mut self) -> Result<()> {
|
||||
if self.get(HeaderDef::AutocryptSetupMessage).is_some() {
|
||||
self.parts = self
|
||||
.parts
|
||||
@@ -198,7 +197,11 @@ impl<'a> MimeMessage<'a> {
|
||||
self.is_system_message = SystemMessage::LocationStreamingEnabled;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses avatar action headers.
|
||||
fn parse_avatar_headers(&mut self) {
|
||||
if let Some(header_value) = self.get(HeaderDef::ChatGroupAvatar).cloned() {
|
||||
self.group_avatar = self.avatar_action_from_header(header_value);
|
||||
} else if let Some(header_value) = self.get(HeaderDef::ChatGroupImage).cloned() {
|
||||
@@ -210,7 +213,13 @@ impl<'a> MimeMessage<'a> {
|
||||
if let Some(header_value) = self.get(HeaderDef::ChatUserAvatar).cloned() {
|
||||
self.user_avatar = self.avatar_action_from_header(header_value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Squashes mutlipart chat messages with attachment into single-part messages.
|
||||
///
|
||||
/// Delta Chat sends attachments, such as images, in two-part messages, with the first message
|
||||
/// containing an explanation. If such a message is detected, first part can be safely dropped.
|
||||
fn squash_attachment_parts(&mut self) {
|
||||
if self.has_chat_version() && self.parts.len() == 2 {
|
||||
let need_drop = {
|
||||
let textpart = &self.parts[0];
|
||||
@@ -238,6 +247,48 @@ impl<'a> MimeMessage<'a> {
|
||||
std::mem::replace(&mut self.parts[0], filepart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes chat messages with attachments.
|
||||
fn parse_attachments(&mut self) {
|
||||
// Attachment messages should be squashed into a single part
|
||||
// before calling this function.
|
||||
if self.parts.len() == 1 {
|
||||
if self.parts[0].typ == Viewtype::Audio
|
||||
&& self.get(HeaderDef::ChatVoiceMessage).is_some()
|
||||
{
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.typ = Viewtype::Voice;
|
||||
}
|
||||
if self.parts[0].typ == Viewtype::Image {
|
||||
if let Some(value) = self.get(HeaderDef::ChatContent) {
|
||||
if value == "sticker" {
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.typ = Viewtype::Sticker;
|
||||
}
|
||||
}
|
||||
}
|
||||
let part = &self.parts[0];
|
||||
if part.typ == Viewtype::Audio
|
||||
|| part.typ == Viewtype::Voice
|
||||
|| part.typ == Viewtype::Video
|
||||
{
|
||||
if let Some(field_0) = self.get(HeaderDef::ChatDuration) {
|
||||
let duration_ms = field_0.parse().unwrap_or_default();
|
||||
if duration_ms > 0 && duration_ms < 24 * 60 * 60 * 1000 {
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.param.set_int(Param::Duration, duration_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_headers(&mut self) -> Result<()> {
|
||||
self.parse_system_message_headers()?;
|
||||
self.parse_avatar_headers();
|
||||
self.squash_attachment_parts();
|
||||
|
||||
if let Some(ref subject) = self.get_subject() {
|
||||
let mut prepend_subject = true;
|
||||
if !self.decrypting_failed {
|
||||
@@ -273,35 +324,8 @@ impl<'a> MimeMessage<'a> {
|
||||
part.param.set_int(Param::Forwarded, 1);
|
||||
}
|
||||
}
|
||||
if self.parts.len() == 1 {
|
||||
if self.parts[0].typ == Viewtype::Audio
|
||||
&& self.get(HeaderDef::ChatVoiceMessage).is_some()
|
||||
{
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.typ = Viewtype::Voice;
|
||||
}
|
||||
if self.parts[0].typ == Viewtype::Image {
|
||||
if let Some(value) = self.get(HeaderDef::ChatContent) {
|
||||
if value == "sticker" {
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.typ = Viewtype::Sticker;
|
||||
}
|
||||
}
|
||||
}
|
||||
let part = &self.parts[0];
|
||||
if part.typ == Viewtype::Audio
|
||||
|| part.typ == Viewtype::Voice
|
||||
|| part.typ == Viewtype::Video
|
||||
{
|
||||
if let Some(field_0) = self.get(HeaderDef::ChatDuration) {
|
||||
let duration_ms = field_0.parse().unwrap_or_default();
|
||||
if duration_ms > 0 && duration_ms < 24 * 60 * 60 * 1000 {
|
||||
let part_mut = &mut self.parts[0];
|
||||
part_mut.param.set_int(Param::Duration, duration_ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.parse_attachments();
|
||||
|
||||
// See if an MDN is requested from the other side
|
||||
if !self.decrypting_failed && !self.parts.is_empty() {
|
||||
@@ -719,16 +743,16 @@ impl<'a> MimeMessage<'a> {
|
||||
.and_then(|msgid| parse_message_id(msgid))
|
||||
}
|
||||
|
||||
fn merge_headers(&mut self, fields: &[mailparse::MailHeader<'_>]) {
|
||||
fn merge_headers(headers: &mut HashMap<String, String>, fields: &[mailparse::MailHeader<'_>]) {
|
||||
for field in fields {
|
||||
if let Ok(key) = field.get_key() {
|
||||
// lowercasing all headers is technically not correct, but makes things work better
|
||||
let key = key.to_lowercase();
|
||||
if !self.header.contains_key(&key) || // key already exists, only overwrite known types (protected headers)
|
||||
if !headers.contains_key(&key) || // key already exists, only overwrite known types (protected headers)
|
||||
is_known(&key) || key.starts_with("chat-")
|
||||
{
|
||||
if let Ok(value) = field.get_value() {
|
||||
self.header.insert(key, value);
|
||||
headers.insert(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ fn get_self_fingerprint(context: &Context) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Take a scanned QR-code and do the setup-contact/join-group handshake.
|
||||
/// See the ffi-documentation for more details.
|
||||
pub fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId {
|
||||
let cleanup =
|
||||
|context: &Context, contact_chat_id: ChatId, ongoing_allocated: bool, join_vg: bool| {
|
||||
@@ -257,11 +259,18 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> ChatId {
|
||||
);
|
||||
}
|
||||
|
||||
while !context.shall_stop_ongoing() {
|
||||
// Don't sleep too long, the user is waiting.
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
if join_vg {
|
||||
// for a group-join, wait until the secure-join is done and the group is created
|
||||
while !context.shall_stop_ongoing() {
|
||||
std::thread::sleep(std::time::Duration::from_millis(200));
|
||||
}
|
||||
cleanup(&context, contact_chat_id, true, join_vg)
|
||||
} else {
|
||||
// for a one-to-one-chat, the chat is already known, return the chat-id,
|
||||
// the verification runs in background
|
||||
context.free_ongoing();
|
||||
contact_chat_id
|
||||
}
|
||||
cleanup(&context, contact_chat_id, true, join_vg)
|
||||
}
|
||||
|
||||
fn send_handshake_msg(
|
||||
|
||||
@@ -38,11 +38,11 @@ pub enum Error {
|
||||
Oauth2Error { address: String },
|
||||
|
||||
#[fail(display = "TLS error")]
|
||||
Tls(#[cause] native_tls::Error),
|
||||
Tls(#[cause] async_native_tls::Error),
|
||||
}
|
||||
|
||||
impl From<native_tls::Error> for Error {
|
||||
fn from(err: native_tls::Error) -> Error {
|
||||
impl From<async_native_tls::Error> for Error {
|
||||
fn from(err: async_native_tls::Error) -> Error {
|
||||
Error::Tls(err)
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ impl Smtp {
|
||||
let domain = &lp.send_server;
|
||||
let port = lp.send_port as u16;
|
||||
|
||||
let tls_config = dc_build_tls(lp.smtp_certificate_checks)?.into();
|
||||
let tls_config = dc_build_tls(lp.smtp_certificate_checks);
|
||||
let tls_parameters = ClientTlsParameters::new(domain.to_string(), tls_config);
|
||||
|
||||
let (creds, mechanism) = if 0 != lp.server_flags & (DC_LP_AUTH_OAUTH2 as i32) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
//!
|
||||
//! This module is only compiled for test runs.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use tempfile::{tempdir, TempDir};
|
||||
|
||||
use crate::config::Config;
|
||||
@@ -15,7 +17,7 @@ use crate::key;
|
||||
/// The temporary directory can be used to store the SQLite database,
|
||||
/// see e.g. [test_context] which does this.
|
||||
pub struct TestContext {
|
||||
pub ctx: Context,
|
||||
pub ctx: Arc<Context>,
|
||||
pub dir: TempDir,
|
||||
}
|
||||
|
||||
@@ -32,7 +34,13 @@ pub fn test_context(callback: Option<Box<ContextCallback>>) -> TestContext {
|
||||
Some(cb) => cb,
|
||||
None => Box::new(|_, _| ()),
|
||||
};
|
||||
let ctx = Context::new(cb, "FakeOs".into(), dbfile).unwrap();
|
||||
let ctx = Arc::new(Context::new("FakeOs".into(), dbfile).unwrap());
|
||||
let ctx_1 = ctx.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
ctx_1.run(|context, event| (*cb)(context, event));
|
||||
});
|
||||
|
||||
TestContext { ctx, dir }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Stress some functions for testing; if used as a lib, this file is obsolete.
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deltachat::config;
|
||||
use deltachat::context::*;
|
||||
@@ -207,14 +208,20 @@ fn cb(_context: &Context, _event: Event) {}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct TestContext {
|
||||
ctx: Context,
|
||||
ctx: Arc<Context>,
|
||||
dir: TempDir,
|
||||
}
|
||||
|
||||
fn create_test_context() -> TestContext {
|
||||
let dir = tempdir().unwrap();
|
||||
let dbfile = dir.path().join("db.sqlite");
|
||||
let ctx = Context::new(Box::new(cb), "FakeOs".into(), dbfile).unwrap();
|
||||
let ctx = Arc::new(Context::new("FakeOs".into(), dbfile).unwrap());
|
||||
let ctx_1 = ctx.clone();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
ctx_1.run(cb);
|
||||
});
|
||||
|
||||
TestContext { ctx, dir }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user