mirror of
https://github.com/chatmail/core.git
synced 2026-10-16 10:01:05 +03:00
Compare commits
49
Commits
| 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 | ||
|
|
7ea0e4d4db | ||
|
|
622c1705aa | ||
|
|
3a08929b05 | ||
|
|
99e30d561d | ||
|
|
033a44580c | ||
|
|
4734bcfbb4 | ||
|
|
099fe6f477 | ||
|
|
889327b5f6 | ||
|
|
a2845f44ab | ||
|
|
a7477516d1 | ||
|
|
938d5828fc | ||
|
|
bf3eab453c | ||
|
|
ebab893330 | ||
|
|
4c67b3a118 | ||
|
|
d74b06f8bf | ||
|
|
c54e211147 | ||
|
|
8817cf5116 | ||
|
|
fb568513b2 | ||
|
|
b4ebfdb84a | ||
|
|
7040bd804a |
@@ -1,5 +1,18 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0-beta.24
|
||||
|
||||
- fix oauth2/gmail bug introduced in beta23 (not used in releases) #1219
|
||||
|
||||
- fix panic when receiving eg. cyrillic filenames #1216
|
||||
|
||||
- delete all consumed secure-join handshake messagess #1209 #1212
|
||||
|
||||
- rust-level cleanups #1218 #1217 #1210 #1205
|
||||
|
||||
- python-level cleanups #1204 #1202 #1201
|
||||
|
||||
|
||||
## 1.0.0-beta.23
|
||||
|
||||
- #1197 fix imap-deletion of messages
|
||||
|
||||
Generated
+62
-49
@@ -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"
|
||||
@@ -620,11 +642,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-beta.23"
|
||||
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,8 +654,9 @@ 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 1.0.0-beta.22",
|
||||
"deltachat_derive 2.0.0",
|
||||
"email 0.0.21 (git+https://github.com/deltachat/rust-email)",
|
||||
"encoded-words 0.1.0 (git+https://github.com/async-email/encoded-words)",
|
||||
"escaper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -689,7 +712,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_derive"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -697,9 +720,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-beta.23"
|
||||
version = "1.0.0-beta.24"
|
||||
dependencies = [
|
||||
"deltachat 1.0.0-beta.23",
|
||||
"deltachat 1.0.0-beta.24",
|
||||
"deltachat-provider-database 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"human-panic 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -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"
|
||||
|
||||
+11
-12
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "deltachat"
|
||||
version = "1.0.0-beta.23"
|
||||
version = "1.0.0-beta.24"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
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"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_ffi"
|
||||
version = "1.0.0-beta.23"
|
||||
version = "1.0.0-beta.24"
|
||||
description = "Deltachat FFI"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
|
||||
+36
-308
@@ -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
|
||||
|
||||
+112
-262
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -836,7 +684,7 @@ pub unsafe extern "C" fn dc_set_draft(
|
||||
Some(&mut ffi_msg.message)
|
||||
};
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::set_draft(ctx, ChatId::new(chat_id), msg))
|
||||
.with_inner(|ctx| ChatId::new(chat_id).set_draft(ctx, msg))
|
||||
.unwrap_or(())
|
||||
}
|
||||
|
||||
@@ -911,7 +759,7 @@ pub unsafe extern "C" fn dc_get_draft(context: *mut dc_context_t, chat_id: u32)
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| match chat::get_draft(ctx, ChatId::new(chat_id)) {
|
||||
.with_inner(|ctx| match ChatId::new(chat_id).get_draft(ctx) {
|
||||
Ok(Some(draft)) => {
|
||||
let ffi_msg = MessageWrapper {
|
||||
context,
|
||||
@@ -966,7 +814,7 @@ pub unsafe extern "C" fn dc_get_msg_cnt(context: *mut dc_context_t, chat_id: u32
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::get_msg_cnt(ctx, ChatId::new(chat_id)) as libc::c_int)
|
||||
.with_inner(|ctx| ChatId::new(chat_id).get_msg_cnt(ctx) as libc::c_int)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -981,7 +829,7 @@ pub unsafe extern "C" fn dc_get_fresh_msg_cnt(
|
||||
}
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| chat::get_fresh_msg_cnt(ctx, ChatId::new(chat_id)) as libc::c_int)
|
||||
.with_inner(|ctx| ChatId::new(chat_id).get_fresh_msg_cnt(ctx) as libc::c_int)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
@@ -1145,7 +993,8 @@ pub unsafe extern "C" fn dc_archive_chat(
|
||||
};
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::archive(ctx, ChatId::new(chat_id), archive)
|
||||
ChatId::new(chat_id)
|
||||
.set_archived(ctx, archive)
|
||||
.log_err(ctx, "Failed archive chat")
|
||||
.unwrap_or(())
|
||||
})
|
||||
@@ -1161,7 +1010,8 @@ pub unsafe extern "C" fn dc_delete_chat(context: *mut dc_context_t, chat_id: u32
|
||||
let ffi_context = &*context;
|
||||
ffi_context
|
||||
.with_inner(|ctx| {
|
||||
chat::delete(ctx, ChatId::new(chat_id))
|
||||
ChatId::new(chat_id)
|
||||
.delete(ctx)
|
||||
.log_err(ctx, "Failed chat delete")
|
||||
.unwrap_or(())
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "deltachat_derive"
|
||||
version = "1.0.0-beta.22"
|
||||
version = "2.0.0"
|
||||
authors = ["Delta Chat Developers (ML) <delta@codespeak.net>"]
|
||||
edition = "2018"
|
||||
license = "MPL-2.0"
|
||||
|
||||
+11
-14
@@ -514,7 +514,7 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
chat_prefix(&chat),
|
||||
chat.get_id(),
|
||||
chat.get_name(),
|
||||
chat::get_fresh_msg_cnt(context, chat.get_id()),
|
||||
chat.get_id().get_fresh_msg_cnt(context),
|
||||
);
|
||||
let lot = chatlist.get_summary(context, i, Some(&chat));
|
||||
let statestr = if chat.is_archived() {
|
||||
@@ -598,14 +598,11 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
},
|
||||
);
|
||||
log_msglist(context, &msglist)?;
|
||||
if let Some(draft) = chat::get_draft(context, sel_chat.get_id())? {
|
||||
if let Some(draft) = sel_chat.get_id().get_draft(context)? {
|
||||
log_msg(context, "Draft", &draft);
|
||||
}
|
||||
|
||||
println!(
|
||||
"{} messages.",
|
||||
chat::get_msg_cnt(context, sel_chat.get_id())
|
||||
);
|
||||
println!("{} messages.", sel_chat.get_id().get_msg_cnt(context));
|
||||
chat::marknoticed_chat(context, sel_chat.get_id())?;
|
||||
}
|
||||
"createchat" => {
|
||||
@@ -801,14 +798,14 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
if !arg1.is_empty() {
|
||||
let mut draft = Message::new(Viewtype::Text);
|
||||
draft.set_text(Some(arg1.to_string()));
|
||||
chat::set_draft(
|
||||
context,
|
||||
sel_chat.as_ref().unwrap().get_id(),
|
||||
Some(&mut draft),
|
||||
);
|
||||
sel_chat
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_id()
|
||||
.set_draft(context, Some(&mut draft));
|
||||
println!("Draft saved.");
|
||||
} else {
|
||||
chat::set_draft(context, sel_chat.as_ref().unwrap().get_id(), None);
|
||||
sel_chat.as_ref().unwrap().get_id().set_draft(context, None);
|
||||
println!("Draft deleted.");
|
||||
}
|
||||
}
|
||||
@@ -847,12 +844,12 @@ pub fn dc_cmdline(context: &Context, line: &str) -> Result<(), failure::Error> {
|
||||
"archive" | "unarchive" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat::archive(context, chat_id, arg0 == "archive")?;
|
||||
chat_id.set_archived(context, arg0 == "archive")?;
|
||||
}
|
||||
"delchat" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <chat-id> missing.");
|
||||
let chat_id = ChatId::new(arg1.parse()?);
|
||||
chat::delete(context, chat_id)?;
|
||||
chat_id.delete(context)?;
|
||||
}
|
||||
"msginfo" => {
|
||||
ensure!(!arg1.is_empty(), "Argument <msg-id> missing.");
|
||||
|
||||
+22
-130
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+39
-70
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
+1
-1
@@ -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
@@ -1 +1 @@
|
||||
nightly-2019-11-06
|
||||
nightly-2020-01-26
|
||||
|
||||
+13
-7
@@ -322,13 +322,12 @@ impl<'a> BlobObject<'a> {
|
||||
|
||||
let clean = sanitize_filename::sanitize_with_options(name, opts);
|
||||
let mut iter = clean.splitn(2, '.');
|
||||
let mut stem = iter.next().unwrap_or_default().to_string();
|
||||
let mut ext = iter.next().unwrap_or_default().to_string();
|
||||
stem.truncate(64);
|
||||
ext.truncate(32);
|
||||
match ext.len() {
|
||||
0 => (stem, "".to_string()),
|
||||
_ => (stem, format!(".{}", ext).to_lowercase()),
|
||||
let stem: String = iter.next().unwrap_or_default().chars().take(64).collect();
|
||||
let ext: String = iter.next().unwrap_or_default().chars().take(32).collect();
|
||||
if ext.is_empty() {
|
||||
(stem, "".to_string())
|
||||
} else {
|
||||
(stem, format!(".{}", ext).to_lowercase())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -631,4 +630,11 @@ mod tests {
|
||||
assert!(!BlobObject::is_acceptible_blob_name("foo\\bar"));
|
||||
assert!(!BlobObject::is_acceptible_blob_name("foo\x00bar"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sanitise_name() {
|
||||
let (_, ext) =
|
||||
BlobObject::sanitise_name("Я ЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯЯ.txt");
|
||||
assert_eq!(ext, ".txt");
|
||||
}
|
||||
}
|
||||
|
||||
+273
-273
@@ -104,6 +104,256 @@ impl ChatId {
|
||||
self.0 == DC_CHAT_ID_ALLDONE_HINT
|
||||
}
|
||||
|
||||
pub fn set_selfavatar_timestamp(self, context: &Context, timestamp: i64) -> Result<(), Error> {
|
||||
context.sql.execute(
|
||||
"UPDATE contacts
|
||||
SET selfavatar_sent=?
|
||||
WHERE id IN(SELECT contact_id FROM chats_contacts WHERE chat_id=?);",
|
||||
params![timestamp, self],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_blocked(self, context: &Context, new_blocked: Blocked) -> bool {
|
||||
if self.is_special() {
|
||||
warn!(context, "ignoring setting of Block-status for {}", self);
|
||||
return false;
|
||||
}
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET blocked=? WHERE id=?;",
|
||||
params![new_blocked, self],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn unblock(self, context: &Context) {
|
||||
self.set_blocked(context, Blocked::Not);
|
||||
}
|
||||
|
||||
/// Archives or unarchives a chat.
|
||||
pub fn set_archived(self, context: &Context, new_archived: bool) -> Result<(), Error> {
|
||||
ensure!(
|
||||
!self.is_special(),
|
||||
"bad chat_id, can not be special chat: {}",
|
||||
self
|
||||
);
|
||||
|
||||
if new_archived {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=? WHERE chat_id=? AND state=?;",
|
||||
params![MessageState::InNoticed, self, MessageState::InFresh],
|
||||
)?;
|
||||
}
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET archived=? WHERE id=?;",
|
||||
params![new_archived, self],
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// note that unarchive() is not the same as set_archived(false) -
|
||||
// eg. unarchive() does not send events as done for set_archived(false).
|
||||
pub fn unarchive(self, context: &Context) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET archived=0 WHERE id=?",
|
||||
params![self],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deletes a chat.
|
||||
pub fn delete(self, context: &Context) -> Result<(), Error> {
|
||||
ensure!(
|
||||
!self.is_special(),
|
||||
"bad chat_id, can not be a special chat: {}",
|
||||
self
|
||||
);
|
||||
/* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */
|
||||
|
||||
let _chat = Chat::load_from_db(context, self)?;
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);",
|
||||
params![self],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs WHERE chat_id=?;",
|
||||
params![self],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats_contacts WHERE chat_id=?;",
|
||||
params![self],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats WHERE id=?;",
|
||||
params![self],
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
|
||||
job_kill_action(context, Action::Housekeeping);
|
||||
job_add(context, Action::Housekeeping, 0, Params::new(), 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets draft message.
|
||||
///
|
||||
/// Passing `None` as message just deletes the draft
|
||||
pub fn set_draft(self, context: &Context, msg: Option<&mut Message>) {
|
||||
if self.is_special() {
|
||||
return;
|
||||
}
|
||||
|
||||
let changed = match msg {
|
||||
None => self.maybe_delete_draft(context),
|
||||
Some(msg) => self.set_draft_raw(context, msg),
|
||||
};
|
||||
|
||||
if changed {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id: self,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// similar to as dc_set_draft() but does not emit an event
|
||||
fn set_draft_raw(self, context: &Context, msg: &mut Message) -> bool {
|
||||
let deleted = self.maybe_delete_draft(context);
|
||||
let set = self.do_set_draft(context, msg).is_ok();
|
||||
|
||||
// Can't inline. Both functions above must be called, no shortcut!
|
||||
deleted || set
|
||||
}
|
||||
|
||||
fn get_draft_msg_id(self, context: &Context) -> Option<MsgId> {
|
||||
context.sql.query_get_value::<_, MsgId>(
|
||||
context,
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND state=?;",
|
||||
params![self, MessageState::OutDraft],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_draft(self, context: &Context) -> Result<Option<Message>, Error> {
|
||||
if self.is_special() {
|
||||
return Ok(None);
|
||||
}
|
||||
match self.get_draft_msg_id(context) {
|
||||
Some(draft_msg_id) => Ok(Some(Message::load_from_db(context, draft_msg_id)?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete draft message in specified chat, if there is one.
|
||||
///
|
||||
/// Returns `true`, if message was deleted, `false` otherwise.
|
||||
fn maybe_delete_draft(self, context: &Context) -> bool {
|
||||
match self.get_draft_msg_id(context) {
|
||||
Some(msg_id) => {
|
||||
Message::delete_from_db(context, msg_id);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set provided message as draft message for specified chat.
|
||||
///
|
||||
/// Return true on success, false on database error.
|
||||
fn do_set_draft(self, context: &Context, msg: &mut Message) -> Result<(), Error> {
|
||||
match msg.viewtype {
|
||||
Viewtype::Unknown => bail!("Can not set draft of unknown type."),
|
||||
Viewtype::Text => match msg.text.as_ref() {
|
||||
Some(text) => {
|
||||
if text.is_empty() {
|
||||
bail!("No text in draft");
|
||||
}
|
||||
}
|
||||
None => bail!("No text in draft"),
|
||||
},
|
||||
_ => {
|
||||
let blob = msg
|
||||
.param
|
||||
.get_blob(Param::File, context, !msg.is_increation())?
|
||||
.ok_or_else(|| format_err!("No file stored in params"))?;
|
||||
msg.param.set(Param::File, blob.as_name());
|
||||
}
|
||||
}
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden)
|
||||
VALUES (?,?,?, ?,?,?,?,?);",
|
||||
params![
|
||||
self,
|
||||
DC_CONTACT_ID_SELF,
|
||||
time(),
|
||||
msg.viewtype,
|
||||
MessageState::OutDraft,
|
||||
msg.text.as_ref().map(String::as_str).unwrap_or(""),
|
||||
msg.param.to_string(),
|
||||
1,
|
||||
],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns number of messages in a chat.
|
||||
pub fn get_msg_cnt(self, context: &Context) -> usize {
|
||||
context
|
||||
.sql
|
||||
.query_get_value::<_, i32>(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM msgs WHERE chat_id=?;",
|
||||
params![self],
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
|
||||
pub fn get_fresh_msg_cnt(self, context: &Context) -> usize {
|
||||
context
|
||||
.sql
|
||||
.query_get_value::<_, i32>(
|
||||
context,
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs
|
||||
WHERE state=10
|
||||
AND hidden=0
|
||||
AND chat_id=?;",
|
||||
params![self],
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
|
||||
/// Bad evil escape hatch.
|
||||
///
|
||||
/// Avoid using this, eventually types should be cleaned up enough
|
||||
@@ -396,7 +646,7 @@ impl Chat {
|
||||
/// This is somewhat experimental, even more so than the rest of
|
||||
/// deltachat, and the data returned is still subject to change.
|
||||
pub fn get_info(&self, context: &Context) -> Result<ChatInfo, Error> {
|
||||
let draft = match get_draft(context, self.id)? {
|
||||
let draft = match self.id.get_draft(context)? {
|
||||
Some(message) => message.text.unwrap_or_else(String::new),
|
||||
_ => String::new(),
|
||||
};
|
||||
@@ -759,7 +1009,7 @@ pub fn create_by_msg_id(context: &Context, msg_id: MsgId) -> Result<ChatId, Erro
|
||||
"Message can not belong to a special chat"
|
||||
);
|
||||
if chat.blocked != Blocked::Not {
|
||||
unblock(context, chat.id);
|
||||
chat.id.unblock(context);
|
||||
|
||||
// Sending with 0s as data since multiple messages may have changed.
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
@@ -782,7 +1032,7 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<ChatId
|
||||
Ok((chat_id, chat_blocked)) => {
|
||||
if chat_blocked != Blocked::Not {
|
||||
// unblock chat (typically move it from the deaddrop to view
|
||||
unblock(context, chat_id);
|
||||
chat_id.unblock(context);
|
||||
}
|
||||
chat_id
|
||||
}
|
||||
@@ -811,24 +1061,6 @@ pub fn create_by_contact_id(context: &Context, contact_id: u32) -> Result<ChatId
|
||||
Ok(chat_id)
|
||||
}
|
||||
|
||||
pub fn unblock(context: &Context, chat_id: ChatId) {
|
||||
set_blocking(context, chat_id, Blocked::Not);
|
||||
}
|
||||
|
||||
pub fn set_blocking(context: &Context, chat_id: ChatId, new_blocking: Blocked) -> bool {
|
||||
if chat_id.is_special() {
|
||||
warn!(context, "ignoring setting of Block-status for {}", chat_id);
|
||||
return false;
|
||||
}
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET blocked=? WHERE id=?;",
|
||||
params![new_blocking, chat_id],
|
||||
)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
pub fn update_saved_messages_icon(context: &Context) -> Result<(), Error> {
|
||||
// if there is no saved-messages chat, there is nothing to update. this is no error.
|
||||
if let Ok((chat_id, _)) = lookup_by_contact_id(context, DC_CONTACT_ID_SELF) {
|
||||
@@ -1053,7 +1285,7 @@ fn prepare_msg_common(
|
||||
) -> Result<MsgId, Error> {
|
||||
msg.id = MsgId::new_unset();
|
||||
prepare_msg_blob(context, msg)?;
|
||||
unarchive(context, chat_id)?;
|
||||
chat_id.unarchive(context)?;
|
||||
|
||||
let mut chat = Chat::load_from_db(context, chat_id)?;
|
||||
ensure!(chat.can_send(), "cannot send to {}", chat_id);
|
||||
@@ -1086,18 +1318,6 @@ pub fn is_contact_in_chat(context: &Context, chat_id: ChatId, contact_id: u32) -
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// note that unarchive() is not the same as archive(false) -
|
||||
// eg. unarchive() does not send events as done for archive(false).
|
||||
pub fn unarchive(context: &Context, chat_id: ChatId) -> Result<(), Error> {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET archived=0 WHERE id=?",
|
||||
params![chat_id],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send a message defined by a dc_msg_t object to a chat.
|
||||
///
|
||||
/// Sends the event #DC_EVENT_MSGS_CHANGED on succcess.
|
||||
@@ -1173,106 +1393,6 @@ pub fn send_text_msg(
|
||||
send_msg(context, chat_id, &mut msg)
|
||||
}
|
||||
|
||||
// passing `None` as message jsut deletes the draft
|
||||
pub fn set_draft(context: &Context, chat_id: ChatId, msg: Option<&mut Message>) {
|
||||
if chat_id.is_special() {
|
||||
return;
|
||||
}
|
||||
|
||||
let changed = match msg {
|
||||
None => maybe_delete_draft(context, chat_id),
|
||||
Some(msg) => set_draft_raw(context, chat_id, msg),
|
||||
};
|
||||
|
||||
if changed {
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
chat_id,
|
||||
msg_id: MsgId::new(0),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete draft message in specified chat, if there is one.
|
||||
///
|
||||
/// Return {true}, if message was deleted, {false} otherwise.
|
||||
fn maybe_delete_draft(context: &Context, chat_id: ChatId) -> bool {
|
||||
match get_draft_msg_id(context, chat_id) {
|
||||
Some(msg_id) => {
|
||||
Message::delete_from_db(context, msg_id);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set provided message as draft message for specified chat.
|
||||
///
|
||||
/// Return true on success, false on database error.
|
||||
fn do_set_draft(context: &Context, chat_id: ChatId, msg: &mut Message) -> Result<(), Error> {
|
||||
match msg.viewtype {
|
||||
Viewtype::Unknown => bail!("Can not set draft of unknown type."),
|
||||
Viewtype::Text => match msg.text.as_ref() {
|
||||
Some(text) => {
|
||||
if text.is_empty() {
|
||||
bail!("No text in draft");
|
||||
}
|
||||
}
|
||||
None => bail!("No text in draft"),
|
||||
},
|
||||
_ => {
|
||||
let blob = msg
|
||||
.param
|
||||
.get_blob(Param::File, context, !msg.is_increation())?
|
||||
.ok_or_else(|| format_err!("No file stored in params"))?;
|
||||
msg.param.set(Param::File, blob.as_name());
|
||||
}
|
||||
}
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"INSERT INTO msgs (chat_id, from_id, timestamp, type, state, txt, param, hidden)
|
||||
VALUES (?,?,?, ?,?,?,?,?);",
|
||||
params![
|
||||
chat_id,
|
||||
DC_CONTACT_ID_SELF,
|
||||
time(),
|
||||
msg.viewtype,
|
||||
MessageState::OutDraft,
|
||||
msg.text.as_ref().map(String::as_str).unwrap_or(""),
|
||||
msg.param.to_string(),
|
||||
1,
|
||||
],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// similar to as dc_set_draft() but does not emit an event
|
||||
fn set_draft_raw(context: &Context, chat_id: ChatId, msg: &mut Message) -> bool {
|
||||
let deleted = maybe_delete_draft(context, chat_id);
|
||||
let set = do_set_draft(context, chat_id, msg).is_ok();
|
||||
|
||||
// Can't inline. Both functions above must be called, no shortcut!
|
||||
deleted || set
|
||||
}
|
||||
|
||||
fn get_draft_msg_id(context: &Context, chat_id: ChatId) -> Option<MsgId> {
|
||||
context.sql.query_get_value::<_, MsgId>(
|
||||
context,
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND state=?;",
|
||||
params![chat_id, MessageState::OutDraft],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_draft(context: &Context, chat_id: ChatId) -> Result<Option<Message>, Error> {
|
||||
if chat_id.is_special() {
|
||||
return Ok(None);
|
||||
}
|
||||
match get_draft_msg_id(context, chat_id) {
|
||||
Some(draft_msg_id) => Ok(Some(Message::load_from_db(context, draft_msg_id)?)),
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_chat_msgs(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
@@ -1360,33 +1480,6 @@ pub fn get_chat_msgs(
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns number of messages in a chat.
|
||||
pub fn get_msg_cnt(context: &Context, chat_id: ChatId) -> usize {
|
||||
context
|
||||
.sql
|
||||
.query_get_value::<_, i32>(
|
||||
context,
|
||||
"SELECT COUNT(*) FROM msgs WHERE chat_id=?;",
|
||||
params![chat_id],
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
|
||||
pub fn get_fresh_msg_cnt(context: &Context, chat_id: ChatId) -> usize {
|
||||
context
|
||||
.sql
|
||||
.query_get_value::<_, i32>(
|
||||
context,
|
||||
"SELECT COUNT(*)
|
||||
FROM msgs
|
||||
WHERE state=10
|
||||
AND hidden=0
|
||||
AND chat_id=?;",
|
||||
params![chat_id],
|
||||
)
|
||||
.unwrap_or_default() as usize
|
||||
}
|
||||
|
||||
pub fn marknoticed_chat(context: &Context, chat_id: ChatId) -> Result<(), Error> {
|
||||
if !context.sql.exists(
|
||||
"SELECT id FROM msgs WHERE chat_id=? AND state=?;",
|
||||
@@ -1535,87 +1628,6 @@ pub fn get_next_media(
|
||||
ret
|
||||
}
|
||||
|
||||
/// Archives or unarchives a chat.
|
||||
pub fn archive(context: &Context, chat_id: ChatId, archive: bool) -> Result<(), Error> {
|
||||
ensure!(
|
||||
!chat_id.is_special(),
|
||||
"bad chat_id, can not be special chat: {}",
|
||||
chat_id
|
||||
);
|
||||
|
||||
if archive {
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE msgs SET state=? WHERE chat_id=? AND state=?;",
|
||||
params![MessageState::InNoticed, chat_id, MessageState::InFresh],
|
||||
)?;
|
||||
}
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"UPDATE chats SET archived=? WHERE id=?;",
|
||||
params![archive, chat_id],
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Deletes a chat.
|
||||
pub fn delete(context: &Context, chat_id: ChatId) -> Result<(), Error> {
|
||||
ensure!(
|
||||
!chat_id.is_special(),
|
||||
"bad chat_id, can not be a special chat: {}",
|
||||
chat_id
|
||||
);
|
||||
/* Up to 2017-11-02 deleting a group also implied leaving it, see above why we have changed this. */
|
||||
|
||||
let _chat = Chat::load_from_db(context, chat_id)?;
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs_mdns WHERE msg_id IN (SELECT id FROM msgs WHERE chat_id=?);",
|
||||
params![chat_id],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM msgs WHERE chat_id=?;",
|
||||
params![chat_id],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats_contacts WHERE chat_id=?;",
|
||||
params![chat_id],
|
||||
)?;
|
||||
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
"DELETE FROM chats WHERE id=?;",
|
||||
params![chat_id],
|
||||
)?;
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
msg_id: MsgId::new(0),
|
||||
chat_id: ChatId::new(0),
|
||||
});
|
||||
|
||||
job_kill_action(context, Action::Housekeeping);
|
||||
job_add(context, Action::Housekeeping, 0, Params::new(), 10);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_chat_contacts(context: &Context, chat_id: ChatId) -> Vec<u32> {
|
||||
/* Normal chats do not include SELF. Group chats do (as it may happen that one is deleted from a
|
||||
groupchat but the chats stays visible, moreover, this makes displaying lists easier) */
|
||||
@@ -1675,7 +1687,7 @@ pub fn create_group_chat(
|
||||
if add_to_chat_contacts_table(context, chat_id, DC_CONTACT_ID_SELF) {
|
||||
let mut draft_msg = Message::new(Viewtype::Text);
|
||||
draft_msg.set_text(Some(draft_txt));
|
||||
set_draft_raw(context, chat_id, &mut draft_msg);
|
||||
chat_id.set_draft_raw(context, &mut draft_msg);
|
||||
}
|
||||
|
||||
context.call_cb(Event::MsgsChanged {
|
||||
@@ -1893,20 +1905,6 @@ pub fn shall_attach_selfavatar(context: &Context, chat_id: ChatId) -> Result<boo
|
||||
Ok(needs_attach)
|
||||
}
|
||||
|
||||
pub fn set_selfavatar_timestamp(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
timestamp: i64,
|
||||
) -> Result<(), Error> {
|
||||
context.sql.execute(
|
||||
"UPDATE contacts
|
||||
SET selfavatar_sent=?
|
||||
WHERE id IN(SELECT contact_id FROM chats_contacts WHERE chat_id=?);",
|
||||
params![timestamp, chat_id],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_contact_from_chat(
|
||||
context: &Context,
|
||||
chat_id: ChatId,
|
||||
@@ -2152,7 +2150,7 @@ pub fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId) -> Re
|
||||
let mut created_msgs: Vec<MsgId> = Vec::new();
|
||||
let mut curr_timestamp: i64;
|
||||
|
||||
unarchive(context, chat_id)?;
|
||||
chat_id.unarchive(context)?;
|
||||
if let Ok(mut chat) = Chat::load_from_db(context, chat_id) {
|
||||
ensure!(chat.can_send(), "cannot send to {}", chat_id);
|
||||
curr_timestamp = dc_create_smeared_timestamps(context, msg_ids.len());
|
||||
@@ -2295,7 +2293,7 @@ pub fn add_device_msg(
|
||||
let rfc724_mid = dc_create_outgoing_rfc724_mid(None, "@device");
|
||||
msg.try_calc_and_set_dimensions(context).ok();
|
||||
prepare_msg_blob(context, msg)?;
|
||||
unarchive(context, chat_id)?;
|
||||
chat_id.unarchive(context)?;
|
||||
|
||||
context.sql.execute(
|
||||
"INSERT INTO msgs (chat_id,from_id,to_id, timestamp,type,state, txt,param,rfc724_mid) \
|
||||
@@ -2432,14 +2430,16 @@ mod tests {
|
||||
fn test_get_draft_no_draft() {
|
||||
let t = dummy_context();
|
||||
let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
|
||||
let draft = get_draft(&t.ctx, chat_id).unwrap();
|
||||
let draft = chat_id.get_draft(&t.ctx).unwrap();
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_draft_special_chat_id() {
|
||||
let t = dummy_context();
|
||||
let draft = get_draft(&t.ctx, ChatId::new(DC_CHAT_ID_LAST_SPECIAL)).unwrap();
|
||||
let draft = ChatId::new(DC_CHAT_ID_LAST_SPECIAL)
|
||||
.get_draft(&t.ctx)
|
||||
.unwrap();
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
@@ -2448,7 +2448,7 @@ mod tests {
|
||||
// This is a weird case, maybe this should be an error but we
|
||||
// do not get this info from the database currently.
|
||||
let t = dummy_context();
|
||||
let draft = get_draft(&t.ctx, ChatId::new(42)).unwrap();
|
||||
let draft = ChatId::new(42).get_draft(&t.ctx).unwrap();
|
||||
assert!(draft.is_none());
|
||||
}
|
||||
|
||||
@@ -2458,8 +2458,8 @@ mod tests {
|
||||
let chat_id = create_by_contact_id(&t.ctx, DC_CONTACT_ID_SELF).unwrap();
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.set_text(Some("hello".to_string()));
|
||||
set_draft(&t.ctx, chat_id, Some(&mut msg));
|
||||
let draft = get_draft(&t.ctx, chat_id).unwrap().unwrap();
|
||||
chat_id.set_draft(&t.ctx, Some(&mut msg));
|
||||
let draft = chat_id.get_draft(&t.ctx).unwrap().unwrap();
|
||||
let msg_text = msg.get_text();
|
||||
let draft_text = draft.get_text();
|
||||
assert_eq!(msg_text, draft_text);
|
||||
@@ -2535,7 +2535,7 @@ mod tests {
|
||||
assert_eq!(msg2.text.as_ref().unwrap(), "second message");
|
||||
|
||||
// check device chat
|
||||
assert_eq!(get_msg_cnt(&t.ctx, msg2.chat_id), 2);
|
||||
assert_eq!(msg2.chat_id.get_msg_cnt(&t.ctx), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -2568,7 +2568,7 @@ mod tests {
|
||||
|
||||
// check device chat
|
||||
let chat_id = msg1.chat_id;
|
||||
assert_eq!(get_msg_cnt(&t.ctx, chat_id), 1);
|
||||
assert_eq!(chat_id.get_msg_cnt(&t.ctx), 1);
|
||||
assert!(!chat_id.is_special());
|
||||
let chat = Chat::load_from_db(&t.ctx, chat_id);
|
||||
assert!(chat.is_ok());
|
||||
@@ -2636,7 +2636,7 @@ mod tests {
|
||||
assert_eq!(chats.len(), 1);
|
||||
|
||||
// after the device-chat and all messages are deleted, a re-adding should do nothing
|
||||
delete(&t.ctx, chats.get_chat_id(0)).ok();
|
||||
chats.get_chat_id(0).delete(&t.ctx).ok();
|
||||
add_device_msg(&t.ctx, Some("some-label"), Some(&mut msg)).ok();
|
||||
assert_eq!(chatlist_len(&t.ctx, 0), 0)
|
||||
}
|
||||
@@ -2703,7 +2703,7 @@ mod tests {
|
||||
assert_eq!(DC_GCL_NO_SPECIALS, 0x02);
|
||||
|
||||
// archive first chat
|
||||
assert!(archive(&t.ctx, chat_id1, true).is_ok());
|
||||
assert!(chat_id1.set_archived(&t.ctx, true).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(!Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
@@ -2712,7 +2712,7 @@ mod tests {
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 1);
|
||||
|
||||
// archive second chat
|
||||
assert!(archive(&t.ctx, chat_id2, true).is_ok());
|
||||
assert!(chat_id2.set_archived(&t.ctx, true).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
@@ -2721,9 +2721,9 @@ mod tests {
|
||||
assert_eq!(chatlist_len(&t.ctx, DC_GCL_ARCHIVED_ONLY), 2);
|
||||
|
||||
// archive already archived first chat, unarchive second chat two times
|
||||
assert!(archive(&t.ctx, chat_id1, true).is_ok());
|
||||
assert!(archive(&t.ctx, chat_id2, false).is_ok());
|
||||
assert!(archive(&t.ctx, chat_id2, false).is_ok());
|
||||
assert!(chat_id1.set_archived(&t.ctx, true).is_ok());
|
||||
assert!(chat_id2.set_archived(&t.ctx, false).is_ok());
|
||||
assert!(chat_id2.set_archived(&t.ctx, false).is_ok());
|
||||
assert!(Chat::load_from_db(&t.ctx, chat_id1).unwrap().is_archived());
|
||||
assert!(!Chat::load_from_db(&t.ctx, chat_id2).unwrap().is_archived());
|
||||
assert_eq!(get_chat_cnt(&t.ctx), 2);
|
||||
@@ -2778,7 +2778,7 @@ mod tests {
|
||||
t.ctx.set_config(Config::Selfavatar, None).unwrap(); // setting to None also forces re-sending
|
||||
assert!(shall_attach_selfavatar(&t.ctx, chat_id).unwrap());
|
||||
|
||||
assert!(set_selfavatar_timestamp(&t.ctx, chat_id, time()).is_ok());
|
||||
assert!(chat_id.set_selfavatar_timestamp(&t.ctx, time()).is_ok());
|
||||
assert!(!shall_attach_selfavatar(&t.ctx, chat_id).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -358,7 +358,6 @@ fn get_last_deaddrop_fresh_msg(context: &Context) -> Option<MsgId> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::chat;
|
||||
use crate::test_utils::*;
|
||||
|
||||
#[test]
|
||||
@@ -378,7 +377,7 @@ mod tests {
|
||||
// drafts are sorted to the top
|
||||
let mut msg = Message::new(Viewtype::Text);
|
||||
msg.set_text(Some("hello".to_string()));
|
||||
set_draft(&t.ctx, chat_id2, Some(&mut msg));
|
||||
chat_id2.set_draft(&t.ctx, Some(&mut msg));
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.get_chat_id(0), chat_id2);
|
||||
|
||||
@@ -389,7 +388,7 @@ mod tests {
|
||||
let chats = Chatlist::try_load(&t.ctx, DC_GCL_ARCHIVED_ONLY, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
|
||||
chat::archive(&t.ctx, chat_id1, true).ok();
|
||||
chat_id1.set_archived(&t.ctx, true).ok();
|
||||
let chats = Chatlist::try_load(&t.ctx, DC_GCL_ARCHIVED_ONLY, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 1);
|
||||
}
|
||||
|
||||
+2
-1
@@ -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),
|
||||
};
|
||||
|
||||
+2
-21
@@ -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
|
||||
|
||||
+138
-23
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -390,7 +390,7 @@ fn add_parts(
|
||||
&& chat_id_blocked != Blocked::Not
|
||||
&& create_blocked == Blocked::Not
|
||||
{
|
||||
chat::unblock(context, new_chat_id);
|
||||
new_chat_id.unblock(context);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -423,7 +423,7 @@ fn add_parts(
|
||||
}
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
if Blocked::Not == create_blocked {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id.unblock(context);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
} else if is_reply_to_known_message(context, mime_parser) {
|
||||
// we do not want any chat to be created implicitly. Because of the origin-scale-up,
|
||||
@@ -476,7 +476,7 @@ fn add_parts(
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
// automatically unblock chat when the user sends a message
|
||||
if !chat_id.is_unset() && chat_id_blocked != Blocked::Not {
|
||||
chat::unblock(context, new_chat_id);
|
||||
new_chat_id.unblock(context);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -497,7 +497,7 @@ fn add_parts(
|
||||
&& Blocked::Not != chat_id_blocked
|
||||
&& Blocked::Not == create_blocked
|
||||
{
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id.unblock(context);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -516,7 +516,7 @@ fn add_parts(
|
||||
chat_id_blocked = bl;
|
||||
|
||||
if !chat_id.is_unset() && Blocked::Not != chat_id_blocked {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id.unblock(context);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -538,7 +538,7 @@ fn add_parts(
|
||||
);
|
||||
|
||||
// unarchive chat
|
||||
chat::unarchive(context, *chat_id)?;
|
||||
chat_id.unarchive(context)?;
|
||||
|
||||
// if the mime-headers should be saved, find out its size
|
||||
// (the mime-header ends with an empty line)
|
||||
|
||||
+12
-68
@@ -222,43 +222,6 @@ pub(crate) fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
|
||||
None
|
||||
}
|
||||
|
||||
// Function returns a sanitized basename that does not contain
|
||||
// win/linux path separators and also not any non-ascii chars
|
||||
fn get_safe_basename(filename: &str) -> String {
|
||||
// return the (potentially mangled) basename of the input filename
|
||||
// this might be a path that comes in from another operating system
|
||||
let mut index: usize = 0;
|
||||
|
||||
if let Some(unix_index) = filename.rfind('/') {
|
||||
index = unix_index + 1;
|
||||
}
|
||||
if let Some(win_index) = filename.rfind('\\') {
|
||||
index = max(index, win_index + 1);
|
||||
}
|
||||
if index >= filename.len() {
|
||||
"nobasename".to_string()
|
||||
} else {
|
||||
// we don't allow any non-ascii to be super-safe
|
||||
filename[index..].replace(|c: char| !c.is_ascii() || c == ':', "-")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dc_derive_safe_stem_ext(filename: &str) -> (String, String) {
|
||||
let basename = get_safe_basename(&filename);
|
||||
let (mut stem, mut ext) = if let Some(index) = basename.rfind('.') {
|
||||
(
|
||||
basename[0..index].to_string(),
|
||||
basename[index..].to_string(),
|
||||
)
|
||||
} else {
|
||||
(basename, "".to_string())
|
||||
};
|
||||
// limit length of stem and ext
|
||||
stem.truncate(32);
|
||||
ext.truncate(32);
|
||||
(stem, ext)
|
||||
}
|
||||
|
||||
// the returned suffix is lower-case
|
||||
pub fn dc_get_filesuffix_lc(path_filename: impl AsRef<str>) -> Option<String> {
|
||||
Path::new(path_filename.as_ref())
|
||||
@@ -497,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),
|
||||
/// };
|
||||
@@ -511,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)
|
||||
@@ -707,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::*;
|
||||
@@ -765,19 +722,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_get_safe_basename() {
|
||||
assert_eq!(get_safe_basename("12312/hello"), "hello");
|
||||
assert_eq!(get_safe_basename("12312\\hello"), "hello");
|
||||
assert_eq!(get_safe_basename("//12312\\hello"), "hello");
|
||||
assert_eq!(get_safe_basename("//123:12\\hello"), "hello");
|
||||
assert_eq!(get_safe_basename("//123:12/\\\\hello"), "hello");
|
||||
assert_eq!(get_safe_basename("//123:12//hello"), "hello");
|
||||
assert_eq!(get_safe_basename("//123:12//"), "nobasename");
|
||||
assert_eq!(get_safe_basename("//123:12/"), "nobasename");
|
||||
assert!(get_safe_basename("123\x012.hello").ends_with(".hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_handling() {
|
||||
let t = dummy_context();
|
||||
|
||||
+12
-6
@@ -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
|
||||
|
||||
+4
-7
@@ -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))
|
||||
}
|
||||
|
||||
+1
-2
@@ -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);
|
||||
|
||||
+14
-22
@@ -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();
|
||||
@@ -849,7 +849,7 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<()> {
|
||||
}
|
||||
|
||||
if attach_selfavatar {
|
||||
if let Err(err) = chat::set_selfavatar_timestamp(context, msg.chat_id, time()) {
|
||||
if let Err(err) = msg.chat_id.set_selfavatar_timestamp(context, time()) {
|
||||
error!(context, "Failed to set selfavatar timestamp: {:?}", err);
|
||||
}
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
|
||||
+3
-6
@@ -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)]
|
||||
|
||||
+3
-5
@@ -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
|
||||
|
||||
+34
-24
@@ -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 },
|
||||
@@ -595,6 +602,27 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
Some(part)
|
||||
}
|
||||
|
||||
fn get_location_kml_part(&mut self) -> Result<PartBuilder, Error> {
|
||||
let (kml_content, last_added_location_id) =
|
||||
location::get_kml(self.context, self.msg.chat_id)?;
|
||||
let part = PartBuilder::new()
|
||||
.content_type(
|
||||
&"application/vnd.google-earth.kml+xml"
|
||||
.parse::<mime::Mime>()
|
||||
.unwrap(),
|
||||
)
|
||||
.header((
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"location.kml\"",
|
||||
))
|
||||
.body(kml_content);
|
||||
if !self.msg.param.exists(Param::SetLatitude) {
|
||||
// otherwise, the independent location is already filed
|
||||
self.last_added_location_id = last_added_location_id;
|
||||
}
|
||||
Ok(part)
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn render_message(
|
||||
&mut self,
|
||||
@@ -825,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, "")?;
|
||||
@@ -842,28 +870,10 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
}
|
||||
|
||||
if location::is_sending_locations_to_chat(context, self.msg.chat_id) {
|
||||
match location::get_kml(context, self.msg.chat_id) {
|
||||
Ok((kml_content, last_added_location_id)) => {
|
||||
parts.push(
|
||||
PartBuilder::new()
|
||||
.content_type(
|
||||
&"application/vnd.google-earth.kml+xml"
|
||||
.parse::<mime::Mime>()
|
||||
.unwrap(),
|
||||
)
|
||||
.header((
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"location.kml\"",
|
||||
))
|
||||
.body(kml_content),
|
||||
);
|
||||
if !self.msg.param.exists(Param::SetLatitude) {
|
||||
// otherwise, the independent location is already filed
|
||||
self.last_added_location_id = last_added_location_id;
|
||||
}
|
||||
}
|
||||
match self.get_location_kml_part() {
|
||||
Ok(part) => parts.push(part),
|
||||
Err(err) => {
|
||||
warn!(context, "mimefactory: could not get location: {}", err);
|
||||
warn!(context, "mimefactory: could not send location: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1101,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,
|
||||
}
|
||||
@@ -1110,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)
|
||||
|
||||
+88
-64
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-2
@@ -311,9 +311,15 @@ impl Oauth2 {
|
||||
return None;
|
||||
}
|
||||
if let Ok(response) = parsed {
|
||||
// serde_json::Value.as_str() removes the quotes of json-strings
|
||||
// CAVE: serde_json::Value.as_str() removes the quotes of json-strings
|
||||
// but serde_json::Value.to_string() does not!
|
||||
if let Some(addr) = response.get("email") {
|
||||
Some(addr.to_string())
|
||||
if let Some(s) = addr.as_str() {
|
||||
Some(s.to_string())
|
||||
} else {
|
||||
warn!(context, "E-mail in userinfo is not a string: {}", addr);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
warn!(context, "E-mail missing in userinfo.");
|
||||
None
|
||||
|
||||
+30
-12
@@ -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(
|
||||
@@ -402,7 +411,7 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
match chat::create_or_lookup_by_contact_id(context, contact_id, Blocked::Not) {
|
||||
Ok((chat_id, blocked)) => {
|
||||
if blocked != Blocked::Not {
|
||||
chat::unblock(context, chat_id);
|
||||
chat_id.unblock(context);
|
||||
}
|
||||
chat_id
|
||||
}
|
||||
@@ -623,22 +632,27 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
==== Bob - the joiner's side ====
|
||||
==== Step 7 in "Setup verified contact" protocol ====
|
||||
=======================================================*/
|
||||
let abort_retval = if join_vg {
|
||||
HandshakeMessage::Propagate
|
||||
} else {
|
||||
HandshakeMessage::Ignore
|
||||
};
|
||||
|
||||
if context.bob.read().unwrap().expects != DC_VC_CONTACT_CONFIRM {
|
||||
info!(context, "Message belongs to a different handshake.",);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
return Ok(abort_retval);
|
||||
}
|
||||
let cond = {
|
||||
let bob = context.bob.read().unwrap();
|
||||
let scan = bob.qr_scan.as_ref();
|
||||
scan.is_none() || join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup
|
||||
scan.is_none() || (join_vg && scan.unwrap().state != LotState::QrAskVerifyGroup)
|
||||
};
|
||||
if cond {
|
||||
warn!(
|
||||
context,
|
||||
"Message out of sync or belongs to a different handshake.",
|
||||
);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
return Ok(abort_retval);
|
||||
}
|
||||
let scanned_fingerprint_of_alice = get_qr_attr!(context, fingerprint).to_string();
|
||||
|
||||
@@ -669,7 +683,7 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
"Contact confirm message not encrypted.",
|
||||
);
|
||||
context.bob.write().unwrap().status = 0;
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
return Ok(abort_retval);
|
||||
}
|
||||
|
||||
if mark_peer_as_verified(context, &scanned_fingerprint_of_alice).is_err() {
|
||||
@@ -678,7 +692,7 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
contact_chat_id,
|
||||
"Fingerprint mismatch on joiner-side.",
|
||||
);
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
return Ok(abort_retval);
|
||||
}
|
||||
Contact::scaleup_origin_by_id(context, contact_id, Origin::SecurejoinJoined);
|
||||
emit_event!(context, Event::ContactsChanged(None));
|
||||
@@ -692,7 +706,7 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
.map_err(|_| HandshakeError::NoSelfAddr)?
|
||||
{
|
||||
info!(context, "Message belongs to a different handshake (scaled up contact anyway to allow creation of group).");
|
||||
return Ok(HandshakeMessage::Propagate);
|
||||
return Ok(abort_retval);
|
||||
}
|
||||
secure_connection_established(context, contact_chat_id);
|
||||
context.bob.write().unwrap().expects = 0;
|
||||
@@ -709,7 +723,11 @@ pub(crate) fn handle_securejoin_handshake(
|
||||
}
|
||||
context.bob.write().unwrap().status = 1;
|
||||
context.stop_ongoing();
|
||||
Ok(HandshakeMessage::Propagate)
|
||||
Ok(if join_vg {
|
||||
HandshakeMessage::Propagate
|
||||
} else {
|
||||
HandshakeMessage::Done
|
||||
})
|
||||
}
|
||||
"vg-member-added-received" => {
|
||||
/*==========================================================
|
||||
|
||||
+4
-4
@@ -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
-2
@@ -547,8 +547,8 @@ mod tests {
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 2);
|
||||
|
||||
chat::delete(&t.ctx, chats.get_chat_id(0)).ok();
|
||||
chat::delete(&t.ctx, chats.get_chat_id(1)).ok();
|
||||
chats.get_chat_id(0).delete(&t.ctx).ok();
|
||||
chats.get_chat_id(1).delete(&t.ctx).ok();
|
||||
let chats = Chatlist::try_load(&t.ctx, 0, None, None).unwrap();
|
||||
assert_eq!(chats.len(), 0);
|
||||
|
||||
|
||||
+10
-2
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
+9
-2
@@ -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